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/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; +}