diff --git a/Modules/Applications/AppTest/test/otbWrapperApplicationDocTests.cxx b/Modules/Applications/AppTest/test/otbWrapperApplicationDocTests.cxx index 01dc19cdbb019791042345d3c05e593c0b48f0a6..1fbaf04a5943511a562c4b80e7874ee9d798126f 100644 --- a/Modules/Applications/AppTest/test/otbWrapperApplicationDocTests.cxx +++ b/Modules/Applications/AppTest/test/otbWrapperApplicationDocTests.cxx @@ -39,7 +39,6 @@ int otbWrapperApplicationDocTest(int argc, char* argv[]) std::copy(argv + 1, argv + argc, std::back_inserter(modulePathList)); // Load the path in the environment - std::string specificEnv("ITK_AUTOLOAD_PATH="); std::list<std::string>::const_iterator it = modulePathList.begin(); while( it != modulePathList.end() ) { diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplicationRegistry.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplicationRegistry.h index 4eeb7d7b12279cfbc6470962440dff6fe850801d..ba6a46771e7ae17bda62838de7a629f9af490790 100644 --- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplicationRegistry.h +++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplicationRegistry.h @@ -58,16 +58,21 @@ public: /** Add the specified path to the list of application search path */ static void AddApplicationPath(std::string path); + /** Return the application search path */ + static std::string GetApplicationPath(); + /** Return the list of available applications */ - static std::vector<std::string> GetAvailableApplications(); + static std::vector<std::string> GetAvailableApplications(bool useFactory=true); /** Create the specified Application */ - static Application::Pointer CreateApplication(const std::string& applicationName); + static Application::Pointer CreateApplication(const std::string& applicationName, bool useFactory=true); /** Create the specified Application (faster) * method using dynamic library name to load the right module */ static Application::Pointer CreateApplicationFaster(const std::string& applicationName); + /** Clean registry by releasing unused modules */ + static void CleanRegistry(); protected: ApplicationRegistry(); @@ -76,8 +81,9 @@ protected: private: ApplicationRegistry(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented - - static void RefreshApplicationFactories(); + + /** Load an application from a shared library */ + static Application::Pointer LoadApplicationFromPath(std::string path,std::string name); }; diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplicationRegistry.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplicationRegistry.cxx index 49875353c22c9f4aefdd3968d428f05b0ea61e32..52c050ebf578d65b89bd7ca189c70cd1bc80347f 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplicationRegistry.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplicationRegistry.cxx @@ -20,6 +20,10 @@ #include "otbMacro.h" #include "itksys/SystemTools.hxx" #include "itkDynamicLoader.h" +#include "itkDirectory.h" +#include "itkMutexLock.h" +#include "itkMutexLockHolder.h" + #include <iterator> namespace otb @@ -27,6 +31,97 @@ namespace otb namespace Wrapper { +// Constant : environment variable for application path +static const char OTB_APPLICATION_VAR[] = "OTB_APPLICATION_PATH"; + +class ApplicationPrivateRegistry +{ +public: + typedef std::pair<Application*, void* > AppHandlePairType; + typedef std::list<AppHandlePairType> AppHandleContainerType; + + /** Add a pair (application, library handle) in the private registry */ + bool AddPair(Application *app, void *handle) + { + AppHandlePairType pair; + if (app && handle) + { + // mutex lock to ensure thread safety + itk::MutexLockHolder<itk::SimpleMutexLock> mutexHolder(m_Mutex); + pair.first = app; + pair.second = handle; + m_Container.push_back(pair); + return true; + } + return false; + } + + /** When an application is deleted, unregister its pointer from private registry */ + void UnregisterApp(const Application *app) + { + if (app) + { + // mutex lock to ensure thread safety + itk::MutexLockHolder<itk::SimpleMutexLock> mutexHolder(m_Mutex); + AppHandleContainerType::iterator it = m_Container.begin(); + while (it != m_Container.end()) + { + if ((*it).first == app) + { + (*it).first = NULL; + } + ++it; + } + } + } + + /** Release the library handles from applications already deleted */ + void ReleaseUnusedHandle() + { + itk::MutexLockHolder<itk::SimpleMutexLock> mutexHolder(m_Mutex); + AppHandleContainerType::iterator it; + for (it = m_Container.begin() ; it != m_Container.end() ; ++it) + { + if ((*it).first == NULL) + { + itk::DynamicLoader::CloseLibrary( (*it).second); + (*it).second = NULL; + } + } + m_Container.remove(AppHandlePairType(NULL,NULL)); + } + + /** close all handles at program exit */ + ~ApplicationPrivateRegistry() + { + AppHandleContainerType::iterator it; + for (it = m_Container.begin() ; it != m_Container.end() ; ++it) + { + itk::DynamicLoader::CloseLibrary( (*it).second); + } + m_Container.clear(); + } + +private: + AppHandleContainerType m_Container; + + itk::SimpleMutexLock m_Mutex; +}; +// static finalizer to close opened libraries +static ApplicationPrivateRegistry m_ApplicationPrivateRegistryGlobal; + +// Define callbacks to unregister applications in ApplicationPrivateRegistry +void DeleteAppCallback(itk::Object *obj,const itk::EventObject &, void *) + { + Application *appPtr = dynamic_cast<Application*>(obj); + m_ApplicationPrivateRegistryGlobal.UnregisterApp(appPtr); + } +void DeleteAppConstCallback(const itk::Object *obj,const itk::EventObject &, void *) + { + const Application *appPtr = dynamic_cast<const Application*>(obj); + m_ApplicationPrivateRegistryGlobal.UnregisterApp(appPtr); + } + ApplicationRegistry::ApplicationRegistry() { } @@ -39,7 +134,7 @@ void ApplicationRegistry::SetApplicationPath(std::string newpath) { std::ostringstream putEnvPath; - putEnvPath << "OTB_APPLICATION_PATH=" << newpath; + putEnvPath << OTB_APPLICATION_VAR << "=" << newpath; // do NOT use putenv() directly, since the string memory must be managed carefully itksys::SystemTools::PutEnv(putEnvPath.str().c_str()); @@ -49,10 +144,10 @@ void ApplicationRegistry::AddApplicationPath(std::string newpath) { std::ostringstream putEnvPath; - putEnvPath << "OTB_APPLICATION_PATH="; + putEnvPath << OTB_APPLICATION_VAR <<"="; // Can be NULL if the env var is not set - const char* currentEnv = itksys::SystemTools::GetEnv("OTB_APPLICATION_PATH"); + const char* currentEnv = itksys::SystemTools::GetEnv(OTB_APPLICATION_VAR); #if defined(WIN32) const char pathSeparator = ';'; @@ -71,35 +166,48 @@ ApplicationRegistry::AddApplicationPath(std::string newpath) itksys::SystemTools::PutEnv(putEnvPath.str().c_str()); } +std::string +ApplicationRegistry::GetApplicationPath() +{ + std::string ret; + // Can be NULL if the env var is not set + const char* currentEnv = itksys::SystemTools::GetEnv(OTB_APPLICATION_VAR); + if (currentEnv) + { + ret = std::string(currentEnv); + } + return ret; +} + Application::Pointer -ApplicationRegistry::CreateApplication(const std::string& name) +ApplicationRegistry::CreateApplication(const std::string& name, bool useFactory) { ApplicationPointer appli; - // Fast search + // Fast search : uses OTB_APPLICATION_PATH appli = ApplicationRegistry::CreateApplicationFaster(name); if (appli.IsNotNull()) { return appli; } - // Classic search - ApplicationRegistry::RefreshApplicationFactories(); - - LightObject::Pointer possibleApp = itk::ObjectFactoryBase::CreateInstance(name.c_str()); - - if (possibleApp.IsNotNull()) + // Classic search : uses factories registered by ITK ( see ITK_AUTOLOAD_PATH ) + if (useFactory) { - // Downcast - Application* app = dynamic_cast<Application*> (possibleApp.GetPointer()); - if (app) + LightObject::Pointer possibleApp = itk::ObjectFactoryBase::CreateInstance(name.c_str()); + if (possibleApp.IsNotNull()) { - appli = app; - appli->Init(); - } - else - { - otbMsgDevMacro( << "Error ApplicationRegistry factory did not return an Application: " << possibleApp->GetNameOfClass() << std::endl ); + // Downcast + Application* app = dynamic_cast<Application*> (possibleApp.GetPointer()); + if (app) + { + appli = app; + appli->Init(); + } + else + { + otbMsgDevMacro( << "Error ApplicationRegistry factory did not return an Application: " << possibleApp->GetNameOfClass() << std::endl ); + } } } @@ -132,20 +240,12 @@ ApplicationRegistry::CreateApplicationFaster(const std::string& name) const char sep = '/'; #endif - const char* otbAppPath = itksys::SystemTools::GetEnv("OTB_APPLICATION_PATH"); - const char* itkLoadPath = itksys::SystemTools::GetEnv("ITK_AUTOLOAD_PATH"); - - std::ostringstream currentPath; - if (otbAppPath) + std::string otbAppPath = GetApplicationPath(); + std::vector<itksys::String> pathList; + if (!otbAppPath.empty()) { - currentPath << otbAppPath << pathSeparator; + pathList = itksys::SystemTools::SplitString(otbAppPath.c_str(),pathSeparator,false); } - if (itkLoadPath) - { - currentPath << itkLoadPath; - } - - std::vector<itksys::String> pathList = itksys::SystemTools::SplitString(currentPath.str().c_str(),pathSeparator,false); for (unsigned int i=0 ; i<pathList.size() ; ++i) { std::string possiblePath = pathList[i]; @@ -155,41 +255,10 @@ ApplicationRegistry::CreateApplicationFaster(const std::string& name) } possiblePath += appLibName.str(); - if (itksys::SystemTools::FileExists(possiblePath.c_str(),true)) + appli = LoadApplicationFromPath(possiblePath,name); + if (appli.IsNotNull()) { - itk::LibHandle lib = itk::DynamicLoader::OpenLibrary( possiblePath.c_str() ); - if ( lib ) - { - /** - * Look for the symbol itkLoad in the library - */ - ITK_LOAD_FUNCTION loadfunction = - ( ITK_LOAD_FUNCTION ) itk::DynamicLoader::GetSymbolAddress(lib, "itkLoad"); - /** - * if the symbol is found call it to create the factory - * from the library - */ - if ( loadfunction ) - { - itk::ObjectFactoryBase *newfactory = ( *loadfunction )( ); - // Downcast - ApplicationFactoryBase* appFactory = dynamic_cast<ApplicationFactoryBase*>(newfactory); - - if (appFactory) - { - appli = appFactory->CreateApplication(name.c_str()); - appli->Init(); - break; - } - } - else - { - // In the past, some platforms crashed on the call - // DynamicLoader::CloseLibrary(lib) if the lib has symbols - // that the current executable is using. - itk::DynamicLoader::CloseLibrary(lib); - } - } + break; } } @@ -197,38 +266,88 @@ ApplicationRegistry::CreateApplicationFaster(const std::string& name) } std::vector<std::string> -ApplicationRegistry::GetAvailableApplications() +ApplicationRegistry::GetAvailableApplications(bool useFactory) { ApplicationPointer appli; + std::set<std::string> appSet; + + std::string appPrefix("otbapp_"); + std::string appExtension = itksys::DynamicLoader::LibExtension(); +#ifdef __APPLE__ + appExtension = ".dylib"; +#endif - ApplicationRegistry::RefreshApplicationFactories(); +#if defined(WIN32) + const char pathSeparator = ';'; +#else + const char pathSeparator = ':'; +#endif - std::list<ApplicationPointer> possibleApp; - std::list<LightObject::Pointer> allobjects = itk::ObjectFactoryBase::CreateAllInstance("otbWrapperApplication"); +#ifdef _WIN32 + const char sep = '\\'; +#else + const char sep = '/'; +#endif - // Downcast and Sanity check - for (std::list<LightObject::Pointer>::iterator i = allobjects.begin(); i != allobjects.end(); ++i) + std::string otbAppPath = GetApplicationPath(); + std::vector<itksys::String> pathList; + if (!otbAppPath.empty()) + { + pathList = itksys::SystemTools::SplitString(otbAppPath.c_str(),pathSeparator,false); + } + for (unsigned int k=0 ; k<pathList.size() ; ++k) { - Application* io = dynamic_cast<Application*> (i->GetPointer()); - if (io) + itk::Directory::Pointer dir = itk::Directory::New(); + if (!dir->Load(pathList[k].c_str())) { - possibleApp.push_back(io); + continue; } - else + for (unsigned int i = 0; i < dir->GetNumberOfFiles(); i++) { - otbMsgDevMacro( "Error ApplicationRegistry factory did not return an Application: " << (*i)->GetNameOfClass() << std::endl ); + const char *filename = dir->GetFile(i); + std::string sfilename(filename); + std::string::size_type extPos = sfilename.rfind(appExtension); + std::string::size_type prefixPos = sfilename.find(appPrefix); + + // Check if current file is a shared lib with the right pattern + if (extPos + appExtension.size() == sfilename.size() && + prefixPos == 0) + { + std::string name = sfilename.substr(appPrefix.size(),extPos-appPrefix.size()); + std::string fullpath = pathList[k]; + if (!fullpath.empty() && fullpath[fullpath.size() - 1] != sep) + { + fullpath.push_back(sep); + } + fullpath.append(sfilename); + appli = LoadApplicationFromPath(fullpath,name); + if (appli.IsNotNull()) + { + appSet.insert(name); + } + appli = NULL; + } } } - // Get all the app names - // If ITK_AUTOLOAD_PATH contains several times the same path, then the same app appear several times - // Use a temporary std::set to fix this - std::set<std::string> appSet; - for(std::list<ApplicationPointer>::iterator k = possibleApp.begin(); - k != possibleApp.end(); ++k) + if (useFactory) { - (*k)->Init(); - appSet.insert((*k)->GetName()); + std::list<LightObject::Pointer> allobjects = itk::ObjectFactoryBase::CreateAllInstance("otbWrapperApplication"); + // Downcast and Sanity check + for (std::list<LightObject::Pointer>::iterator i = allobjects.begin(); i != allobjects.end(); ++i) + { + Application* app = dynamic_cast<Application*> (i->GetPointer()); + if (app) + { + app->Init(); + std::string curName(app->GetName()); + appSet.insert(curName); + } + else + { + otbMsgDevMacro( << "Error ApplicationRegistry factory did not return an Application: " << (*i)->GetNameOfClass() << std::endl ); + } + } } std::vector<std::string> appVec; @@ -237,46 +356,57 @@ ApplicationRegistry::GetAvailableApplications() } void -ApplicationRegistry::RefreshApplicationFactories() +ApplicationRegistry::CleanRegistry() { - std::ostringstream putEnvPath; - putEnvPath << "ITK_AUTOLOAD_PATH="; - - // Can be NULL if the env var is not set - const char* currentEnv = itksys::SystemTools::GetEnv("ITK_AUTOLOAD_PATH"); - - // OTB specific application path - const char* otbApplicationPath = itksys::SystemTools::GetEnv("OTB_APPLICATION_PATH"); - -#if defined(WIN32) - const char pathSeparator = ';'; -#else - const char pathSeparator = ':'; -#endif + m_ApplicationPrivateRegistryGlobal.ReleaseUnusedHandle(); +} - if (otbApplicationPath) - { - putEnvPath << otbApplicationPath << pathSeparator; - } +Application::Pointer +ApplicationRegistry::LoadApplicationFromPath(std::string path,std::string name) +{ + Application::Pointer appli; - if (currentEnv) + if (itksys::SystemTools::FileExists(path.c_str(),true)) { - putEnvPath << currentEnv; - } - - // do NOT use putenv() directly, since the string memory must be managed carefully - itksys::SystemTools::PutEnv(putEnvPath.str().c_str()); - - // Reload factories to take into account new path - itk::ObjectFactoryBase::ReHash(); + itk::LibHandle lib = itk::DynamicLoader::OpenLibrary(path.c_str()); + if (lib) + { + /** + * Look for the symbol itkLoad in the library + */ + ITK_LOAD_FUNCTION loadfunction = + ( ITK_LOAD_FUNCTION ) itk::DynamicLoader::GetSymbolAddress(lib, "itkLoad"); + /** + * if the symbol is found call it to create the factory + * from the library + */ + if ( loadfunction ) + { + itk::ObjectFactoryBase *newfactory = ( *loadfunction )( ); + // Downcast + ApplicationFactoryBase* appFactory = dynamic_cast<ApplicationFactoryBase*>(newfactory); - std::ostringstream resetEnvPath; - resetEnvPath << "ITK_AUTOLOAD_PATH="; - if (currentEnv) - { - resetEnvPath << currentEnv; + if (appFactory) + { + appli = appFactory->CreateApplication(name.c_str()); + if (appli.IsNotNull()) + { + appli->Init(); + // register library handle + m_ApplicationPrivateRegistryGlobal.AddPair(appli.GetPointer(), (void*) lib); + // set a callback on DeleteEvent + itk::CStyleCommand::Pointer command = itk::CStyleCommand::New(); + command->SetCallback(&DeleteAppCallback); + command->SetConstCallback(&DeleteAppConstCallback); + appli->AddObserver(itk::DeleteEvent(),command); + return appli; + } + } + } + itk::DynamicLoader::CloseLibrary(lib); + } } - itksys::SystemTools::PutEnv(resetEnvPath.str().c_str()); + return appli; } diff --git a/Modules/Wrappers/CommandLine/src/otbWrapperCommandLineLauncher.cxx b/Modules/Wrappers/CommandLine/src/otbWrapperCommandLineLauncher.cxx index dcc59f91dccf05c975fa1e74e9eaa860a65da8c8..c3cd479b1791c7fdd14906dcffd7166f6b6d91b1 100644 --- a/Modules/Wrappers/CommandLine/src/otbWrapperCommandLineLauncher.cxx +++ b/Modules/Wrappers/CommandLine/src/otbWrapperCommandLineLauncher.cxx @@ -321,9 +321,8 @@ void CommandLineLauncher::LoadApplication() if (m_Application.IsNull()) { std::cerr << "ERROR: Could not find application \"" << moduleName << "\"" << std::endl; - - const char * ITK_AUTOLOAD_PATH = itksys::SystemTools::GetEnv("ITK_AUTOLOAD_PATH"); - std::cerr << "ERROR: Module search path: " << (ITK_AUTOLOAD_PATH ? ITK_AUTOLOAD_PATH : "none (check ITK_AUTOLOAD_PATH)") << std::endl; + std::string modulePath = ApplicationRegistry::GetApplicationPath(); + std::cerr << "ERROR: Module search path: " << (modulePath.empty() ? "none (check OTB_APPLICATION_PATH)" : modulePath) << std::endl; std::vector<std::string> list = ApplicationRegistry::GetAvailableApplications(); if (list.size() == 0) diff --git a/Modules/Wrappers/QtWidget/src/otbApplicationLauncherQt.cxx b/Modules/Wrappers/QtWidget/src/otbApplicationLauncherQt.cxx index 24036ac4ac4ab276a50635fd9da69152547502c9..99e0779a571dd5ebcb88e8b87d1a9f51f7b44290 100644 --- a/Modules/Wrappers/QtWidget/src/otbApplicationLauncherQt.cxx +++ b/Modules/Wrappers/QtWidget/src/otbApplicationLauncherQt.cxx @@ -53,7 +53,6 @@ int main(int argc, char* argv[]) std::copy(argv + 2, argv + argc, std::back_inserter(modulePathList)); // Load the path in the environment - std::string specificEnv("ITK_AUTOLOAD_PATH="); std::list<std::string>::const_iterator it = modulePathList.begin(); while( it != modulePathList.end() ) { @@ -68,9 +67,8 @@ int main(int argc, char* argv[]) { std::cerr << "Could not find application " << moduleName << std::endl; - - const char* modulePath = itksys::SystemTools::GetEnv("ITK_AUTOLOAD_PATH"); - std::cout << "Module search path : " << (modulePath ? modulePath : "") << std::endl; + std::string modulePath = ApplicationRegistry::GetApplicationPath(); + std::cout << "Module search path : " << modulePath << std::endl; std::vector<std::string> list = ApplicationRegistry::GetAvailableApplications(); std::cout << "Available applications : " << (list.empty() ? "None" : "") << std::endl; diff --git a/Modules/Wrappers/QtWidget/test/otbWrapperQtWidgetShowWidget.cxx b/Modules/Wrappers/QtWidget/test/otbWrapperQtWidgetShowWidget.cxx index c3e3086e236552147cf0d71ed0deebde104f74bd..3dae376a4f942ac760dfda3860d59f67b517dc53 100644 --- a/Modules/Wrappers/QtWidget/test/otbWrapperQtWidgetShowWidget.cxx +++ b/Modules/Wrappers/QtWidget/test/otbWrapperQtWidgetShowWidget.cxx @@ -41,7 +41,6 @@ int otbWrapperQtWidgetShowWidget(int argc, char* argv[]) std::copy(argv + 1, argv + argc, std::back_inserter(modulePathList)); // Load the path in the environment - std::string specificEnv("ITK_AUTOLOAD_PATH="); std::list<std::string>::const_iterator it = modulePathList.begin(); while( it != modulePathList.end() ) { diff --git a/Modules/Wrappers/SWIG/test/java/CMakeLists.txt b/Modules/Wrappers/SWIG/test/java/CMakeLists.txt index 993160e4fb8500ce0de0079cac57e5ce1700d888..3b0917f47872a0f4444c3992754904e04f584eb7 100644 --- a/Modules/Wrappers/SWIG/test/java/CMakeLists.txt +++ b/Modules/Wrappers/SWIG/test/java/CMakeLists.txt @@ -1,7 +1,7 @@ include( UseJava ) set(TEST_DRIVER otbTestDriver - --add-before-env ITK_AUTOLOAD_PATH $<TARGET_FILE_DIR:otbapp_Smoothing> + --add-before-env OTB_APPLICATION_PATH $<TARGET_FILE_DIR:otbapp_Smoothing> ) set( PATH_SEPARATOR ":") diff --git a/Modules/Wrappers/SWIG/test/python/CMakeLists.txt b/Modules/Wrappers/SWIG/test/python/CMakeLists.txt index 2eb46785fb95436f872bfec5c724d7070f1ce62b..c93fb38965b0791888e2c613221c388fd7ea0f77 100644 --- a/Modules/Wrappers/SWIG/test/python/CMakeLists.txt +++ b/Modules/Wrappers/SWIG/test/python/CMakeLists.txt @@ -1,7 +1,7 @@ set(TEST_DRIVER otbTestDriver --add-before-env PYTHONPATH "${OTBSWIGWrapper_BINARY_DIR}/src" --add-before-env PYTHONPATH $<TARGET_FILE_DIR:_otbApplication> - --add-before-env ITK_AUTOLOAD_PATH $<TARGET_FILE_DIR:otbapp_Smoothing> ) + --add-before-env OTB_APPLICATION_PATH $<TARGET_FILE_DIR:otbapp_Smoothing> ) add_test( NAME pyTvSmoothing COMMAND ${TEST_DRIVER} Execute