diff --git a/Core/Code/Service/mitkCoreActivator.cpp b/Core/Code/Service/mitkCoreActivator.cpp index dfeffc4e2e..3ffb401c84 100644 --- a/Core/Code/Service/mitkCoreActivator.cpp +++ b/Core/Code/Service/mitkCoreActivator.cpp @@ -1,54 +1,59 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkRenderingManager.h" #include "mitkPlanePositionManager.h" #include /* * This is the module activator for the "Mitk" module. It registers core services * like ... */ class MitkCoreActivator : public mitk::ModuleActivator { public: void Load(mitk::ModuleContext* context) { //m_RenderingManager = mitk::RenderingManager::New(); //context->RegisterService(renderingManager.GetPointer()); m_PlanePositionManager = mitk::PlanePositionManagerService::New(); m_PlanePositionManagerReg = context->RegisterService(m_PlanePositionManager); } void Unload(mitk::ModuleContext* ) { + // The mitk::ModuleContext* argument of the Unload() method + // will always be 0 for the Mitk library. It makes no sense + // to use it at this stage anyway, since all libraries which + // know about the module system have already been unloaded. + m_PlanePositionManagerReg.Unregister(); m_PlanePositionManager = 0; } private: mitk::RenderingManager::Pointer m_RenderingManager; mitk::PlanePositionManagerService::Pointer m_PlanePositionManager; mitk::ServiceRegistration m_PlanePositionManagerReg; }; MITK_EXPORT_MODULE_ACTIVATOR(Mitk, MitkCoreActivator) diff --git a/Core/Code/Service/mitkModuleRegistry.cpp b/Core/Code/Service/mitkModuleRegistry.cpp index 58a7522228..6a321a9859 100644 --- a/Core/Code/Service/mitkModuleRegistry.cpp +++ b/Core/Code/Service/mitkModuleRegistry.cpp @@ -1,205 +1,215 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkModuleRegistry.h" #include "mitkModule.h" #include "mitkModuleInfo.h" #include "mitkModuleContext.h" +#include "mitkModuleActivator.h" #include "mitkCoreModuleContext_p.h" #include "mitkStaticInit.h" #include #include #include namespace mitk { #ifdef MITK_HAS_UNORDERED_MAP_H typedef std::unordered_map ModuleMap; #else typedef itk::hash_map ModuleMap; #endif MITK_GLOBAL_STATIC(CoreModuleContext, coreModuleContext) template struct ModuleDeleter { void operator()(GlobalStatic& globalStatic) const { ModuleMap* moduleMap = globalStatic.pointer; for (ModuleMap::const_iterator i = moduleMap->begin(); i != moduleMap->end(); ++i) { delete i->second; } DefaultGlobalStaticDeleter defaultDeleter; defaultDeleter(globalStatic); } }; /** * Table of all installed modules in this framework. * Key is the module id. */ MITK_GLOBAL_STATIC_WITH_DELETER(ModuleMap, modules, ModuleDeleter) typedef itk::SimpleFastMutexLock MutexType; typedef itk::MutexLockHolder MutexLocker; /** * Lock for protecting the modules object */ MITK_GLOBAL_STATIC(MutexType, modulesLock) /** * Lock for protecting the register count */ MITK_GLOBAL_STATIC(MutexType, countLock) void ModuleRegistry::Register(ModuleInfo* info) { static long regCount = 0; if (info->id > 0) { // The module was already registered Module* module = 0; { MutexLocker lock(*modulesLock()); module = modules()->operator[](info->id); assert(module != 0); } module->Start(); } else { Module* module = 0; // check if the module is reloaded { MutexLocker lock(*modulesLock()); ModuleMap* map = modules(); for (ModuleMap::const_iterator i = map->begin(); i != map->end(); ++i) { if (i->second->GetLocation() == info->location) { module = i->second; info->id = module->GetModuleId(); } } } if (!module) { module = new Module(); countLock()->Lock(); info->id = ++regCount; countLock()->Unlock(); module->Init(coreModuleContext(), info); MutexLocker lock(*modulesLock()); ModuleMap* map = modules(); map->insert(std::make_pair(info->id, module)); } else { module->Init(coreModuleContext(), info); } module->Start(); } } void ModuleRegistry::UnRegister(const ModuleInfo* info) { - // If we are unregistering the mitkCore module, there is - // nothing to do. - if (info->id == 1) return; + // If we are unregistering the Mitk module, we just call + // the module activators Unload() method (if there is a + // module activator). Since we cannot be sure that the + // ModuleContext for the Mitk library is still valid, we + // just pass a null-pointer. Using the module context during + // static deinitalization time of the Mitk library makes + // no sense anyway. + if (info->id == 1 && info->activatorHook) + { + info->activatorHook()->Unload(0); + return; + } Module* curr = 0; { MutexLocker lock(*modulesLock()); curr = modules()->operator[](info->id); assert(curr != 0); } curr->Stop(); curr->Uninit(); } Module* ModuleRegistry::GetModule(long id) { MutexLocker lock(*modulesLock()); ModuleMap::const_iterator iter = modules()->find(id); if (iter != modules()->end()) { return iter->second; } return 0; } Module* ModuleRegistry::GetModule(const std::string& name) { MutexLocker lock(*modulesLock()); ModuleMap::const_iterator iter = modules()->begin(); ModuleMap::const_iterator iterEnd = modules()->end(); for (; iter != iterEnd; ++iter) { if (iter->second->GetName() == name) { return iter->second; } } return 0; } void ModuleRegistry::GetModules(std::vector& m) { MutexLocker lock(*modulesLock()); ModuleMap* map = modules(); ModuleMap::const_iterator iter = map->begin(); ModuleMap::const_iterator iterEnd = map->end(); for (; iter != iterEnd; ++iter) { m.push_back(iter->second); } } void ModuleRegistry::GetLoadedModules(std::vector& m) { MutexLocker lock(*modulesLock()); ModuleMap::const_iterator iter = modules()->begin(); ModuleMap::const_iterator iterEnd = modules()->end(); for (; iter != iterEnd; ++iter) { if (iter->second->IsLoaded()) { m.push_back(iter->second); } } } } diff --git a/Core/Code/Testing/TestModules/libA/mitkTestModuleA.cpp b/Core/Code/Testing/TestModules/libA/mitkTestModuleA.cpp index 0757d3d60a..6929b8c657 100644 --- a/Core/Code/Testing/TestModules/libA/mitkTestModuleA.cpp +++ b/Core/Code/Testing/TestModules/libA/mitkTestModuleA.cpp @@ -1,62 +1,72 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkTestModuleAService.h" #include #include #include #include namespace mitk { struct TestModuleA : public itk::LightObject, public TestModuleAService { - TestModuleA(ModuleContext* mc) + TestModuleA(ModuleContext* mc) : m_UnloadCalled(0) { MITK_INFO << "Registering TestModuleAService"; mc->RegisterService(this); } + void SetUnloadedFlag(bool* flag) + { + m_UnloadCalled = flag; + } + + bool* m_UnloadCalled; + }; class TestModuleAActivator : public ModuleActivator { public: void Load(ModuleContext* context) { s = new TestModuleA(context); } void Unload(ModuleContext*) { - + if (s->m_UnloadCalled) + { + *(s->m_UnloadCalled) = true; + } } private: itk::SmartPointer s; }; } MITK_EXPORT_MODULE_ACTIVATOR(TestModuleA, mitk::TestModuleAActivator) diff --git a/Core/Code/Testing/TestModules/libA/mitkTestModuleAService.h b/Core/Code/Testing/TestModules/libA/mitkTestModuleAService.h index 3df74eaf90..7564f6bf87 100644 --- a/Core/Code/Testing/TestModules/libA/mitkTestModuleAService.h +++ b/Core/Code/Testing/TestModules/libA/mitkTestModuleAService.h @@ -1,35 +1,37 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKTESTMODULEASERVICE_H #define MITKTESTMODULEASERVICE_H #include namespace mitk { struct TestModuleAService { virtual ~TestModuleAService() {} + + virtual void SetUnloadedFlag(bool* flag) = 0; }; } MITK_DECLARE_SERVICE_INTERFACE(mitk::TestModuleAService, "org.mitk.TestModuleAService") #endif // MITKTESTMODULEASERVICE_H diff --git a/Core/Code/Testing/mitkModuleTest.cpp b/Core/Code/Testing/mitkModuleTest.cpp index ddb65f35d6..f5d4db3e12 100644 --- a/Core/Code/Testing/mitkModuleTest.cpp +++ b/Core/Code/Testing/mitkModuleTest.cpp @@ -1,354 +1,363 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include #include #include #include #include "mitkTestUtilSharedLibrary.cpp" #include #include +#include "TestModules/libA/mitkTestModuleAService.h" + class ModuleListener { public: ModuleListener(mitk::ModuleContext* mc) : mc(mc) {} void ModuleChanged(const mitk::ModuleEvent& event) { moduleEvents.push_back(event); MITK_DEBUG << "ModuleEvent:" << event; } void ServiceChanged(const mitk::ServiceEvent& event) { serviceEvents.push_back(event); MITK_DEBUG << "ServiceEvent:" << event; } mitk::ModuleEvent GetModuleEvent() const { if (moduleEvents.empty()) { return mitk::ModuleEvent(); } return moduleEvents.back(); } mitk::ServiceEvent GetServiceEvent() const { if (serviceEvents.empty()) { return mitk::ServiceEvent(); } return serviceEvents.back(); } bool CheckListenerEvents( bool pexp, mitk::ModuleEvent::Type ptype, bool sexp, mitk::ServiceEvent::Type stype, mitk::Module* moduleX, mitk::ServiceReference* servX) { std::vector pEvts; std::vector seEvts; if (pexp) pEvts.push_back(mitk::ModuleEvent(ptype, moduleX)); if (sexp) seEvts.push_back(mitk::ServiceEvent(stype, *servX)); return CheckListenerEvents(pEvts, seEvts); } bool CheckListenerEvents( const std::vector& pEvts, const std::vector& seEvts) { bool listenState = true; // assume everything will work if (pEvts.size() != moduleEvents.size()) { listenState = false; MITK_DEBUG << "*** Module event mismatch: expected " << pEvts.size() << " event(s), found " << moduleEvents.size() << " event(s)."; const std::size_t max = pEvts.size() > moduleEvents.size() ? pEvts.size() : moduleEvents.size(); for (std::size_t i = 0; i < max; ++i) { const mitk::ModuleEvent& pE = i < pEvts.size() ? pEvts[i] : mitk::ModuleEvent(); const mitk::ModuleEvent& pR = i < moduleEvents.size() ? moduleEvents[i] : mitk::ModuleEvent(); MITK_DEBUG << " " << pE << " - " << pR; } } else { for (std::size_t i = 0; i < pEvts.size(); ++i) { const mitk::ModuleEvent& pE = pEvts[i]; const mitk::ModuleEvent& pR = moduleEvents[i]; if (pE.GetType() != pR.GetType() || pE.GetModule() != pR.GetModule()) { listenState = false; MITK_DEBUG << "*** Wrong module event: " << pR << " expected " << pE; } } } if (seEvts.size() != serviceEvents.size()) { listenState = false; MITK_DEBUG << "*** Service event mismatch: expected " << seEvts.size() << " event(s), found " << serviceEvents.size() << " event(s)."; const std::size_t max = seEvts.size() > serviceEvents.size() ? seEvts.size() : serviceEvents.size(); for (std::size_t i = 0; i < max; ++i) { const mitk::ServiceEvent& seE = i < seEvts.size() ? seEvts[i] : mitk::ServiceEvent(); const mitk::ServiceEvent& seR = i < serviceEvents.size() ? serviceEvents[i] : mitk::ServiceEvent(); MITK_DEBUG << " " << seE << " - " << seR; } } else { for (std::size_t i = 0; i < seEvts.size(); ++i) { const mitk::ServiceEvent& seE = seEvts[i]; const mitk::ServiceEvent& seR = serviceEvents[i]; if (seE.GetType() != seR.GetType() || (!(seE.GetServiceReference() == seR.GetServiceReference()))) { listenState = false; MITK_DEBUG << "*** Wrong service event: " << seR << " expected " << seE; } } } moduleEvents.clear(); serviceEvents.clear(); return listenState; } private: mitk::ModuleContext* const mc; std::vector serviceEvents; std::vector moduleEvents; }; // Verify information from the ModuleInfo struct void frame005a(mitk::ModuleContext* mc) { mitk::Module* m = mc->GetModule(); // check expected headers MITK_TEST_CONDITION("MitkTestDriver" == m->GetName(), "Test module name"); // MITK_DEBUG << "Version: " << m->GetVersion(); MITK_TEST_CONDITION(mitk::ModuleVersion(0,1,0) == m->GetVersion(), "Test module version") } // Get context id, location and status of the module void frame010a(mitk::ModuleContext* mc) { mitk::Module* m = mc->GetModule(); long int contextid = m->GetModuleId(); MITK_DEBUG << "CONTEXT ID:" << contextid; std::string location = m->GetLocation(); MITK_DEBUG << "LOCATION:" << location; MITK_TEST_CONDITION(!location.empty(), "Test for non-empty module location") MITK_TEST_CONDITION(m->IsLoaded(), "Test for loaded flag") } //---------------------------------------------------------------------------- //Test result of GetService(mitk::ServiceReference()). Should throw std::invalid_argument void frame018a(mitk::ModuleContext* mc) { try { itk::LightObject* obj = mc->GetService(mitk::ServiceReference()); MITK_DEBUG << "Got service object = " << obj->GetNameOfClass() << ", excpected std::invalid_argument exception"; MITK_TEST_FAILED_MSG(<< "Got service object, excpected std::invalid_argument exception") } catch (const std::invalid_argument& ) {} catch (...) { MITK_TEST_FAILED_MSG(<< "Got wrong exception, expected std::invalid_argument") } } // Load libA and check that it exists and that the service it registers exists, // also check that the expected events occur void frame020a(mitk::ModuleContext* mc, ModuleListener& listener, mitk::SharedLibraryHandle& libA) { try { libA.Load(); } catch (const std::exception& e) { MITK_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } mitk::Module* moduleA = mitk::ModuleRegistry::GetModule("TestModuleA"); MITK_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for existing moudle TestModuleA") MITK_TEST_CONDITION(moduleA->GetName() == "TestModuleA", "Test module name") // Check if libA registered the expected service try { mitk::ServiceReference sr1 = mc->GetServiceReference("org.mitk.TestModuleAService"); itk::LightObject* o1 = mc->GetService(sr1); MITK_TEST_CONDITION(o1 != 0, "Test if service object found"); try { MITK_TEST_CONDITION(mc->UngetService(sr1), "Test if Service UnGet returns true"); } catch (const std::logic_error le) { MITK_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what()) } // check the listeners for events std::vector pEvts; pEvts.push_back(mitk::ModuleEvent(mitk::ModuleEvent::LOADING, moduleA)); pEvts.push_back(mitk::ModuleEvent(mitk::ModuleEvent::LOADED, moduleA)); std::vector seEvts; seEvts.push_back(mitk::ServiceEvent(mitk::ServiceEvent::REGISTERED, sr1)); MITK_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } catch (const mitk::ServiceException& /*se*/) { MITK_TEST_FAILED_MSG(<< "test moudle, expected service not found"); } MITK_TEST_CONDITION(moduleA->IsLoaded() == true, "Test if loaded correctly"); } // Unload libA and check for correct events void frame030b(mitk::ModuleContext* mc, ModuleListener& listener, mitk::SharedLibraryHandle& libA) { mitk::Module* moduleA = mitk::ModuleRegistry::GetModule("TestModuleA"); MITK_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for non-null module") mitk::ServiceReference sr1 = mc->GetServiceReference("org.mitk.TestModuleAService"); MITK_TEST_CONDITION(sr1, "Test for valid service reference") + mitk::TestModuleAService* moduleAService = mc->GetService(sr1); + MITK_TEST_CONDITION(moduleAService != 0, "Test for valid service object") + + bool unloadCalled = false; + moduleAService->SetUnloadedFlag(&unloadCalled); + try { libA.Unload(); + MITK_TEST_CONDITION(unloadCalled == true, "Test if the mitk::ModuleActivator::Unload() method was called") MITK_TEST_CONDITION(moduleA->IsLoaded() == false, "Test for unloaded state") } catch (const std::exception& e) { MITK_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what()) } std::vector pEvts; pEvts.push_back(mitk::ModuleEvent(mitk::ModuleEvent::UNLOADING, moduleA)); pEvts.push_back(mitk::ModuleEvent(mitk::ModuleEvent::UNLOADED, moduleA)); std::vector seEvts; seEvts.push_back(mitk::ServiceEvent(mitk::ServiceEvent::UNREGISTERING, sr1)); MITK_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } struct LocalListener { void ServiceChanged(const mitk::ServiceEvent&) {} }; // Add a service listener with a broken LDAP filter to Get an exception void frame045a(mitk::ModuleContext* mc) { LocalListener sListen1; std::string brokenFilter = "A broken LDAP filter"; try { mc->AddServiceListener(&sListen1, &LocalListener::ServiceChanged, brokenFilter); } catch (const std::invalid_argument& /*ia*/) { //assertEquals("InvalidSyntaxException.GetFilter should be same as input string", brokenFilter, ise.GetFilter()); } catch (...) { MITK_TEST_FAILED_MSG(<< "test module, wrong exception on broken LDAP filter:"); } } int mitkModuleTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("ModuleTest"); mitk::ModuleContext* mc = mitk::GetModuleContext(); ModuleListener listener(mc); try { mc->AddModuleListener(&listener, &ModuleListener::ModuleChanged); } catch (const std::logic_error& ise) { MITK_TEST_OUTPUT( << "module listener registration failed " << ise.what() ); throw; } try { mc->AddServiceListener(&listener, &ModuleListener::ServiceChanged); } catch (const std::logic_error& ise) { MITK_TEST_OUTPUT( << "service listener registration failed " << ise.what() ); throw; } frame005a(mc); frame010a(mc); frame018a(mc); mitk::SharedLibraryHandle libA("TestModuleA"); frame020a(mc, listener, libA); frame030b(mc, listener, libA); frame045a(mc); mc->RemoveModuleListener(&listener, &ModuleListener::ModuleChanged); mc->RemoveServiceListener(&listener, &ModuleListener::ServiceChanged); MITK_TEST_END() }