Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
loader.cpp
1 
2 /***************************************************************************
3  * loader.cpp - Loads plugins from .so shared objects
4  *
5  * Created: Wed Aug 23 15:23:36 2006
6  * Copyright 2006-2008 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <plugin/loader.h>
25 
26 #include <utils/system/dynamic_module/module_manager.h>
27 #include <utils/system/dynamic_module/module.h>
28 
29 #include <map>
30 
31 namespace fawkes {
32 #if 0 /* just to make Emacs auto-indent happy */
33 }
34 #endif
35 
36 /// @cond QA
37 class PluginLoader::Data
38 {
39  public:
40  ModuleManager *mm;
41  std::map< Plugin *, Module * > plugin_module_map;
42  std::map< std::string, Plugin * > name_plugin_map;
43  std::map< Plugin *, std::string > plugin_name_map;
44 };
45 /// @endcond
46 
47 /** @class PluginLoadException <plugin/loader.h>
48  * This exception is thrown if the requested plugin could not be loaded.
49  */
50 
51 /** Constructor.
52  * @param plugin name of the plugin that caused the exception
53  * @param message message of exception
54  */
55 PluginLoadException::PluginLoadException(const char *plugin, const char *message)
56  : Exception(), __plugin_name(plugin)
57 {
58  append("Plugin '%s' could not be loaded: %s", plugin, message);
59 }
60 
61 
62 /** Destructor. */
64 {
65 }
66 
67 /** Constructor.
68  * @param plugin name of the plugin that caused the exception
69  * @param message message of exception
70  * @param e exception to copy further messages from
71  */
72 PluginLoadException::PluginLoadException(const char *plugin, const char *message,
73  Exception &e)
74  : Exception(), __plugin_name(plugin)
75 {
76  append("Plugin '%s' could not be loaded: %s", plugin, message);
77  copy_messages(e);
78 }
79 
80 /** Get name of plugin which failed to load.
81  * @return plugin name
82  */
83 std::string
85 {
86  return __plugin_name;
87 }
88 
89 
90 /** @class PluginUnloadException <plugin/loader.h>
91  * This exception is thrown if the requested plugin could not be unloaded.
92  */
93 
94 /** Constructor.
95  * @param plugin_name name of the plugin
96  * @param add_msg additional message, reason for problem
97  */
99  const char *add_msg)
100  : Exception()
101 {
102  append("Plugin '%s' could not be unloaded", plugin_name);
103  append(add_msg);
104 }
105 
106 
107 /** @class PluginLoader <plugin/loader.h>
108  * This class manages plugins.
109  * With this class plugins can be loaded and unloaded. Information is
110  * kept about active plugins.
111  *
112  * @author Tim Niemueller
113  */
114 
115 /** Constructor
116  * @param plugin_base_dir The base directory where to search for the shared
117  * libraries which contain the plugins
118  * @param config Fawkes configuration
119  */
120 PluginLoader::PluginLoader(const char *plugin_base_dir, Configuration *config)
121 {
122  d = new Data();
123  __config = config;
124  d->mm = new ModuleManager(plugin_base_dir);
125 }
126 
127 /** Destructor */
129 {
130  delete d->mm;
131  delete d;
132 }
133 
134 
135 /** Get module manager.
136  * This should be used rarely, but may be useful, for example, to pass specific
137  * module opening flags in some situations.
138  * @return internally used module manager
139  */
142 {
143  return d->mm;
144 }
145 
146 
147 Module *
148 PluginLoader::open_module(const char *plugin_name)
149 {
150  std::string module_name = std::string(plugin_name) + "." + d->mm->get_module_file_extension();
151 
152  try {
153  return d->mm->open_module(module_name.c_str());
154  } catch (ModuleOpenException &e) {
155  throw PluginLoadException(plugin_name, "failed to open module", e);
156  }
157 }
158 
159 
160 Plugin *
161 PluginLoader::create_instance(const char *plugin_name, Module *module)
162 {
163  if ( ! module->has_symbol("plugin_factory") ) {
164  throw PluginLoadException(plugin_name, "Symbol 'plugin_factory' not found. Forgot EXPORT_PLUGIN?");
165  }
166  if ( ! module->has_symbol("plugin_description") ) {
167  throw PluginLoadException(plugin_name, "Symbol 'plugin_description' not found. Forgot PLUGIN_DESCRIPTION?");
168  }
169 
170  PluginFactoryFunc pff = (PluginFactoryFunc)module->get_symbol("plugin_factory");
171  Plugin *p = NULL;
172 
173  p = pff(__config);
174  if ( p == NULL ) {
175  throw PluginLoadException(plugin_name, "Plugin could not be instantiated");
176  } else {
177  p->set_name(plugin_name);
178  }
179 
180  return p;
181 }
182 
183 
184 /** Load a specific plugin
185  * The plugin loader is clever and guarantees that every plugin is only
186  * loaded once (as long as you use only one instance of the PluginLoader,
187  * using multiple instances is discouraged. If you try to open a plugin
188  * a second time it will return the
189  * very same instance that it returned on previous load()s.
190  * @param plugin_name The name of the plugin to be loaded, the plugin name has to
191  * correspond to a plugin name and the name of the shared object that will
192  * be opened for this plugin (for instance on Linux systems opening the
193  * plugin test_plugin will look for plugin_base_dir/test_plugin.so)
194  * @return Returns a pointer to the opened plugin. Do not under any
195  * circumstances delete this object, use unload() instead! Since the delete
196  * operator could be overloaded this would result in memory chaos.
197  * @exception PluginLoadException thrown if plugin could not be loaded
198  * @exception ModuleOpenException passed along from module manager
199  */
200 Plugin *
201 PluginLoader::load(const char *plugin_name)
202 {
203  std::string pn = plugin_name;
204 
205  if ( d->name_plugin_map.find(pn) != d->name_plugin_map.end() ) {
206  return d->name_plugin_map[pn];
207  }
208 
209  try {
210  Module *module = open_module(plugin_name);
211  Plugin *p = create_instance(plugin_name, module);
212 
213  d->plugin_module_map[p] = module;
214  d->name_plugin_map[pn] = p;
215  d->plugin_name_map[p] = pn;
216 
217  return p;
218  } catch (PluginLoadException &e) {
219  throw;
220  }
221 }
222 
223 
224 /** Get plugin description.
225  * @param plugin_name name of the plugin
226  * @return plugin description tring
227  * @throw PluginLoadException thrown if opening the plugin fails
228  */
229 std::string
230 PluginLoader::get_description(const char *plugin_name)
231 {
232  Module *module = open_module(plugin_name);
233 
234  if ( ! module->has_symbol("plugin_description") ) {
235  throw PluginLoadException(plugin_name, "Symbol 'plugin_description' not found. Forgot PLUGIN_DESCRIPTION?");
236  }
237 
238  PluginDescriptionFunc pdf = (PluginDescriptionFunc)module->get_symbol("plugin_description");
239  std::string rv = pdf();
240  d->mm->close_module(module);
241 
242  return rv;
243 }
244 
245 
246 /** Check if a plugin is loaded.
247  * @param plugin_name name of the plugin to chekc
248  * @return true if the plugin is loaded, false otherwise
249  */
250 bool
251 PluginLoader::is_loaded(const char *plugin_name)
252 {
253  return ( d->name_plugin_map.find(plugin_name) != d->name_plugin_map.end() );
254 }
255 
256 
257 /** Unload the given plugin
258  * This will unload the given plugin. The plugin is destroyed with the
259  * proper destroy method from the shared object. The shared object is unloaded
260  * after the destruction of the plugin.
261  * Note that even though you may call load() multiple times per plugin you may
262  * only unload() it once! Every further access will lead to a segmentation
263  * fault.
264  * Make sure that you have closed any resources claimed by the plugin like
265  * threads, memory access etc.
266  * @param plugin The plugin that has to be unloaded
267  */
268 void
270 {
271  if ( d->plugin_module_map.find(plugin) != d->plugin_module_map.end() ) {
272 
273  PluginDestroyFunc pdf = (PluginDestroyFunc)d->plugin_module_map[plugin]->get_symbol("plugin_destroy");
274  if ( pdf != NULL ) {
275  pdf(plugin);
276  }
277  d->mm->close_module(d->plugin_module_map[plugin]);
278  d->plugin_module_map.erase(plugin);
279 
280  d->name_plugin_map.erase(d->plugin_name_map[plugin]);
281  d->plugin_name_map.erase(plugin);
282  }
283 }
284 
285 } // end namespace fawkes
PluginLoader(const char *plugin_base_dir, Configuration *config)
Constructor.
Definition: loader.cpp:120
Plugin interface class.
Definition: plugin.h:33
PluginUnloadException(const char *plugin_type, const char *add_msg=NULL)
Constructor.
Definition: loader.cpp:98
This exception is thrown if the requested plugin could not be loaded.
Definition: loader.h:41
~PluginLoadException()
Destructor.
Definition: loader.cpp:63
PluginLoadException(const char *plugin, const char *message)
Constructor.
Definition: loader.cpp:55
std::string plugin_name() const
Get name of plugin which failed to load.
Definition: loader.cpp:84
void unload(Plugin *plugin)
Unload the given plugin This will unload the given plugin.
Definition: loader.cpp:269
ModuleManager * get_module_manager() const
Get module manager.
Definition: loader.cpp:141
Dynamic module loader for Linux, FreeBSD, and MacOS X.
Definition: module.h:40
Base class for exceptions in Fawkes.
Definition: exception.h:36
std::string get_description(const char *plugin_name)
Get plugin description.
Definition: loader.cpp:230
const char *(* PluginDescriptionFunc)()
Plugin description function for the shared library.
Definition: plugin.h:82
virtual void * get_symbol(const char *symbol_name)
Get a symbol from the module.
Definition: module.cpp:253
void copy_messages(const Exception &exc)
Copy messages from given exception.
Definition: exception.cpp:533
bool is_loaded(const char *plugin_name)
Check if a plugin is loaded.
Definition: loader.cpp:251
Dynamic module manager.
~PluginLoader()
Destructor.
Definition: loader.cpp:128
Interface for configuration handling.
Definition: config.h:63
void append(const char *format,...)
Append messages to the message list.
Definition: exception.cpp:341
virtual bool has_symbol(const char *symbol_name)
Check if the module has the given symbol.
Definition: module.cpp:230
Plugin * load(const char *plugin_name)
Load a specific plugin The plugin loader is clever and guarantees that every plugin is only loaded on...
Definition: loader.cpp:201