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