Main app: get rid of default plugin load exception message
[fawkes.git] / src / libs / plugin / 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_factory.h>
27 #include <utils/system/dynamic_module/module_manager.h>
28 #include <utils/system/dynamic_module/module.h>
29
30 #include <map>
31
32 namespace fawkes {
33 #if 0 /* just to make Emacs auto-indent happy */
34 }
35 #endif
36
37 /// @cond QA
38 class PluginLoaderData
39 {
40  public:
41   ModuleManager  *mm;
42   std::map< Plugin *, Module * >    plugin_module_map;
43   std::map< std::string, Plugin * > name_plugin_map;
44   std::map< Plugin *, std::string > plugin_name_map;
45 };
46 /// @endcond
47
48 /** @class PluginLoadException <plugin/loader.h>
49  * This exception is thrown if the requested plugin could not be loaded.
50  */
51
52 /** Constructor.
53  * @param plugin name of the plugin that caused the exception
54  * @param message message of exception
55  */
56 PluginLoadException::PluginLoadException(const char *plugin, const char *message)
57   : Exception(), __plugin_name(plugin)
58 {
59   append("Plugin '%s' could not be loaded: %s", plugin, message);
60 }
61
62
63 /** Destructor. */
64 PluginLoadException::~PluginLoadException() throw()
65 {
66 }
67
68 /** Constructor.
69  * @param plugin name of the plugin that caused the exception
70  * @param message message of exception
71  * @param e exception to copy further messages from
72  */
73 PluginLoadException::PluginLoadException(const char *plugin, const char *message,
74                                          Exception &e)
75   : Exception(), __plugin_name(plugin)
76 {
77   append("Plugin '%s' could not be loaded: %s", plugin, message);
78   copy_messages(e);
79 }
80
81 /** Get name of plugin which failed to load.
82  * @return plugin name
83  */
84 std::string
85 PluginLoadException::plugin_name() const
86 {
87   return __plugin_name;
88 }
89
90
91 /** @class PluginUnloadException <plugin/loader.h>
92  * This exception is thrown if the requested plugin could not be unloaded.
93  */
94
95 /** Constructor.
96  * @param plugin_name name of the plugin
97  * @param add_msg additional message, reason for problem
98  */
99 PluginUnloadException::PluginUnloadException(const char *plugin_name,
100                                              const char *add_msg)
101   : Exception()
102 {
103   append("Plugin '%s' could not be unloaded", plugin_name);
104   append(add_msg);
105 }
106
107
108 /** @class PluginLoader <plugin/loader.h>
109  * This class manages plugins.
110  * With this class plugins can be loaded and unloaded. Information is
111  * kept about active plugins.
112  *
113  * @author Tim Niemueller
114  */
115
116 /** Constructor
117  * @param plugin_base_dir The base directory where to search for the shared
118  * libraries which contain the plugins
119  * @param config Fawkes configuration
120  */
121 PluginLoader::PluginLoader(const char *plugin_base_dir, Configuration *config)
122 {
123   d = new PluginLoaderData();
124   __config = config;
125   d->mm = ModuleManagerFactory::getInstance(ModuleManagerFactory::MMT_DL, plugin_base_dir);
126 }
127
128 /** Destructor */
129 PluginLoader::~PluginLoader()
130 {
131   delete d->mm;
132   delete d;
133 }
134
135
136 Module *
137 PluginLoader::open_module(const char *plugin_name)
138 {
139   std::string module_name = std::string(plugin_name) + "." + d->mm->get_module_file_extension();
140
141   try {
142     return d->mm->open_module(module_name.c_str());
143   } catch (ModuleOpenException &e) {
144     throw PluginLoadException(plugin_name, "failed to open module", e);
145   }
146 }
147
148
149 Plugin *
150 PluginLoader::create_instance(const char *plugin_name, Module *module)
151 {
152   if ( ! module->has_symbol("plugin_factory") ) {
153     throw PluginLoadException(plugin_name, "Symbol 'plugin_factory' not found. Forgot EXPORT_PLUGIN?");
154   }
155   if ( ! module->has_symbol("plugin_description") ) {
156     throw PluginLoadException(plugin_name, "Symbol 'plugin_description' not found. Forgot PLUGIN_DESCRIPTION?");
157   }
158
159   PluginFactoryFunc pff = (PluginFactoryFunc)module->get_symbol("plugin_factory");
160   Plugin *p = NULL;
161
162   p = pff(__config);
163   if ( p == NULL ) {
164     throw PluginLoadException(plugin_name, "Plugin could not be instantiated");
165   } else {
166     p->set_name(plugin_name);
167   }
168
169   return p;
170 }
171
172
173 /** Load a specific plugin
174  * The plugin loader is clever and guarantees that every plugin is only
175  * loaded once (as long as you use only one instance of the PluginLoader,
176  * using multiple instances is discouraged. If you try to open a plugin
177  * a second time it will return the
178  * very same instance that it returned on previous load()s.
179  * @param plugin_name The name of the plugin to be loaded, the plugin name has to
180  * correspond to a plugin name and the name of the shared object that will
181  * be opened for this plugin (for instance on Linux systems opening the
182  * plugin test_plugin will look for plugin_base_dir/test_plugin.so)
183  * @return Returns a pointer to the opened plugin.  Do not under any
184  * circumstances delete this object, use unload() instead! Since the delete
185  * operator could be overloaded this would result in memory chaos.
186  * @exception PluginLoadException thrown if plugin could not be loaded
187  * @exception ModuleOpenException passed along from module manager
188  */
189 Plugin *
190 PluginLoader::load(const char *plugin_name)
191 {
192   std::string pn = plugin_name;
193
194   if ( d->name_plugin_map.find(pn) != d->name_plugin_map.end() ) {
195     return d->name_plugin_map[pn];
196   }
197
198   try {
199     Module *module = open_module(plugin_name);
200     Plugin *p = create_instance(plugin_name, module);
201
202     d->plugin_module_map[p] = module;
203     d->name_plugin_map[pn]  = p;
204     d->plugin_name_map[p]   = pn;
205
206     return p;
207   } catch (PluginLoadException &e) {
208     throw;
209   }
210 }
211
212
213 /** Get plugin description.
214  * @param plugin_name name of the plugin
215  * @return plugin description tring
216  * @throw PluginLoadException thrown if opening the plugin fails
217  */
218 std::string
219 PluginLoader::get_description(const char *plugin_name)
220 {
221   Module *module = open_module(plugin_name);
222
223   if ( ! module->has_symbol("plugin_description") ) {
224     throw PluginLoadException(plugin_name, "Symbol 'plugin_description' not found. Forgot PLUGIN_DESCRIPTION?");
225   }
226
227   PluginDescriptionFunc pdf = (PluginDescriptionFunc)module->get_symbol("plugin_description");
228   std::string rv = pdf();
229   d->mm->close_module(module);
230
231   return rv;
232 }
233
234
235 /** Check if a plugin is loaded.
236  * @param plugin_name name of the plugin to chekc
237  * @return true if the plugin is loaded, false otherwise
238  */
239 bool
240 PluginLoader::is_loaded(const char *plugin_name)
241 {
242   return ( d->name_plugin_map.find(plugin_name) != d->name_plugin_map.end() );
243 }
244
245
246 /** Unload the given plugin
247  * This will unload the given plugin. The plugin is destroyed with the
248  * proper destroy method from the shared object. The shared object is unloaded
249  * after the destruction of the plugin.
250  * Note that even though you may call load() multiple times per plugin you may
251  * only unload() it once! Every further access will lead to a segmentation
252  * fault.
253  * Make sure that you have closed any resources claimed by the plugin like
254  * threads, memory access etc.
255  * @param plugin The plugin that has to be unloaded
256  */
257 void
258 PluginLoader::unload(Plugin *plugin)
259 {
260   if ( d->plugin_module_map.find(plugin) != d->plugin_module_map.end() ) {
261     
262     PluginDestroyFunc pdf = (PluginDestroyFunc)d->plugin_module_map[plugin]->get_symbol("plugin_destroy");
263     if ( pdf != NULL ) {
264       pdf(plugin);
265     }
266     d->mm->close_module(d->plugin_module_map[plugin]);
267     d->plugin_module_map.erase(plugin);
268
269     d->name_plugin_map.erase(d->plugin_name_map[plugin]);
270     d->plugin_name_map.erase(plugin);
271   }
272 }
273
274 } // end namespace fawkes