src/terralib/core/plugin/PluginManager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 National Institute For Space Research (INPE) - Brazil.
3 
4  This file is part of the TerraLib - a Framework for building GIS enabled
5  applications.
6 
7  TerraLib is free software: you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published by
9  the Free Software Foundation, either version 3 of the License,
10  or (at your option) any later version.
11 
12  TerraLib is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public License
18  along with TerraLib. See COPYING. If not, write to
19  TerraLib Team at <terralib-team@terralib.org>.
20  */
21 
22 /*!
23  \file terralib/common/plugin/PluginManager.cpp
24 
25  \brief A singleton for managing plugins.
26 
27  \author Gilberto Ribeiro de Queiroz
28  \author Matheus Cavassan Zaglia
29  */
30 
31 // TerraLib
32 #include "PluginManager.h"
33 #include "../logger/Logger.h"
34 #include "../translator/Translator.h"
35 #include "AbstractPlugin.h"
36 #include "AbstractPluginEngine.h"
37 #include "Exception.h"
38 #include "PluginEngineManager.h"
39 
40 // STL
41 #include <map>
42 
43 // Boost
44 #include <boost/algorithm/string/join.hpp>
45 #include <boost/format.hpp>
46 
48 {
49  /*! \brief The list of managed plugins: this will be needed to unload
50  accordinly the plugins! */
51  std::vector<std::shared_ptr<AbstractPlugin> > plugins;
52  /*! \brief The list of plugins that are not loaded. */
53  std::vector<PluginInfo> unloaded_plugins;
54  /*! \brief The list of plugins that could not be loaded. */
55  std::vector<PluginInfo> broken_plugins;
56  /*! \brief The list of plugins dependencies */
57  std::map<std::string, std::vector<std::string> > dependency_map;
58 
59  void move_from_unload_to_broken_list(std::size_t plugin_pos);
60  void move_from_broken_to_unloaded_list(std::size_t plugin_pos);
61 };
62 
63 std::vector<std::string> te::core::PluginManager::getPlugins() const
64 {
65  std::vector<std::string> plugins;
66 
67  // retrieve the list of loaded plugins
68  for(std::shared_ptr<AbstractPlugin> p : m_pimpl->plugins)
69  {
70  plugins.push_back(p->info().name);
71  }
72 
73  // retrieve the list of unloaded plugins
74  for(const PluginInfo& p : m_pimpl->unloaded_plugins)
75  {
76  plugins.push_back(p.name);
77  }
78 
79  // retrieve the list of broken plugins
80  for(const PluginInfo& p : m_pimpl->broken_plugins)
81  {
82  plugins.push_back(p.name);
83  }
84 
85  return plugins;
86 }
87 
89  const std::string& name) const
90 {
91  // check in the possible list of loaded plugins first
92  for(const std::shared_ptr<AbstractPlugin>& p : m_pimpl->plugins)
93  {
94  if(p->info().name == name)
95  return p->info();
96  }
97 
98  // check in the list of plugins not loaded yet
99  for(const PluginInfo& p : m_pimpl->unloaded_plugins)
100  {
101  if(p.name == name)
102  return p;
103  }
104 
105  // check in the list of "problematic"(or broken) plugins
106  for(const PluginInfo& p : m_pimpl->broken_plugins)
107  {
108  if(p.name == name)
109  return p;
110  }
111 
113  (boost::format("could not find plugin: '%1%'.") % name).str());
114 }
115 
116 std::vector<te::core::PluginInfo> te::core::PluginManager::getLoadedPlugins()
117  const
118 {
119  std::vector<te::core::PluginInfo> plugins;
120 
121  // retrieve the list of loaded plugins
122  for(std::shared_ptr<AbstractPlugin> p : m_pimpl->plugins)
123  plugins.push_back(p->info());
124 
125  return plugins;
126 }
127 
128 std::vector<te::core::PluginInfo> te::core::PluginManager::getUnloadedPlugins()
129  const
130 {
131  return m_pimpl->unloaded_plugins;
132 }
133 
134 std::vector<te::core::PluginInfo> te::core::PluginManager::getBrokenPlugins()
135  const
136 {
137  return m_pimpl->broken_plugins;
138 }
139 std::vector<std::string> te::core::PluginManager::getDependents(
140  const std::string plugin_name)
141 {
142  auto it = m_pimpl->dependency_map.find(plugin_name);
143 
144  if(it != m_pimpl->dependency_map.end())
145  return it->second;
146 
147  boost::format err_msg(TE_TR("Could not find plugin: '%1%'."));
148 
149  throw OutOfRangeException()
150  << ErrorDescription((err_msg % plugin_name).str());
151 }
152 
153 bool te::core::PluginManager::isBroken(const std::string& plugin_name) const
154 {
155  return std::find_if(m_pimpl->broken_plugins.begin(),
156  m_pimpl->broken_plugins.end(),
157  [&plugin_name](const PluginInfo& p) {
158  return p.name == plugin_name;
159  }) != m_pimpl->broken_plugins.end();
160 }
161 
162 bool te::core::PluginManager::isUnloaded(const std::string& plugin_name) const
163 {
164  return std::find_if(m_pimpl->unloaded_plugins.begin(),
165  m_pimpl->unloaded_plugins.end(),
166  [&plugin_name](const PluginInfo& p) {
167  return p.name == plugin_name;
168  }) != m_pimpl->unloaded_plugins.end();
169 }
170 
171 bool te::core::PluginManager::isLoaded(const std::string& plugin_name) const
172 {
173  return std::find_if(m_pimpl->plugins.begin(), m_pimpl->plugins.end(),
174  [&plugin_name](const std::shared_ptr<AbstractPlugin>& p) {
175  return p->info().name == plugin_name;
176  }) != m_pimpl->plugins.end();
177 }
178 bool te::core::PluginManager::isFixed(const std::string& plugin_name)
179 {
180  if(!isBroken(plugin_name))
181  return true;
182 
183  std::size_t plugin_pos;
184  for(plugin_pos = 0; plugin_pos != m_pimpl->broken_plugins.size();
185  ++plugin_pos)
186  {
187  if(m_pimpl->broken_plugins[plugin_pos].name == plugin_name)
188  break;
189  }
190 
191  te::core::PluginInfo pInfo = m_pimpl->broken_plugins[plugin_pos];
192 
193  if(pInfo.dependencies.size() == 0)
194  return false;
195 
196  for(auto it = pInfo.dependencies.begin(); it != pInfo.dependencies.end();
197  ++it)
198  {
199  if(!isLoaded(*it))
200  return false;
201  }
203  return true;
204 }
205 
206 bool te::core::PluginManager::exists(const std::string& plugin_name) const
207 {
208  return isBroken(plugin_name) || isUnloaded(plugin_name) ||
209  isLoaded(plugin_name);
210 }
211 
213 {
214  if(isLoaded(pinfo.name))
215  {
216  boost::format err_msg(
217  TE_TR("There is already a plugin registered with name: '%1%', already "
218  "loaded."));
219 
221  << ErrorDescription((err_msg % pinfo.name).str());
222  }
223 
224  if(isUnloaded(pinfo.name))
225  {
226  boost::format err_msg(TE_TR(
227  "There is already a plugin registered with name: '%1%', but not loaded "
228  "yet."));
229 
231  << ErrorDescription((err_msg % pinfo.name).str());
232  }
233 
234  if(isBroken(pinfo.name))
235  {
236  boost::format err_msg(
237  TE_TR("There is already a plugin registered with the name: '%1%', but "
238  "presenting some problems to load or startup."));
239 
241  << ErrorDescription((err_msg % pinfo.name).str());
242  }
243 
244  m_pimpl->unloaded_plugins.push_back(pinfo);
245 }
246 
247 void te::core::PluginManager::remove(const std::string& plugin_name)
248 {
249  // if plugin is loaded we need to unload it (and maybe propagate changes to
250  // other dependent plugins)
251  if(isLoaded(plugin_name))
252  {
253  // stop the plugin
254  stop(plugin_name);
255  unload(plugin_name);
256  // now plugin belongs to the unloaded list!
257  }
258 
259  if(isUnloaded(plugin_name))
260  {
261  for(std::vector<PluginInfo>::iterator it =
262  m_pimpl->unloaded_plugins.begin();
263  it != m_pimpl->unloaded_plugins.end(); ++it)
264  {
265  if(plugin_name == it->name)
266  {
267  m_pimpl->unloaded_plugins.erase(it);
268  return;
269  }
270  }
271  }
272 
273  if(isBroken(plugin_name))
274  {
275  for(std::vector<PluginInfo>::iterator it = m_pimpl->broken_plugins.begin();
276  it != m_pimpl->broken_plugins.end(); ++it)
277  {
278  if(plugin_name == it->name)
279  {
280  m_pimpl->broken_plugins.erase(it);
281  return;
282  }
283  }
284  }
285  boost::format err_msg(TE_TR("Could not find plugin: '%1%'."));
286 
287  throw OutOfRangeException()
288  << ErrorDescription((err_msg % plugin_name).str());
289 }
290 
291 void te::core::PluginManager::load(const std::string& plugin_name,
292  const bool start)
293 {
294  // if plugin is already loaded raise an exception
295  if(isLoaded(plugin_name))
296  {
297  boost::format err_msg(TE_TR("The plugin '%1%' is already loaded."));
298 
300  << ErrorDescription((err_msg % plugin_name).str());
301  }
302 
303  // if plugin is not in the broken list nor in the unloaded list raise an
304  // exception
305  if(!isBroken(plugin_name) && !isUnloaded(plugin_name))
306  {
307  boost::format err_msg(
308  TE_TR("The plugin '%1%' is not registered in the manager."));
309 
311  << ErrorDescription((err_msg % plugin_name).str());
312  }
313 
314  // in which list and position is the plugin_info?
315  bool found_in_unloaded_list = false;
316  bool found_in_broken_list = false;
317 
318  std::size_t plugin_pos = 0;
319 
320  for(; plugin_pos != m_pimpl->unloaded_plugins.size(); ++plugin_pos)
321  {
322  if(m_pimpl->unloaded_plugins[plugin_pos].name == plugin_name)
323  {
324  found_in_unloaded_list = true;
325  break;
326  }
327  }
328 
329  if(!found_in_unloaded_list)
330  {
331  for(plugin_pos = 0; plugin_pos != m_pimpl->broken_plugins.size();
332  ++plugin_pos)
333  {
334  if(m_pimpl->broken_plugins[plugin_pos].name == plugin_name)
335  {
336  found_in_broken_list = true;
337  break;
338  }
339  }
340 
341  if(!found_in_broken_list)
342  {
343  boost::format err_msg(TE_TR("Could not find plugin: '%1%'."));
344 
345  throw OutOfRangeException()
346  << ErrorDescription((err_msg % plugin_name).str());
347  }
348  }
349 
350  // get plugin_info (make a temp copy to avoid loose references!)
351  PluginInfo pinfo = found_in_unloaded_list
352  ? m_pimpl->unloaded_plugins[plugin_pos]
353  : m_pimpl->broken_plugins[plugin_pos];
354 
355  // check if required plugins is already loaded
356  std::vector<std::string> missing_deps;
357  for(const std::string& plugin_dependency : pinfo.dependencies)
358  {
359  if(!isLoaded(plugin_dependency))
360  missing_deps.push_back(plugin_dependency);
361  }
362  if(!missing_deps.empty())
363  {
364  if(found_in_unloaded_list)
366 
367  boost::format err_msg(
368  TE_TR("Plugin '%1%' has the following dependency: '%2%'."));
369 
371  (err_msg % plugin_name % boost::algorithm::join(missing_deps, ", "))
372  .str());
373  }
374 
375  std::unique_ptr<AbstractPlugin> plugin(nullptr);
376 
377  try
378  {
379  // if everything is ready for loading the plugin let's call the underlying
380  // engine to load the plugin
381  AbstractPluginEngine& engine =
383 
384  plugin = engine.load(pinfo);
385 
386  if(plugin.get() == nullptr)
387  {
388  boost::format err_msg(TE_TR("Could not load plugin: '%1%'."));
389 
390  throw PluginLoadException()
391  << ErrorDescription((err_msg % plugin_name).str());
392  }
393 
394  TE_LOG_TRACE(TE_TR("Pluging '" + plugin_name + "' loaded."));
395 
396  if(start)
397  plugin->startup();
398 
399  TE_LOG_TRACE(TE_TR("Pluging '" + plugin_name + "' started."));
400 
401  m_pimpl->dependency_map[pinfo.name] = {};
402 
403  for(const std::string& plugin_dependency : pinfo.dependencies)
404  m_pimpl->dependency_map[plugin_dependency].push_back(pinfo.name);
405 
406  m_pimpl->plugins.push_back(
407  std::shared_ptr<AbstractPlugin>(plugin.release()));
408 
409  if(found_in_unloaded_list)
410  {
412  plugin_pos);
413  }
414  else if(found_in_broken_list)
415  {
416  m_pimpl->broken_plugins.erase(m_pimpl->broken_plugins.begin() +
417  plugin_pos);
418  }
419  else
420  {
421  boost::format err_msg(
422  TE_TR("Unexpected error after loading plugin: '%1%'."));
423  throw PluginLoadException()
424  << ErrorDescription((err_msg % plugin_name).str());
425  }
426  }
427  catch(const boost::exception& e)
428  {
429  // WARNING: if plugin goes out-of scope the exception thrown exception is
430  // not valid this is why we make a copy here
431  if(found_in_unloaded_list)
433 
434  if(const std::string* d = boost::get_error_info<ErrorDescription>(e))
435  {
437  }
438  else
439  {
440  boost::format err_msg(TE_TR("Unknown error loading plugin: %1%."));
441  throw PluginLoadException()
442  << ErrorDescription((err_msg % pinfo.name).str());
443  }
444  }
445  catch(...)
446  {
447  boost::format err_msg(TE_TR("Unknown error loading plugin: %1%."));
448  throw PluginLoadException()
449  << ErrorDescription((err_msg % pinfo.name).str());
450  }
451 }
452 
453 void te::core::PluginManager::start(const std::string& plugin_name)
454 {
455  for(std::shared_ptr<AbstractPlugin> p : m_pimpl->plugins)
456  {
457  if(p->info().name == plugin_name)
458  {
459  p->startup();
460  TE_LOG_TRACE(TE_TR("Pluging '" + plugin_name + "' started."));
461  return;
462  }
463  }
464 
465  boost::format err_msg(
466  TE_TR("The plugin '%1%' is not loaded, load it before starting it."));
467 
468  throw PluginStartupException()
469  << ErrorDescription((err_msg % plugin_name).str());
470 }
471 
472 void te::core::PluginManager::stop(const std::string& plugin_name)
473 {
474  const auto it = m_pimpl->dependency_map.find(plugin_name);
475 
476  // checks if the plugin has a dependent
477  if(it->second.size() == 0)
478  {
479  for(std::shared_ptr<AbstractPlugin> p : m_pimpl->plugins)
480  {
481  if(p->info().name == plugin_name)
482  {
483  m_pimpl->dependency_map.erase(it);
484  p->shutdown();
485  TE_LOG_TRACE(TE_TR("Pluging '" + plugin_name + "' stoped."));
486  return;
487  }
488  }
489  boost::format err_msg(TE_TR(
490  "The plugin '%1%' is not loaded, load it before trying to stop it."));
491 
493  << ErrorDescription((err_msg % plugin_name).str());
494  }
495 
496  boost::format err_msg(
497  TE_TR("The plugin '%1%' cannot be stopped, because is required by: %2%"));
498 
499  std::string dependents = boost::algorithm::join(it->second, ", ");
500 
501  TE_LOG_TRACE((err_msg % plugin_name % dependents).str());
502 
504  << ErrorDescription((err_msg % plugin_name % dependents).str());
505 }
506 
507 void te::core::PluginManager::unload(const std::string& plugin_name)
508 {
509  for(std::size_t plugin_pos = 0; plugin_pos != m_pimpl->plugins.size();
510  ++plugin_pos)
511  {
512  if(m_pimpl->plugins[plugin_pos]->info().name == plugin_name)
513  {
514  if(!m_pimpl->plugins[plugin_pos]->initialized())
515  {
516  // insert the plugin into the unloaded list
517  m_pimpl->unloaded_plugins.push_back(
518  m_pimpl->plugins[plugin_pos]->info());
519 
520  auto dependencies = m_pimpl->plugins[plugin_pos]->info().dependencies;
521  for(std::string dependency : dependencies)
522  {
523  // remove the plugin assossiation with its dependencies
524  m_pimpl->dependency_map[dependency].erase(std::find(
525  m_pimpl->dependency_map[dependency].begin(),
526  m_pimpl->dependency_map[dependency].end(), plugin_name));
527  TE_LOG_TRACE(TE_TR("Removing '" + plugin_name +
528  "' dependency from: " + dependency));
529  }
530 
531  // remove the plugin from the loaded list
532  m_pimpl->plugins.erase(m_pimpl->plugins.begin() + plugin_pos);
533  TE_LOG_TRACE(TE_TR("Pluging '" + plugin_name + "' unloaded."));
534  return;
535  }
536 
537  boost::format err_msg(TE_TR(
538  "The plugin '%1%' is initialized, stop it before trying to unload "
539  "it."));
540 
541  throw PluginUnloadException()
542  << ErrorDescription((err_msg % plugin_name).str());
543  }
544  }
545 }
546 
547 void te::core::PluginManager::recursiveUnload(const std::string& plugin_name)
548 {
549  PluginInfo pInfo = getPluginInfo(plugin_name);
550 
551  for(auto dependent : getDependents(plugin_name))
552  recursiveUnload(dependent);
553 
554  if(isUnloaded(plugin_name))
555  return;
556 
557  stop(plugin_name);
558  unload(plugin_name);
559 }
560 
562 {
563  std::vector<te::core::PluginInfo> v_pInfo = getLoadedPlugins();
564  for(auto it = v_pInfo.rbegin(); it != v_pInfo.rend(); ++it)
565  {
566  std::string plugin_name = it->name;
567  remove(plugin_name);
568  }
569 
570  m_pimpl->broken_plugins.clear();
571  m_pimpl->dependency_map.clear();
572  m_pimpl->unloaded_plugins.clear();
573 }
574 
576 {
577  static PluginManager inst;
578 
579  return inst;
580 }
581 
583 {
584  m_pimpl = new Impl;
585 }
586 
588 {
589  delete m_pimpl;
590 }
591 
593  std::size_t plugin_pos)
594 {
595  assert(plugin_pos < unloaded_plugins.size());
596 
597  PluginInfo pinfo = unloaded_plugins[plugin_pos];
598 
599  unloaded_plugins.erase(unloaded_plugins.begin() + plugin_pos);
600 
601  broken_plugins.push_back(pinfo);
602 }
603 
605  std::size_t plugin_pos)
606 {
607  assert(plugin_pos < broken_plugins.size());
608 
609  PluginInfo pinfo = broken_plugins[plugin_pos];
610 
611  broken_plugins.erase(broken_plugins.begin() + plugin_pos);
612 
613  unloaded_plugins.push_back(pinfo);
614 }
An exception indicating an error when shutting down a plugin.
An exception indicating an error when unloading a plugin.
void move_from_broken_to_unloaded_list(std::size_t plugin_pos)
void recursiveUnload(const std::string &plugin_name)
Try to unload a given plugin and its dependents recursively.
An exception indicating an error when loading a plugin.
void stop(const std::string &plugin_name)
Stop a loaded plugin.
std::string engine
The type of plugin execution engine: C++, JAVA, LUA or any other supported engine.
A singleton that can be used to register plugin engines.
An exception indicating an error when trying to shutdown a plugin with a dependent.
void insert(const PluginInfo &pinfo)
Adds plugin with its plugin information to the list of unloaded plugins.
std::vector< PluginInfo > getBrokenPlugins() const
Return the list of plugins that could not be loaded.
An exception indicating an error when trying to start a plugin.
std::vector< std::shared_ptr< AbstractPlugin > > plugins
The list of managed plugins: this will be needed to unload accordinly the plugins! ...
Basic information about a plugin.
#define TE_TR(message)
It marks a string in order to get translated.
Definition: Translator.h:242
bool isUnloaded(const std::string &plugin_name) const
Returns true if the plugin is in the not-loaded list of plugins.
void load(const std::string &plugin_name, const bool start=true)
It tries to load the informed plugin.
bool isFixed(const std::string &plugin_name)
Returns true if the plugin has been fixed and moves it to the unloaded list otherwise returns false...
boost::error_info< struct tag_error_description, std::string > ErrorDescription
The base type for error report messages.
virtual std::unique_ptr< AbstractPlugin > load(const PluginInfo &pinfo)=0
Load the informed plugin.
std::string name
The plugin name: an internal value used to identify the plugin in the system. Must be a unique value...
std::vector< PluginInfo > broken_plugins
The list of plugins that could not be loaded.
std::vector< std::string > getPlugins() const
Return the list of plugins managed by PluginManager.
static PluginManager & instance()
Access the singleton.
bool exists(const std::string &plugin_name) const
Tells if a given plugin is registered or not.
std::vector< PluginInfo > getUnloadedPlugins() const
Return the list of plugins that were not loaded.
The base class for plugin engines.
std::vector< std::string > getDependents(const std::string plugin_name)
Return the list of plugins that depends of a given plugin.
bool isBroken(const std::string &plugin_name) const
Returns true if the plugin is in the broken list of plugins.
static te::dt::DateTime d(2010, 8, 9, 15, 58, 39)
A singleton for managing plugins.
bool isLoaded(const std::string &plugin_name) const
Returns true if the plugin is loaded otherwise returns false.
AbstractPluginEngine & get(const std::string &engine_id) const
Find a plugin engine with the given id.
std::vector< std::string > dependencies
The list of required plugins in order to launch the plugin.
te::gm::Polygon * p
const PluginInfo & getPluginInfo(const std::string &name) const
Return information about a plugin identified by the given name.
An exception indicating that a given item was not found in a collection (or range).
The base class for plugins in TerraLib.
std::vector< PluginInfo > unloaded_plugins
The list of plugins that are not loaded.
#define TE_LOG_TRACE(message)
Use this tag in order to log a message to the TerraLib default logger with the TRACE level...
Definition: Logger.h:293
void remove(const std::string &plugin_name)
Remove plugin from the manager.
void start(const std::string &plugin_name)
Start a loaded plugin.
An exception indicating that a given argument is not valid, for instance if a given item already exis...
void clear()
Stop and unload all plugins, then clear the internal list of plugins.
std::vector< PluginInfo > getLoadedPlugins() const
Return the list of plugins that are loaded.
void unload(const std::string &plugin_name)
Try to unload a given plugin.
static PluginEngineManager & instance()
Access the singleton.
std::map< std::string, std::vector< std::string > > dependency_map
The list of plugins dependencies.
void move_from_unload_to_broken_list(std::size_t plugin_pos)