00001
00002 #define OCL_STATIC
00003 #include "ocl/Component.hpp"
00004 #include "ComponentLoader.hpp"
00005 #include <rtt/TaskContext.hpp>
00006 #include <rtt/Logger.hpp>
00007 #include <boost/filesystem.hpp>
00008 #include <rtt/plugin/PluginLoader.hpp>
00009 #include <rtt/types/TypekitRepository.hpp>
00010
00011 #ifdef HAS_ROSLIB
00012 #include <ros/package.h>
00013 #endif
00014
00015 #ifndef _WIN32
00016 # include <dlfcn.h>
00017 #endif
00018
00019 using namespace RTT;
00020 using namespace RTT::detail;
00021 using namespace std;
00022 using namespace boost::filesystem;
00023
00024 namespace OCL
00025 {
00026
00027
00028 FactoryMap* ComponentFactories::Factories = 0;
00029 }
00030
00031 using namespace OCL;
00032
00033
00034 #ifdef __APPLE__
00035 static const std::string SO_EXT(".dylib");
00036 static const std::string SO_POSTFIX("");
00037 #else
00038 # ifdef _WIN32
00039 static const std::string SO_EXT(".dll");
00040 # ifdef _DEBUG
00041 static const std::string SO_POSTFIX("d");
00042 # else
00043 static const std::string SO_POSTFIX("");
00044 # endif // _DEBUG
00045 # else
00046 static const std::string SO_EXT(".so");
00047 static const std::string SO_POSTFIX("");
00048 # endif
00049 #endif
00050
00051
00052 static const std::string FULL_COMPONENT_SUFFIX(string("-") + string(OROCOS_TARGET_NAME) + SO_POSTFIX + SO_EXT);
00053
00054
00055 # ifdef _WIN32
00056 static const std::string delimiters(";");
00057 static const char default_delimiter(';');
00058 # else
00059 static const std::string delimiters(":;");
00060 static const char default_delimiter(':');
00061 # endif
00062
00063 boost::shared_ptr<ComponentLoader> ComponentLoader::minstance;
00064
00065 static boost::shared_ptr<ComponentLoader> instance2;
00066
00067 namespace {
00068
00069
00070 vector<string> splitPaths(string const& str)
00071 {
00072 vector<string> paths;
00073
00074
00075 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
00076
00077 string::size_type pos = str.find_first_of(delimiters, lastPos);
00078
00079 while (string::npos != pos || string::npos != lastPos)
00080 {
00081
00082 if ( !str.substr(lastPos, pos - lastPos).empty() )
00083 paths.push_back(str.substr(lastPos, pos - lastPos));
00084
00085 lastPos = str.find_first_not_of(delimiters, pos);
00086
00087 pos = str.find_first_of(delimiters, lastPos);
00088 }
00089 if ( paths.empty() )
00090 paths.push_back(".");
00091 return paths;
00092 }
00093
00100 string makeShortFilename(string const& str) {
00101 string ret = str;
00102 if (str.substr(0,3) == "lib")
00103 ret = str.substr(3);
00104 if (ret.rfind(FULL_COMPONENT_SUFFIX) != string::npos)
00105 ret = ret.substr(0, ret.rfind(FULL_COMPONENT_SUFFIX) );
00106 return ret;
00107 }
00108
00109 }
00110
00111 bool hasEnding(string const &fullString, string const &ending)
00112 {
00113 if (fullString.length() > ending.length()) {
00114 return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
00115 } else {
00116 return false;
00117 }
00118 }
00119
00120 boost::shared_ptr<ComponentLoader> ComponentLoader::Instance() {
00121 if (!instance2)
00122 instance2.reset( new ComponentLoader() );
00123 return instance2;
00124 }
00125
00126 void ComponentLoader::Release() {
00127 instance2.reset();
00128 }
00129
00130
00131
00132 bool ComponentLoader::import( std::string const& path_list )
00133 {
00134 if (path_list.empty() ) {
00135 log(Error) << "import paths: No paths were given for loading ( path_list = '' )."<<endlog();
00136 return false;
00137 }
00138
00139
00140 bool all_good = true, found = false;
00141 vector<string> paths;
00142 paths = splitPaths( path_list );
00143
00144 for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it)
00145 {
00146
00147 path p = path(*it);
00148 if (is_directory(p))
00149 {
00150 log(Info) << "Importing directory " << p.string() << " ..."<<endlog();
00151 for (directory_iterator itr(p); itr != directory_iterator(); ++itr)
00152 {
00153 log(Debug) << "Scanning file " << itr->path().string() << " ...";
00154 if (is_regular_file(itr->status()) && itr->path().extension() == SO_EXT )
00155 {
00156 std::string libname;
00157 #if BOOST_VERSION >= 104600
00158 libname = itr->path().filename().string();
00159 #else
00160 libname = itr->path().filename();
00161 #endif
00162 if(!isCompatibleComponent(libname))
00163 {
00164 log(Debug) << "not a compatible component: ignored."<<endlog();
00165 }
00166 else
00167 {
00168 found = true;
00169 all_good = loadInProcess( itr->path().string(), makeShortFilename(libname ), true) && all_good;
00170 }
00171 } else {
00172 if (!is_regular_file(itr->status()))
00173 log(Debug) << "not a regular file: ignored."<<endlog();
00174 else
00175 log(Debug) << "not a " + SO_EXT + " library: ignored."<<endlog();
00176 }
00177 }
00178 log(Debug) << "Looking for plugins or typekits in directory " << p.string() << " ..."<<endlog();
00179 try {
00180 found = PluginLoader::Instance()->loadTypekits( p.string() ) || found;
00181 found = PluginLoader::Instance()->loadPlugins( p.string() ) || found;
00182 } catch (std::exception& e) {
00183 all_good = false;
00184 log(Error) << e.what() <<endlog();
00185 }
00186 }
00187 else {
00188
00189 log(Debug) << "No such directory: " << p<< endlog();
00190 all_good = false;
00191 }
00192 #if 0
00193
00194 p = path(*it) / OROCOS_TARGET_NAME;
00195 if (is_directory(p))
00196 {
00197 log(Info) << "Importing component libraries from directory " << p.string() << " ..."<<endlog();
00198 for (directory_iterator itr(p); itr != directory_iterator(); ++itr)
00199 {
00200 log(Debug) << "Scanning file " << itr->path().string() << " ...";
00201 if (is_regular_file(itr->status()) && itr->path().extension() == SO_EXT ) {
00202 found = true;
00203 #if BOOST_VERSION >= 104600
00204 all_good = loadInProcess( itr->path().string(), makeShortFilename(itr->path().filename().string() ), true) && all_good;
00205 #else
00206 all_good = loadInProcess( itr->path().string(), makeShortFilename(itr->path().filename() ), true) && all_good;
00207 #endif
00208 }else {
00209 if (!is_regular_file(itr->status()))
00210 log(Debug) << "not a regular file: ignored."<<endlog();
00211 else
00212 log(Debug) << "not a " + SO_EXT + " library: ignored."<<endlog();
00213 }
00214 }
00215 log(Info) << "Importing plugins and typekits from directory " << p.string() << " ..."<<endlog();
00216 try {
00217 found = PluginLoader::Instance()->loadTypekits( p.string() ) || found;
00218 found = PluginLoader::Instance()->loadPlugins( p.string() ) || found;
00219 } catch (std::exception& e) {
00220 all_good = false;
00221 log(Error) << e.what() <<endlog();
00222 }
00223 }
00224 #endif
00225 }
00226 return found && all_good;
00227 }
00228
00229
00230
00231 bool ComponentLoader::import( std::string const& package, std::string const& path_list )
00232 {
00233
00234 path arg( package );
00235 if (is_regular_file(arg)) {
00236 #if BOOST_VERSION >= 104600
00237 return loadInProcess(arg.string(), makeShortFilename( arg.filename().string() ), true);
00238 #else
00239 return loadInProcess(arg.string(), makeShortFilename( arg.filename() ), true);
00240 #endif
00241 }
00242
00243
00244 if ( arg.is_complete() ) {
00245
00246 bool ret = import(package);
00247
00248 if ( arg.parent_path().leaf() != OROCOS_TARGET_NAME )
00249 ret = import( (arg / OROCOS_TARGET_NAME).string() ) || ret;
00250
00251 if (ret)
00252 return true;
00253
00254 log(Error) << "Could not import absolute path '"<<package << "': nothing found."<<endlog();
00255 return false;
00256 }
00257
00258 if ( isImported(package) ) {
00259 log(Info) <<"Component package '"<< package <<"' already imported." <<endlog();
00260 return true;
00261 }
00262
00263
00264
00265
00266
00267
00268
00269 #ifdef HAS_ROSLIB
00270 using namespace ros::package;
00271 try {
00272 bool all_good = true, found = false;
00273 string ppath = getPath( package );
00274 if ( !ppath.empty() ) {
00275 path rospath = path(ppath) / "lib" / "orocos";
00276 path rospath_target = rospath / OROCOS_TARGET_NAME;
00277
00278 V_string rospackresult;
00279 command("depends " + package, rospackresult);
00280 for(V_string::iterator it = rospackresult.begin(); it != rospackresult.end(); ++it) {
00281 if ( isImported(*it) ) {
00282 log(Debug) <<"Package dependency '"<< *it <<"' already imported." <<endlog();
00283 continue;
00284 }
00285 ppath = getPath( *it );
00286 path deppath = path(ppath) / "lib" / "orocos";
00287 path deppath_target = path(deppath) / OROCOS_TARGET_NAME;
00288
00289 if ( is_directory( deppath_target ) ) {
00290 log(Debug) << "Ignoring files under " << deppath.string() << " since " << deppath_target.string() << " was found."<<endlog();
00291 found = true;
00292 if ( import( deppath_target.string() ) ) {
00293 loadedPackages.push_back( *it );
00294 } else
00295 all_good = false;
00296 }
00297 else if ( is_directory( deppath ) ) {
00298 found = true;
00299 if ( import( deppath.string() ) ) {
00300 loadedPackages.push_back( *it );
00301 } else
00302 all_good = false;
00303 }
00304 }
00305
00306 if ( is_directory( rospath_target ) ) {
00307 log(Debug) << "Ignoring files under " << rospath.string() << " since " << rospath_target.string() << " was found."<<endlog();
00308 found = true;
00309 if ( import( rospath_target.string() ) ) {
00310 loadedPackages.push_back( package );
00311 } else
00312 all_good = false;
00313 } else if ( is_directory( rospath ) ) {
00314 found = true;
00315 if ( import( rospath.string() ) ) {
00316 loadedPackages.push_back( package );
00317 } else
00318 all_good = false;
00319 }
00320
00321 if (!found) {
00322 log(Error) <<"The ROS package '"<< package <<"' in '"<< ppath << "' nor its dependencies contained a lib/orocos directory."<<endlog();
00323 }
00324 return all_good && found;
00325 } else
00326 log(Info) << "Not a ros package: " << package << endlog();
00327 } catch(...) {
00328 log(Info) << "Not a ros package: " << package << endlog();
00329 }
00330 #endif
00331
00332 string paths;
00333 string trypaths;
00334 vector<string> tryouts;
00335 if (path_list.empty())
00336 paths = component_path + default_delimiter + ".";
00337 else
00338 paths = path_list;
00339
00340 bool path_found = false;
00341
00342
00343
00344 vector<string> vpaths;
00345 vpaths = splitPaths(paths);
00346 trypaths = paths;
00347 paths.clear();
00348
00349 path p( package );
00350 if (is_directory( p )) {
00351 path_found = true;
00352
00353 paths += p.string() + default_delimiter + (p / OROCOS_TARGET_NAME).string() + default_delimiter;
00354 if ( p.is_complete() ) {
00355
00356
00357
00358 }
00359 }
00360
00361 for(vector<string>::iterator it = vpaths.begin(); it != vpaths.end(); ++it) {
00362 p = *it;
00363 p = p / package;
00364
00365 if (is_directory( p )) {
00366 path_found = true;
00367 paths += p.string() + default_delimiter ;
00368 } else
00369 tryouts.push_back( p.string() );
00370 p = *it;
00371 p = p / OROCOS_TARGET_NAME / package;
00372
00373 if (is_directory( p )) {
00374 path_found = true;
00375 paths += p.string() + default_delimiter ;
00376 } else
00377 tryouts.push_back( p.string() );
00378 }
00379 if ( path_found )
00380 paths.erase( paths.size() - 1 );
00381
00382
00383 if (path_found) {
00384 if ( import(paths) ) {
00385 loadedPackages.push_back( package );
00386 return true;
00387 } else {
00388 log(Error) << "Failed to import components, types or plugins from package or directory '"<< package <<"' found in:"<< endlog();
00389 log(Error) << paths << endlog();
00390 return false;
00391 }
00392 }
00393 log(Error) << "No such package or directory found in search path: " << package << ". Search path is: "<< endlog();
00394 log(Error) << trypaths << endlog();
00395 for(vector<string>::iterator it=tryouts.begin(); it != tryouts.end(); ++it)
00396 log(Error) << *it << endlog();
00397
00398 #ifdef HAS_ROSLIB
00399 log(Error) << "Package also not found in ROS_PACKAGE_PATH paths." <<endlog();
00400 #endif
00401 return false;
00402
00403 }
00404
00405 bool ComponentLoader::loadLibrary( std::string const& name )
00406 {
00407 path arg = name;
00408
00409 #if BOOST_VERSION >= 104600
00410 if (is_regular_file( arg ) && loadInProcess( arg.string(), makeShortFilename( arg.filename().string() ), true ) )
00411 #else
00412 if (is_regular_file( arg ) && loadInProcess( arg.string(), makeShortFilename( arg.filename() ), true ) )
00413 #endif
00414 return true;
00415
00416 if ( arg.is_complete() )
00417 return false;
00418
00419
00420 vector<string> paths = splitPaths( component_path );
00421 vector<string> tryouts( paths.size() * 4 );
00422 tryouts.clear();
00423 path dir = arg.parent_path();
00424 #if BOOST_VERSION >= 104600
00425 string file = arg.filename().string();
00426 #else
00427 string file = arg.filename();
00428 #endif
00429
00430 for (vector<string>::iterator it = paths.begin(); it != paths.end(); ++it)
00431 {
00432 path p = path(*it) / dir / (file + FULL_COMPONENT_SUFFIX);
00433 tryouts.push_back( p.string() );
00434 if (is_regular_file( p ) && loadInProcess( p.string(), makeShortFilename(file), true ) )
00435 return true;
00436 p = path(*it) / dir / ("lib" + file + FULL_COMPONENT_SUFFIX);
00437 tryouts.push_back( p.string() );
00438 if (is_regular_file( p ) && loadInProcess( p.string(), makeShortFilename(file), true ) )
00439 return true;
00440 p = path(*it) / OROCOS_TARGET_NAME / dir / (file + FULL_COMPONENT_SUFFIX);
00441 tryouts.push_back( p.string() );
00442 if (is_regular_file( p ) && loadInProcess( p.string(), makeShortFilename(file), true ) )
00443 return true;
00444 p = path(*it) / OROCOS_TARGET_NAME / dir / ("lib" + file + FULL_COMPONENT_SUFFIX);
00445 tryouts.push_back( p.string() );
00446 if (is_regular_file( p ) && loadInProcess( p.string(), makeShortFilename(file), true ) )
00447 return true;
00448 }
00449 log(Debug) << "No such library found in path: " << name << ". Tried:"<< endlog();
00450 for(vector<string>::iterator it=tryouts.begin(); it != tryouts.end(); ++it)
00451 log(Debug) << *it << endlog();
00452 return false;
00453 }
00454
00455 bool ComponentLoader::isImported(string type_name)
00456 {
00457 if (ComponentFactories::Instance().find(type_name) != ComponentFactories::Instance().end() )
00458 return true;
00459 if (find(loadedPackages.begin(), loadedPackages.end(), type_name) != loadedPackages.end())
00460 return true;
00461
00462
00463 if ( type_name == "ocl" && TypekitRepository::hasTypekit("OCLTypekit")) {
00464 return true;
00465 }
00466 return false;
00467 }
00468
00469
00470 bool ComponentLoader::loadInProcess(string file, string libname, bool log_error) {
00471 path p(file);
00472 char* error;
00473 void* handle;
00474 bool success=false;
00475
00476
00477
00478
00479
00480 std::vector<LoadedLib>::iterator lib = loadedLibs.begin();
00481 while (lib != loadedLibs.end()) {
00482
00483 if ( lib->shortname == libname) {
00484 log(Warning) <<"Library "<< lib->filename <<" already loaded... " ;
00485
00486 bool can_unload = true;
00487 CompList::iterator cit;
00488 for( std::vector<std::string>::iterator ctype = lib->components_type.begin(); ctype != lib->components_type.end() && can_unload; ++ctype) {
00489 for ( cit = comps.begin(); cit != comps.end(); ++cit) {
00490 if( (*ctype) == cit->second.type ) {
00491
00492 log(Warning) << "can NOT reload library because of the instance " << cit->second.type <<"::"<<cit->second.instance->getName() <<endlog();
00493 can_unload = false;
00494 }
00495 }
00496 }
00497 if( can_unload ) {
00498 log(Warning) << "try to RELOAD"<<endlog();
00499 dlclose(lib->handle);
00500
00501 std::vector<LoadedLib>::iterator lib_un = lib;
00502 loadedLibs.erase(lib_un);
00503 lib = loadedLibs.end();
00504 }
00505 else
00506 return false;
00507 }
00508 else lib++;
00509 }
00510
00511
00512 if(!isCompatibleComponent(file))
00513 {
00514 if(log_error)
00515 log(Error) << "Could not load library '"<< p.string() <<"': incompatible." <<endlog();
00516 return false;
00517 }
00518
00519 handle = dlopen ( p.string().c_str(), RTLD_NOW | RTLD_GLOBAL );
00520
00521 if (!handle) {
00522 if ( log_error ) {
00523 log(Error) << "Could not load library '"<< p.string() <<"':"<<endlog();
00524 log(Error) << dlerror() << endlog();
00525 }
00526 return false;
00527 }
00528
00529
00530 log(Debug)<<"Succesfully loaded "<<libname<<endlog();
00531 LoadedLib loading_lib(file, libname, handle);
00532 dlerror();
00533
00534
00535 FactoryMap* (*getfactory)(void) = 0;
00536 vector<string> (*getcomponenttypes)(void) = 0;
00537 FactoryMap* fmap = 0;
00538 getfactory = (FactoryMap*(*)(void))( dlsym(handle, "getComponentFactoryMap") );
00539 if ((error = dlerror()) == NULL) {
00540
00541 fmap = (*getfactory)();
00542 ComponentFactories::Instance().insert( fmap->begin(), fmap->end() );
00543 log(Info) << "Loaded multi component library '"<< file <<"'"<<endlog();
00544 getcomponenttypes = (vector<string>(*)(void))(dlsym(handle, "getComponentTypeNames"));
00545 if ((error = dlerror()) == NULL) {
00546 log(Debug) << "Components:";
00547 vector<string> ctypes = getcomponenttypes();
00548 for (vector<string>::iterator it = ctypes.begin(); it != ctypes.end(); ++it)
00549 log(Debug) <<" "<< *it;
00550 log(Debug) << endlog();
00551 }
00552 loadedLibs.push_back(loading_lib);
00553 success = true;
00554 }
00555
00556
00557 dlerror();
00558
00559 RTT::TaskContext* (*factory)(std::string) = 0;
00560 std::string(*tname)(void) = 0;
00561 factory = (RTT::TaskContext*(*)(std::string))(dlsym(handle, "createComponent") );
00562 string create_error;
00563 error = dlerror();
00564 if (error) create_error = error;
00565 tname = (std::string(*)(void))(dlsym(handle, "getComponentType") );
00566 string gettype_error;
00567 error = dlerror();
00568 if (error) gettype_error = error;
00569 if ( factory && tname ) {
00570 std::string cname = (*tname)();
00571 if ( ComponentFactories::Instance().count(cname) == 1 ) {
00572 log(Warning) << "Component type name "<<cname<<" already used: overriding."<<endlog();
00573 }
00574 ComponentFactories::Instance()[cname] = factory;
00575 log(Info) << "Loaded component type '"<< cname <<"'"<<endlog();
00576 loading_lib.components_type.push_back( cname );
00577 loadedLibs.push_back(loading_lib);
00578 success = true;
00579 }
00580
00581 if (success) return true;
00582
00583 log(Error) <<"Unloading "<< loading_lib.filename <<": not a valid component library:" <<endlog();
00584 if (!create_error.empty())
00585 log(Error) << " " << create_error << endlog();
00586 if (!gettype_error.empty())
00587 log(Error) << " " << gettype_error << endlog();
00588 dlclose(handle);
00589 return false;
00590 }
00591
00592 std::vector<std::string> ComponentLoader::listComponentTypes() const {
00593 vector<string> names;
00594 OCL::FactoryMap::iterator it;
00595 for( it = OCL::ComponentFactories::Instance().begin(); it != OCL::ComponentFactories::Instance().end(); ++it) {
00596 names.push_back( it->first );
00597 }
00598 return names;
00599 }
00600
00601 std::string ComponentLoader::getComponentPath() const {
00602 string ret = component_path;
00603
00604 if ( ret.length() && ret[ ret.length() -1 ] != default_delimiter )
00605 ret += default_delimiter;
00606 return ret;
00607 }
00608
00609 void ComponentLoader::setComponentPath( std::string const& newpath ) {
00610 component_path = newpath;
00611 }
00612
00613
00614 RTT::TaskContext *ComponentLoader::loadComponent(const std::string & name, const std::string & type)
00615 {
00616 TaskContext* instance = 0;
00617 RTT::TaskContext* (*factory)(std::string name) = 0;
00618 log(Debug) << "Trying to create component "<< name <<" of type "<< type << endlog();
00619
00620
00621 if ( ComponentFactories::Instance().count(type) == 1 ) {
00622 factory = ComponentFactories::Instance()[ type ];
00623 if (factory == 0 ) {
00624 log(Error) <<"Found empty factory for Component type "<<type<<endlog();
00625 return 0;
00626 }
00627 }
00628
00629 if ( factory ) {
00630 log(Debug) <<"Found factory for Component type "<<type<<endlog();
00631 } else {
00632 log(Error) << "Unable to create Orocos Component '"<<type<<"': unknown component type." <<endlog();
00633 return 0;
00634 }
00635
00636 comps[name].type = type;
00637
00638 try {
00639 comps[name].instance = instance = (*factory)(name);
00640 } catch(...) {
00641 log(Error) <<"The constructor of component type "<<type<<" threw an exception!"<<endlog();
00642 }
00643
00644 if ( instance == 0 ) {
00645 log(Error) <<"Failed to load component with name "<<name<<": refused to be created."<<endlog();
00646 }
00647 return instance;
00648 }
00649
00650 bool ComponentLoader::unloadComponent( RTT::TaskContext* tc ) {
00651 if (!tc)
00652 return false;
00653 CompList::iterator it;
00654 it = comps.find( tc->getName() );
00655
00656 if ( it != comps.end() ) {
00657 delete tc;
00658 comps.erase(it);
00659 return true;
00660 }
00661 log(Error) <<"Refusing to unload a component I didn't load myself."<<endlog();
00662 return false;
00663 }
00664
00665 std::vector<std::string> ComponentLoader::listComponents() const
00666 {
00667 vector<string> names( comps.size() );
00668 for(map<string,ComponentData>::const_iterator it = comps.begin(); it != comps.end(); ++it)
00669 names.push_back( it->first );
00670 return names;
00671 }
00672
00673 bool ComponentLoader::isCompatibleComponent(std::string const& filepath)
00674 {
00675 path p(filepath);
00676
00677 #if BOOST_VERSION >= 104600
00678 string libname = p.filename().string();
00679 #else
00680 string libname = p.filename();
00681 #endif
00682
00683
00684
00685 #ifdef _WIN32
00686
00687
00688
00689 if(!hasEnding(libname, FULL_COMPONENT_SUFFIX))
00690 {
00691
00692 return false;
00693 }
00694 #endif // _WIN32
00695
00696
00697
00698
00699
00700 return true;
00701 }