diff --git a/CMake/mitkFunctionCompileSnippets.cmake b/CMake/mitkFunctionCompileSnippets.cmake index c0ea9dae33..238e086ec1 100644 --- a/CMake/mitkFunctionCompileSnippets.cmake +++ b/CMake/mitkFunctionCompileSnippets.cmake @@ -1,40 +1,49 @@ function(mitkFunctionCompileSnippets snippet_path) # get all files called "main.cpp" file(GLOB_RECURSE main_cpp_list "${snippet_path}/main.cpp") foreach(main_cpp_file ${main_cpp_list}) # get the directory containing the main.cpp file get_filename_component(main_cpp_dir "${main_cpp_file}" PATH) set(snippet_src_files ) # If there exists a "files.cmake" file in the snippet directory, # include it and assume it sets the variable "snippet_src_files" # to a list of source files for the snippet. if(EXISTS "${main_cpp_dir}/files.cmake") include("${main_cpp_dir}/files.cmake") + set(_tmp_src_files ${snippet_src_files}) + set(snippet_src_files ) + foreach(_src_file ${_tmp_src_files}) + if(IS_ABSOLUTE ${_src_file}) + list(APPEND snippet_src_files ${_src_file}) + else() + list(APPEND snippet_src_files ${main_cpp_dir}/${_src_file}) + endif() + endforeach() else() # glob all files in the directory and add them to the snippet src list file(GLOB_RECURSE snippet_src_files "${main_cpp_dir}/*") endif() # Uset the top-level directory name as the executable name string(REPLACE "/" ";" main_cpp_dir_tokens "${main_cpp_dir}") list(GET main_cpp_dir_tokens -1 snippet_exec_name) set(snippet_target_name "Snippet-${snippet_exec_name}") add_executable(${snippet_target_name} ${snippet_src_files}) if(ARGN) target_link_libraries(${snippet_target_name} ${ARGN}) endif() set_target_properties(${snippet_target_name} PROPERTIES LABELS Documentation RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/snippets" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/snippets" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/snippets" OUTPUT_NAME ${snippet_exec_name} ) endforeach() endfunction() diff --git a/CMake/mitkModuleInit.cpp b/CMake/mitkModuleInit.cpp index c1f9f937b7..90c174b18f 100644 --- a/CMake/mitkModuleInit.cpp +++ b/CMake/mitkModuleInit.cpp @@ -1,78 +1,84 @@ /*============================================================================= Library: CTK Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "mitkStaticInit.h" #include "mitkModuleRegistry.h" #include "mitkModuleContext.h" #include "mitkModule.h" #include "mitkModuleInfo.h" #include "mitkModuleUtils.h" namespace mitk { MITK_GLOBAL_STATIC_WITH_ARGS(ModuleInfo, moduleInfo, ("@MODULE_NAME@", "@MODULE_LIBNAME@", "@MODULE_DEPENDS_STR@", "@MODULE_PACKAGE_DEPENDS_STR@", "@MODULE_VERSION@", @MODULE_QT_BOOL@)) class MITK_LOCAL ModuleInitializer { public: ModuleInitializer() { std::string location = ModuleUtils::GetLibraryPath(moduleInfo()->libName, (void*)moduleInfo); std::string activator_func = "_mitk_module_activator_instance_"; activator_func.append(moduleInfo()->name); moduleInfo()->location = location; + + if (moduleInfo()->libName.empty()) + { + // make sure we retrieve symbols from the executable, if "libName" is empty + location.clear(); + } moduleInfo()->activatorHook = (ModuleInfo::ModuleActivatorHook)ModuleUtils::GetSymbol(location, activator_func.c_str()); Register(); } static void Register() { ModuleRegistry::Register(moduleInfo()); } ~ModuleInitializer() { ModuleRegistry::UnRegister(moduleInfo()); } }; ModuleContext* GetModuleContext() { // make sure the module is registered if (moduleInfo()->id == 0) { ModuleInitializer::Register(); } return ModuleRegistry::GetModule(moduleInfo()->id)->GetModuleContext(); } } static mitk::ModuleInitializer coreModule; diff --git a/Core/Code/Service/mitkModuleRegistry.cpp b/Core/Code/Service/mitkModuleRegistry.cpp index 9e09d842cb..4f3e5ad21f 100644 --- a/Core/Code/Service/mitkModuleRegistry.cpp +++ b/Core/Code/Service/mitkModuleRegistry.cpp @@ -1,195 +1,203 @@ /*========================================================================= 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 "mitkCoreModuleContext_p.h" #include "mitkStaticInit.h" #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 - MutexLocker lock(*modulesLock()); - Module* module = modules()->operator[](info->id); + 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; - MutexLocker lock(*modulesLock()); - Module* curr = modules()->operator[](info->id); + 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/Service/mitkModuleUtils.cpp b/Core/Code/Service/mitkModuleUtils.cpp index 68d52d3868..27df0575e0 100644 --- a/Core/Code/Service/mitkModuleUtils.cpp +++ b/Core/Code/Service/mitkModuleUtils.cpp @@ -1,156 +1,176 @@ /*========================================================================= 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 "mitkModuleUtils.h" #include namespace mitk { #ifdef __GNUC__ #define _GNU_SOURCE #include std::string GetLibraryPath_impl(const std::string& /*libName*/, const void* symbol) { Dl_info info; if (dladdr(symbol, &info)) { return info.dli_fname; } return ""; } void* GetSymbol_impl(const std::string& libName, const char* symbol) { - void* selfHandle = dlopen(libName.c_str(), RTLD_LAZY); + void* selfHandle = 0; + if (libName.empty()) + { + // Get the handle of the executable + selfHandle = dlopen(0, RTLD_LAZY); + } + else + { + selfHandle = dlopen(libName.c_str(), RTLD_LAZY); + } + if (selfHandle) { void* addr = dlsym(selfHandle, symbol); dlclose(selfHandle); return addr; } return 0; } #elif _WIN32 #include #include void PrintLastError_impl(const std::string& context) { // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); // Display the error message and exit the process lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)context.c_str()) + 50) * sizeof(TCHAR)); StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), TEXT("Error in context %s (%d): %s"), context.c_str(), dw, lpMsgBuf); std::string errMsg((LPCTSTR)lpDisplayBuf); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); MITK_DEBUG(true) << errMsg; } std::string GetLibraryPath_impl(const std::string& libName, const void *symbol) { HMODULE handle = 0; if (libName.empty()) { // get the handle for the executable handle = GetModuleHandle(0); } else { handle = GetModuleHandle(libName.c_str()); } if (!handle) { PrintLastError_impl("GetLibraryPath_impl():GetModuleHandle()"); return ""; } char modulePath[512]; if (GetModuleFileName(handle, modulePath, 512)) { return modulePath; } PrintLastError_impl("GetLibraryPath_impl():GetModuleFileName()"); return ""; } void* GetSymbol_impl(const std::string& libName, const char* symbol) { - HMODULE handle = GetModuleHandle(libName.c_str()); + HMODULE handle = 0; + if (libName.empty()) + { + // Get the handle of the executable + handle = GetModuleHandle(NULL); + } + else + { + handle = GetModuleHandle(libName.c_str()); + } + if (!handle) { PrintLastError_impl("GetSymbol_impl():GetModuleHandle()"); return 0; } void* addr = (void*)GetProcAddress(handle, symbol); if (!addr) { PrintLastError_impl(std::string("GetSymbol_impl():GetProcAddress(handle,") + std::string(symbol) + ")"); } return addr; } #else std::string GetLibraryPath_impl(const std::string& libName, const void* symbol) { return ""; } void* GetSymbol_impl(const std::string& libName, const char* symbol) { return 0; } #endif std::string ModuleUtils::GetLibraryPath(const std::string& libName, const void* symbol) { return GetLibraryPath_impl(libName, symbol); } void* ModuleUtils::GetSymbol(const std::string& libName, const char* symbol) { return GetSymbol_impl(libName, symbol); } } diff --git a/Core/Code/Service/mitkServiceListeners.cpp b/Core/Code/Service/mitkServiceListeners.cpp index 39ea0974a9..592d691dc1 100644 --- a/Core/Code/Service/mitkServiceListeners.cpp +++ b/Core/Code/Service/mitkServiceListeners.cpp @@ -1,270 +1,271 @@ /*========================================================================= 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 "mitkServiceListeners_p.h" #include "mitkServiceReferencePrivate.h" #include "mitkModule.h" #include namespace mitk { const int ServiceListeners::OBJECTCLASS_IX = 0; const int ServiceListeners::SERVICE_ID_IX = 1; ServiceListeners::ServiceListeners() { hashedServiceKeys.push_back(ServiceConstants::OBJECTCLASS()); hashedServiceKeys.push_back(ServiceConstants::SERVICE_ID()); } void ServiceListeners::AddServiceListener(ModuleContext* mc, const ServiceListenerDelegate& listener, const std::string& filter) { MutexLocker lock(mutex); ServiceListenerEntry sle(mc->GetModule(), listener, filter); if (serviceSet.find(sle) != serviceSet.end()) { RemoveServiceListener_unlocked(sle); } serviceSet.insert(sle); CheckSimple(sle); } void ServiceListeners::RemoveServiceListener(ModuleContext* mc, const ServiceListenerDelegate& listener) { ServiceListenerEntry entryToRemove(mc->GetModule(), listener); MutexLocker lock(mutex); RemoveServiceListener_unlocked(entryToRemove); } void ServiceListeners::RemoveServiceListener_unlocked(const ServiceListenerEntry& entryToRemove) { for (ServiceListenerEntries::iterator it = serviceSet.begin(); it != serviceSet.end(); ++it) { if (it->operator ==(entryToRemove)) { it->SetRemoved(true); RemoveFromCache(*it); serviceSet.erase(it); break; } } } void ServiceListeners::AddModuleListener(ModuleContext* mc, const ModuleListenerDelegate& listener) { MutexLocker lock(moduleListenerMapMutex); moduleListenerMap[mc] += listener; } void ServiceListeners::RemoveModuleListener(ModuleContext* mc, const ModuleListenerDelegate& listener) { MutexLocker lock(moduleListenerMapMutex); moduleListenerMap[mc] -= listener; } void ServiceListeners::RemoveAllListeners(ModuleContext* mc) { { MutexLocker lock(mutex); for (ServiceListenerEntries::iterator it = serviceSet.begin(); it != serviceSet.end(); ) { if (it->GetModule() == mc->GetModule()) { RemoveFromCache(*it); serviceSet.erase(it++); } else { ++it; } } } { MutexLocker lock(moduleListenerMapMutex); moduleListenerMap.erase(mc); } } void ServiceListeners::ServiceChanged(const ServiceListenerEntries& receivers, const ServiceEvent& evt) { ServiceListenerEntries matchBefore; ServiceChanged(receivers, evt, matchBefore); } void ServiceListeners::ServiceChanged(const ServiceListenerEntries& receivers, const ServiceEvent& evt, ServiceListenerEntries& matchBefore) { ServiceReference sr = evt.GetServiceReference(); int n = 0; for (ServiceListenerEntries::const_iterator l = receivers.begin(); l != receivers.end(); ++l) { if (!matchBefore.empty()) { matchBefore.erase(*l); } if (!l->IsRemoved()) { try { ++n; l->CallDelegate(evt); } catch (...) { MITK_ERROR << "Service listener in " << l->GetModule()->GetName() << " threw an exception!"; } } } MITK_INFO << "Notified " << n << " listeners"; } -void ServiceListeners::GetMatchingServiceListeners(const ServiceReference& sr, ServiceListenerEntries& set) +void ServiceListeners::GetMatchingServiceListeners(const ServiceReference& sr, ServiceListenerEntries& set, + bool lockProps) { MutexLocker lock(mutex); // Check complicated or empty listener filters int n = 0; for (std::list::const_iterator sse = complicatedListeners.begin(); sse != complicatedListeners.end(); ++sse) { ++n; if (sse->GetLDAPExpr().IsNull() || sse->GetLDAPExpr().Evaluate(sr.d->GetProperties(), false)) { set.insert(*sse); } } MITK_INFO << "Added " << set.size() << " out of " << n << " listeners with complicated filters"; // Check the cache const std::list c(any_cast > - (sr.GetProperty(ServiceConstants::OBJECTCLASS()))); + (sr.d->GetProperty(ServiceConstants::OBJECTCLASS(), lockProps))); for (std::list::const_iterator objClass = c.begin(); objClass != c.end(); ++objClass) { AddToSet(set, OBJECTCLASS_IX, *objClass); } - long service_id = any_cast(sr.GetProperty(ServiceConstants::SERVICE_ID())); + long service_id = any_cast(sr.d->GetProperty(ServiceConstants::SERVICE_ID(), lockProps)); std::stringstream ss; ss << service_id; AddToSet(set, SERVICE_ID_IX, ss.str()); } void ServiceListeners::ModuleChanged(const ModuleEvent& evt) { MutexLocker lock(moduleListenerMapMutex); for(ModuleListenerMap::iterator i = moduleListenerMap.begin(); i != moduleListenerMap.end(); ++i) { i->second.Send(evt); } } void ServiceListeners::RemoveFromCache(const ServiceListenerEntry& sle) { if (!sle.GetLocalCache().empty()) { for (std::size_t i = 0; i < hashedServiceKeys.size(); ++i) { CacheType& keymap = cache[i]; std::vector& l = sle.GetLocalCache()[i]; for (std::vector::const_iterator it = l.begin(); it != l.end(); ++it) { std::list& sles = keymap[*it]; sles.remove(sle); if (sles.empty()) { keymap.erase(*it); } } } } else { complicatedListeners.remove(sle); } } void ServiceListeners::CheckSimple(const ServiceListenerEntry& sle) { if (sle.GetLDAPExpr().IsNull()) { complicatedListeners.push_back(sle); } else { LDAPExpr::LocalCache local_cache; if (sle.GetLDAPExpr().IsSimple(hashedServiceKeys, local_cache, false)) { sle.GetLocalCache() = local_cache; for (std::size_t i = 0; i < hashedServiceKeys.size(); ++i) { for (std::vector::const_iterator it = local_cache[i].begin(); it != local_cache[i].end(); ++it) { std::list& sles = cache[i][*it]; sles.push_back(sle); } } } else { MITK_INFO << "Too complicated filter: " << sle.GetFilter(); complicatedListeners.push_back(sle); } } } void ServiceListeners::AddToSet(ServiceListenerEntries& set, int cache_ix, const std::string& val) { std::list& l = cache[cache_ix][val]; if (!l.empty()) { MITK_INFO << hashedServiceKeys[cache_ix] << " matches " << l.size(); for (std::list::const_iterator entry = l.begin(); entry != l.end(); ++entry) { set.insert(*entry); } } else { MITK_INFO << hashedServiceKeys[cache_ix] << " matches none"; } } } diff --git a/Core/Code/Service/mitkServiceListeners_p.h b/Core/Code/Service/mitkServiceListeners_p.h index e6b2270950..205d8a9598 100644 --- a/Core/Code/Service/mitkServiceListeners_p.h +++ b/Core/Code/Service/mitkServiceListeners_p.h @@ -1,191 +1,192 @@ /*========================================================================= 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 MITKSERVICELISTENERS_H #define MITKSERVICELISTENERS_H #include #include #include #include #ifdef MITK_HAS_UNORDERED_MAP_H #include #else #include "mitkItkHashMap.h" #endif #ifdef MITK_HAS_UNORDERED_SET_H #include #else #include "mitkItkHashSet.h" #endif #include #include "mitkServiceUtils.h" #include "mitkServiceListenerEntry_p.h" namespace mitk { class CoreModuleContext; class ModuleContext; /** * Here we handle all listeners that modules have registered. * */ class ServiceListeners { public: typedef Message1 ModuleMessage; #ifdef MITK_HAS_UNORDERED_MAP_H typedef std::unordered_map > CacheType; typedef std::unordered_map ModuleListenerMap; #else typedef itk::hash_map > CacheType; typedef itk::hash_map ModuleListenerMap; #endif #ifdef MITK_HAS_UNORDERED_SET_H typedef std::unordered_set ServiceListenerEntries; #else typedef itk::hash_set ServiceListenerEntries; #endif private: std::vector hashedServiceKeys; static const int OBJECTCLASS_IX; // = 0; static const int SERVICE_ID_IX; // = 1; /* Service listeners with complicated or empty filters */ std::list complicatedListeners; /* Service listeners with "simple" filters are cached. */ CacheType cache[2]; ServiceListenerEntries serviceSet; ModuleListenerMap moduleListenerMap; typedef itk::SimpleFastMutexLock MutexType; typedef itk::MutexLockHolder MutexLocker; MutexType mutex; MutexType moduleListenerMapMutex; public: ServiceListeners(); /** * Add a new service listener. If an old one exists, and it has the * same owning module, the old listener is removed first. * * @param mc The module context adding this listener. * @param listener The service listener to add. * @param filter An LDAP filter string to check when a service is modified. * @exception org.osgi.framework.InvalidSyntaxException * If the filter is not a correct LDAP expression. */ void AddServiceListener(ModuleContext* mc, const ServiceListenerDelegate& listener, const std::string& filter); /** * Remove service listener from current framework. Silently ignore * if listener doesn't exist. If listener is registered more than * once remove all instances. * * @param mc The module context who wants to remove listener. * @param listener Object to remove. */ void RemoveServiceListener(ModuleContext* mc, const ServiceListenerDelegate& listener); /** * Add a new service listener. * * @param mc The module context adding this listener. * @param listener The service listener to add. * @param filter An LDAP filter string to check when a service is modified. * @exception org.osgi.framework.InvalidSyntaxException * If the filter is not a correct LDAP expression. */ void AddModuleListener(ModuleContext* mc, const ModuleListenerDelegate& listener); /** * Remove service listener from current framework. Silently ignore * if listener doesn't exist. * * @param mc The module context who wants to remove listener. * @param listener Object to remove. */ void RemoveModuleListener(ModuleContext* mc, const ModuleListenerDelegate& listener); /** * Remove all listener registered by a module in the current framework. * * @param mc Module context which listeners we want to remove. */ void RemoveAllListeners(ModuleContext* mc); /** * Receive notification that a service has had a change occur in its lifecycle. * * @see org.osgi.framework.ServiceListener#serviceChanged */ void ServiceChanged(const ServiceListenerEntries& receivers, const ServiceEvent& evt, ServiceListenerEntries& matchBefore); void ServiceChanged(const ServiceListenerEntries& receivers, const ServiceEvent& evt); /** * * */ - void GetMatchingServiceListeners(const ServiceReference& sr, ServiceListenerEntries& listeners); + void GetMatchingServiceListeners(const ServiceReference& sr, ServiceListenerEntries& listeners, + bool lockProps = true); void ModuleChanged(const ModuleEvent& evt); private: void RemoveServiceListener_unlocked(const ServiceListenerEntry& entryToRemove); /** * Remove all references to a service listener from the service listener * cache. */ void RemoveFromCache(const ServiceListenerEntry& sle); /** * Checks if the specified service listener's filter is simple enough * to cache. */ void CheckSimple(const ServiceListenerEntry& sle); void AddToSet(ServiceListenerEntries& set, int cache_ix, const std::string& val); }; } #endif // MITKSERVICELISTENERS_H diff --git a/Core/Code/Service/mitkServiceReferencePrivate.cpp b/Core/Code/Service/mitkServiceReferencePrivate.cpp index fc964ab2cc..e8f0f348f8 100644 --- a/Core/Code/Service/mitkServiceReferencePrivate.cpp +++ b/Core/Code/Service/mitkServiceReferencePrivate.cpp @@ -1,159 +1,177 @@ /*========================================================================= 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 "mitkServiceReferencePrivate.h" #include "mitkServiceFactory.h" #include "mitkServiceException.h" #include "mitkServiceRegistry_p.h" #include "mitkServiceRegistrationPrivate.h" #include "mitkModule.h" #include "mitkModulePrivate.h" #include "mitkCoreModuleContext_p.h" #include #include namespace mitk { typedef ServiceRegistrationPrivate::MutexLocker MutexLocker; ServiceReferencePrivate::ServiceReferencePrivate(ServiceRegistrationPrivate* reg) : ref(1), registration(reg) { } itk::LightObject* ServiceReferencePrivate::GetService(Module* module) { itk::LightObject* s = 0; { MutexLocker lock(registration->propsLock); if (registration->available) { int count = registration->dependents[module]; if (count == 0) { const std::list& classes = ref_any_cast >(registration->properties[ServiceConstants::OBJECTCLASS()]); registration->dependents[module] = 1; if (ServiceFactory* serviceFactory = dynamic_cast(registration->GetService())) { try { s = serviceFactory->GetService(module, ServiceRegistration(registration)); } catch (...) { MITK_WARN << "mitk::ServiceFactory threw an exception"; return 0; } if (s == 0) { MITK_WARN << "mitk::ServiceFactory produced null"; return 0; } for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { if (!registration->module->coreCtx->services.CheckServiceClass(s, *i)) { MITK_WARN << "mitk::ServiceFactory produced an object " "that did not implement: " << (*i); return 0; } } registration->serviceInstances.insert(std::make_pair(module, s)); } else { s = registration->GetService(); } } else { registration->dependents.insert(std::make_pair(module, count + 1)); if (dynamic_cast(registration->GetService())) { s = registration->serviceInstances[module]; } else { s = registration->GetService(); } } } } return s; } bool ServiceReferencePrivate::UngetService(Module* module, bool checkRefCounter) { MutexLocker lock(registration->propsLock); bool hadReferences = false; bool removeService = false; int count= registration->dependents[module]; if (count > 0) { hadReferences = true; } if(checkRefCounter) { if (count > 1) { registration->dependents[module] = count - 1; } else if(count == 1) { removeService = true; } } else { removeService = true; } if (removeService) { itk::LightObject* sfi = registration->serviceInstances[module]; registration->serviceInstances.erase(module); if (sfi != 0) { try { dynamic_cast( registration->GetService())->UngetService(module, ServiceRegistration(registration), sfi); } catch (const std::exception& /*e*/) { MITK_WARN << "mitk::ServiceFactory threw an exception"; } } registration->dependents.erase(module); } return hadReferences; } ServiceProperties ServiceReferencePrivate::GetProperties() const { return registration->properties; } +Any ServiceReferencePrivate::GetProperty(const std::string& key, bool lock) const +{ + if (lock) + { + MutexLocker lock(registration->propsLock); + ServiceProperties::const_iterator iter = registration->properties.find(key); + if (iter != registration->properties.end()) + return iter->second; + } + else + { + ServiceProperties::const_iterator iter = registration->properties.find(key); + if (iter != registration->properties.end()) + return iter->second; + } + return Any(); +} + } diff --git a/Core/Code/Service/mitkServiceReferencePrivate.h b/Core/Code/Service/mitkServiceReferencePrivate.h index 830baf39c9..faa8491066 100644 --- a/Core/Code/Service/mitkServiceReferencePrivate.h +++ b/Core/Code/Service/mitkServiceReferencePrivate.h @@ -1,89 +1,111 @@ /*========================================================================= 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 MITKSERVICEREFERENCEPRIVATE_H #define MITKSERVICEREFERENCEPRIVATE_H #include "mitkAtomicInt.h" #include "mitkServiceProperties.h" namespace itk { class LightObject; } namespace mitk { class Module; class ServiceRegistrationPrivate; class ServiceReferencePrivate; /** * \ingroup MicroServices */ class ServiceReferencePrivate { public: ServiceReferencePrivate(ServiceRegistrationPrivate* reg); virtual ~ServiceReferencePrivate() {} /** * Get the service object. * * @param module requester of service. * @return Service requested or null in case of failure. */ itk::LightObject* GetService(Module* module); /** * Unget the service object. * * @param module Module who wants remove service. * @param checkRefCounter If true decrement refence counter and remove service * if we reach zero. If false remove service without * checking refence counter. * @return True if service was remove or false if only refence counter was * decremented. */ bool UngetService(Module* module, bool checkRefCounter); /** * Get all properties registered with this service. * * @return A ServiceProperties object containing properties or being empty * if service has been removed. */ ServiceProperties GetProperties() const; + /** + * Returns the property value to which the specified property key is mapped + * in the properties ServiceProperties object of the service + * referenced by this ServiceReference object. + * + *

+ * Property keys are case-insensitive. + * + *

+ * This method must continue to return property values after the service has + * been unregistered. This is so references to unregistered services can + * still be interrogated. + * + * @param key The property key. + * @param lock If true, access of the properties of the service + * referenced by this ServiceReference object will be + * synchronized. + * @return The property value to which the key is mapped; an invalid Any + * if there is no property named after the key. + */ + Any GetProperty(const std::string& key, bool lock) const; + /** * Reference count for implicitly shared private implementation. */ AtomicInt ref; /** * Link to registration object for this reference. */ ServiceRegistrationPrivate* const registration; }; } #endif // MITKSERVICEREFERENCEPRIVATE_H diff --git a/Core/Code/Service/mitkServiceRegistration.cpp b/Core/Code/Service/mitkServiceRegistration.cpp index 60633cbf3b..fe358914f7 100644 --- a/Core/Code/Service/mitkServiceRegistration.cpp +++ b/Core/Code/Service/mitkServiceRegistration.cpp @@ -1,222 +1,222 @@ /*========================================================================= 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 "mitkServiceRegistration.h" #include "mitkServiceRegistrationPrivate.h" #include "mitkServiceListenerEntry_p.h" #include "mitkServiceRegistry_p.h" #include "mitkServiceFactory.h" #include "mitkModulePrivate.h" #include "mitkCoreModuleContext_p.h" #include #include namespace mitk { typedef ServiceRegistrationPrivate::MutexLocker MutexLocker; ServiceRegistration::ServiceRegistration() : d(0) { } ServiceRegistration::ServiceRegistration(const ServiceRegistration& reg) : d(reg.d) { - d->ref.Ref(); + if (d) d->ref.Ref(); } ServiceRegistration::ServiceRegistration(ServiceRegistrationPrivate* registrationPrivate) : d(registrationPrivate) { d->ref.Ref(); } ServiceRegistration::ServiceRegistration(ModulePrivate* module, itk::LightObject* service, const ServiceProperties& props) : d(new ServiceRegistrationPrivate(module, service, props)) { } ServiceRegistration::operator bool() const { return d != 0; } ServiceRegistration& ServiceRegistration::operator=(int null) { if (null == 0) { if (d && !d->ref.Deref()) { delete d; } d = 0; } return *this; } ServiceRegistration::~ServiceRegistration() { if (d && !d->ref.Deref()) delete d; } ServiceReference ServiceRegistration::GetReference() const { if (!d) throw std::logic_error("ServiceRegistration object invalid"); if (!d->available) throw std::logic_error("Service is unregistered"); return d->reference; } void ServiceRegistration::SetProperties(const ServiceProperties& props) { if (!d) throw std::logic_error("ServiceRegistration object invalid"); MutexLocker lock(d->eventLock); ServiceListeners::ServiceListenerEntries before; // TBD, optimize the locking of services { //MutexLocker lock2(d->module->coreCtx->globalFwLock); MutexLocker lock3(d->propsLock); if (d->available) { // NYI! Optimize the MODIFIED_ENDMATCH code int old_rank = any_cast(d->properties[ServiceConstants::SERVICE_RANKING()]); d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, before); const std::list& classes = ref_any_cast >(d->properties[ServiceConstants::OBJECTCLASS()]); long int sid = any_cast(d->properties[ServiceConstants::SERVICE_ID()]); d->properties = ServiceRegistry::CreateServiceProperties(props, classes, sid); int new_rank = any_cast(d->properties[ServiceConstants::SERVICE_RANKING()]); if (old_rank != new_rank) { d->module->coreCtx->services.UpdateServiceRegistrationOrder(*this, classes); } } else { throw std::logic_error("Service is unregistered"); } } ServiceListeners::ServiceListenerEntries matchingListeners; - d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, matchingListeners); + d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, matchingListeners, false); d->module->coreCtx->listeners.ServiceChanged(matchingListeners, ServiceEvent(ServiceEvent::MODIFIED, d->reference), before); d->module->coreCtx->listeners.ServiceChanged(before, ServiceEvent(ServiceEvent::MODIFIED_ENDMATCH, d->reference)); } void ServiceRegistration::Unregister() { if (!d) throw std::logic_error("ServiceRegistration object invalid"); if (d->unregistering) return; // Silently ignore redundant unregistration. { MutexLocker lock(d->eventLock); if (d->unregistering) return; d->unregistering = true; if (d->available) { if (d->module) { d->module->coreCtx->services.RemoveServiceRegistration(*this); } } else { throw std::logic_error("Service is unregistered"); } } if (d->module) { ServiceListeners::ServiceListenerEntries listeners; d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, listeners); d->module->coreCtx->listeners.ServiceChanged( listeners, ServiceEvent(ServiceEvent::UNREGISTERING, d->reference)); } { MutexLocker lock(d->eventLock); { MutexLocker lock2(d->propsLock); d->available = false; if (d->module) { ServiceRegistrationPrivate::ModuleToServicesMap::const_iterator end = d->serviceInstances.end(); for (ServiceRegistrationPrivate::ModuleToServicesMap::const_iterator i = d->serviceInstances.begin(); i != end; ++i) { itk::LightObject* obj = i->second; try { // NYI, don't call inside lock dynamic_cast(d->service)->UngetService(i->first, *this, obj); } catch (const std::exception& /*ue*/) { MITK_WARN << "mitk::ServiceFactory UngetService implementation threw an exception"; } } } d->module = 0; d->dependents.clear(); d->service = 0; d->serviceInstances.clear();; d->unregistering = false; } } } bool ServiceRegistration::operator<(const ServiceRegistration& o) const { if (!d) return true; return d->reference <(o.d->reference); } bool ServiceRegistration::operator==(const ServiceRegistration& registration) const { return d == registration.d; } ServiceRegistration& ServiceRegistration::operator=(const ServiceRegistration& registration) { ServiceRegistrationPrivate* curr_d = d; d = registration.d; d->ref.Ref(); if (curr_d && !curr_d->ref.Deref()) delete curr_d; return *this; } } diff --git a/Core/Code/Service/mitkServiceRegistry.cpp b/Core/Code/Service/mitkServiceRegistry.cpp index b295497328..31034526fa 100644 --- a/Core/Code/Service/mitkServiceRegistry.cpp +++ b/Core/Code/Service/mitkServiceRegistry.cpp @@ -1,331 +1,331 @@ /*========================================================================= 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 "mitkServiceRegistry_p.h" #include "mitkServiceFactory.h" #include "mitkServiceRegistry_p.h" #include "mitkServiceRegistrationPrivate.h" #include "mitkModulePrivate.h" #include "mitkCoreModuleContext_p.h" #include #include "itkMutexLockHolder.h" namespace mitk { typedef itk::MutexLockHolder MutexLocker; std::size_t HashServiceRegistration::operator()(const ServiceRegistration& s) const { return reinterpret_cast(s.d); } struct ServiceRegistrationComparator { bool operator()(const ServiceRegistration& a, const ServiceRegistration& b) const { return a < b; } }; ServiceProperties ServiceRegistry::CreateServiceProperties(const ServiceProperties& in, const std::list& classes, long sid) { static long nextServiceID = 1; ServiceProperties props(in); if (!classes.empty()) { props.insert(std::make_pair(ServiceConstants::OBJECTCLASS(), classes)); } props.insert(std::make_pair(ServiceConstants::SERVICE_ID(), sid != -1 ? sid : nextServiceID++)); return props; } ServiceRegistry::ServiceRegistry(CoreModuleContext* coreCtx) : core(coreCtx) { } ServiceRegistry::~ServiceRegistry() { Clear(); } void ServiceRegistry::Clear() { services.clear(); serviceRegistrations.clear(); classServices.clear(); core = 0; } ServiceRegistration ServiceRegistry::RegisterService(ModulePrivate* module, const std::list& classes, itk::LightObject* service, const ServiceProperties& properties) { if (service == 0) { throw std::invalid_argument("Can't register 0 as a service"); } // Check if service implements claimed classes and that they exist. for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { if (i->empty()) { throw std::invalid_argument("Can't register as null class"); } if (!(dynamic_cast(service))) { if (!CheckServiceClass(service, *i)) { std::string msg; std::stringstream ss(msg); ss << "Service class " << service->GetNameOfClass() << " is not an instance of " << (*i) << ". Maybe you forgot to export the RTTI information for the interface."; throw std::invalid_argument(msg); } } } ServiceRegistration res(module, service, CreateServiceProperties(properties, classes)); { MutexLocker lock(mutex); services.insert(std::make_pair(res, classes)); serviceRegistrations.push_back(res); for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::list& s = classServices[*i]; std::list::iterator ip = std::lower_bound(s.begin(), s.end(), res, ServiceRegistrationComparator()); s.insert(ip, res); } } ServiceReference r = res.GetReference(); ServiceListeners::ServiceListenerEntries listeners; module->coreCtx->listeners.GetMatchingServiceListeners(r, listeners); module->coreCtx->listeners.ServiceChanged(listeners, ServiceEvent(ServiceEvent::REGISTERED, r)); return res; } void ServiceRegistry::UpdateServiceRegistrationOrder(const ServiceRegistration& sr, const std::list& classes) { MutexLocker lock(mutex); for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::list& s = classServices[*i]; std::remove(s.begin(), s.end(), sr); s.insert(std::lower_bound(s.begin(), s.end(), sr, ServiceRegistrationComparator()), sr); } } bool ServiceRegistry::CheckServiceClass(itk::LightObject* , const std::string& ) const { //return service->inherits(cls.toAscii()); // No possibility to check inheritance based on string literals. return true; } void ServiceRegistry::Get(const std::string& clazz, std::list& serviceRegs) const { MutexLocker lock(mutex); MapClassServices::const_iterator i = classServices.find(clazz); if (i != classServices.end()) { serviceRegs = i->second; } } ServiceReference ServiceRegistry::Get(ModulePrivate* module, const std::string& clazz) const { MutexLocker lock(mutex); try { std::list srs; - Get_Unlocked(clazz, "", module, srs); + Get_unlocked(clazz, "", module, srs); MITK_INFO << "get service ref " << clazz << " for module " << module->info.name << " = " << srs.size() << " refs"; if (!srs.empty()) { return srs.front(); } } catch (const std::invalid_argument& ) { } return ServiceReference(); } void ServiceRegistry::Get(const std::string& clazz, const std::string& filter, ModulePrivate* module, std::list& res) const { MutexLocker lock(mutex); - Get_Unlocked(clazz, filter, module, res); + Get_unlocked(clazz, filter, module, res); } -void ServiceRegistry::Get_Unlocked(const std::string& clazz, const std::string& filter, +void ServiceRegistry::Get_unlocked(const std::string& clazz, const std::string& filter, ModulePrivate* /*module*/, std::list& res) const { std::list::const_iterator s; std::list::const_iterator send; std::list v; LDAPExpr ldap; if (clazz.empty()) { if (!filter.empty()) { ldap = LDAPExpr(filter); LDAPExpr::ObjectClassSet matched; if (ldap.GetMatchedObjectClasses(matched)) { v.clear(); for(LDAPExpr::ObjectClassSet::const_iterator className = matched.begin(); className != matched.end(); ++className) { MapClassServices::const_iterator i = classServices.find(*className); if (i != classServices.end()) { std::copy(i->second.begin(), i->second.end(), std::back_inserter(v)); } } if (!v.empty()) { s = v.begin(); send = v.end(); } else { return; } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { MapClassServices::const_iterator it = classServices.find(clazz); if (it != classServices.end()) { s = it->second.begin(); send = it->second.end(); } else { return; } if (!filter.empty()) { ldap = LDAPExpr(filter); } } for (; s != send; ++s) { ServiceReference sri = s->GetReference(); if (filter.empty() || ldap.Evaluate(s->d->properties, false)) { res.push_back(sri); } } } void ServiceRegistry::RemoveServiceRegistration(const ServiceRegistration& sr) { MutexLocker lock(mutex); const std::list& classes = ref_any_cast >( sr.d->properties[ServiceConstants::OBJECTCLASS()]); services.erase(sr); serviceRegistrations.remove(sr); for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::list& s = classServices[*i]; if (s.size() > 1) { std::remove(s.begin(), s.end(), sr); } else { classServices.erase(*i); } } } void ServiceRegistry::GetRegisteredByModule(ModulePrivate* p, std::list& res) const { MutexLocker lock(mutex); for (std::list::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->module == p) { res.push_back(*i); } } } void ServiceRegistry::GetUsedByModule(Module* p, std::list& res) const { MutexLocker lock(mutex); for (std::list::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->IsUsedByModule(p)) { res.push_back(*i); } } } } // end namespace mitk diff --git a/Core/Code/Service/mitkServiceRegistry_p.h b/Core/Code/Service/mitkServiceRegistry_p.h index 34c7938797..b77c3011fc 100644 --- a/Core/Code/Service/mitkServiceRegistry_p.h +++ b/Core/Code/Service/mitkServiceRegistry_p.h @@ -1,198 +1,198 @@ /*========================================================================= 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 MITKSERVICEREGISTRY_H #define MITKSERVICEREGISTRY_H #include #include #include "mitkServiceRegistration.h" #include "mitkServiceProperties.h" #include "mitkServiceUtils.h" namespace mitk { class CoreModuleContext; class ModulePrivate; struct HashServiceRegistration { std::size_t operator()(const ServiceRegistration& s) const; }; /** * Here we handle all the MITK services that are registered. */ class ServiceRegistry { public: typedef itk::SimpleFastMutexLock MutexType; mutable MutexType mutex; /** * Creates a new ServiceProperties object containing in * with the keys converted to lower case. * * @param classes A list of class names which will be added to the * created ServiceProperties object under the key * ModuleConstants::OBJECTCLASS. * @param sid A service id which will be used instead of a default one. */ static ServiceProperties CreateServiceProperties(const ServiceProperties& in, const std::list& classes = std::list(), long sid = -1); #ifdef MITK_HAS_UNORDERED_MAP_H typedef std::unordered_map, HashServiceRegistration> MapServiceClasses; typedef std::unordered_map > MapClassServices; #else typedef itk::hash_map, HashServiceRegistration> MapServiceClasses; typedef itk::hash_map > MapClassServices; #endif /** * All registered services in the current framework. * Mapping of registered service to class names under which * the service is registerd. */ MapServiceClasses services; std::list serviceRegistrations; /** * Mapping of classname to registered service. * The List of registered services are ordered with the highest * ranked service first. */ MapClassServices classServices; CoreModuleContext* core; ServiceRegistry(CoreModuleContext* coreCtx); ~ServiceRegistry(); void Clear(); /** * Register a service in the framework wide register. * * @param module The module registering the service. * @param classes The class names under which the service can be located. * @param service The service object. * @param properties The properties for this service. * @return A ServiceRegistration object. * @exception std::invalid_argument If one of the following is true: *

    *
  • The service object is 0.
  • *
  • The service parameter is not a ServiceFactory or an * instance of all the named classes in the classes parameter.
  • *
*/ ServiceRegistration RegisterService(ModulePrivate* module, const std::list& clazzes, itk::LightObject* service, const ServiceProperties& properties); /** * Service ranking changed, reorder registered services * according to ranking. * * @param serviceRegistration The ServiceRegistrationPrivate object. * @param rank New rank of object. */ void UpdateServiceRegistrationOrder(const ServiceRegistration& sr, const std::list& classes); /** * Checks that a given service object is an instance of the given * class name. * * @param service The service object to check. * @param cls The class name to check for. */ bool CheckServiceClass(itk::LightObject* service, const std::string& cls) const; /** * Get all services implementing a certain class. * Only used internally by the framework. * * @param clazz The class name of the requested service. * @return A sorted list of {@link ServiceRegistrationPrivate} objects. */ void Get(const std::string& clazz, std::list& serviceRegs) const; /** * Get a service implementing a certain class. * * @param module The module requesting reference * @param clazz The class name of the requested service. * @return A {@link ServiceReference} object. */ ServiceReference Get(ModulePrivate* module, const std::string& clazz) const; /** * Get all services implementing a certain class and then * filter these with a property filter. * * @param clazz The class name of requested service. * @param filter The property filter. * @param module The module requesting reference. * @return A list of {@link ServiceReference} object. */ void Get(const std::string& clazz, const std::string& filter, ModulePrivate* module, std::list& serviceRefs) const; /** * Remove a registered service. * * @param sr The ServiceRegistration object that is registered. */ void RemoveServiceRegistration(const ServiceRegistration& sr) ; /** * Get all services that a module has registered. * * @param p The module * @return A set of {@link ServiceRegistration} objects */ void GetRegisteredByModule(ModulePrivate* m, std::list& serviceRegs) const; /** * Get all services that a module uses. * * @param p The module * @return A set of {@link ServiceRegistration} objects */ void GetUsedByModule(Module* m, std::list& serviceRegs) const; private: - void Get_Unlocked(const std::string& clazz, const std::string& filter, + void Get_unlocked(const std::string& clazz, const std::string& filter, ModulePrivate* module, std::list& serviceRefs) const; }; } #endif // MITKSERVICEREGISTRY_H diff --git a/Core/Code/Service/mitkServiceTracker.tpp b/Core/Code/Service/mitkServiceTracker.tpp index 05490a2028..bd9aab1ecf 100644 --- a/Core/Code/Service/mitkServiceTracker.tpp +++ b/Core/Code/Service/mitkServiceTracker.tpp @@ -1,438 +1,434 @@ /*========================================================================= 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 "mitkServiceTrackerPrivate.h" #include "mitkTrackedService.h" #include "mitkServiceException.h" #include "mitkModuleContext.h" #include #include #include namespace mitk { template ServiceTracker::~ServiceTracker() { delete d; } template ServiceTracker::ServiceTracker(ModuleContext* context, const ServiceReference& reference, _ServiceTrackerCustomizer* customizer) : d(new _ServiceTrackerPrivate(this, context, reference, customizer)) { } template ServiceTracker::ServiceTracker(ModuleContext* context, const std::string& clazz, _ServiceTrackerCustomizer* customizer) : d(new _ServiceTrackerPrivate(this, context, clazz, customizer)) { } template ServiceTracker::ServiceTracker(ModuleContext* context, const LDAPFilter& filter, _ServiceTrackerCustomizer* customizer) : d(new _ServiceTrackerPrivate(this, context, filter, customizer)) { } template ServiceTracker::ServiceTracker(ModuleContext *context, ServiceTrackerCustomizer *customizer) : d(new _ServiceTrackerPrivate(this, context, mitk_service_interface_iid(), customizer)) { const char* clazz = mitk_service_interface_iid(); if (clazz == 0) throw ServiceException("The service interface class has no MITK_DECLARE_SERVICE_INTERFACE macro"); } template void ServiceTracker::Open() { itk::SmartPointer<_TrackedService> t; { MutexLocker lock(d->mutex); if (d->trackedService) { return; } MITK_DEBUG(d->DEBUG) << "ServiceTracker::Open: " << d->filter; t = itk::SmartPointer<_TrackedService>( new _TrackedService(this, d->customizer)); { itk::MutexLockHolder lockT(*t); try { d->context->AddServiceListener(t.GetPointer(), &_TrackedService::ServiceChanged, d->listenerFilter); std::list references; if (!d->trackClass.empty()) { references = d->GetInitialReferences(d->trackClass, std::string()); } else { if (d->trackReference.GetModule() != 0) { references.push_back(d->trackReference); } else { /* user supplied filter */ references = d->GetInitialReferences(std::string(), (d->listenerFilter.empty()) ? d->filter.ToString() : d->listenerFilter); } } /* set tracked with the initial references */ t->SetInitial(references); } catch (const std::invalid_argument& e) { throw std::runtime_error(std::string("unexpected std::invalid_argument exception: ") + e.what()); } } d->trackedService = t; } /* Call tracked outside of synchronized region */ t->TrackInitial(); /* process the initial references */ } template void ServiceTracker::Close() { itk::SmartPointer<_TrackedService> outgoing; std::list references; { MutexLocker lock(d->mutex); outgoing = d->trackedService; if (outgoing.IsNull()) { return; } MITK_DEBUG(d->DEBUG) << "ServiceTracker::close:" << d->filter; outgoing->Close(); GetServiceReferences(references); d->trackedService = 0; try { d->context->RemoveServiceListener(outgoing.GetPointer(), &_TrackedService::ServiceChanged); } catch (const std::logic_error& /*e*/) { /* In case the context was stopped. */ } } d->Modified(); /* clear the cache */ { itk::MutexLockHolder lockT(*outgoing); outgoing->WakeAll(); /* wake up any waiters */ } for(std::list::const_iterator ref = references.begin(); ref != references.end(); ++ref) { outgoing->Untrack(*ref, ServiceEvent()); } if (d->DEBUG) { MutexLocker lock(d->mutex); if ((d->cachedReference.GetModule() == 0) && (d->cachedService == 0)) { MITK_DEBUG(true) << "ServiceTracker::close[cached cleared]:" << d->filter; } } } template T ServiceTracker::WaitForService() { T object = GetService(); while (object == 0) { itk::SmartPointer<_TrackedService> t = d->Tracked(); if (t.IsNull()) { /* if ServiceTracker is not open */ return 0; } { itk::MutexLockHolder lockT(*t); if (t->Size() == 0) { t->Wait(); } } object = GetService(); } return object; } template void ServiceTracker::GetServiceReferences(std::list& refs) const { itk::SmartPointer<_TrackedService> t = d->Tracked(); if (t.IsNull()) { /* if ServiceTracker is not open */ return; } { itk::MutexLockHolder lockT(*t); - if (t->Size() == 0) - { - return; - } - t->GetTracked(refs); + d->GetServiceReferences_unlocked(refs, t.GetPointer()); } } template ServiceReference ServiceTracker::GetServiceReference() const { ServiceReference reference(0); { MutexLocker lock(d->mutex); reference = d->cachedReference; } if (reference.GetModule() != 0) { MITK_DEBUG(d->DEBUG) << "ServiceTracker::getServiceReference[cached]:" << d->filter; return reference; } MITK_DEBUG(d->DEBUG) << "ServiceTracker::getServiceReference:" << d->filter; std::list references; GetServiceReferences(references); int length = references.size(); if (length == 0) { /* if no service is being tracked */ throw ServiceException("No service is being tracked"); } std::list::const_iterator selectedRef; if (length > 1) { /* if more than one service, select highest ranking */ std::vector rankings(length); int count = 0; int maxRanking = std::numeric_limits::min(); std::list::const_iterator refIter = references.begin(); for (int i = 0; i < length; i++) { bool ok = false; Any rankingAny = refIter->GetProperty(ServiceConstants::SERVICE_RANKING()); int ranking = 0; if (rankingAny.Type() == typeid(int)) { ranking = any_cast(rankingAny); ok = true; } rankings[i] = ranking; if (ranking > maxRanking) { selectedRef = refIter; maxRanking = ranking; count = 1; } else { if (ranking == maxRanking) { count++; } } } if (count > 1) { /* if still more than one service, select lowest id */ long int minId = std::numeric_limits::max(); for (int i = 0; i < length; i++) { if (rankings[i] == maxRanking) { Any idAny = refIter->GetProperty(ServiceConstants::SERVICE_ID()); long int id = 0; if (idAny.Type() == typeid(long int)) { id = any_cast(idAny); } if (id < minId) { selectedRef = refIter; minId = id; } } } } ++refIter; } { MutexLocker lock(d->mutex); d->cachedReference = *selectedRef; return d->cachedReference; } } template T ServiceTracker::GetService(const ServiceReference& reference) const { itk::SmartPointer<_TrackedService> t = d->Tracked(); if (t.IsNull()) { /* if ServiceTracker is not open */ return 0; } { itk::MutexLockHolder lockT(*t); return t->GetCustomizedObject(reference); } } template void ServiceTracker::GetServices(std::list& services) const { itk::SmartPointer<_TrackedService> t = d->Tracked(); if (t.IsNull()) { /* if ServiceTracker is not open */ return; } { itk::MutexLockHolder lockT(*t); std::list references; - GetServiceReferences(references); + d->GetServiceReferences_unlocked(references, t.GetPointer()); for(std::list::const_iterator ref = references.begin(); ref != references.end(); ++ref) { services.push_back(GetService(*ref)); } } } template T ServiceTracker::GetService() const { T service = d->cachedService; if (service != 0) { MITK_DEBUG(d->DEBUG) << "ServiceTracker::getService[cached]:" << d->filter; return service; } MITK_DEBUG(d->DEBUG) << "ServiceTracker::getService:" << d->filter; try { ServiceReference reference = GetServiceReference(); if (reference.GetModule() == 0) { return 0; } return d->cachedService = GetService(reference); } catch (const ServiceException&) { return 0; } } template void ServiceTracker::Remove(const ServiceReference& reference) { itk::SmartPointer<_TrackedService> t = d->Tracked(); if (t.IsNull()) { /* if ServiceTracker is not open */ return; } t->Untrack(reference, ServiceEvent()); } template int ServiceTracker::Size() const { itk::SmartPointer<_TrackedService> t = d->Tracked(); if (t.IsNull()) { /* if ServiceTracker is not open */ return 0; } { itk::MutexLockHolder lockT(*t); return t->Size(); } } template int ServiceTracker::GetTrackingCount() const { itk::SmartPointer<_TrackedService> t = d->Tracked(); if (t.IsNull()) { /* if ServiceTracker is not open */ return -1; } { itk::MutexLockHolder lockT(*t); return t->GetTrackingCount(); } } template void ServiceTracker::GetTracked(TrackingMap& map) const { itk::SmartPointer<_TrackedService> t = d->Tracked(); if (t.IsNull()) { /* if ServiceTracker is not open */ return; } { itk::MutexLockHolder lockT(*t); t->CopyEntries(map); } } template bool ServiceTracker::IsEmpty() const { itk::SmartPointer<_TrackedService> t = d->Tracked(); if (t.IsNull()) { /* if ServiceTracker is not open */ return true; } { itk::MutexLockHolder lockT(*t); return t->IsEmpty(); } } template T ServiceTracker::AddingService(const ServiceReference& reference) { return dynamic_cast(d->context->GetService(reference)); } template void ServiceTracker::ModifiedService(const ServiceReference& /*reference*/, T /*service*/) { /* do nothing */ } template void ServiceTracker::RemovedService(const ServiceReference& reference, T /*service*/) { d->context->UngetService(reference); } } // end namespace mitk diff --git a/Core/Code/Service/mitkServiceTrackerPrivate.h b/Core/Code/Service/mitkServiceTrackerPrivate.h index a4ccf33b03..7c8dab3468 100644 --- a/Core/Code/Service/mitkServiceTrackerPrivate.h +++ b/Core/Code/Service/mitkServiceTrackerPrivate.h @@ -1,170 +1,172 @@ /*========================================================================= 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 MITKSERVICETRACKERPRIVATE_H #define MITKSERVICETRACKERPRIVATE_H #include "mitkServiceReference.h" #include "mitkLDAPFilter.h" #include namespace mitk { /** * \ingroup MicroServices */ template class ServiceTrackerPrivate { public: typedef itk::SimpleFastMutexLock MutexType; ServiceTrackerPrivate(ServiceTracker* st, ModuleContext* context, const ServiceReference& reference, ServiceTrackerCustomizer* customizer); ServiceTrackerPrivate(ServiceTracker* st, ModuleContext* context, const std::string& clazz, ServiceTrackerCustomizer* customizer); ServiceTrackerPrivate(ServiceTracker* st, ModuleContext* context, const LDAPFilter& filter, ServiceTrackerCustomizer* customizer); ~ServiceTrackerPrivate(); /** * Returns the list of initial ServiceReferences that will be * tracked by this ServiceTracker. * * @param className The class name with which the service was registered, or * null for all services. * @param filterString The filter criteria or null for all * services. * @return The list of initial ServiceReferences. * @throws std::invalid_argument If the specified filterString has an * invalid syntax. */ std::list GetInitialReferences(const std::string& className, const std::string& filterString); + void GetServiceReferences_unlocked(std::list& refs, TrackedService* t) const; + /* set this to true to compile in debug messages */ static const bool DEBUG; // = false; /** * The Module Context used by this ServiceTracker. */ ModuleContext* const context; /** * The filter used by this ServiceTracker which specifies the * search criteria for the services to track. */ LDAPFilter filter; /** * The ServiceTrackerCustomizer for this tracker. */ ServiceTrackerCustomizer* customizer; /** * Filter string for use when adding the ServiceListener. If this field is * set, then certain optimizations can be taken since we don't have a user * supplied filter. */ std::string listenerFilter; /** * Class name to be tracked. If this field is set, then we are tracking by * class name. */ std::string trackClass; /** * Reference to be tracked. If this field is set, then we are tracking a * single ServiceReference. */ ServiceReference trackReference; /** * Tracked services: ServiceReference -> customized Object and * ServiceListenerEntry object */ itk::SmartPointer > trackedService; /** * Accessor method for the current TrackedService object. This method is only * intended to be used by the unsynchronized methods which do not modify the * trackedService field. * * @return The current Tracked object. */ itk::SmartPointer > Tracked() const; /** * Called by the TrackedService object whenever the set of tracked services is * modified. Clears the cache. */ /* * This method must not be synchronized since it is called by TrackedService while * TrackedService is synchronized. We don't want synchronization interactions * between the listener thread and the user thread. */ void Modified(); /** * Cached ServiceReference for getServiceReference. */ mutable ServiceReference cachedReference; /** * Cached service object for GetService. * * This field is volatile since it is accessed by multiple threads. */ mutable T volatile cachedService; mutable MutexType mutex; private: inline ServiceTracker* q_func() { return static_cast *>(q_ptr); } inline const ServiceTracker* q_func() const { return static_cast *>(q_ptr); } friend class ServiceTracker; ServiceTracker * const q_ptr; }; } #include "mitkServiceTrackerPrivate.tpp" #endif // MITKSERVICETRACKERPRIVATE_H diff --git a/Core/Code/Service/mitkServiceTrackerPrivate.tpp b/Core/Code/Service/mitkServiceTrackerPrivate.tpp index bc56586828..905c27ff33 100644 --- a/Core/Code/Service/mitkServiceTrackerPrivate.tpp +++ b/Core/Code/Service/mitkServiceTrackerPrivate.tpp @@ -1,128 +1,141 @@ /*========================================================================= 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 "mitkTrackedService.h" #include "mitkModuleContext.h" #include "mitkLDAPFilter.h" #include namespace mitk { template const bool ServiceTrackerPrivate::DEBUG = true; template ServiceTrackerPrivate::ServiceTrackerPrivate( ServiceTracker* st, ModuleContext* context, const ServiceReference& reference, ServiceTrackerCustomizer* customizer) : context(context), customizer(customizer), trackReference(reference), trackedService(0), cachedReference(0), cachedService(0), q_ptr(st) { this->customizer = customizer ? customizer : q_func(); - this->listenerFilter = std::string("(") + ServiceConstants::SERVICE_ID() + - "=" + any_cast(reference.GetProperty(ServiceConstants::SERVICE_ID())) + ")"; + std::stringstream ss; + ss << "(" << ServiceConstants::SERVICE_ID() << "=" + << any_cast(reference.GetProperty(ServiceConstants::SERVICE_ID())) << ")"; + this->listenerFilter = ss.str(); try { this->filter = LDAPFilter(listenerFilter); } catch (const std::invalid_argument& e) { /* * we could only get this exception if the ServiceReference was * invalid */ std::invalid_argument ia(std::string("unexpected std::invalid_argument exception: ") + e.what()); throw ia; } } template ServiceTrackerPrivate::ServiceTrackerPrivate( ServiceTracker* st, ModuleContext* context, const std::string& clazz, ServiceTrackerCustomizer* customizer) : context(context), customizer(customizer), trackClass(clazz), trackReference(0), trackedService(0), cachedReference(0), cachedService(0), q_ptr(st) { this->customizer = customizer ? customizer : q_func(); this->listenerFilter = std::string("(") + mitk::ServiceConstants::OBJECTCLASS() + "=" + clazz + ")"; try { this->filter = LDAPFilter(listenerFilter); } catch (const std::invalid_argument& e) { /* * we could only get this exception if the clazz argument was * malformed */ std::invalid_argument ia( std::string("unexpected std::invalid_argument exception: ") + e.what()); throw ia; } } template ServiceTrackerPrivate::ServiceTrackerPrivate( ServiceTracker* st, ModuleContext* context, const LDAPFilter& filter, ServiceTrackerCustomizer* customizer) : context(context), filter(filter), customizer(customizer), listenerFilter(filter.ToString()), trackReference(0), trackedService(0), cachedReference(0), cachedService(0), q_ptr(st) { this->customizer = customizer ? customizer : q_func(); if (context == 0) { throw std::invalid_argument("The module context cannot be null."); } } template ServiceTrackerPrivate::~ServiceTrackerPrivate() { } template std::list ServiceTrackerPrivate::GetInitialReferences( const std::string& className, const std::string& filterString) { return context->GetServiceReferences(className, filterString); } +template +void ServiceTrackerPrivate::GetServiceReferences_unlocked(std::list& refs, + TrackedService* t) const +{ + if (t->Size() == 0) + { + return; + } + t->GetTracked(refs); +} + template itk::SmartPointer > ServiceTrackerPrivate::Tracked() const { return trackedService; } template void ServiceTrackerPrivate::Modified() { cachedReference = 0; /* clear cached value */ cachedService = 0; /* clear cached value */ MITK_DEBUG(DEBUG) << "ServiceTracker::Modified(): " << filter; } } // end namespace mitk diff --git a/Documentation/Doxygen/Modules/ModuleMicroServices.dox b/Documentation/Doxygen/Modules/ModuleMicroServices.dox index dd8176c71d..3abc0e1aee 100644 --- a/Documentation/Doxygen/Modules/ModuleMicroServices.dox +++ b/Documentation/Doxygen/Modules/ModuleMicroServices.dox @@ -1,10 +1,29 @@ /** \defgroup MicroServices Micro Services Classes \ingroup Core \brief This category includes classes related to the MITK Micro Services component. +The MITK Micro Services component provides a dynamic service registry based on the service layer +as specified in the OSGi R4.2 specifications. + +For further information, see the separate \ref MicroServices_Overview page. +*/ + +/** + +\page MicroServices_Overview The MITK Micro Services + +The MITK Micro Services component provides a dynamic service registry based on the service layer +as specified in the OSGi R4.2 specifications. It enables users to realize a service oriented +approach within their software stack. The advantages include higher reuse of components, looser +coupling, better organization of responsibilities, cleaner API contracts, etc. + +Following is a list of use cases and common patterns + +- \subpage MicroServices_EmulateSingleton + */ diff --git a/Documentation/Doxygen/Modules/ModuleMicroServices_EmulateSingleton.dox b/Documentation/Doxygen/Modules/ModuleMicroServices_EmulateSingleton.dox new file mode 100644 index 0000000000..f5c89649c7 --- /dev/null +++ b/Documentation/Doxygen/Modules/ModuleMicroServices_EmulateSingleton.dox @@ -0,0 +1,90 @@ +/** + +\page MicroServices_EmulateSingleton Emulating singletons with micro services + +\section MicroServices_EmulateSingleton_1 Meyers Singleton + +Singletons are a well known pattern to ensure that only one instance of a class exists +during the whole life-time of the application. A self-deleting variant is the "Meyers Singleton": + +\snippet uServices-singleton/SingletonOne.h s1 + +where the GetInstance() method is implemented as + +\snippet uServices-singleton/SingletonOne.cpp s1 + +If such a singleton is accessed during static deinitialization (which happens during unloading +of shared libraries or application termination), your program might crash or even worse, exhibit undefined +behavior, depending on your compiler and/or weekday. Such an access might happen in destructors of +other objects with static life-time. + +For example, suppose that SingletonOne needs to call a second Meyers singleton during destruction: + +\snippet uServices-singleton/SingletonOne.cpp s1d + +If SingletonTwo was destroyed before SingletonOne, this leads to the mentioned problems. Note that +this problem only occurs for static objects defined in the same shared library. + +Since you cannot reliably control the destruction order of global static objects, you must not +introduce dependencies between them during static deinitialization. This is one reason why one +should consider an alternative approach to singletons (unless you can absolutely make sure that nothing in +your shared library will introduce such dependencies. Never.) + +Of course you could use something like a "Phoenix singleton" but that will have other drawbacks in certain +scenarios. Returning pointers instead of references in GetInstance() would open up the possibility to +return NULL, but than again this would not help if you require a non-NULL instance in your destructor. + +Another reason for an alternative approach is that singletons are usually not meant to be singletons for eternity. +If your design evolves, you might hit a point where you suddenly need multiple instances of your singleton. + +\section MicroServices_EmulateSingleton_2 Singletons as a service + +The MITK Micro Services can be used to emulate the singleton pattern using a non-singleton class. This +leaves room for future extensions without the need for heavy refactoring. Additionally, it gives you +full control about the construction and destruction order of your "singletons" inside your shared library +or executable, making it possible to have dependencies between them during destruction. + +\subsection MicroServices_EmulateSingleton_2_1 Converting a classic singleton + +We modify the previous SingletonOne class such that it internally uses the MITK micro services API. The changes +are discussed in detail below. + +\snippet uServices-singleton/SingletonOne.h ss1 + + - Inherit itk::LightObject: All service implementations (not their interfaces) must inherit from itk::LightObject. + In the implementation above, the class SingletonOneService provides the implementation as well as the interface. + - Friend activator: We move the responsibility of constructing instances of SingletonOneService from the GetInstance() + method to the module activator. + - Service interface declaration: Because the SingletonOneService class introduces a new service interface, it must + be registered under a unique name using the helper macro MITK_DECLARE_SERVICE_INTERFACE. + +Let's have a look at the modified GetInstance() and ~SingletonOneService() methods. + +\snippet uServices-singleton/SingletonOne.cpp ss1gi + +The inline comments should explain the details. Note that we now had to change the return type to a pointer, instead +of a reference as in the classic singleton. This is necessary since we can no longer guarantee that an instance +always exists. Clients of the GetInstance() method must check for null pointers and react appropriately. + +\warning Newly created "singletons" should not expose a GetInstance() method. They should be handled as proper +services and hence should be retrieved by clients using the mitk::ModuleContext or mitk::ServiceTracker API. The +GetInstance() method is for migration purposes only. + +\snippet uServices-singleton/SingletonOne.cpp ss1d + +The SingletonTwoService::GetInstance() method is implemented exactly as in SingletonOneService. Because we know +that the module activator guarantees that a SingletonTwoService instance will always be available during the +life-time of a SingletonOneService instance (see below), we can assert a non-null pointer. Otherwise, we would +have to handle the null-pointer case. + +The order of construction/registration and destruction/unregistration of our singletons (or any other services) is +defined in the Load() and Unload() methods of the module activator. + +\snippet uServices-singleton/main.cpp 0 + +The Unload() method is defined as: + +\snippet uServices-singleton/main.cpp 1 + + +*/ diff --git a/Documentation/Snippets/CMakeLists.txt b/Documentation/Snippets/CMakeLists.txt index 65273aae45..b7543e01f6 100644 --- a/Documentation/Snippets/CMakeLists.txt +++ b/Documentation/Snippets/CMakeLists.txt @@ -1,5 +1,6 @@ if(BUILD_TESTING) - include_directories(${MITK_INCLUDE_DIRS} ${ITK_INCLUDE_DIRS} ${VTK_INCLUDE_DIRS}) - link_directories(${MITK_LINK_DIRECTORIES}) - mitkFunctionCompileSnippets("${CMAKE_CURRENT_SOURCE_DIR}" ${MITK_LIBRARIES}) + MITK_USE_MODULE(Mitk) + include_directories(${ALL_INCLUDE_DIRECTORIES}) + link_directories(${ALL_LIBRARY_DIRS}) + mitkFunctionCompileSnippets("${CMAKE_CURRENT_SOURCE_DIR}" ${ALL_LIBRARIES}) endif() diff --git a/Documentation/Snippets/uServices-singleton/SingletonOne.cpp b/Documentation/Snippets/uServices-singleton/SingletonOne.cpp new file mode 100644 index 0000000000..ec9ee5f23d --- /dev/null +++ b/Documentation/Snippets/uServices-singleton/SingletonOne.cpp @@ -0,0 +1,79 @@ +#include "SingletonOne.h" + +#include "SingletonTwo.h" + +#include + +#include +#include + +#include + +//![s1] +SingletonOne& SingletonOne::GetInstance() +{ + static SingletonOne instance; + return instance; +} +//![s1] + +SingletonOne::SingletonOne() : a(1) +{ +} + +//![s1d] +SingletonOne::~SingletonOne() +{ + std::cout << "SingletonTwo::b = " << SingletonTwo::GetInstance().b << std::endl; +} +//![s1d] + +//![ss1gi] +SingletonOneService* SingletonOneService::GetInstance() +{ + static mitk::ServiceReference serviceRef; + static mitk::ModuleContext* context = mitk::GetModuleContext(); + + if (!serviceRef) + { + // This is either the first time GetInstance() was called, + // or a SingletonOneService instance has not yet been registered. + serviceRef = context->GetServiceReference(); + } + + if (serviceRef) + { + // We have a valid service reference. It always points to the service + // with the lowest id (usually the one which was registered first). + // This still might return a null pointer, if all SingletonOneService + // instances have been unregistered (during unloading of the library, + // for example). + return context->GetService(serviceRef); + } + else + { + // No SingletonOneService instance was registered yet. + return 0; + } +} +//![ss1gi] + +SingletonOneService::SingletonOneService() + : a(1) +{ + SingletonTwoService* singletonTwoService = SingletonTwoService::GetInstance(); + assert(singletonTwoService != 0); + std::cout << "SingletonTwoService::b = " << singletonTwoService->b << std::endl; +} + +//![ss1d] +SingletonOneService::~SingletonOneService() +{ + SingletonTwoService* singletonTwoService = SingletonTwoService::GetInstance(); + + // The module activator must ensure that a SingletonTwoService instance is + // available during destruction of a SingletonOneService instance. + assert(singletonTwoService != 0); + std::cout << "SingletonTwoService::b = " << singletonTwoService->b << std::endl; +} +//![ss1d] diff --git a/Documentation/Snippets/uServices-singleton/SingletonOne.h b/Documentation/Snippets/uServices-singleton/SingletonOne.h new file mode 100644 index 0000000000..1d862df6fd --- /dev/null +++ b/Documentation/Snippets/uServices-singleton/SingletonOne.h @@ -0,0 +1,73 @@ +#ifndef SINGLETONONE_H +#define SINGLETONONE_H + +#include +#include +#include +#include + +#include + +namespace mitk { class ModuleContext; } + +//![s1] +class SingletonOne +{ +public: + + static SingletonOne& GetInstance(); + + // Just some member + int a; + +private: + + SingletonOne(); + ~SingletonOne(); + + // Disable copy constructor and assignment operator. + SingletonOne(const SingletonOne&); + SingletonOne& operator=(const SingletonOne&); +}; +//![s1] + +class SingletonTwoService; + +//![ss1] +class SingletonOneService : public itk::LightObject +{ +public: + + // This will return a SingletonOneService instance with the + // lowest service id at the time this method was called the first + // time and returned a non-null value (which is usually the instance + // which was registered first). A null-pointer is returned if no + // instance was registered yet. + static SingletonOneService* GetInstance(); + + int a; + +private: + + // Only our module activator class should be able to instantiate + // a SingletonOneService object. + friend class MyActivator; + + // Creates some typedefs + mitkClassMacro(SingletonOneService, itk::LightObject) + + // Creates a static New() method which correct reference counting + itkFactorylessNewMacro(SingletonOneService) + + SingletonOneService(); + ~SingletonOneService(); + + // Disable copy constructor and assignment operator. + SingletonOneService(const SingletonOneService&); + SingletonOneService& operator=(const SingletonOneService&); +}; + +MITK_DECLARE_SERVICE_INTERFACE(SingletonOneService, "org.mitk.snippet.SingletonOneService") +//![ss1] + +#endif // SINGLETONONE_H diff --git a/Documentation/Snippets/uServices-singleton/SingletonTwo.cpp b/Documentation/Snippets/uServices-singleton/SingletonTwo.cpp new file mode 100644 index 0000000000..793605579c --- /dev/null +++ b/Documentation/Snippets/uServices-singleton/SingletonTwo.cpp @@ -0,0 +1,63 @@ +#include "SingletonTwo.h" + +#include "SingletonOne.h" +#include + +#include +#include + + +SingletonTwo& SingletonTwo::GetInstance() +{ + static SingletonTwo instance; + return instance; +} + +SingletonTwo::SingletonTwo() : b(2) +{ + std::cout << "Constructing SingletonTwo" << std::endl; +} + +SingletonTwo::~SingletonTwo() +{ + std::cout << "Deleting SingletonTwo" << std::endl; + std::cout << "SingletonOne::a = " << SingletonOne::GetInstance().a << std::endl; +} + +SingletonTwoService* SingletonTwoService::GetInstance() +{ + static mitk::ServiceReference serviceRef; + static mitk::ModuleContext* context = mitk::GetModuleContext(); + + if (!serviceRef) + { + // This is either the first time GetInstance() was called, + // or a SingletonTwoService instance has not yet been registered. + serviceRef = context->GetServiceReference(); + } + + if (serviceRef) + { + // We have a valid service reference. It always points to the service + // with the lowest id (usually the one which was registered first). + // This still might return a null pointer, if all SingletonTwoService + // instances have been unregistered (during unloading of the library, + // for example). + return context->GetService(serviceRef); + } + else + { + // No SingletonTwoService instance was registered yet. + return 0; + } +} + +SingletonTwoService::SingletonTwoService() : b(2) +{ + std::cout << "Constructing SingletonTwoService" << std::endl; +} + +SingletonTwoService::~SingletonTwoService() +{ + std::cout << "Deleting SingletonTwoService" << std::endl; +} diff --git a/Documentation/Snippets/uServices-singleton/SingletonTwo.h b/Documentation/Snippets/uServices-singleton/SingletonTwo.h new file mode 100644 index 0000000000..c7e17fdf0a --- /dev/null +++ b/Documentation/Snippets/uServices-singleton/SingletonTwo.h @@ -0,0 +1,52 @@ +#ifndef SINGLETONTWO_H +#define SINGLETONTWO_H + +#include + +#include +#include + +class SingletonTwo +{ +public: + + static SingletonTwo& GetInstance(); + + int b; + +private: + + SingletonTwo(); + ~SingletonTwo(); + + // Disable copy constructor and assignment operator. + SingletonTwo(const SingletonTwo&); + SingletonTwo& operator=(const SingletonTwo&); +}; + +class SingletonTwoService : public itk::LightObject +{ +public: + + static SingletonTwoService* GetInstance(); + + int b; + +private: + + friend class MyActivator; + + mitkClassMacro(SingletonTwoService, itk::LightObject) + itkFactorylessNewMacro(SingletonTwoService) + + SingletonTwoService(); + ~SingletonTwoService(); + + // Disable copy constructor and assignment operator. + SingletonTwoService(const SingletonTwoService&); + SingletonTwoService& operator=(const SingletonTwoService&); +}; + +MITK_DECLARE_SERVICE_INTERFACE(SingletonTwoService, "org.mitk.snippet.SingletonTwoService") + +#endif // SINGLETONTWO_H diff --git a/Documentation/Snippets/uServices-singleton/files.cmake b/Documentation/Snippets/uServices-singleton/files.cmake new file mode 100644 index 0000000000..15e028e436 --- /dev/null +++ b/Documentation/Snippets/uServices-singleton/files.cmake @@ -0,0 +1,16 @@ +set(_init_file ${CMAKE_CURRENT_BINARY_DIR}/uServices_singleton_init.cpp) + +set(MODULE_NAME "uServices_singleton") +set(MODULE_LIBNAME "") +set(MODULE_DEPENDS_STR "Mitk") +set(MODULE_PACKAGE_DEPENDS_STR) +set(MODULE_VERSION) +set(MODULE_QT_BOOL "false") +configure_file(${MITK_SOURCE_DIR}/CMake/mitkModuleInit.cpp ${_init_file} @ONLY) + +set(snippet_src_files + main.cpp + SingletonOne.cpp + SingletonTwo.cpp + ${_init_file} +) diff --git a/Documentation/Snippets/uServices-singleton/main.cpp b/Documentation/Snippets/uServices-singleton/main.cpp new file mode 100644 index 0000000000..02c7d7aca7 --- /dev/null +++ b/Documentation/Snippets/uServices-singleton/main.cpp @@ -0,0 +1,66 @@ +#include +#include +#include + +#include "SingletonOne.h" +#include "SingletonTwo.h" + +class MyActivator : public mitk::ModuleActivator +{ + +public: + + //![0] + void Load(mitk::ModuleContext* context) + { + // The Load() method of the module activator is called during static + // initialization time of the shared library. + + // First create and register a SingletonTwoService instance. + m_SingletonTwo = SingletonTwoService::New(); + context->RegisterService(m_SingletonTwo); + + // Now the SingletonOneService constructor will get a valid + // SingletonTwoService instance. + m_SingletonOne = SingletonOneService::New(); + m_SingletonOneReg = context->RegisterService(m_SingletonOne); + } + //![0] + + //![1] + void Unload(mitk::ModuleContext* context) + { + // Services are automatically unregistered during unloading of + // the shared library after the call to Unload(mitk::ModuleContext*) + // has returned. + + // Since SingletonOneService needs a non-null SingletonTwoService + // instance in its destructor, we explicitly unregister and delete the + // SingletonOneService instance here. This way, the SingletonOneService + // destructor will still get a valid SingletonTwoService instance. + m_SingletonOneReg.Unregister(); + m_SingletonOne = 0; + + // For singletonTwoService, we rely on the automatic unregistering + // by the service registry and on automatic deletion due to the + // smart pointer reference counting. You must not delete service instances + // in this method without unregistering them first. + } + //![1] + +private: + + // We use smart pointers for automatic garbage collection + SingletonOneService::Pointer m_SingletonOne; + SingletonTwoService::Pointer m_SingletonTwo; + + mitk::ServiceRegistration m_SingletonOneReg; + +}; + +MITK_EXPORT_MODULE_ACTIVATOR(uServices_singleton, MyActivator) + +int main(int argc, char* argv[]) +{ + return 0; +}