loader.cpp

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

Generated on Tue Feb 22 13:31:27 2011 for Fawkes API by  doxygen 1.4.7