All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
146  "Signature from plugin file " + PluginFilename +
147  " cannot be instanciated");
148 
149  WareItem->Verified = (WareItem->Signature->ID == ID);
150 
151  WareItem->Body = 0;
152 
153  GetWareLinkUIDProc LinkUIDProc = (GetWareLinkUIDProc)PlugLib->resolve(WARELINKUID_PROC_NAME);
154 
155  if (LinkUIDProc)
156  WareItem->LinkUID = LinkUIDProc();
157  }
158  else
159  {
160  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
161  "Format error in plugin file " + PluginFilename);
162  }
163  }
164  else
165  {
166  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
167  "Compatibility version mismatch for plugin file " + PluginFilename);
168  }
169  }
170  else
171  {
172  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
173  "Unable to load plugin from file " + PluginFilename);
174  }
175 
176  return WareItem;
177  }
178 
179 
180  // =====================================================================
181  // =====================================================================
182 
183 
184  SignatureType* getWareSignature(const std::string& PluginFilename)
185  {
186  std::string PluginFullPath = getPluginFullPath(PluginFilename);
187  SignatureType* Sign = nullptr;
188 
191  .addInfos({{"pluginfullpath",PluginFullPath},{"pluginfilename",PluginFilename}});
192 
193  // library loading
194  QLibrary* PlugLib = loadPluginLibrary(PluginFullPath);
195 
196 
197  if (PlugLib)
198  {
199  if (PlugLib->load())
200  {
201  Sign = new SignatureType();
202  Sign->FileFullPath = PluginFullPath;
203 
204  GetWareABIVersionProc ABIVersionProc = (GetWareABIVersionProc)PlugLib->resolve(WAREABIVERSION_PROC_NAME);
205 
206  if (ABIVersionProc)
207  {
208  Sign->Verified =
209  (openfluid::tools::compareVersions(openfluid::config::VERSION_FULL,ABIVersionProc(),false) == 0);
210  }
211  else
212  {
213  Sign->Verified = false;
214  }
215 
216  if (Sign->Verified)
217  {
218  BodyProc BProc = (BodyProc)PlugLib->resolve(WAREBODY_PROC_NAME);
219  SignatureProc SProc = (SignatureProc)PlugLib->resolve(WARESIGNATURE_PROC_NAME);
220 
221  // checks if the handle procs exist
222  if (SProc && BProc)
223  {
224  try
225  {
226  Sign->Signature = SProc();
227  }
229  {
231  }
232 
233  if (Sign->Signature == nullptr)
234  {
235  throw openfluid::base::FrameworkException(ECtxt,"Signature cannot be instanciated from plugin file");
236  }
237 
238  Sign->Verified =
239  QString::fromStdString(PluginFilename).startsWith(QString::fromStdString(Sign->Signature->ID));
240 
241  GetWareLinkUIDProc LinkUIDProc = (GetWareLinkUIDProc)PlugLib->resolve(WARELINKUID_PROC_NAME);
242 
243  if (LinkUIDProc)
244  Sign->LinkUID = LinkUIDProc();
245  }
246  else
247  {
248  throw openfluid::base::FrameworkException(ECtxt,"Format error in plugin file");
249  }
250  }
251  }
252  else
253  {
254  throw openfluid::base::FrameworkException(ECtxt,PlugLib->errorString().toStdString());
255  }
256  }
257  else
258  {
259  throw openfluid::base::FrameworkException(ECtxt,"Unable to find plugin file");
260  }
261 
262  return Sign;
263  }
264 
265 
266  // =====================================================================
267  // =====================================================================
268 
269 
270  protected:
271 
272  std::map<std::string,std::unique_ptr<QLibrary>> m_LoadedPluginsLibraries;
273 
274  /**
275  Default constructor
276  */
278  {
280  }
281 
282 
283  // =====================================================================
284  // =====================================================================
285 
286 
287  public:
288 
290  {
291  public:
292 
293  std::vector<SignatureType*> AvailablePlugins;
294 
295  std::map<std::string,std::string> ErroredFiles;
296  };
297 
298 
300  {
301 
302  }
303 
304 
305  // =====================================================================
306  // =====================================================================
307 
308 
309  /**
310  Returns the full path of the plugin from its filename
311  @param[in] Filename The filename of the plugin
312  @return The full path of the plugin, empty if not found
313  */
314  virtual std::string getPluginFullPath(const std::string& Filename) const = 0;
315 
316 
317  // =====================================================================
318  // =====================================================================
319 
320 
321  /**
322  Returns ordered search paths for plugins
323  @return The search paths
324  */
325  virtual std::vector<std::string> getPluginsSearchPaths() const = 0;
326 
327 
328  // =====================================================================
329  // =====================================================================
330 
331 
332  /**
333  Returns the filename suffix for the plugins
334  @return The filename suffix
335  */
336  virtual std::string getPluginFilenameSuffix() const = 0;
337 
338 
339  // =====================================================================
340  // =====================================================================
341 
342 
343  /**
344  Lists available wares
345  @param[in] Pattern if not empty, the list of available wares is filtered using the given pattern
346  based on wildcard matching
347  @return The list of found wares
348  */
349  PluginsSearchResults getAvailableWaresSignatures(const std::string& Pattern = "")
350  {
351  PluginsSearchResults SearchResults;
352  std::vector<std::string> PluginsPaths = getPluginsSearchPaths();
353  std::vector<std::string> PluginFiles;
354  std::vector<std::string> TmpFiles;
355  unsigned int i,j;
356 
357 
358  for (i=0;i<PluginsPaths.size();i++)
359  {
360  TmpFiles = openfluid::tools::findFilesBySuffixAndExtension(PluginsPaths[i],
361  getPluginFilenameSuffix(),
362  openfluid::config::PLUGINS_EXT,false,true);
363 
364  for (j=0;j<TmpFiles.size();j++)
365  {
366  PluginFiles.push_back(TmpFiles[j]);
367  }
368  }
369 
370 
371  SignatureType* CurrentPlug = nullptr;
372 
373  for (i=0;i<PluginFiles.size();i++)
374  {
375  try
376  {
377  CurrentPlug = getWareSignature(PluginFiles[i]);
378 
379  if (CurrentPlug && CurrentPlug->Verified)
380  {
381  if (Pattern.empty())
382  {
383  SearchResults.AvailablePlugins.push_back(CurrentPlug);
384  }
385  else if (openfluid::tools::matchWithWildcard(Pattern,CurrentPlug->Signature->ID))
386  {
387  SearchResults.AvailablePlugins.push_back(CurrentPlug);
388  }
389  }
390  }
392  {
393  SearchResults.ErroredFiles[E.getContext().at("pluginfullpath")] = E.getMessage();
394  }
395  }
396 
397  return SearchResults;
398  }
399 
400 
401  // =====================================================================
402  // =====================================================================
403 
404 
405  /**
406  Loads only the signature of a ware given by its ID
407  @param[in] ID The ID of the ware to load
408  @return The ware container including the signature
409  */
410  ItemType* loadWareSignatureOnly(const std::string& ID)
411  {
412  ItemType* WareItem = buildWareContainerWithSignatureOnly(ID);
413 
414  if (WareItem != nullptr && WareItem->Verified)
415  {
416  return WareItem;
417  }
418 
419  return nullptr;
420  }
421 
422 
423  // =====================================================================
424  // =====================================================================
425 
426 
427  /**
428  Loads only the body of a ware if the signature is already loaded
429  @param[inout] WareItem The ware container to complete which already includes the signature
430  */
431  void completeSignatureWithWareBody(ItemType* WareItem)
432  {
433  std::string PluginFullPath = WareItem->FileFullPath;
434 
435  QLibrary* PlugLib = loadPluginLibrary(PluginFullPath);
436 
437  // library loading
438  if (PlugLib && PlugLib->load())
439  {
440  BodyProc BProc = (BodyProc)PlugLib->resolve(WAREBODY_PROC_NAME);
441 
442  // checks if the handle proc exists
443  if (BProc)
444  {
445  WareItem->Body.reset(BProc());
446 
447  if (WareItem->Body == nullptr)
448  {
449  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
450  "Ware from plugin file " + PluginFullPath +
451  " cannot be instanciated");
452  }
453 
454  }
455  else
456  {
457  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
458  "Format error in plugin file " + PluginFullPath);
459  }
460  }
461  else
462  {
463  throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,
464  "Unable to find plugin file " + PluginFullPath);
465  }
466  }
467 
468 
469  // =====================================================================
470  // =====================================================================
471 
472 
473  /**
474  Unloads all already loaded wares and clears the cache
475  */
477  {
478  for (auto it=m_LoadedPluginsLibraries.begin();it != m_LoadedPluginsLibraries.end(); ++it)
479  {
480  it->second.get()->unload();
481  }
482 
483  m_LoadedPluginsLibraries.clear();
484  }
485 
486 };
487 
488 
489 } } //namespaces
490 
491 
492 #endif /* __OPENFLUID_MACHINE_WAREPLUGINSMANAGER_HPP__ */
int OPENFLUID_API compareVersions(const std::string &VersionA, const std::string &VersionB, bool Strict=true)
void completeSignatureWithWareBody(ItemType *WareItem)
Definition: WarePluginsManager.hpp:431
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)
#define OPENFLUID_API
Definition: dllexport.hpp:86
#define WARESIGNATURE_PROC_NAME
Definition: PluggableWare.hpp:62
ExceptionContext & addInfos(const std::map< std::string, std::string > &Infos)
Definition: ExceptionContext.hpp:70
std::map< std::string, std::unique_ptr< QLibrary > > m_LoadedPluginsLibraries
Definition: WarePluginsManager.hpp:272
Definition: ApplicationException.hpp:47
#define WAREBODY_PROC_NAME
Definition: PluggableWare.hpp:56
std::map< std::string, std::string > ErroredFiles
Definition: WarePluginsManager.hpp:295
bool OPENFLUID_API matchWithWildcard(const std::string &Pattern, const std::string &Str)
Definition: FrameworkException.hpp:50
const ExceptionContext getContext() const
Definition: Exception.hpp:121
std::vector< SignatureType * > AvailablePlugins
Definition: WarePluginsManager.hpp:293
#define WARELINKUID_PROC_NAME
Definition: PluggableWare.hpp:75
std::string(* GetWareLinkUIDProc)()
Definition: WarePluginsManager.hpp:65
const std::string getMessage() const
Definition: Exception.hpp:101
virtual ~WarePluginsManager()
Definition: WarePluginsManager.hpp:299
void unloadAllWares()
Definition: WarePluginsManager.hpp:476
#define WAREABIVERSION_PROC_NAME
Definition: PluggableWare.hpp:68
Definition: ExceptionContext.hpp:53
WarePluginsManager()
Definition: WarePluginsManager.hpp:277
std::string(* GetWareABIVersionProc)()
Definition: WarePluginsManager.hpp:63
PluginsSearchResults getAvailableWaresSignatures(const std::string &Pattern="")
Definition: WarePluginsManager.hpp:349
static ExceptionContext computeContext()
Definition: FrameworkException.hpp:101
ItemType * loadWareSignatureOnly(const std::string &ID)
Definition: WarePluginsManager.hpp:410