diff --git a/Modules/DynamicMappers/Testing/mitkDynamicMappersTest.cpp b/Modules/DynamicMappers/Testing/mitkDynamicMappersTest.cpp index 3c8d8be68c..71676aa124 100644 --- a/Modules/DynamicMappers/Testing/mitkDynamicMappersTest.cpp +++ b/Modules/DynamicMappers/Testing/mitkDynamicMappersTest.cpp @@ -1,129 +1,142 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDynamicMappers.h" #include "mitkIOUtil.h" #include "usGetModuleContext.h" #include #include /** * \brief Test class for mitkDynamicMappersModule * * This test covers: * - Registering new mapper services * - Retrieving info about all registered services * - Changing a data node's mapper service */ class mitkDynamicMappersTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkDynamicMappersTestSuite); MITK_TEST(RegisterMapperService_SUCCESS); MITK_TEST(RegisterMapperServiceTwice_FAILURE); MITK_TEST(RegisterMapperServiceInvalidDll_FAILURE); MITK_TEST(FetchMapperServiceInfo_SUCCESS); MITK_TEST(ChangeMapperService_SUCCESS); MITK_TEST(ChangeMapperServiceInvalidId_FAILURE); MITK_TEST(ChangeMapperServiceInvalidDataNode_FAILURE); CPPUNIT_TEST_SUITE_END(); public: void setUp() override; void tearDown() override; void RegisterMapperService_SUCCESS(); void RegisterMapperServiceTwice_FAILURE(); void RegisterMapperServiceInvalidDll_FAILURE(); void FetchMapperServiceInfo_SUCCESS(); void ChangeMapperService_SUCCESS(); void ChangeMapperServiceInvalidId_FAILURE(); void ChangeMapperServiceInvalidDataNode_FAILURE(); private: std::shared_ptr instance; // TODO remove hard coded string - std::string dllPath = "D:\\Arbeit\\Programming\\mitk_bin\\MITK-build\\bin\\Debug\\MitkAwesomeMapperService.dll"; - std::string invalidDllPath = ".\\invalid.dll"; - std::set infoKeys; + std::string validLibraryPath = + "D:\\Arbeit\\Programming\\mitk_bin\\MITK-build\\bin\\Debug\\MitkAwesomeMapperService.dll"; + std::string invalidLibraryPath = ".\\invalid.dll"; }; void mitkDynamicMappersTestSuite::setUp() { instance = mitk::DynamicMappers::GetInstance(); } void mitkDynamicMappersTestSuite::tearDown() { instance->UnloadAllSharedLibraries(); - infoKeys.clear(); } void mitkDynamicMappersTestSuite::RegisterMapperService_SUCCESS() { - CPPUNIT_ASSERT_NO_THROW(instance->RegisterMapper(dllPath)); + CPPUNIT_ASSERT_NO_THROW(instance->LoadSharedLibrary(validLibraryPath)); } void mitkDynamicMappersTestSuite::RegisterMapperServiceTwice_FAILURE() { - CPPUNIT_ASSERT_NO_THROW(instance->RegisterMapper(dllPath)); - CPPUNIT_ASSERT_THROW(instance->RegisterMapper(dllPath), mitk::Exception); + CPPUNIT_ASSERT_NO_THROW(instance->LoadSharedLibrary(validLibraryPath)); + CPPUNIT_ASSERT_NO_THROW(instance->LoadSharedLibrary(validLibraryPath)); } void mitkDynamicMappersTestSuite::RegisterMapperServiceInvalidDll_FAILURE() { - CPPUNIT_ASSERT_THROW(instance->RegisterMapper(invalidDllPath), mitk::Exception); + CPPUNIT_ASSERT_THROW(instance->LoadSharedLibrary(invalidLibraryPath), mitk::Exception); } void mitkDynamicMappersTestSuite::FetchMapperServiceInfo_SUCCESS() { - auto infoCollection = instance->GetRegisteredMapperInfo(infoKeys); + CPPUNIT_ASSERT_NO_THROW(instance->GetRegisteredServiceInfo()); + auto infoCollection = instance->GetRegisteredServiceInfo(); auto refs = us::GetModuleContext()->GetServiceReferences(); CPPUNIT_ASSERT(infoCollection.size() == refs.size()); } void mitkDynamicMappersTestSuite::ChangeMapperService_SUCCESS() { - // TODO create test - auto infoCollection = instance->GetRegisteredMapperInfo(infoKeys); - if (!infoCollection.empty()) + auto slotId = mitk::BaseRenderer::StandardMapperSlot::Standard2D; + auto node = mitk::DataNode::New(); + auto dataCollection = mitk::IOUtil::Load(GetTestDataFilePath("brain.nrrd")); + auto infoCollection = instance->GetRegisteredServiceInfo(); + if (dataCollection.empty() || infoCollection.empty()) { - auto info = infoCollection[0]; + CPPUNIT_FAIL("Couldn't create a valid data node for testing"); } + node->SetData(dataCollection[0]); + + auto newMapperId = infoCollection[0].id; + instance->ChangeMapperService(node, newMapperId); + auto newMapper = node->GetMapper(slotId); + CPPUNIT_ASSERT(newMapper->GetNameOfClass() == infoCollection[0].name); + + instance->ChangeMapperService(node, newMapperId); + CPPUNIT_ASSERT(newMapper == node->GetMapper(slotId)); + + instance->ChangeMapperService(node, newMapperId, true); + CPPUNIT_ASSERT(newMapper->GetNameOfClass() == node->GetMapper(slotId)->GetNameOfClass()); + CPPUNIT_ASSERT(newMapper != node->GetMapper(slotId)); } void mitkDynamicMappersTestSuite::ChangeMapperServiceInvalidId_FAILURE() { auto node = mitk::DataNode::New(); auto dataCollection = mitk::IOUtil::Load(GetTestDataFilePath("brain.nrrd")); if (dataCollection.empty()) { CPPUNIT_FAIL("Couldn't create a valid data node for testing"); } node->SetData(dataCollection[0]); CPPUNIT_ASSERT_THROW(instance->ChangeMapperService(node, -1), mitk::Exception); CPPUNIT_ASSERT_THROW(instance->ChangeMapperService(node, -2), mitk::Exception); } void mitkDynamicMappersTestSuite::ChangeMapperServiceInvalidDataNode_FAILURE() { - auto infoCollection = instance->GetRegisteredMapperInfo(infoKeys); - long serviceId = - infoCollection.empty() ? 0 : std::stol(infoCollection[0]["id"]); + auto infoCollection = instance->GetRegisteredServiceInfo(); + long serviceId = infoCollection.empty() ? 0 : infoCollection[0].id; CPPUNIT_ASSERT_THROW(instance->ChangeMapperService(nullptr, serviceId), mitk::Exception); - CPPUNIT_ASSERT_THROW(instance->ChangeMapperService(mitk::DataNode::New(), serviceId), mitk::Exception); } MITK_TEST_SUITE_REGISTRATION(mitkDynamicMappers) \ No newline at end of file diff --git a/Modules/DynamicMappers/mitkDynamicMappers.cpp b/Modules/DynamicMappers/mitkDynamicMappers.cpp index 489aa534f7..a7fad09cec 100644 --- a/Modules/DynamicMappers/mitkDynamicMappers.cpp +++ b/Modules/DynamicMappers/mitkDynamicMappers.cpp @@ -1,187 +1,161 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDynamicMappers.h" -#include "mitkMapper.h" -#include "usLDAPProp.h" -#include "usModuleContext.h" -#include -#include #include +#include #include -#include +#include "mitkCoreObjectFactory.h" mitk::DynamicMappers::~DynamicMappers() { - UnloadAllSharedLibraries(); + this->UnloadAllSharedLibraries(); } std::shared_ptr mitk::DynamicMappers::GetInstance() { if (nullptr == m_Self) { m_Self = std::shared_ptr(new DynamicMappers()); } return m_Self; } -void mitk::DynamicMappers::RegisterMapper(const std::string &pathToDll) +void mitk::DynamicMappers::LoadSharedLibrary(const std::string &pathToSharedLibrary) { - auto separator = pathToDll.find_last_of('/'); + auto separator = pathToSharedLibrary.find_last_of('/'); if (std::string::npos == separator) { - separator = pathToDll.find_last_of('\\'); + separator = pathToSharedLibrary.find_last_of('\\'); } if (std::string::npos == separator) { mitkThrow() << "Relative or invalid path given"; } - auto suffixPos = pathToDll.find_last_of('.'); + auto suffixPos = pathToSharedLibrary.find_last_of('.'); - auto dllBasePath = pathToDll.substr(0, separator); + auto dllBasePath = pathToSharedLibrary.substr(0, separator); auto dllName = - pathToDll.substr(separator + 1, std::string::npos == suffixPos ? suffixPos : suffixPos - (separator + 1)); + pathToSharedLibrary.substr(separator + 1, std::string::npos == suffixPos ? suffixPos : suffixPos - (separator + 1)); - RegisterMapper(dllBasePath, dllName); + this->LoadSharedLibrary(dllBasePath, dllName); } -void mitk::DynamicMappers::RegisterMapper(const std::string &dllBasePath, const std::string &dllName) +void mitk::DynamicMappers::LoadSharedLibrary(const std::string &sharedLibraryBasePath, + const std::string &sharedLibraryName) { - if (nullptr == m_LibraryHandles[dllName]) + if (m_LibraryHandles.end() == m_LibraryHandles.find(sharedLibraryName)) { - m_LibraryHandles[dllName] = std::make_shared(); - m_LibraryHandles[dllName]->SetLibraryPath(dllBasePath); - m_LibraryHandles[dllName]->SetName(dllName); + auto sharedLibrary = std::make_shared(); + sharedLibrary->SetLibraryPath(sharedLibraryBasePath); + sharedLibrary->SetName(sharedLibraryName); + m_LibraryHandles[sharedLibraryName] = sharedLibrary; } - if (m_LibraryHandles[dllName]->IsLoaded()) - { - mitkThrow() << "Tried loading an already loaded mapper service"; - } - else + if (!m_LibraryHandles[sharedLibraryName]->IsLoaded()) { try { - m_LibraryHandles[dllName]->Load(); + m_LibraryHandles[sharedLibraryName]->Load(); + } + catch (const std::logic_error &e) + { + MITK_WARN << "Tried loading already loaded library " << sharedLibraryName << " at " << sharedLibraryBasePath + << " caught error: " << e.what(); } catch (const std::runtime_error &e) { - // TODO catch or propagate? - mitkThrow() << e.what(); + m_LibraryHandles.erase(sharedLibraryName); + mitkThrow() << "Loading library " << sharedLibraryName << " failed with: " << e.what(); } } } void mitk::DynamicMappers::UnloadAllSharedLibraries() { for (const auto &slp : m_LibraryHandles) { slp.second->Unload(); } + m_LibraryHandles.clear(); } -mitk::DynamicMappers::MapperInfoCollection mitk::DynamicMappers::GetRegisteredMapperInfo( - std::set &infoKeys) +mitk::DynamicMappers::ServiceInfoCollection mitk::DynamicMappers::GetRegisteredServiceInfo() { - MapperInfoCollection infoCollections; + ServiceInfoCollection infoCollection; auto context = us::GetModuleContext(); auto refs = context->GetServiceReferences(); for (const auto &ref : refs) { - MapperInfo info = CollectServiceInfo(ref); - infoCollections.push_back(info); - for (auto iter = info.begin(); iter != info.end(); ++iter) - { - infoKeys.insert(iter->first); - } + infoCollection.push_back(this->CollectServiceInfo(ref)); } - return infoCollections; + return infoCollection; } -mitk::DynamicMappers::MapperInfo mitk::DynamicMappers::CollectServiceInfo(const us::ServiceReference &ref) +mitk::DynamicMappers::ServiceInfo mitk::DynamicMappers::CollectServiceInfo( + const us::ServiceReference &ref) { - if (!ref) + ServiceInfo info; + if (ref) { - mitkThrow() << "The service reference was invalid"; + info.id = this->ReadServiceProperty(ref, "service.id"); + info.scope = this->ReadServiceProperty(ref, "service.scope"); + info.name = this->ReadServiceProperty(ref, "PROP_CLASS_NAME"); + info.dataType = this->ReadServiceProperty(ref, "PROP_DATA_TYPE_NAME"); + info.slot = this->ReadServiceProperty(ref, "PROP_SLOT_ID"); + info.priority = this->ReadServiceProperty(ref, "PROP_PRIORITY"); + info.module = ref.GetModule()->GetName(); } - - MapperInfo info; - info["id"] = ReadPropertyWithFallback(ref, "service.id", "NA"); - info["scope"] = ReadPropertyWithFallback(ref, "service.scope", "NA"); - info["name"] = ReadPropertyWithFallback(ref, "PROP_CLASS_NAME", "No name"); - info["dataType"] = ReadPropertyWithFallback(ref, "PROP_SUPPORTED_DATA_TYPE_NAME", "No data type specified"); - info["slot"] = ReadPropertyWithFallback(ref, "PROP_SUPPORTED_SLOT_ID", "No slot id"); - info["priority"] = ReadPropertyWithFallback(ref, "PROP_PRIORITY", "No priority"); - info["module"] = ref.GetModule()->GetName(); - return info; } -void mitk::DynamicMappers::ChangeMapperService(DataNode::Pointer node, long mapperServiceId, bool forceChange) +void mitk::DynamicMappers::ChangeMapperService(DataNode::Pointer node, long mapperServiceId, bool forceChangeDespiteEquality) { auto context = us::GetModuleContext(); auto refs = context->GetServiceReferences(us::LDAPProp("service.id") == mapperServiceId); if (nullptr == node) { mitkThrow() << "The data node was nullptr"; } if (refs.empty()) { mitkThrow() << "No mapper with specified service id found"; } const auto ref = refs[0]; - const int slotId = ReadServiceProperty(ref, "PROP_SUPPORTED_SLOT_ID"); - const std::string futureMapperClass = ReadServiceProperty(ref, "PROP_CLASS_NAME"); + const int slotId = this->ReadServiceProperty(ref, "PROP_SLOT_ID"); + const std::string futureMapperClass = this->ReadServiceProperty(ref, "PROP_CLASS_NAME"); mitk::Mapper *currentMapper = node->GetMapper(slotId); - if (nullptr == currentMapper) - { - mitkThrow() << "Mapper returned from node->GetMapper(" << slotId << ") was nullptr"; - } - if (futureMapperClass != currentMapper->GetNameOfClass() || forceChange) + if (nullptr == currentMapper || futureMapperClass != currentMapper->GetNameOfClass() || forceChangeDespiteEquality) { - const auto service = context->GetServiceObjects(ref).GetService(); - node->SetMapper(slotId, service); - } -} -MITKDYNAMICMAPPERS_DEPRECATED std::string mitk::DynamicMappers::ReadPropertyWithFallback( - const us::ServiceReference &ref, const std::string &propName, const std::string &fallbackString) const -{ - if (!ref) - { - return fallbackString; - } - us::Any prop = ref.GetProperty(propName); - if (typeid(std::string) == prop.Type()) - { - return us::any_cast(prop); - } - if (typeid(int) == prop.Type()) - { - return std::to_string(us::any_cast(prop)); - } - if (typeid(long) == prop.Type()) - { - return std::to_string(us::any_cast(prop)); + try + { + // const auto service = mitk::CoreObjectFactory::GetInstance()->GetMapperFromServiceRegistry(ref, node);//context->GetServiceObjects(ref).GetService(); + node->SetMapperFromRef(slotId, ref); + } + catch (const std::invalid_argument &e) + { + mitkThrow() << "The service reference acquired with id " << mapperServiceId << "was invalid.\n" + << "original errMsg: " << e.what(); + } } - return fallbackString; } std::shared_ptr mitk::DynamicMappers::m_Self = nullptr; \ No newline at end of file diff --git a/Modules/DynamicMappers/mitkDynamicMappers.h b/Modules/DynamicMappers/mitkDynamicMappers.h index 493bd32871..676d2913a5 100644 --- a/Modules/DynamicMappers/mitkDynamicMappers.h +++ b/Modules/DynamicMappers/mitkDynamicMappers.h @@ -1,96 +1,143 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKDYNAMICMAPPERS_H #define MITKDYNAMICMAPPERS_H #include "MitkDynamicMappersExports.h" -#include "mitkMapper.h" -#include "usServiceReference.h" -#include "usSharedLibrary.h" +#include +#include +#include #include #include namespace mitk { class MITKDYNAMICMAPPERS_EXPORT DynamicMappers { public: - //TODO discuss with Stefan - /*struct MapperInfo + struct ServiceInfo { - enum class Keys - { - ID, - SCOPE, - NAME, - DATA_TYPE, - SLOT, - PRIORITY, - MODULE - }; - std::unordered_map data; - };*/ - typedef std::map MapperInfo; - typedef std::vector MapperInfoCollection; + long id = -1; + std::string scope = "none"; + std::string name = "none"; + std::string dataType = "none"; + int slot = -1; + int priority = -1; + std::string module = "none"; + }; + typedef std::vector ServiceInfoCollection; DynamicMappers(const DynamicMappers &) = delete; DynamicMappers(const DynamicMappers &&) = delete; DynamicMappers &operator=(const DynamicMappers &) = delete; DynamicMappers &operator=(DynamicMappers &&) = delete; ~DynamicMappers(); static std::shared_ptr GetInstance(); - void RegisterMapper(const std::string &pathToDll); - void RegisterMapper(const std::string &dllBasePath, const std::string &dllName); + /** + * Convenience method that splits pathToSharedLibrary into base path and name and calls + * LoadSharedLibrary(basePath, name) + * Can only load shared libraries containing a us::ModuleActivator + * + * @param pathToSharedLibrary The absolute path of the shared library + * + * @throw mitk::Exception if loading of the library failed (e.g. the library doesn't contain a + * us::ModuleActivator or uses a different version of CppMicroservices than MITK) + * + * @see LoadSharedLibrary(const std::string& sharedLibraryBasePath, const std::string& sharedLibraryName) + */ + void LoadSharedLibrary(const std::string &pathToSharedLibrary); + /** + * Loads the shared library pointed to by the path given. + * Can only load shared libraries containing a us::ModuleActivator + * The shared library should register one or multiple mapper services as this is the intent of this method + * + * @param sharedLibraryBasePath The (absolute) base path of the shared library + * @param sharedLibraryName The name of the shared library without a file extension + * + * @throw mitk::Exception if loading of the library failed (e.g. the library doesn't contain a + * us::ModuleActivator or uses a different version of CppMicroservices than MITK) + */ + void LoadSharedLibrary(const std::string &sharedLibraryBasePath, const std::string &sharedLibraryName); + + /** + * Unloads all shared libraries loaded through this module but doesn't unregister the services (this has to be done + * in the library's activator) + * + * @throws mitk::Exception If an error occured while un-loading a shared library. Shared libraries that would have + * been un-loaded after the error will not be unloaded + */ void UnloadAllSharedLibraries(); - MapperInfoCollection GetRegisteredMapperInfo(std::set& infoKeys); - - void ChangeMapperService(DataNode::Pointer node, long mapperServiceId, bool forceChange = false); + /** + * Retrieves information about all registered mapper services + * + * @return A collection of ServiceInfo structs, one for each mapper service, in no particular order + * + * @throws std::logic_error If no valid ModuleContext could be retrieved. + * @throws mitk::Exception If a retrieved ServiceReference is invalidated during excecution of this + * method. + * @throws mitk::Exception If a retrieved ServiceReference is missing a queried service property + */ + ServiceInfoCollection GetRegisteredServiceInfo(); + + /** + * Changes the mapper used by node to the mapper service + * with the corresponding mapperServiceId + * + * @param node A pointer to the data node whose mapper shall be changed + * @param mapperServiceId The id of the mapper service that shall be changed to + * @param forceChangeDespiteEquality On default the mapper is not changed if the current mapper and the specified mapper are the + * same. setting forceChange to true will not check for mapper equality and change mappers + * either way. + * + * @throws mitk::Exception If node is nullptr. + * @throws mitk::Exception If the specified mapperServiceId + * does not correspond to a mapper service or the service is no longer valid. + * @throws std::logic_error If no valid ModuleContext could be retrieved. + */ + void ChangeMapperService(DataNode::Pointer node, long mapperServiceId, bool forceChangeDespiteEquality = false); private: DynamicMappers() = default; - MapperInfo CollectServiceInfo(const us::ServiceReference &ref); + ServiceInfo CollectServiceInfo(const us::ServiceReference &ref); - std::string ReadPropertyWithFallback(const us::ServiceReference &ref, - const std::string &propName, - const std::string &fallbackString) const; template T ReadServiceProperty(const us::ServiceReference &ref, const std::string &propName) const; static std::shared_ptr m_Self; std::map> m_LibraryHandles; }; template T DynamicMappers::ReadServiceProperty(const us::ServiceReference &ref, const std::string &propName) const { if (!ref) mitkThrow() << "The service reference was invalid"; auto any = ref.GetProperty(propName); if (typeid(T) == any.Type()) { T value = us::any_cast(any); return value; } mitkThrow() << "Bad cast: The property" << propName << " of type" << any.Type().name() << "can't be cast to" << typeid(T).name(); } } #endif