WarePluginsManager.hpp
Go to the documentation of this file.
1 /*
2 
3  This file is part of OpenFLUID software
4  Copyright(c) 2007, INRA - Montpellier SupAgro
5 
6 
7  == GNU General Public License Usage ==
8 
9  OpenFLUID is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation, either version 3 of the License, or
12  (at your option) any later version.
13 
14  OpenFLUID is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with OpenFLUID. If not, see <http://www.gnu.org/licenses/>.
21 
22 
23  == Other Usage ==
24 
25  Other Usage means a use of OpenFLUID that is inconsistent with the GPL
26  license, and requires a written agreement between You and INRA.
27  Licensees for Other Usage of OpenFLUID may use this file in accordance
28  with the terms contained in the written agreement between You and INRA.
29 
30 */
31 
32 
33 /**
34  @file WarePluginsManager.hpp
35 
36  @author Jean-Christophe FABRE <jean-christophe.fabre@inra.fr>
37  */
38 
39 
40 #ifndef __OPENFLUID_MACHINE_WAREPLUGINSMANAGER_HPP__
41 #define __OPENFLUID_MACHINE_WAREPLUGINSMANAGER_HPP__
42 
43 
44 #include <vector>
45 #include <map>
46 #include <set>
47 #include <string>
48 #include <memory>
49 
50 #include <QLibrary>
51 #include <QFileInfo>
52 
56 #include <openfluid/dllexport.hpp>
57 #include <openfluid/config.hpp>
58 
59 
60 namespace openfluid { namespace machine {
61 
62 
63 typedef std::string (*GetWareABIVersionProc)();
64 
65 typedef std::string (*GetWareLinkUIDProc)();
66 
67 
68 // =====================================================================
69 // =====================================================================
70 
71 
72 /**
73  Management class for pluggable wares
74  @tparam SignatureType class defining the container for ware signature only
75  @tparam ItemType class defining the container for ware signature and body
76  @tparam SignatureProc procedure definition for instantiation of the signature
77  @tparam BodyProc procedure definition for instantiation of the body
78 */
79 template<class SignatureType, class ItemType, typename SignatureProc, typename BodyProc>
81 {
82  private:
83 
84  /**
85  Loads the plugin of a ware from the given file path.
86  The plugin is automatically cached to avoid multiple loadings.
87  @param[in] FullFilePath The path to the plugin file to load
88  @return a QLibrary pointer to the loaded or cached ware
89  */
90  QLibrary* loadPluginLibrary(const std::string& FullFilePath)
91  {
92  std::string PluginFileName = QFileInfo(QString::fromStdString(FullFilePath)).fileName().toStdString();
93 
94  if (m_LoadedPluginsLibraries.find(PluginFileName) == m_LoadedPluginsLibraries.end())
95  {
96  m_LoadedPluginsLibraries[PluginFileName] = std::make_unique<QLibrary>(QString::fromStdString(FullFilePath));
97  }
98 
99  return m_LoadedPluginsLibraries[PluginFileName].get();
100  }
101 
102 
103  // =====================================================================
104  // =====================================================================
105 
106 
107  ItemType* buildWareContainerWithSignatureOnly(const std::string& ID)
108  {
109  std::string PluginFilename = ID+getPluginFilenameSuffix()+openfluid::config::PLUGINS_EXT;
110  std::string PluginFullPath = getPluginFullPath(PluginFilename);
111  ItemType* WareItem = nullptr;
112 
113  QLibrary* PlugLib = loadPluginLibrary(PluginFullPath);
114 
115  // library loading
116  if (PlugLib && PlugLib->load())
117  {
118  WareItem = new ItemType();
119  WareItem->FileFullPath = PluginFullPath;
120 
121  GetWareABIVersionProc ABIVersionProc = (GetWareABIVersionProc)PlugLib->resolve(WAREABIVERSION_PROC_NAME);
122 
123  if (ABIVersionProc)
124  {
125  WareItem->Verified =
126  (openfluid::tools::compareVersions(openfluid::config::VERSION_FULL,ABIVersionProc(),false) == 0);
127  }
128  else
129  {
130  WareItem->Verified = false;
131  }
132 
133 
134  if (WareItem->Verified)
135  {
136  BodyProc BProc = (BodyProc)PlugLib->resolve(WAREBODY_PROC_NAME);
137  SignatureProc SProc = (SignatureProc)PlugLib->resolve(WARESIGNATURE_PROC_NAME);
138 
139  // checks if the handle procs exist
140  if (SProc && BProc)
141  {
142  WareItem->Signature = SProc();
143 
144  if (WareItem->Signature == nullptr)
145  {
146  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
147  "Signature from plugin file " + PluginFilename +
148  " cannot be instanciated");
149  }
150 
151  WareItem->Verified = (WareItem->Signature->ID == ID);
152 
153  WareItem->Body = 0;
154 
155  GetWareLinkUIDProc LinkUIDProc = (GetWareLinkUIDProc)PlugLib->resolve(WARELINKUID_PROC_NAME);
156 
157  if (LinkUIDProc)
158  {
159  WareItem->LinkUID = LinkUIDProc();
160  }
161  }
162  else
163  {
164  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
165  "Format error in plugin file " + PluginFilename);
166  }
167  }
168  else
169  {
170  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
171  "Compatibility version mismatch for plugin file " + PluginFilename);
172  }
173  }
174  else
175  {
176  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
177  "Unable to load plugin from file " + PluginFilename);
178  }
179 
180  return WareItem;
181  }
182 
183 
184  // =====================================================================
185  // =====================================================================
186 
187 
188  SignatureType* getWareSignature(const std::string& PluginFilename)
189  {
190  std::string PluginFullPath = getPluginFullPath(PluginFilename);
191  SignatureType* Sign = nullptr;
192 
195  .addInfos({{"pluginfullpath",PluginFullPath},{"pluginfilename",PluginFilename}});
196 
197  // library loading
198  QLibrary* PlugLib = loadPluginLibrary(PluginFullPath);
199 
200 
201  if (PlugLib)
202  {
203  if (PlugLib->load())
204  {
205  Sign = new SignatureType();
206  Sign->FileFullPath = PluginFullPath;
207 
208  GetWareABIVersionProc ABIVersionProc = (GetWareABIVersionProc)PlugLib->resolve(WAREABIVERSION_PROC_NAME);
209 
210  if (ABIVersionProc)
211  {
212  Sign->Verified =
213  (openfluid::tools::compareVersions(openfluid::config::VERSION_FULL,ABIVersionProc(),false) == 0);
214  }
215  else
216  {
217  Sign->Verified = false;
218  }
219 
220  if (Sign->Verified)
221  {
222  BodyProc BProc = (BodyProc)PlugLib->resolve(WAREBODY_PROC_NAME);
223  SignatureProc SProc = (SignatureProc)PlugLib->resolve(WARESIGNATURE_PROC_NAME);
224 
225  // checks if the handle procs exist
226  if (SProc && BProc)
227  {
228  try
229  {
230  Sign->Signature = SProc();
231  }
233  {
235  }
236 
237  if (Sign->Signature == nullptr)
238  {
239  throw openfluid::base::FrameworkException(ECtxt,"Signature cannot be instanciated from plugin file");
240  }
241 
242  Sign->Verified =
243  QString::fromStdString(PluginFilename).startsWith(QString::fromStdString(Sign->Signature->ID));
244 
245  GetWareLinkUIDProc LinkUIDProc = (GetWareLinkUIDProc)PlugLib->resolve(WARELINKUID_PROC_NAME);
246 
247  if (LinkUIDProc)
248  {
249  Sign->LinkUID = LinkUIDProc();
250  }
251  }
252  else
253  {
254  throw openfluid::base::FrameworkException(ECtxt,"Format error in plugin file");
255  }
256  }
257  }
258  else
259  {
260  throw openfluid::base::FrameworkException(ECtxt,PlugLib->errorString().toStdString());
261  }
262  }
263  else
264  {
265  throw openfluid::base::FrameworkException(ECtxt,"Unable to find plugin file");
266  }
267 
268  return Sign;
269  }
270 
271 
272  // =====================================================================
273  // =====================================================================
274 
275 
276  protected:
277 
278  std::map<std::string,std::unique_ptr<QLibrary>> m_LoadedPluginsLibraries;
279 
280  /**
281  Default constructor
282  */
284  {
286  }
287 
288 
289  // =====================================================================
290  // =====================================================================
291 
292 
293  public:
294 
296  {
297  public:
298 
299  std::vector<SignatureType*> AvailablePlugins;
300 
301  std::map<std::string,std::string> ErroredFiles;
302  };
303 
304 
306  {
307 
308  }
309 
310 
311  // =====================================================================
312  // =====================================================================
313 
314 
315  /**
316  Returns the full path of the plugin from its filename
317  @param[in] Filename The filename of the plugin
318  @return The full path of the plugin, empty if not found
319  */
320  virtual std::string getPluginFullPath(const std::string& Filename) const = 0;
321 
322 
323  // =====================================================================
324  // =====================================================================
325 
326 
327  /**
328  Returns ordered search paths for plugins
329  @return The search paths
330  */
331  virtual std::vector<std::string> getPluginsSearchPaths() const = 0;
332 
333 
334  // =====================================================================
335  // =====================================================================
336 
337 
338  /**
339  Returns the filename suffix for the plugins
340  @return The filename suffix
341  */
342  virtual std::string getPluginFilenameSuffix() const = 0;
343 
344 
345  // =====================================================================
346  // =====================================================================
347 
348 
349  /**
350  Lists available wares
351  @param[in] Pattern if not empty, the list of available wares is filtered using the given pattern
352  based on wildcard matching
353  @return The list of found wares
354  */
355  PluginsSearchResults getAvailableWaresSignatures(const std::string& Pattern = "")
356  {
357  PluginsSearchResults SearchResults;
358  std::vector<std::string> PluginsPaths = getPluginsSearchPaths();
359  std::vector<std::string> PluginFiles;
360  std::vector<std::string> TmpFiles;
361  unsigned int i,j;
362 
363 
364  for (i=0;i<PluginsPaths.size();i++)
365  {
366  TmpFiles = openfluid::tools::findFilesBySuffixAndExtension(PluginsPaths[i],
367  getPluginFilenameSuffix(),
368  openfluid::config::PLUGINS_EXT,false,true);
369 
370  for (j=0;j<TmpFiles.size();j++)
371  {
372  PluginFiles.push_back(TmpFiles[j]);
373  }
374  }
375 
376 
377  SignatureType* CurrentPlug = nullptr;
378 
379  for (i=0;i<PluginFiles.size();i++)
380  {
381  try
382  {
383  CurrentPlug = getWareSignature(PluginFiles[i]);
384 
385  if (CurrentPlug && CurrentPlug->Verified)
386  {
387  if (Pattern.empty())
388  {
389  SearchResults.AvailablePlugins.push_back(CurrentPlug);
390  }
391  else if (openfluid::tools::matchWithWildcard(Pattern,CurrentPlug->Signature->ID))
392  {
393  SearchResults.AvailablePlugins.push_back(CurrentPlug);
394  }
395  }
396  }
398  {
399  SearchResults.ErroredFiles[E.getContext().at("pluginfullpath")] = E.getMessage();
400  }
401  }
402 
403  return SearchResults;
404  }
405 
406 
407  // =====================================================================
408  // =====================================================================
409 
410 
411  /**
412  Loads only the signature of a ware given by its ID
413  @param[in] ID The ID of the ware to load
414  @return The ware container including the signature
415  */
416  ItemType* loadWareSignatureOnly(const std::string& ID)
417  {
418  ItemType* WareItem = buildWareContainerWithSignatureOnly(ID);
419 
420  if (WareItem != nullptr && WareItem->Verified)
421  {
422  return WareItem;
423  }
424 
425  return nullptr;
426  }
427 
428 
429  // =====================================================================
430  // =====================================================================
431 
432 
433  /**
434  Loads only the body of a ware if the signature is already loaded
435  @param[inout] WareItem The ware container to complete which already includes the signature
436  */
437  void completeSignatureWithWareBody(ItemType* WareItem)
438  {
439  std::string PluginFullPath = WareItem->FileFullPath;
440 
441  QLibrary* PlugLib = loadPluginLibrary(PluginFullPath);
442 
443  // library loading
444  if (PlugLib && PlugLib->load())
445  {
446  BodyProc BProc = (BodyProc)PlugLib->resolve(WAREBODY_PROC_NAME);
447 
448  // checks if the handle proc exists
449  if (BProc)
450  {
451  WareItem->Body.reset(BProc());
452 
453  if (WareItem->Body == nullptr)
454  {
455  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
456  "Ware from plugin file " + PluginFullPath +
457  " cannot be instanciated");
458  }
459 
460  }
461  else
462  {
463  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
464  "Format error in plugin file " + PluginFullPath);
465  }
466  }
467  else
468  {
469  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
470  "Unable to find plugin file " + PluginFullPath);
471  }
472  }
473 
474 
475  // =====================================================================
476  // =====================================================================
477 
478 
479  /**
480  Unloads all already loaded wares and clears the cache
481  */
483  {
484  for (auto it=m_LoadedPluginsLibraries.begin();it != m_LoadedPluginsLibraries.end(); ++it)
485  {
486  it->second.get()->unload();
487  }
488 
489  m_LoadedPluginsLibraries.clear();
490  }
491 
492 };
493 
494 
495 } } //namespaces
496 
497 
498 #endif /* __OPENFLUID_MACHINE_WAREPLUGINSMANAGER_HPP__ */
ItemType * loadWareSignatureOnly(const std::string &ID)
Definition: WarePluginsManager.hpp:416
std::map< std::string, std::unique_ptr< QLibrary > > m_LoadedPluginsLibraries
Definition: WarePluginsManager.hpp:278
Definition: ExceptionContext.hpp:53
#define WAREBODY_PROC_NAME
Definition: PluggableWare.hpp:56
virtual ~WarePluginsManager()
Definition: WarePluginsManager.hpp:305
bool OPENFLUID_API matchWithWildcard(const std::string &Pattern, const std::string &Str)
std::string(* GetWareABIVersionProc)()
Definition: WarePluginsManager.hpp:63
Definition: WarePluginsManager.hpp:80
std::vector< std::string > OPENFLUID_API findFilesBySuffixAndExtension(const std::string &Path, const std::string &Suffix, const std::string &Ext, bool WithPath=false, bool ExtIncludeDot=false)
ExceptionContext & addInfos(const std::map< std::string, std::string > &Infos)
Definition: ExceptionContext.hpp:70
Definition: FrameworkException.hpp:50
PluginsSearchResults getAvailableWaresSignatures(const std::string &Pattern="")
Definition: WarePluginsManager.hpp:355
const std::string getMessage() const
Definition: Exception.hpp:101
#define WARESIGNATURE_PROC_NAME
Definition: PluggableWare.hpp:62
Definition: ApplicationException.hpp:47
#define WAREABIVERSION_PROC_NAME
Definition: PluggableWare.hpp:68
std::string(* GetWareLinkUIDProc)()
Definition: WarePluginsManager.hpp:65
std::map< std::string, std::string > ErroredFiles
Definition: WarePluginsManager.hpp:301
#define WARELINKUID_PROC_NAME
Definition: PluggableWare.hpp:75
const ExceptionContext getContext() const
Definition: Exception.hpp:121
int OPENFLUID_API compareVersions(const std::string &VersionA, const std::string &VersionB, bool Strict=true)
void completeSignatureWithWareBody(ItemType *WareItem)
Definition: WarePluginsManager.hpp:437
#define OPENFLUID_API
Definition: dllexport.hpp:86
void unloadAllWares()
Definition: WarePluginsManager.hpp:482
WarePluginsManager()
Definition: WarePluginsManager.hpp:283
std::vector< SignatureType * > AvailablePlugins
Definition: WarePluginsManager.hpp:299
static ExceptionContext computeContext()
Definition: FrameworkException.hpp:101