Fawkes API  Fawkes Development Version
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.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