Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * loader.cpp - Loads plugins from .so shared objects 00004 * 00005 * Created: Wed Aug 23 15:23:36 2006 00006 * Copyright 2006-2008 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. A runtime exception applies to 00014 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00022 */ 00023 00024 #include <plugin/loader.h> 00025 00026 #include <utils/system/dynamic_module/module_manager.h> 00027 #include <utils/system/dynamic_module/module.h> 00028 00029 #include <map> 00030 00031 namespace fawkes { 00032 #if 0 /* just to make Emacs auto-indent happy */ 00033 } 00034 #endif 00035 00036 /// @cond QA 00037 class PluginLoader::Data 00038 { 00039 public: 00040 ModuleManager *mm; 00041 std::map< Plugin *, Module * > plugin_module_map; 00042 std::map< std::string, Plugin * > name_plugin_map; 00043 std::map< Plugin *, std::string > plugin_name_map; 00044 }; 00045 /// @endcond 00046 00047 /** @class PluginLoadException <plugin/loader.h> 00048 * This exception is thrown if the requested plugin could not be loaded. 00049 */ 00050 00051 /** Constructor. 00052 * @param plugin name of the plugin that caused the exception 00053 * @param message message of exception 00054 */ 00055 PluginLoadException::PluginLoadException(const char *plugin, const char *message) 00056 : Exception(), __plugin_name(plugin) 00057 { 00058 append("Plugin '%s' could not be loaded: %s", plugin, message); 00059 } 00060 00061 00062 /** Destructor. */ 00063 PluginLoadException::~PluginLoadException() throw() 00064 { 00065 } 00066 00067 /** Constructor. 00068 * @param plugin name of the plugin that caused the exception 00069 * @param message message of exception 00070 * @param e exception to copy further messages from 00071 */ 00072 PluginLoadException::PluginLoadException(const char *plugin, const char *message, 00073 Exception &e) 00074 : Exception(), __plugin_name(plugin) 00075 { 00076 append("Plugin '%s' could not be loaded: %s", plugin, message); 00077 copy_messages(e); 00078 } 00079 00080 /** Get name of plugin which failed to load. 00081 * @return plugin name 00082 */ 00083 std::string 00084 PluginLoadException::plugin_name() const 00085 { 00086 return __plugin_name; 00087 } 00088 00089 00090 /** @class PluginUnloadException <plugin/loader.h> 00091 * This exception is thrown if the requested plugin could not be unloaded. 00092 */ 00093 00094 /** Constructor. 00095 * @param plugin_name name of the plugin 00096 * @param add_msg additional message, reason for problem 00097 */ 00098 PluginUnloadException::PluginUnloadException(const char *plugin_name, 00099 const char *add_msg) 00100 : Exception() 00101 { 00102 append("Plugin '%s' could not be unloaded", plugin_name); 00103 append(add_msg); 00104 } 00105 00106 00107 /** @class PluginLoader <plugin/loader.h> 00108 * This class manages plugins. 00109 * With this class plugins can be loaded and unloaded. Information is 00110 * kept about active plugins. 00111 * 00112 * @author Tim Niemueller 00113 */ 00114 00115 /** Constructor 00116 * @param plugin_base_dir The base directory where to search for the shared 00117 * libraries which contain the plugins 00118 * @param config Fawkes configuration 00119 */ 00120 PluginLoader::PluginLoader(const char *plugin_base_dir, Configuration *config) 00121 { 00122 d = new Data(); 00123 __config = config; 00124 d->mm = new ModuleManager(plugin_base_dir); 00125 } 00126 00127 /** Destructor */ 00128 PluginLoader::~PluginLoader() 00129 { 00130 delete d->mm; 00131 delete d; 00132 } 00133 00134 00135 /** Get module manager. 00136 * This should be used rarely, but may be useful, for example, to pass specific 00137 * module opening flags in some situations. 00138 * @return internally used module manager 00139 */ 00140 ModuleManager * 00141 PluginLoader::get_module_manager() const 00142 { 00143 return d->mm; 00144 } 00145 00146 00147 Module * 00148 PluginLoader::open_module(const char *plugin_name) 00149 { 00150 std::string module_name = std::string(plugin_name) + "." + d->mm->get_module_file_extension(); 00151 00152 try { 00153 return d->mm->open_module(module_name.c_str()); 00154 } catch (ModuleOpenException &e) { 00155 throw PluginLoadException(plugin_name, "failed to open module", e); 00156 } 00157 } 00158 00159 00160 Plugin * 00161 PluginLoader::create_instance(const char *plugin_name, Module *module) 00162 { 00163 if ( ! module->has_symbol("plugin_factory") ) { 00164 throw PluginLoadException(plugin_name, "Symbol 'plugin_factory' not found. Forgot EXPORT_PLUGIN?"); 00165 } 00166 if ( ! module->has_symbol("plugin_description") ) { 00167 throw PluginLoadException(plugin_name, "Symbol 'plugin_description' not found. Forgot PLUGIN_DESCRIPTION?"); 00168 } 00169 00170 PluginFactoryFunc pff = (PluginFactoryFunc)module->get_symbol("plugin_factory"); 00171 Plugin *p = NULL; 00172 00173 p = pff(__config); 00174 if ( p == NULL ) { 00175 throw PluginLoadException(plugin_name, "Plugin could not be instantiated"); 00176 } else { 00177 p->set_name(plugin_name); 00178 } 00179 00180 return p; 00181 } 00182 00183 00184 /** Load a specific plugin 00185 * The plugin loader is clever and guarantees that every plugin is only 00186 * loaded once (as long as you use only one instance of the PluginLoader, 00187 * using multiple instances is discouraged. If you try to open a plugin 00188 * a second time it will return the 00189 * very same instance that it returned on previous load()s. 00190 * @param plugin_name The name of the plugin to be loaded, the plugin name has to 00191 * correspond to a plugin name and the name of the shared object that will 00192 * be opened for this plugin (for instance on Linux systems opening the 00193 * plugin test_plugin will look for plugin_base_dir/test_plugin.so) 00194 * @return Returns a pointer to the opened plugin. Do not under any 00195 * circumstances delete this object, use unload() instead! Since the delete 00196 * operator could be overloaded this would result in memory chaos. 00197 * @exception PluginLoadException thrown if plugin could not be loaded 00198 * @exception ModuleOpenException passed along from module manager 00199 */ 00200 Plugin * 00201 PluginLoader::load(const char *plugin_name) 00202 { 00203 std::string pn = plugin_name; 00204 00205 if ( d->name_plugin_map.find(pn) != d->name_plugin_map.end() ) { 00206 return d->name_plugin_map[pn]; 00207 } 00208 00209 try { 00210 Module *module = open_module(plugin_name); 00211 Plugin *p = create_instance(plugin_name, module); 00212 00213 d->plugin_module_map[p] = module; 00214 d->name_plugin_map[pn] = p; 00215 d->plugin_name_map[p] = pn; 00216 00217 return p; 00218 } catch (PluginLoadException &e) { 00219 throw; 00220 } 00221 } 00222 00223 00224 /** Get plugin description. 00225 * @param plugin_name name of the plugin 00226 * @return plugin description tring 00227 * @throw PluginLoadException thrown if opening the plugin fails 00228 */ 00229 std::string 00230 PluginLoader::get_description(const char *plugin_name) 00231 { 00232 Module *module = open_module(plugin_name); 00233 00234 if ( ! module->has_symbol("plugin_description") ) { 00235 throw PluginLoadException(plugin_name, "Symbol 'plugin_description' not found. Forgot PLUGIN_DESCRIPTION?"); 00236 } 00237 00238 PluginDescriptionFunc pdf = (PluginDescriptionFunc)module->get_symbol("plugin_description"); 00239 std::string rv = pdf(); 00240 d->mm->close_module(module); 00241 00242 return rv; 00243 } 00244 00245 00246 /** Check if a plugin is loaded. 00247 * @param plugin_name name of the plugin to chekc 00248 * @return true if the plugin is loaded, false otherwise 00249 */ 00250 bool 00251 PluginLoader::is_loaded(const char *plugin_name) 00252 { 00253 return ( d->name_plugin_map.find(plugin_name) != d->name_plugin_map.end() ); 00254 } 00255 00256 00257 /** Unload the given plugin 00258 * This will unload the given plugin. The plugin is destroyed with the 00259 * proper destroy method from the shared object. The shared object is unloaded 00260 * after the destruction of the plugin. 00261 * Note that even though you may call load() multiple times per plugin you may 00262 * only unload() it once! Every further access will lead to a segmentation 00263 * fault. 00264 * Make sure that you have closed any resources claimed by the plugin like 00265 * threads, memory access etc. 00266 * @param plugin The plugin that has to be unloaded 00267 */ 00268 void 00269 PluginLoader::unload(Plugin *plugin) 00270 { 00271 if ( d->plugin_module_map.find(plugin) != d->plugin_module_map.end() ) { 00272 00273 PluginDestroyFunc pdf = (PluginDestroyFunc)d->plugin_module_map[plugin]->get_symbol("plugin_destroy"); 00274 if ( pdf != NULL ) { 00275 pdf(plugin); 00276 } 00277 d->mm->close_module(d->plugin_module_map[plugin]); 00278 d->plugin_module_map.erase(plugin); 00279 00280 d->name_plugin_map.erase(d->plugin_name_map[plugin]); 00281 d->plugin_name_map.erase(plugin); 00282 } 00283 } 00284 00285 } // end namespace fawkes