// ----------------------------------------------------------------------- // pion-common: a collection of common libraries used by the Pion Platform // ----------------------------------------------------------------------- // Copyright (C) 2007-2008 Atomic Labs, Inc. (http://www.atomiclabs.com) // // Distributed under the Boost Software License, Version 1.0. // See http://www.boost.org/LICENSE_1_0.txt // #ifndef __PION_PLUGINMANAGER_HEADER__ #define __PION_PLUGINMANAGER_HEADER__ #include #include #include #include #include #include #include #include #include namespace pion { // begin namespace pion /// /// PluginManager: used to manage a collection of plug-in objects /// template class PluginManager { public: /// exception thrown if a plug-in cannot be found class PluginNotFoundException : public PionException { public: PluginNotFoundException(const std::string& plugin_id) : PionException("No plug-ins found for identifier: ", plugin_id) {} }; /// exception thrown if we try to add or load a duplicate plug-in class DuplicatePluginException : public PionException { public: DuplicatePluginException(const std::string& plugin_id) : PionException("A plug-in already exists for identifier: ", plugin_id) {} }; /// data type for a function that may be called by the run() method typedef boost::function1 PluginRunFunction; /// data type for a function that may be called by the getStat() method typedef boost::function1 PluginStatFunction; /// default constructor PluginManager(void) {} /// default destructor virtual ~PluginManager() {} /// clears all the plug-in objects being managed inline void clear(void) { boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); m_plugin_map.clear(); } /// returns true if there are no plug-in objects being managed inline bool empty(void) const { boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); return m_plugin_map.empty(); } /** * adds a new plug-in object * * @param plugin_id unique identifier associated with the plug-in * @param plugin_object_ptr pointer to the plug-in object to add */ inline void add(const std::string& plugin_id, PLUGIN_TYPE *plugin_object_ptr); /** * removes a plug-in object * * @param plugin_id unique identifier associated with the plug-in */ inline void remove(const std::string& plugin_id); /** * replaces an existing plug-in object with a new one * * @param plugin_id unique identifier associated with the plug-in * @param plugin_ptr pointer to the new plug-in object which will replace the old one */ inline void replace(const std::string& plugin_id, PLUGIN_TYPE *plugin_ptr); /** * clones an existing plug-in object (creates a new one of the same type) * * @param plugin_id unique identifier associated with the plug-in * @return PLUGIN_TYPE* pointer to the new plug-in object */ inline PLUGIN_TYPE *clone(const std::string& plugin_id); /** * loads a new plug-in object * * @param plugin_id unique identifier associated with the plug-in * @param plugin_type the name or type of the plug-in to load (searches * plug-in directories and appends extensions) * @return PLUGIN_TYPE* pointer to the new plug-in object */ inline PLUGIN_TYPE *load(const std::string& plugin_id, const std::string& plugin_type); /** * gets the plug-in object associated with a particular plugin_id (exact match) * * @param plugin_id unique identifier associated with the plug-in * @return PLUGIN_TYPE* pointer to the matching plug-in object or NULL if not found */ inline PLUGIN_TYPE *get(const std::string& plugin_id); /** * gets the plug-in object associated with a particular plugin_id (exact match) * * @param plugin_id unique identifier associated with the plug-in * @return PLUGIN_TYPE* pointer to the matching plug-in object or NULL if not found */ inline const PLUGIN_TYPE *get(const std::string& plugin_id) const; /** * gets a smart pointer to the plugin shared library for a particular plugin_id (exact match) * * @param plugin_id unique identifier associated with the plug-in * @return PionPluginPtr pointer to the plugin shared library if found */ inline PionPluginPtr getLibPtr(const std::string& plugin_id) const; /** * finds the plug-in object associated with a particular resource (fuzzy match) * * @param resource resource identifier (uri-stem) to search for * @return PLUGIN_TYPE* pointer to the matching plug-in object or NULL if not found */ inline PLUGIN_TYPE *find(const std::string& resource); /** * runs a method for every plug-in being managed * * @param run_func the function to execute for each plug-in object */ inline void run(PluginRunFunction run_func); /** * runs a method for a particular plug-in * * @param plugin_id unique identifier associated with the plug-in * @param run_func the function to execute */ inline void run(const std::string& plugin_id, PluginRunFunction run_func); /** * returns a total statistic value summed for every plug-in being managed * * @param stat_func the statistic function to execute for each plug-in object */ inline boost::uint64_t getStatistic(PluginStatFunction stat_func) const; /** * returns a statistic value for a particular plug-in * * @param plugin_id unique identifier associated with the plug-in * @param stat_func the statistic function to execute */ inline boost::uint64_t getStatistic(const std::string& plugin_id, PluginStatFunction stat_func) const; protected: /// data type that maps identifiers to plug-in objects class PluginMap : public std::map > > { public: inline void clear(void); virtual ~PluginMap() { PluginMap::clear(); } PluginMap(void) {} }; /// collection of plug-in objects being managed PluginMap m_plugin_map; /// mutex to make class thread-safe mutable boost::mutex m_plugin_mutex; }; // PluginManager member functions template inline void PluginManager::add(const std::string& plugin_id, PLUGIN_TYPE *plugin_object_ptr) { PionPluginPtr plugin_ptr; boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); m_plugin_map.insert(std::make_pair(plugin_id, std::make_pair(plugin_object_ptr, plugin_ptr))); } template inline void PluginManager::remove(const std::string& plugin_id) { boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); typename pion::PluginManager::PluginMap::iterator i = m_plugin_map.find(plugin_id); if (i == m_plugin_map.end()) throw PluginNotFoundException(plugin_id); if (i->second.second.is_open()) { i->second.second.destroy(i->second.first); } else { delete i->second.first; } m_plugin_map.erase(i); } template inline void PluginManager::replace(const std::string& plugin_id, PLUGIN_TYPE *plugin_ptr) { PION_ASSERT(plugin_ptr); boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); typename pion::PluginManager::PluginMap::iterator i = m_plugin_map.find(plugin_id); if (i == m_plugin_map.end()) throw PluginNotFoundException(plugin_id); if (i->second.second.is_open()) { i->second.second.destroy(i->second.first); } else { delete i->second.first; } i->second.first = plugin_ptr; } template inline PLUGIN_TYPE *PluginManager::clone(const std::string& plugin_id) { boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); typename pion::PluginManager::PluginMap::iterator i = m_plugin_map.find(plugin_id); if (i == m_plugin_map.end()) throw PluginNotFoundException(plugin_id); return i->second.second.create(); } template inline PLUGIN_TYPE *PluginManager::load(const std::string& plugin_id, const std::string& plugin_type) { // search for the plug-in file using the configured paths bool is_static; void *create_func; void *destroy_func; if (m_plugin_map.find(plugin_id) != m_plugin_map.end()) throw DuplicatePluginException(plugin_id); // check if plug-in is statically linked, and if not, try to resolve for dynamic is_static = PionPlugin::findStaticEntryPoint(plugin_type, &create_func, &destroy_func); // open up the plug-in's shared object library PionPluginPtr plugin_ptr; if (is_static) { plugin_ptr.openStaticLinked(plugin_type, create_func, destroy_func); // may throw } else { plugin_ptr.open(plugin_type); // may throw } // create a new object using the plug-in library PLUGIN_TYPE *plugin_object_ptr(plugin_ptr.create()); // add the new plug-in object to our map boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); m_plugin_map.insert(std::make_pair(plugin_id, std::make_pair(plugin_object_ptr, plugin_ptr))); return plugin_object_ptr; } template inline PLUGIN_TYPE *PluginManager::get(const std::string& plugin_id) { PLUGIN_TYPE *plugin_object_ptr = NULL; boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); typename pion::PluginManager::PluginMap::iterator i = m_plugin_map.find(plugin_id); if (i != m_plugin_map.end()) plugin_object_ptr = i->second.first; return plugin_object_ptr; } template inline const PLUGIN_TYPE *PluginManager::get(const std::string& plugin_id) const { const PLUGIN_TYPE *plugin_object_ptr = NULL; boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); typename pion::PluginManager::PluginMap::const_iterator i = m_plugin_map.find(plugin_id); if (i != m_plugin_map.end()) plugin_object_ptr = i->second.first; return plugin_object_ptr; } template inline PionPluginPtr PluginManager::getLibPtr(const std::string& plugin_id) const { PionPluginPtr plugin_ptr; boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); typename pion::PluginManager::PluginMap::const_iterator i = m_plugin_map.find(plugin_id); if (i != m_plugin_map.end()) plugin_ptr = i->second.second; return plugin_ptr; } template inline PLUGIN_TYPE *PluginManager::find(const std::string& resource) { // will point to the matching plug-in object, if found PLUGIN_TYPE *plugin_object_ptr = NULL; // lock mutex for thread safety (this should probably use ref counters) boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); // check if no plug-ins are being managed if (m_plugin_map.empty()) return plugin_object_ptr; // iterate through each plug-in whose identifier may match the resource typename pion::PluginManager::PluginMap::iterator i = m_plugin_map.upper_bound(resource); while (i != m_plugin_map.begin()) { --i; // keep checking while the first part of the strings match if (resource.compare(0, i->first.size(), i->first) != 0) { // the first part no longer matches if (i != m_plugin_map.begin()) { // continue to next plug-in in list if its size is < this one typename pion::PluginManager::PluginMap::iterator j=i; --j; if (j->first.size() < i->first.size()) continue; } // otherwise we've reached the end; stop looking for a match break; } // only if the resource matches the plug-in's identifier // or if resource is followed first with a '/' character if (resource.size() == i->first.size() || resource[i->first.size()]=='/') { plugin_object_ptr = i->second.first; break; } } return plugin_object_ptr; } template inline void PluginManager::run(PluginRunFunction run_func) { boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); for (typename pion::PluginManager::PluginMap::iterator i = m_plugin_map.begin(); i != m_plugin_map.end(); ++i) { run_func(i->second.first); } } template inline void PluginManager::run(const std::string& plugin_id, PluginRunFunction run_func) { // no need to lock (handled by PluginManager::get()) PLUGIN_TYPE *plugin_object_ptr = get(plugin_id); if (plugin_object_ptr == NULL) throw PluginNotFoundException(plugin_id); run_func(plugin_object_ptr); } template inline boost::uint64_t PluginManager::getStatistic(PluginStatFunction stat_func) const { boost::uint64_t stat_value = 0; boost::mutex::scoped_lock plugins_lock(m_plugin_mutex); for (typename pion::PluginManager::PluginMap::const_iterator i = m_plugin_map.begin(); i != m_plugin_map.end(); ++i) { stat_value += stat_func(i->second.first); } return stat_value; } template inline boost::uint64_t PluginManager::getStatistic(const std::string& plugin_id, PluginStatFunction stat_func) const { // no need to lock (handled by PluginManager::get()) const PLUGIN_TYPE *plugin_object_ptr = const_cast*>(this)->get(plugin_id); if (plugin_object_ptr == NULL) throw PluginNotFoundException(plugin_id); return stat_func(plugin_object_ptr); } // PluginManager::PluginMap member functions template inline void PluginManager::PluginMap::clear(void) { if (! empty()) { for (typename pion::PluginManager::PluginMap::iterator i = std::map > >::begin(); i != std::map > >::end(); ++i) { if (i->second.second.is_open()) { i->second.second.destroy(i->second.first); } else { delete i->second.first; } } erase(std::map > >::begin(), std::map > >::end()); } } } // end namespace pion #endif