Go to the documentation of this file.00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 #include "rve_pluginloader/plugin.h"
00031 #include "rve_pluginloader/type_registry.h"
00032 
00033 #include <iostream>
00034 #include <fstream>
00035 #include <yaml-cpp/yaml.h>
00036 
00037 #include <ros/assert.h>
00038 #include <ros/package.h>
00039 #include <ros/debug.h>
00040 
00041 #include <boost/filesystem.hpp>
00042 
00043 namespace fs = boost::filesystem;
00044 
00045 namespace rve_pluginloader
00046 {
00047 
00048 Plugin::Plugin()
00049 : loaded_(false)
00050 , doc_(0)
00051 {
00052 
00053 }
00054 
00055 Plugin::~Plugin()
00056 {
00057   unload(false);
00058 
00059   delete doc_;
00060 }
00061 
00062 void Plugin::loadDescription(const std::string& description_path)
00063 {
00064   description_path_ = description_path;
00065 
00066   std::ifstream fin(description_path.c_str());
00067 
00068   try
00069   {
00070     YAML::Parser parser(fin);
00071     doc_ = new YAML::Node;
00072     YAML::Node& doc = *doc_;
00073     parser.GetNextDocument(doc);
00074 
00075     std::string library;
00076     doc["library"] >> library;
00077 
00078     fs::path p(description_path);
00079     fs::path parent = p.parent_path();
00080     
00081     while (true)
00082     {
00083       if (fs::exists(parent / "manifest.xml"))
00084       {
00085         std::string package = parent.filename();
00086         std::string package_path = ros::package::getPath(package);
00087         if (description_path.find(package_path) == 0)
00088         {
00089           package_name_ = package;
00090           package_path_ = package_path;
00091           break;
00092         }
00093       }
00094 
00095       parent = parent.parent_path();
00096 
00097       if (parent.string().empty())
00098       {
00099         ROS_ERROR("Could not find package name for plugin [%s]", description_path.c_str());
00100         break;
00101       }
00102     }
00103 
00104     std::string library_dir = (fs::path(package_path_) / (fs::path(library).parent_path())).string();
00105     std::string library_name = fs::path(library).filename();
00106     library_path_ = (fs::path(library_dir) / fs::path(rve_dynlib::prefix() + library_name + rve_dynlib::suffix())).string();
00107 
00108     doc["name"] >> name_;
00109   }
00110   catch (YAML::ParserException& e)
00111   {
00112     throw PluginParseException(description_path, e.msg);
00113   }
00114   catch (YAML::RepresentationException& e)
00115   {
00116     throw PluginParseException(description_path, e.msg);
00117   }
00118 }
00119 
00120 void Plugin::load()
00121 {
00122   if (loaded_)
00123   {
00124     return;
00125   }
00126 
00127   loading_signal_(PluginStatus(this));
00128 
00129   if (!fs::exists(library_path_))
00130   {
00131     throw LibraryDoesNotExistException(library_path_);
00132   }
00133 
00134   try
00135   {
00136     library_ = rve_dynlib::load(library_path_);
00137   }
00138   catch (rve_dynlib::LibraryLoadException& e)
00139   {
00140     throw LibraryLoadException(library_path_, e.what());
00141   }
00142 
00143   if (!rve_dynlib::hasSymbol(library_, "rvePluginRegister"))
00144   {
00145     throw NoPluginRegisterFunctionException(library_path_);
00146   }
00147 
00148   typedef void (*RegisterFunc)(TypeRegistry*);
00149   RegisterFunc init = reinterpret_cast<RegisterFunc>(rve_dynlib::getSymbol(library_, "rvePluginRegister"));
00150 
00151   TypeRegistry reg(package_name_);
00152   (*init)(®);
00153 
00154   class_entries_ = reg.getClassEntries();
00155 
00156   loaded_ = true;
00157   loaded_signal_(PluginStatus(this));
00158 }
00159 
00160 const V_ClassEntry* Plugin::getClassEntries(const std::string& base_class) const
00161 {
00162   M_ClassEntry::const_iterator it = class_entries_.find(base_class);
00163   if (it == class_entries_.end())
00164   {
00165     return 0;
00166   }
00167 
00168   return &it->second;
00169 }
00170 
00171 void Plugin::unload()
00172 {
00173   unload(true);
00174 }
00175 
00176 void Plugin::unload(bool allow_throw)
00177 {
00178   if (!loaded_)
00179   {
00180     return;
00181   }
00182 
00183   unloading_signal_(PluginStatus(this));
00184 
00185   
00186   
00187   if (!allocations_.empty())
00188   {
00189     std::stringstream ss;
00190     ss << "Plugin loaded from [" << description_path_ << "] still has outstanding allocations:\n";
00191 
00192     M_AllocationInfo::iterator it = allocations_.begin();
00193     M_AllocationInfo::iterator end = allocations_.end();
00194     for (; it != end; ++it)
00195     {
00196       AllocationInfo& info = it->second;
00197       ss << "====================================================\n";
00198       ss << "Base Class: " << info.base_class << "\n";
00199       ss << "Derived Class: " << info.derived_class << "\n";
00200       ss << "Pointer Address: " << info.pointer << "\n";
00201 
00202       std::string backtrace  = ros::debug::backtraceToString(info.backtrace);
00203       ss << "Allocated from:\n";
00204       ss << backtrace;
00205     }
00206 
00207     if (allow_throw)
00208     {
00209       throw OutstandingAllocationException(ss.str());
00210     }
00211     else
00212     {
00213       ROS_ERROR_STREAM(ss.str());
00214     }
00215   }
00216 
00217   class_entries_.clear();
00218 
00219   rve_dynlib::unload(library_);
00220   loaded_ = false;
00221 
00222   unloaded_signal_(PluginStatus(this));
00223 }
00224 
00225 bool Plugin::isLoaded()
00226 {
00227   return loaded_;
00228 }
00229 
00230 bool Plugin::hasClass(const std::string& base_class, const std::string& derived_class) const
00231 {
00232   try
00233   {
00234     getClassEntry(base_class, derived_class);
00235   }
00236   catch (PluginDoesNotContainClassException&)
00237   {
00238     return false;
00239   }
00240 
00241   return true;
00242 }
00243 
00244 const ClassEntryPtr& Plugin::getClassEntry(const std::string& base_class, const std::string& derived_class) const
00245 {
00246   M_ClassEntry::const_iterator it = class_entries_.find(base_class);
00247   if (it == class_entries_.end())
00248   {
00249     throw PluginDoesNotContainClassException(package_name_, derived_class);
00250   }
00251 
00252   V_ClassEntry::const_iterator lit = it->second.begin();
00253   V_ClassEntry::const_iterator lend = it->second.end();
00254   for (; lit != lend; ++lit)
00255   {
00256     const ClassEntryPtr& entry = *lit;
00257     if (entry->class_name == derived_class)
00258     {
00259       return entry;
00260     }
00261   }
00262 
00263   throw PluginDoesNotContainClassException(package_name_, derived_class);
00264 }
00265 
00266 void* Plugin::create(const std::string& base_class, const std::string& derived_class)
00267 {
00268   const ClassEntryPtr& ent = getClassEntry(base_class, derived_class);
00269   void* mem = ent->creator->create();
00270   AllocationInfo info;
00271   info.base_class = base_class;
00272   info.derived_class = derived_class;
00273   info.pointer = mem;
00274   ros::debug::getBacktrace(info.backtrace);
00275   allocations_.insert(std::make_pair(mem, info));
00276   return mem;
00277 }
00278 
00279 struct ClassDeleter
00280 {
00281   ClassDeleter(Plugin* plugin)
00282   : plugin_(plugin)
00283   {}
00284 
00285   void operator()(void* mem)
00286   {
00287     plugin_->destroy(mem);
00288   }
00289 
00290   Plugin* plugin_;
00291 };
00292 
00293 boost::shared_ptr<void> Plugin::createShared(const std::string& base_class, const std::string& derived_class)
00294 {
00295   return boost::shared_ptr<void>(create(base_class, derived_class), ClassDeleter(this));
00296 }
00297 
00298 void Plugin::destroy(void* mem)
00299 {
00300   M_AllocationInfo::iterator it = allocations_.find(mem);
00301   ROS_ASSERT(it != allocations_.end());
00302   allocations_.erase(it);
00303 }
00304 
00305 bool Plugin::ownsAllocation(void* mem)
00306 {
00307   M_AllocationInfo::iterator it = allocations_.find(mem);
00308   return it != allocations_.end();
00309 }
00310 
00311 }