Explorar o código

ENH: PluginFramework: The service framework is now functional with the exception of filters

The service framework now supports registering and querying service classes. Filtering is still work in
progress, as well as integrating the Qt Mobility service framework.

Also added CMake support for embedding files via the Qt Resource system into a plugin which should
be cached by the framework (for example, the servicedescroptor.xml file).
Sascha Zelzer %!s(int64=16) %!d(string=hai) anos
pai
achega
75b0b031c5
Modificáronse 31 ficheiros con 1980 adicións e 154 borrados
  1. 11 0
      Applications/ctkPluginBrowser/ctkPluginBrowser.cxx
  2. 20 2
      CMake/ctkMacroBuildPlugin.cmake
  3. 2 1
      CMake/ctkMacroGeneratePluginManifest.cmake
  4. 34 0
      CMake/ctkMacroGeneratePluginResourceFile.cmake
  5. 1 0
      CMakeLists.txt
  6. 5 1
      Libs/PluginFramework/CMakeLists.txt
  7. 1 0
      Libs/PluginFramework/ctkPlugin.h
  8. 5 0
      Libs/PluginFramework/ctkPluginConstants.cxx
  9. 50 0
      Libs/PluginFramework/ctkPluginConstants.h
  10. 21 7
      Libs/PluginFramework/ctkPluginContext.cxx
  11. 202 6
      Libs/PluginFramework/ctkPluginContext.h
  12. 1 1
      Libs/PluginFramework/ctkPluginFrameworkContext.cxx
  13. 2 1
      Libs/PluginFramework/ctkPluginFrameworkContext_p.h
  14. 34 0
      Libs/PluginFramework/ctkPluginFramework_global.h
  15. 85 0
      Libs/PluginFramework/ctkServiceException.cxx
  16. 90 0
      Libs/PluginFramework/ctkServiceException.h
  17. 119 0
      Libs/PluginFramework/ctkServiceFactory.h
  18. 64 5
      Libs/PluginFramework/ctkServiceReference.cxx
  19. 129 2
      Libs/PluginFramework/ctkServiceReference.h
  20. 177 0
      Libs/PluginFramework/ctkServiceReferencePrivate.cxx
  21. 66 0
      Libs/PluginFramework/ctkServiceReferencePrivate.h
  22. 77 121
      Libs/PluginFramework/ctkServiceRegistration.cxx
  23. 12 6
      Libs/PluginFramework/ctkServiceRegistration.h
  24. 47 0
      Libs/PluginFramework/ctkServiceRegistrationPrivate.cxx
  25. 116 0
      Libs/PluginFramework/ctkServiceRegistrationPrivate.h
  26. 369 0
      Libs/PluginFramework/ctkServices.cxx
  27. 186 0
      Libs/PluginFramework/ctkServices_p.h
  28. 7 1
      Plugins/org.commontk.cli/CMakeLists.txt
  29. 5 0
      Plugins/org.commontk.cli/ctkCLIPlugin.cxx
  30. 41 0
      Plugins/org.commontk.cli/ctkCLIRegistry.h
  31. 1 0
      Plugins/org.commontk.cli/manifest_headers.cmake

+ 11 - 0
Applications/ctkPluginBrowser/ctkPluginBrowser.cxx

@@ -24,6 +24,8 @@
 #include "ctkPluginTableModel.h"
 #include "ctkPluginResourcesTreeModel.h"
 #include "ctkQtResourcesTreeModel.h"
+#include "ctkServiceReference.h"
+#include "ctkPluginConstants.h"
 
 #include <ui_ctkPluginBrowserMainWindow.h>
 
@@ -99,6 +101,15 @@ namespace ctk {
     QByteArray mfContent = plugin->getResource("/META-INF/MANIFEST.MF");
     QString location = QString("/") + plugin->getSymbolicName() + "/META-INF/MANIFEST.MF";
     editors->openEditor(location, mfContent, location + " [cached]");
+
+    QList<ServiceReference*> serviceRefs = plugin->getPluginContext()->getServiceReferences("");
+    QListIterator<ServiceReference*> it(serviceRefs);
+    while (it.hasNext())
+    {
+      ServiceReference* ref = it.next();
+      qDebug() << "Service from" << ref->getPlugin()->getSymbolicName() << ":" << ref->getPropertyKeys();
+      qDebug() << "Object Classes:" << ref->getProperty(PluginConstants::OBJECTCLASS).toStringList();
+    }
   }
 
   void PluginBrowser::qtResourceDoubleClicked(const QModelIndex& index)

+ 20 - 2
CMake/ctkMacroBuildPlugin.cmake

@@ -29,7 +29,7 @@
 #
 MACRO(ctkMacroBuildPlugin)
   CtkMacroParseArguments(MY
-    "NAME;EXPORT_DIRECTIVE;SRCS;MOC_SRCS;UI_FORMS;INCLUDE_DIRECTORIES;TARGET_LIBRARIES;RESOURCES;LIBRARY_TYPE"
+    "NAME;EXPORT_DIRECTIVE;SRCS;MOC_SRCS;UI_FORMS;INCLUDE_DIRECTORIES;TARGET_LIBRARIES;RESOURCES;CACHED_RESOURCEFILES;LIBRARY_TYPE"
     ""
     ${ARGN}
     )
@@ -99,8 +99,10 @@ MACRO(ctkMacroBuildPlugin)
   SET(Plugin-Version )
 
   # If a file named manifest_headers.cmake exists, read it
+  SET(manifest_headers_dep )
   IF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake")
     INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake)
+    SET(manifest_headers_dep "${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake")
   ENDIF()
 
   # Set the plugin_symbolicname to the library name if it is not set
@@ -109,7 +111,8 @@ MACRO(ctkMacroBuildPlugin)
   ENDIF()
 
   # Add the generated manifest qrc file
-  ctkMacroGeneratePluginManifest(MY_QRC_SRCS
+  SET(manifest_qrc_src )
+  ctkMacroGeneratePluginManifest(manifest_qrc_src
     ACTIVATIONPOLICY ${Plugin-ActivationPolicy}
     CATEGORY ${Plugin-Category}
     CONTACT_ADDRESS ${Plugin-ContactAddress}
@@ -125,6 +128,21 @@ MACRO(ctkMacroBuildPlugin)
     VERSION ${Plugin-Version}
     )
 
+  IF(manifest_headers_dep)
+    SET_PROPERTY(SOURCE ${manifest_qrc_src} APPEND
+                   PROPERTY OBJECT_DEPENDS ${manifest_headers_dep})
+  ENDIF()
+  LIST(APPEND MY_QRC_SRCS ${manifest_qrc_src})
+
+  # Add any other additional resource files
+  IF(MY_CACHED_RESOURCEFILES)
+    STRING(REPLACE "." "_" _plugin_symbolicname ${Plugin-SymbolicName})
+    ctkMacroGeneratePluginResourceFile(MY_QRC_SRCS
+      NAME ${_plugin_symbolicname}_cached.qrc
+      PREFIX ${Plugin-SymbolicName}
+      RESOURCES ${MY_CACHED_RESOURCEFILES})
+  ENDIF()
+
   SOURCE_GROUP("Resources" FILES
     ${MY_RESOURCES}
     ${MY_UI_FORMS}

+ 2 - 1
CMake/ctkMacroGeneratePluginManifest.cmake

@@ -73,7 +73,8 @@ MACRO(ctkMacroGeneratePluginManifest QRC_SRCS)
 
   SET(_manifest_filename "MANIFEST.MF")
   SET(_manifest_filepath "${CMAKE_CURRENT_BINARY_DIR}/${_manifest_filename}")
-  SET(_manifest_qrc_filepath "${CMAKE_CURRENT_BINARY_DIR}/${MY_SYMBOLIC_NAME}_manifest.qrc")
+  STRING(REPLACE "." "_" _symbolic_name ${MY_SYMBOLIC_NAME})
+  SET(_manifest_qrc_filepath "${CMAKE_CURRENT_BINARY_DIR}/${_symbolic_name}_manifest.qrc")
 
   SET(_manifest_qrc_content
 "<!DOCTYPE RCC><RCC version=\"1.0\">

+ 34 - 0
CMake/ctkMacroGeneratePluginResourceFile.cmake

@@ -0,0 +1,34 @@
+#
+# Depends on:
+#  CTK/CMake/ctkMacroParseArguments.cmake
+#
+
+MACRO(ctkMacroGeneratePluginResourceFile QRC_SRCS)
+
+  CtkMacroParseArguments(MY
+    "NAME;PREFIX;RESOURCES"
+    ""
+    ${ARGN}
+    )
+
+  SET(_qrc_filepath "${CMAKE_CURRENT_BINARY_DIR}/${MY_NAME}")
+
+  SET(_qrc_content
+"<!DOCTYPE RCC><RCC version=\"1.0\">
+<qresource prefix=\"/${MY_PREFIX}\">
+")
+
+  FOREACH(_resource_file ${MY_RESOURCES})
+    CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/${_resource_file}" "${CMAKE_CURRENT_BINARY_DIR}/${_resource_file}" COPYONLY)
+    SET(_qrc_content "${_qrc_content}<file>${_resource_file}</file>
+")
+  ENDFOREACH()
+
+  SET(_qrc_content "${_qrc_content}</qresource>
+</RCC>
+")
+  FILE(WRITE "${_qrc_filepath}" "${_qrc_content}")
+
+  QT4_ADD_RESOURCES(${QRC_SRCS} ${_qrc_filepath})
+
+ENDMACRO()

+ 1 - 0
CMakeLists.txt

@@ -105,6 +105,7 @@ INCLUDE(CMake/ctkMacroAddCtkLibraryOptions.cmake)
 INCLUDE(CMake/ctkFunctionGenerateDGraphInput.cmake)
 INCLUDE(CMake/ctkFunctionGenerateProjectXml.cmake)
 INCLUDE(CMake/ctkMacroGeneratePluginManifest.cmake)
+INCLUDE(CMake/ctkMacroGeneratePluginResourceFile.cmake)
 
 # Used by CTKGenerateCTKConfig.cmake
 SET(CTK_CMAKE_DIR ${CTK_SOURCE_DIR}/CMake)

+ 5 - 1
Libs/PluginFramework/CMakeLists.txt

@@ -62,9 +62,12 @@ SET(KIT_SRCS
   ctkPluginPrivate.cxx
   ctkPlugins.cxx
   ctkRequirePlugin.cxx
+  ctkServiceException.cxx
   ctkServiceReference.cxx
+  ctkServiceReferencePrivate.cxx
   ctkServiceRegistration.cxx
-  #PluginFramework/ctkServiceRegistry.cxx
+  ctkServiceRegistrationPrivate.cxx
+  ctkServices.cxx
   ctkPluginStorage.cxx
   ctkVersion.cxx
   ctkVersionRange.cxx
@@ -79,6 +82,7 @@ SET(KIT_MOC_SRCS
   ctkPluginEvent.h
   ctkPluginFrameworkEvent.h
   ctkPluginFrameworkListeners_p.h
+  ctkServiceFactory.h
 )
 
 # UI files

+ 1 - 0
Libs/PluginFramework/ctkPlugin.h

@@ -589,6 +589,7 @@ namespace ctk {
     friend class PluginFramework;
     friend class PluginFrameworkContext;
     friend class Plugins;
+    friend class ServiceReferencePrivate;
 
     PluginPrivate * const d_ptr;
 

+ 5 - 0
Libs/PluginFramework/ctkPluginConstants.cxx

@@ -45,4 +45,9 @@ namespace ctk {
   const QString PluginConstants::RESOLUTION_MANDATORY = "mandatory";
   const QString PluginConstants::RESOLUTION_OPTIONAL = "optional";
 
+
+  const QString PluginConstants::OBJECTCLASS = "objectclass";
+  const QString PluginConstants::SERVICE_ID = "service.id";
+  const QString PluginConstants::SERVICE_RANKING = "service.ranking";
+
 }

+ 50 - 0
Libs/PluginFramework/ctkPluginConstants.h

@@ -251,6 +251,56 @@ namespace ctk {
      */
     static const QString RESOLUTION_OPTIONAL; // = "optional"
 
+    /**
+     * Service property identifying all of the class names under which a service
+     * was registered in the Framework. The value of this property must be of
+     * type <code>QStringList</code>.
+     *
+     * <p>
+     * This property is set by the Framework when a service is registered.
+     */
+    static const QString OBJECTCLASS; // = "objectclass"
+
+    /**
+     * Service property identifying a service's registration number. The value
+     * of this property must be of type <code>qlonglong</code>.
+     *
+     * <p>
+     * The value of this property is assigned by the Framework when a service is
+     * registered. The Framework assigns a unique value that is larger than all
+     * previously assigned values since the Framework was started. These values
+     * are NOT persistent across restarts of the Framework.
+     */
+    static const QString SERVICE_ID; //	= "service.id"
+
+    /**
+     * Service property identifying a service's ranking number.
+     *
+     * <p>
+     * This property may be supplied in the
+     * <code>ServiceProperties</code> object passed to the
+     * <code>PluginContext::registerService</code> method. The value of this
+     * property must be of type <code>int</code>.
+     *
+     * <p>
+     * The service ranking is used by the Framework to determine the <i>natural
+     * order</i> of services, see {@link ServiceReference::operator<(const ServiceReference&)},
+     * and the <i>default</i> service to be returned from a call to the
+     * {@link PluginContext::getServiceReference} method.
+     *
+     * <p>
+     * The default ranking is zero (0). A service with a ranking of
+     * <code>std::numeric_limits<int>::max()</code> is very likely to be returned as the
+     * default service, whereas a service with a ranking of
+     * <code>std::numeric_limits<int>::min()</code> is very unlikely to be returned.
+     *
+     * <p>
+     * If the supplied property value is not of type <code>int</code>, it is
+     * deemed to have a ranking value of zero.
+     */
+    static const QString SERVICE_RANKING; // = "service.ranking"
+
+
   };
 
 }

+ 21 - 7
Libs/PluginFramework/ctkPluginContext.cxx

@@ -25,6 +25,7 @@
 #include "ctkPluginFrameworkContext_p.h"
 #include "ctkServiceRegistration.h"
 #include "ctkServiceReference.h"
+#include "ctkServiceReferencePrivate.h"
 
 #include <stdexcept>
 
@@ -96,24 +97,37 @@ namespace ctk {
     return d->plugin->fwCtx->plugins->install(location, in);
   }
 
-  ServiceRegistration PluginContext::registerService(const QStringList& clazzes, QObject* service, const ServiceProperties& properties)
+  ServiceRegistration* PluginContext::registerService(const QStringList& clazzes, QObject* service, const ServiceProperties& properties)
   {
-
+    Q_D(PluginContext);
+    d->isPluginContextValid();
+    return d->plugin->fwCtx->services.registerService(d->plugin, clazzes, service, properties);
   }
 
-  QList<ServiceReference> PluginContext::getServiceReferences(const QString& clazz, const QString& filter)
+  QList<ServiceReference*> PluginContext::getServiceReferences(const QString& clazz, const QString& filter)
   {
-
+    Q_D(PluginContext);
+    d->isPluginContextValid();
+    return d->plugin->fwCtx->services.get(clazz, filter);
   }
 
-  ServiceReference PluginContext::getServiceReference(const QString& clazz)
+  ServiceReference* PluginContext::getServiceReference(const QString& clazz)
   {
-
+    Q_D(PluginContext);
+    d->isPluginContextValid();
+    return d->plugin->fwCtx->services.get(d->plugin, clazz);
   }
 
-  QObject* PluginContext::getService(const ServiceReference& reference)
+  QObject* PluginContext::getService(ServiceReference* reference)
   {
+    if (reference == 0)
+    {
+      throw std::invalid_argument("Null ServiceReference is not valid");
+    }
 
+    Q_D(PluginContext);
+    d->isPluginContextValid();
+    return reference->d_func()->getService(d->plugin->q_func());
   }
 
   bool PluginContext::connectPluginListener(const QObject* receiver, const char* method,

+ 202 - 6
Libs/PluginFramework/ctkPluginContext.h

@@ -27,6 +27,8 @@
 #include <QVariant>
 #include <QUrl>
 
+#include "ctkPluginFramework_global.h"
+
 #include "ctkPluginEvent.h"
 
 #include "CTKPluginFrameworkExport.h"
@@ -96,8 +98,6 @@ namespace ctk {
 
   public:
 
-    typedef QHash<QString, QVariant> ServiceProperties;
-
     ~PluginContext();
 
     /**
@@ -134,13 +134,209 @@ namespace ctk {
      */
     QList<Plugin*> getPlugins() const;
 
-    ServiceRegistration registerService(const QStringList& clazzes, QObject* service, const ServiceProperties& properties = ServiceProperties());
+    /**
+     * Registers the specified service object with the specified properties
+     * under the specified class names into the Framework. A
+     * <code>ServiceRegistration</code> object is returned. The
+     * <code>ServiceRegistration</code> object is for the private use of the
+     * plugin registering the service and should not be shared with other
+     * plugins. The registering plugin is defined to be the context plugin.
+     * Other plugins can locate the service by using either the
+     * {@link #getServiceReferences} or {@link #getServiceReference} method.
+     *
+     * <p>
+     * A plugin can register a service object that implements the
+     * {@link ServiceFactory} interface to have more flexibility in providing
+     * service objects to other plugins.
+     *
+     * <p>
+     * The following steps are required to register a service:
+     * <ol>
+     * <li>If <code>service</code> is not a <code>ServiceFactory</code>, an
+     * <code>std::invalid_argument</code> is thrown if <code>service</code>
+     * is not an instance of all the specified class names.
+     * <li>The Framework adds the following service properties to the service
+     * properties from the specified <code>ServiceProperties</code> (which may be
+     * omitted): <br/>
+     * A property named {@link PluginConstants#SERVICE_ID} identifying the
+     * registration number of the service <br/>
+     * A property named {@link PluginConstants#OBJECTCLASS} containing all the
+     * specified classes. <br/>
+     * Properties with these names in the specified <code>ServiceProperties</code> will
+     * be ignored.
+     * <li>The service is added to the Framework service registry and may now be
+     * used by other plugins.
+     * <li>A service event of type {@link ServiceEvent#REGISTERED} is fired.
+     * <li>A <code>ServiceRegistration</code> object for this registration is
+     * returned.
+     * </ol>
+     *
+     * @param clazzes The class names under which the service can be located.
+     *        The class names will be stored in the service's
+     *        properties under the key {@link PluginConstants#OBJECTCLASS}.
+     * @param service The service object or a <code>ServiceFactory</code>
+     *        object.
+     * @param properties The properties for this service. The keys in the
+     *        properties object must all be <code>QString</code> objects. See
+     *        {@link PluginConstants} for a list of standard service property keys.
+     *        Changes should not be made to this object after calling this
+     *        method. To update the service's properties the
+     *        {@link ServiceRegistration::setProperties} method must be called.
+     *        The set of properties may be omitted if the service has
+     *        no properties.
+     * @return A <code>ServiceRegistration</code> object for use by the plugin
+     *         registering the service to update the service's properties or to
+     *         unregister the service.
+     * @throws std::invalid_argument If one of the following is true:
+     *         <ul>
+     *         <li><code>service</code> is <code>0</code>. <li><code>service
+     *         </code> is not a <code>ServiceFactory</code> object and is not an
+     *         instance of all the named classes in <code>clazzes</code>. <li>
+     *         <code>properties</code> contains case variants of the same key
+     *         name.
+     *         </ul>
+     * @throws std::logic_error If this PluginContext is no longer valid.
+     * @see ServiceRegistration
+     * @see ServiceFactory
+     */
+    ServiceRegistration* registerService(const QStringList& clazzes, QObject* service, const ServiceProperties& properties = ServiceProperties());
 
-    QList<ServiceReference> getServiceReferences(const QString& clazz, const QString& filter = QString());
+    /**
+     * Returns a list of <code>ServiceReference</code> objects. The returned
+     * list contains services that
+     * were registered under the specified class and match the specified filter
+     * expression.
+     *
+     * <p>
+     * The list is valid at the time of the call to this method. However since
+     * the Framework is a very dynamic environment, services can be modified or
+     * unregistered at any time.
+     *
+     * <p>
+     * The specified <code>filter</code> expression is used to select the
+     * registered services whose service properties contain keys and values
+     * which satisfy the filter expression. See {@link Filter} for a description
+     * of the filter syntax. If the specified <code>filter</code> is
+     * empty, all registered services are considered to match the
+     * filter. If the specified <code>filter</code> expression cannot be parsed,
+     * an {@link std::invalid_argument} will be thrown with a human readable
+     * message where the filter became unparsable.
+     *
+     * <p>
+     * The result is a list of <code>ServiceReference</code> objects for all
+     * services that meet all of the following conditions:
+     * <ul>
+     * <li>If the specified class name, <code>clazz</code>, is not
+     * empty, the service must have been registered with the
+     * specified class name. The complete list of class names with which a
+     * service was registered is available from the service's
+     * {@link PlugincConstants::OBJECTCLASS objectClass} property.
+     * <li>If the specified <code>filter</code> is not empty, the
+     * filter expression must match the service.
+     * </ul>
+     *
+     * @param clazz The class name with which the service was registered or
+     *        an empty string for all services.
+     * @param filter The filter expression or empty for all
+     *        services.
+     * @return A list of <code>ServiceReference</code> objects or
+     *         an empty list if no services are registered which satisfy the
+     *         search.
+     * @throws std::invalid_argument If the specified <code>filter</code>
+     *         contains an invalid filter expression that cannot be parsed.
+     * @throws std::logic_error If this PluginContext is no longer valid.
+     */
+    QList<ServiceReference*> getServiceReferences(const QString& clazz, const QString& filter = QString());
 
-    ServiceReference getServiceReference(const QString& clazz);
+    /**
+     * Returns a <code>ServiceReference</code> object for a service that
+     * implements and was registered under the specified class.
+     *
+     * <p>
+     * The returned <code>ServiceReference</code> object is valid at the time of
+     * the call to this method. However as the Framework is a very dynamic
+     * environment, services can be modified or unregistered at any time.
+     *
+     * <p>
+     * This method is the same as calling
+     * {@link PluginContext::getServiceReferences(const QString&, const QString&)} with an
+     * empty filter expression. It is provided as a convenience for
+     * when the caller is interested in any service that implements the
+     * specified class.
+     * <p>
+     * If multiple such services exist, the service with the highest ranking (as
+     * specified in its {@link PluginConstants::SERVICE_RANKING} property) is returned.
+     * <p>
+     * If there is a tie in ranking, the service with the lowest service ID (as
+     * specified in its {@link PluginConstants::SERVICE_ID} property); that is, the
+     * service that was registered first is returned.
+     *
+     * @param clazz The class name with which the service was registered.
+     * @return A <code>ServiceReference</code> object, or <code>0</code> if
+     *         no services are registered which implement the named class.
+     * @throws std::logic_error If this PluginContext is no longer valid.
+     * @see #getServiceReferences(const QString&, const QString&)
+     */
+    ServiceReference* getServiceReference(const QString& clazz);
 
-    QObject* getService(const ServiceReference& reference);
+    /**
+     * Returns the service object referenced by the specified
+     * <code>ServiceReference</code> object.
+     * <p>
+     * A plugin's use of a service is tracked by the plugin's use count of that
+     * service. Each time a service's service object is returned by
+     * {@link #getService(ServiceReference*)} the context plugin's use count for
+     * that service is incremented by one. Each time the service is released by
+     * {@link #ungetService(ServiceReference*)} the context plugin's use count
+     * for that service is decremented by one.
+     * <p>
+     * When a plugin's use count for a service drops to zero, the plugin should
+     * no longer use that service.
+     *
+     * <p>
+     * This method will always return <code>0</code> when the service
+     * associated with this <code>reference</code> has been unregistered.
+     *
+     * <p>
+     * The following steps are required to get the service object:
+     * <ol>
+     * <li>If the service has been unregistered, <code>0</code> is returned.
+     * <li>The context plugin's use count for this service is incremented by
+     * one.
+     * <li>If the context plugin's use count for the service is currently one
+     * and the service was registered with an object implementing the
+     * <code>ServiceFactory</code> interface, the
+     * {@link ServiceFactory::getService(Plugin*, ServiceRegistration*)} method is
+     * called to create a service object for the context plugin. This service
+     * object is cached by the Framework. While the context plugin's use count
+     * for the service is greater than zero, subsequent calls to get the
+     * services's service object for the context plugin will return the cached
+     * service object. <br>
+     * If the service object returned by the <code>ServiceFactory</code> object
+     * is not an instance of all the classes named when the service
+     * was registered or the <code>ServiceFactory</code> object throws an
+     * exception, <code>0</code> is returned and a Framework event of type
+     * {@link FrameworkEvent::ERROR} containing a {@link ServiceException}
+     * describing the error is fired.
+     * <li>The service object for the service is returned.
+     * </ol>
+     *
+     * @param reference A reference to the service.
+     * @return A service object for the service associated with
+     *         <code>reference</code> or <code>0</code> if the service is not
+     *         registered, the service object returned by a
+     *         <code>ServiceFactory</code> does not implement the classes under
+     *         which it was registered or the <code>ServiceFactory</code> threw
+     *         an exception.
+     * @throws std::logic_error If this PluginContext is no
+     *         longer valid.
+     * @throws std::invalid_argument If the specified
+     *         <code>ServiceReference</code> was not created by the same
+     *         framework instance as this <code>PluginContext</code>.
+     * @see #ungetService(ServiceReference*)
+     * @see ServiceFactory
+     */
+    QObject* getService(ServiceReference* reference);
 
     Plugin* installPlugin(const QUrl& location, QIODevice* in = 0);
 

+ 1 - 1
Libs/PluginFramework/ctkPluginFrameworkContext.cxx

@@ -33,7 +33,7 @@ namespace ctk {
 
   PluginFrameworkContext::PluginFrameworkContext(
       const PluginFrameworkFactory::Properties& initProps)
-        : plugins(0), /*services(this),*/ systemPlugin(this),
+        : plugins(0), services(this), systemPlugin(this),
         storage(this), props(initProps)
   {
 

+ 2 - 1
Libs/PluginFramework/ctkPluginFrameworkContext_p.h

@@ -30,6 +30,7 @@
 #include "ctkPluginStorage_p.h"
 #include "ctkPlugins_p.h"
 #include "ctkPluginFrameworkListeners_p.h"
+#include "ctkServices_p.h"
 
 namespace ctk {
 
@@ -52,7 +53,7 @@ namespace ctk {
       /**
        * All registered services in this framework.
        */
-      //Services services;
+      Services services;
 
       /**
        * System plugin

+ 34 - 0
Libs/PluginFramework/ctkPluginFramework_global.h

@@ -0,0 +1,34 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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.
+
+=============================================================================*/
+
+
+#ifndef CTKPLUGINFRAMEWORK_GLOBAL_H
+#define CTKPLUGINFRAMEWORK_GLOBAL_H
+
+#include <QHash>
+
+namespace ctk {
+
+  typedef QHash<QString, QVariant> ServiceProperties;
+
+}
+
+#endif // CTKPLUGINFRAMEWORK_GLOBAL_H

+ 85 - 0
Libs/PluginFramework/ctkServiceException.cxx

@@ -0,0 +1,85 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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 "ctkServiceException.h"
+
+#include <QDebug>
+
+namespace ctk {
+
+  ServiceException::ServiceException(const QString& msg, const Type& type, const std::exception& cause)
+    : std::runtime_error(msg.toStdString()),
+      type(type), cause(cause)
+  {
+
+  }
+
+  ServiceException::ServiceException(const QString& msg, const std::exception& cause)
+    : std::runtime_error(msg.toStdString()),
+      type(UNSPECIFIED), cause(cause)
+  {
+
+  }
+
+  ServiceException::ServiceException(const ServiceException& o)
+    : std::runtime_error(o.what()), type(o.type), cause(o.cause)
+  {
+
+  }
+
+  ServiceException& ServiceException::operator=(const ServiceException& o)
+  {
+    std::runtime_error::operator=(o);
+    type = o.type;
+    cause = o.cause;
+    return *this;
+  }
+
+  std::exception ServiceException::getCause() const
+  {
+    return cause;
+  }
+
+  void ServiceException::setCause(const std::exception& cause) throw(std::logic_error)
+  {
+    if (!cause.what()) throw std::logic_error("The cause for this ServiceException instance is already set");
+
+    this->cause = cause;
+  }
+
+  ServiceException::Type ServiceException::getType() const
+  {
+    return type;
+  }
+
+}
+
+QDebug operator<<(QDebug dbg, const ctk::ServiceException& exc)
+{
+  dbg << "ServiceException:" << exc.what();
+
+  const char* causeMsg = exc.getCause().what();
+  if (causeMsg) dbg << "  Caused by:" << causeMsg;
+
+  return dbg.maybeSpace();
+}
+
+

+ 90 - 0
Libs/PluginFramework/ctkServiceException.h

@@ -0,0 +1,90 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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.
+
+=============================================================================*/
+
+
+#ifndef CTKSERVICEEXCEPTION_H
+#define CTKSERVICEEXCEPTION_H
+
+#include <stdexcept>
+
+#include <QString>
+
+#include "CTKPluginFrameworkExport.h"
+
+namespace ctk {
+
+  class CTK_PLUGINFW_EXPORT ServiceException : public std::runtime_error
+  {
+  public:
+
+    enum Type {
+      /**
+       * No exception type is unspecified.
+       */
+      UNSPECIFIED			= 0,
+      /**
+       * The service has been unregistered.
+       */
+      UNREGISTERED		= 1,
+      /**
+       * The service factory produced an invalid service object.
+       */
+      FACTORY_ERROR		= 2,
+      /**
+       * The service factory threw an exception.
+       */
+      FACTORY_EXCEPTION	= 3,
+      /**
+       * The exception is a subclass of ServiceException. The subclass should be
+       * examined for the type of the exception.
+       */
+      SUBCLASSED			= 4,
+      /**
+       * An error occurred invoking a remote service.
+       */
+      REMOTE 				= 5
+    };
+
+    ServiceException(const QString& msg, const Type& type = UNSPECIFIED, const std::exception& cause = std::exception());
+    ServiceException(const QString& msg, const std::exception& cause);
+
+    ServiceException(const ServiceException& o);
+    ServiceException& operator=(const ServiceException& o);
+
+    ~ServiceException() throw() {}
+
+    std::exception getCause() const;
+    void setCause(const std::exception&) throw(std::logic_error);
+    Type getType() const;
+
+
+  private:
+
+    Type type;
+    std::exception cause;
+
+  };
+
+}
+
+CTK_PLUGINFW_EXPORT QDebug operator<<(QDebug dbg, const ctk::ServiceException& exc);
+
+#endif // CTKSERVICEEXCEPTION_H

+ 119 - 0
Libs/PluginFramework/ctkServiceFactory.h

@@ -0,0 +1,119 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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 <QObject>
+
+#include "ctkPlugin.h"
+
+#include "CTKPluginFrameworkExport.h"
+
+namespace ctk {
+
+  /**
+   * Allows services to provide customized service objects in the plugin
+   * environment.
+   *
+   * <p>
+   * When registering a service, a <code>ServiceFactory</code> object can be
+   * used instead of a service object, so that the plugin developer can gain
+   * control of the specific service object granted to a plugin that is using the
+   * service.
+   *
+   * <p>
+   * When this happens, the
+   * <code>PluginContext::getService(const ServiceReference&)</code> method calls the
+   * <code>ServiceFactory::getService</code> method to create a service object
+   * specifically for the requesting plugin. The service object returned by the
+   * <code>ServiceFactory</code> is cached by the Framework until the plugin
+   * releases its use of the service.
+   *
+   * <p>
+   * When the plugin's use count for the service equals zero (including the plugin
+   * stopping or the service being unregistered), the
+   * <code>ServiceFactory::ungetService</code> method is called.
+   *
+   * <p>
+   * <code>ServiceFactory</code> objects are only used by the Framework and are
+   * not made available to other plugins in the plugin environment. The Framework
+   * may concurrently call a <code>ServiceFactory</code>.
+   *
+   * @see PluginContext#getService
+   * @threadsafe
+   */
+
+  class CTK_PLUGINFW_EXPORT ServiceFactory : public QObject
+  {
+    Q_OBJECT
+
+  public:
+
+    /**
+     * Creates a new service object.
+     *
+     * <p>
+     * The Framework invokes this method the first time the specified
+     * <code>plugin</code> requests a service object using the
+     * <code>PluginContext::getService(const ServiceReference&)</code> method. The
+     * service factory can then return a specific service object for each
+     * plugin.
+     *
+     * <p>
+     * The Framework caches the value returned (unless it is 0),
+     * and will return the same service object on any future call to
+     * <code>PluginContext::getService</code> for the same plugins. This means the
+     * Framework must not allow this method to be concurrently called for the
+     * same plugin.
+     *
+     * <p>
+     * The Framework will check if the returned service object is an instance of
+     * all the classes named when the service was registered. If not, then
+     * <code>0</code> is returned to the plugin.
+     *
+     * @param plugin The plugin using the service.
+     * @param registration The <code>ServiceRegistration</code> object for the
+     *        service.
+     * @return A service object that <strong>must</strong> be an instance of all
+     *         the classes named when the service was registered.
+     * @see PluginContext#getService
+     */
+    virtual QObject* getService(Plugin* plugin, ServiceRegistration* registration) = 0;
+
+    /**
+     * Releases a service object.
+     *
+     * <p>
+     * The Framework invokes this method when a service has been released by a
+     * plugin. The service object may then be destroyed.
+     *
+     * @param plugin The Plugin releasing the service.
+     * @param registration The <code>ServiceRegistration</code> object for the
+     *        service.
+     * @param service The service object returned by a previous call to the
+     *        <code>ServiceFactory::getService</code> method.
+     * @see PluginContext#ungetService
+     */
+    virtual void ungetService(Plugin* plugin, ServiceRegistration* registration,
+        QObject* service) = 0;
+  };
+
+}
+

+ 64 - 5
Libs/PluginFramework/ctkServiceReference.cxx

@@ -20,39 +20,98 @@
 =============================================================================*/
 
 #include "ctkServiceReference.h"
+#include "ctkServiceReferencePrivate.h"
+#include "ctkServiceRegistrationPrivate.h"
+#include "ctkPluginPrivate_p.h"
+#include "ctkPluginConstants.h"
 
 #include <QStringList>
+#include <QMutexLocker>
 
 namespace ctk {
 
   ServiceReference::ServiceReference(ServiceRegistrationPrivate* reg)
+    : d_ptr(new ServiceReferencePrivate(reg))
   {
 
   }
 
+  ServiceReference::~ServiceReference()
+  {
+    delete d_ptr;
+  }
+
   QVariant ServiceReference::getProperty(const QString& key) const
   {
-    return QVariant();
+    Q_D(const ServiceReference);
+
+    QMutexLocker lock(&d->registration->propsLock);
+
+    return d->registration->properties.value(key);
   }
 
   QStringList ServiceReference::getPropertyKeys() const
   {
-    return QStringList();
+    Q_D(const ServiceReference);
+
+    QMutexLocker lock(&d->registration->propsLock);
+
+    return d->registration->properties.keys();
   }
 
   Plugin* ServiceReference::getPlugin() const
   {
-    return 0;
+    return d_func()->registration->plugin->q_func();
   }
 
   QList<Plugin*> ServiceReference::getUsingPlugins() const
   {
-    return QList<Plugin*>();
+    Q_D(const ServiceReference);
+
+    QMutexLocker lock(&d->registration->propsLock);
+
+    if (d->registration->reference != 0)
+    {
+      return d->registration->dependents.keys();
+    }
+    else
+    {
+      return QList<Plugin*>();
+    }
   }
 
   bool ServiceReference::operator<(const ServiceReference& reference) const
   {
-    return false;
+    bool sameFw = d_func()->registration->plugin->fwCtx == reference.d_func()->registration->plugin->fwCtx;
+    if (!sameFw)
+    {
+      throw std::invalid_argument("Can not compare service references "
+                                  "belonging to different framework "
+                                  "instances.");
+    }
+
+    int r1 = getProperty(PluginConstants::SERVICE_RANKING).toInt();
+    int r2 = reference.getProperty(PluginConstants::SERVICE_RANKING).toInt();
+
+    if (r1 != r2)
+    {
+      // use ranking if ranking differs
+      return r1 < r2 ? false : true;
+    }
+    else
+    {
+      qlonglong id1 = getProperty(PluginConstants::SERVICE_ID).toLongLong();
+      qlonglong id2 = reference.getProperty(PluginConstants::SERVICE_ID).toLongLong();
+
+      // otherwise compare using IDs,
+      // is less than if it has a higher ID.
+      return id2< id1;
+    }
+  }
+
+  bool ServiceReference::operator==(const ServiceReference& reference) const
+  {
+    return d_func()->registration == reference.d_func()->registration;
   }
 
 }

+ 129 - 2
Libs/PluginFramework/ctkServiceReference.h

@@ -26,30 +26,157 @@
 
 #include "ctkPlugin.h"
 
+#include "CTKPluginFrameworkExport.h"
+
 namespace ctk {
 
   class ServiceRegistrationPrivate;
-
-  class ServiceReference {
+  class ServiceReferencePrivate;
+
+  /**
+   * A reference to a service.
+   *
+   * <p>
+   * The Framework returns <code>ServiceReference</code> objects from the
+   * <code>PluginContext::getServiceReference</code> and
+   * <code>PluginContext::getServiceReferences</code> methods.
+   * <p>
+   * A <code>ServiceReference</code> object may be shared between plugins and
+   * can be used to examine the properties of the service and to get the service
+   * object.
+   * <p>
+   * Every service registered in the Framework has a unique
+   * <code>ServiceRegistration</code> object and may have multiple, distinct
+   * <code>ServiceReference</code> objects referring to it.
+   * <code>ServiceReference</code> objects associated with a
+   * <code>ServiceRegistration</code> are considered equal
+   * (more specifically, their <code>operator==()</code>
+   * method will return <code>true</code> when compared).
+   * <p>
+   * If the same service object is registered multiple times,
+   * <code>ServiceReference</code> objects associated with different
+   * <code>ServiceRegistration</code> objects are not equal.
+   *
+   * @see PluginContext::getServiceReference
+   * @see PluginContext::getServiceReferences
+   * @see PluginContext::getService
+   * @threadsafe
+   */
+  class CTK_PLUGINFW_EXPORT ServiceReference {
+
+    Q_DECLARE_PRIVATE(ServiceReference)
 
   public:
 
+    ~ServiceReference();
+
+    /**
+     * Returns the property value to which the specified property key is mapped
+     * in the properties <code>ServiceProperties</code> object of the service
+     * referenced by this <code>ServiceReference</code> object.
+     *
+     * <p>
+     * Property keys are case-insensitive.
+     *
+     * <p>
+     * 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.
+     * @return The property value to which the key is mapped; an invalid QVariant
+     *         if there is no property named after the key.
+     */
     QVariant getProperty(const QString& key) const;
 
+    /**
+     * Returns a list of the keys in the <code>ServiceProperties</code>
+     * object of the service referenced by this <code>ServiceReference</code>
+     * object.
+     *
+     * <p>
+     * This method will continue to return the keys after the service has been
+     * unregistered. This is so references to unregistered services can
+     * still be interrogated.
+     *
+     * <p>
+     * This method is not <i>case-preserving</i>; this means that every key in the
+     * returned array is in lower case, which may not be the case for the corresponding key in the
+     * properties <code>ServiceProperties</code> that was passed to the
+     * {@link PluginContext::registerService(const QStringList&, QObject*, const ServiceProperties&)} or
+     * {@link ServiceRegistration::setProperties} methods.
+     *
+     * @return A list of property keys.
+     */
     QStringList getPropertyKeys() const;
 
+    /**
+     * Returns the plugin that registered the service referenced by this
+     * <code>ServiceReference</code> object.
+     *
+     * <p>
+     * This method must return <code>0</code> when the service has been
+     * unregistered. This can be used to determine if the service has been
+     * unregistered.
+     *
+     * @return The plugin that registered the service referenced by this
+     *         <code>ServiceReference</code> object; <code>0</code> if that
+     *         service has already been unregistered.
+     * @see PluginContext::registerService(const QStringList&, QObject* , const ServiceProperties&)
+     */
     Plugin* getPlugin() const;
 
+    /**
+     * Returns the plugins that are using the service referenced by this
+     * <code>ServiceReference</code> object. Specifically, this method returns
+     * the plugins whose usage count for that service is greater than zero.
+     *
+     * @return A list of plugins whose usage count for the service referenced
+     *         by this <code>ServiceReference</code> object is greater than
+     *         zero.
+     */
     QList<Plugin*> getUsingPlugins() const;
 
+    /**
+     * Compares this <code>ServiceReference</code> with the specified
+     * <code>ServiceReference</code> for order.
+     *
+     * <p>
+     * If this <code>ServiceReference</code> and the specified
+     * <code>ServiceReference</code> have the same {@link PluginConstants::SERVICE_ID
+     * service id} they are equal. This <code>ServiceReference</code> is less
+     * than the specified <code>ServiceReference</code> if it has a lower
+     * {@link PluginConstants::SERVICE_RANKING service ranking} and greater if it has a
+     * higher service ranking. Otherwise, if this <code>ServiceReference</code>
+     * and the specified <code>ServiceReference</code> have the same
+     * {@link PluginConstants::SERVICE_RANKING service ranking}, this
+     * <code>ServiceReference</code> is less than the specified
+     * <code>ServiceReference</code> if it has a higher
+     * {@link PluginConstants::SERVICE_ID service id} and greater if it has a lower
+     * service id.
+     *
+     * @param reference The <code>ServiceReference</code> to be compared.
+     * @return Returns a false or true if this
+     *         <code>ServiceReference</code> is less than or greater
+     *         than the specified <code>ServiceReference</code>.
+     * @throws std::invalid_argument If the specified
+     *         <code>ServiceReference</code> was not created by the same
+     *         framework instance as this <code>ServiceReference</code>.
+     */
     bool operator<(const ServiceReference& reference) const;
 
+    bool operator==(const ServiceReference& reference) const;
+
+
   protected:
 
     friend class ServiceRegistrationPrivate;
+    friend class PluginContext;
 
     ServiceReference(ServiceRegistrationPrivate* reg);
 
+    ServiceReferencePrivate * const d_ptr;
+
   };
 
 }

+ 177 - 0
Libs/PluginFramework/ctkServiceReferencePrivate.cxx

@@ -0,0 +1,177 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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 "ctkServiceReferencePrivate.h"
+
+#include <QObject>
+#include <QMutexLocker>
+
+#include "ctkPluginConstants.h"
+#include "ctkServiceFactory.h"
+#include "ctkServiceException.h"
+#include "ctkPluginPrivate_p.h"
+#include "ctkServiceRegistrationPrivate.h"
+#include "ctkPluginFrameworkContext_p.h"
+
+namespace ctk {
+
+  ServiceReferencePrivate::ServiceReferencePrivate(ServiceRegistrationPrivate* reg)
+    : registration(reg)
+  {
+  }
+
+  QObject* ServiceReferencePrivate::getService(Plugin* plugin)
+  {
+    QObject* s = 0;
+    {
+      QMutexLocker lock(&registration->propsLock);
+      if (registration->available)
+      {
+        int count = registration->dependents.value(plugin);
+        if (count == 0)
+        {
+          QStringList classes =
+              registration->properties.value(PluginConstants::OBJECTCLASS).toStringList();
+          registration->dependents[plugin] = 1;
+          if (ServiceFactory* serviceFactory = qobject_cast<ServiceFactory*>(registration->service))
+          {
+            try
+            {
+              s = serviceFactory->getService(plugin, registration->q_func());
+            }
+            catch (const std::exception& pe)
+            {
+              ServiceException se("ServiceFactory throw an exception",
+                                  ServiceException::FACTORY_EXCEPTION, pe);
+              plugin->d_func()->fwCtx->listeners.frameworkError
+                (registration->plugin->q_func(), se);
+              return 0;
+            }
+            if (s == 0) {
+              ServiceException se("ServiceFactory produced null",
+                                  ServiceException::FACTORY_ERROR);
+              plugin->d_func()->fwCtx->listeners.frameworkError
+                (registration->plugin->q_func(), se);
+              return 0;
+            }
+            for (QStringListIterator i(classes); i.hasNext(); )
+            {
+              QString cls = i.next();
+              if (!registration->plugin->fwCtx->services.checkServiceClass(s, cls))
+              {
+                ServiceException se(QString("ServiceFactory produced an object ") +
+                                     "that did not implement: " + cls,
+                                     ServiceException::FACTORY_ERROR);
+                plugin->d_func()->fwCtx->listeners.frameworkError
+                  (registration->plugin->q_func(), se);
+                return 0;
+              }
+            }
+            registration->serviceInstances.insert(plugin, s);
+          }
+          else
+          {
+            s = registration->service;
+          }
+        }
+        else
+        {
+          registration->dependents.insert(plugin, count + 1);
+          if (qobject_cast<ServiceFactory*>(registration->service))
+          {
+            s = registration->serviceInstances.value(plugin);
+          }
+          else
+          {
+            s = registration->service;
+          }
+        }
+      }
+    }
+    return s;
+  }
+
+  /**
+     * Unget the service object.
+     *
+     * @param bundle Bundle 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 ServiceReferencePrivate::ungetService(Plugin* plugin, bool checkRefCounter)
+  {
+    QMutexLocker lock(&registration->propsLock);
+    bool hadReferences = false;
+    if (registration->reference != 0)
+    {
+      bool removeService = false;
+
+      int count= registration->dependents.value(plugin);
+      if (count > 0)
+      {
+        hadReferences = true;
+      }
+
+      if(checkRefCounter)
+      {
+        if (count > 1)
+        {
+            registration->dependents[plugin] = count - 1;
+        }
+        else if(count == 1)
+        {
+          removeService = true;
+        }
+      }
+      else
+      {
+        removeService = true;
+      }
+
+      if (removeService)
+      {
+        QObject* sfi = registration->serviceInstances[plugin];
+        registration->serviceInstances.remove(plugin);
+        if (sfi != 0)
+        {
+          try
+          {
+            qobject_cast<ServiceFactory*>(registration->service)->ungetService(plugin,
+                registration->q_func(), sfi);
+          }
+          catch (const std::exception& e)
+          {
+            plugin->d_func()->fwCtx->listeners.frameworkError(registration->plugin->q_func(), e);
+          }
+        }
+        registration->dependents.remove(plugin);
+      }
+    }
+
+    return hadReferences;
+  }
+
+
+}
+

+ 66 - 0
Libs/PluginFramework/ctkServiceReferencePrivate.h

@@ -0,0 +1,66 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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.
+
+=============================================================================*/
+
+
+#ifndef CTKSERVICEREFERENCEPRIVATE_H
+#define CTKSERVICEREFERENCEPRIVATE_H
+
+class QObject;
+
+namespace ctk {
+
+  class ServiceRegistrationPrivate;
+  class Plugin;
+
+  class ServiceReferencePrivate
+  {
+  public:
+
+    ServiceReferencePrivate(ServiceRegistrationPrivate* reg);
+
+    /**
+      * Get the service object.
+      *
+      * @param plugin requester of service.
+      * @return Service requested or null in case of failure.
+      */
+    QObject* getService(Plugin* plugin);
+
+    /**
+     * Unget the service object.
+     *
+     * @param plugin Plugin 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(Plugin* plugin, bool checkRefCounter);
+
+    /**
+     * Link to registration object for this reference.
+     */
+    ServiceRegistrationPrivate* registration;
+  };
+
+}
+#endif // CTKSERVICEREFERENCEPRIVATE_H

+ 77 - 121
Libs/PluginFramework/ctkServiceRegistration.cxx

@@ -20,6 +20,10 @@
 =============================================================================*/
 
 #include "ctkServiceRegistration.h"
+#include "ctkServiceRegistrationPrivate.h"
+#include "ctkPluginFrameworkContext_p.h"
+#include "ctkPluginPrivate_p.h"
+#include "ctkServiceFactory.h"
 
 #include <QMutex>
 
@@ -27,97 +31,23 @@
 
 namespace ctk {
 
-  class ServiceRegistrationPrivate
+  ServiceRegistration::ServiceRegistration(PluginPrivate* plugin, QObject* service,
+                      const ServiceProperties& props)
+    : d_ptr(new ServiceRegistrationPrivate(this, plugin, service, props))
   {
 
-  private:
-
-    /**
-     * Lock object for synchronous event delivery.
-     */
-    QMutex eventLock;
-
-
-  public:
-
-    /**
-     * Plugin registering this service.
-     */
-    Plugin* plugin;
-
-    /**
-     * Service or ServiceFactory object.
-     */
-    QObject* service;
-
-    /**
-     * Reference object to this service registration.
-     */
-    ServiceReference reference;
-
-    /**
-     * Service properties.
-     */
-    PluginContext::ServiceProperties properties;
-
-    /**
-     * Plugins dependent on this service. Integer is used as
-     * reference counter, counting number of unbalanced getService().
-     */
-    QHash<Plugin*,int> dependents;
-
-    /**
-     * Object instances that factory has produced.
-     */
-    QHash<Plugin*, QObject*> serviceInstances;
-
-    /**
-     * Is service available. I.e., if <code>true</code> then holders
-     * of a ServiceReference for the service are allowed to get it.
-     */
-    volatile bool available;
-
-    /**
-     * Avoid recursive unregistrations. I.e., if <code>true</code> then
-     * unregistration of this service has started but is not yet
-     * finished.
-     */
-    volatile bool unregistering;
-
-    QMutex propsLock;
-
-    ServiceRegistrationPrivate(Plugin* plugin, QObject* service,
-                               const PluginContext::ServiceProperties& props)
-                                 : plugin(plugin), service(service), reference(this),
-                                 properties(props), available(true), unregistering(false)
-    {
-
-    }
-
-    /**
-     * Check if a plugin uses this service
-     *
-     * @param p Plugin to check
-     * @return true if plugin uses this service
-     */
-    bool isUsedByPlugin(Plugin* p)
-    {
-      QHash<Plugin*, int> deps = dependents;
-      return deps.contains(p);
-    }
-
-  };
+  }
 
-  ServiceReference ServiceRegistration::getReference() const
+  ServiceReference* ServiceRegistration::getReference()
   {
-    Q_D(const ServiceRegistration);
+    Q_D(ServiceRegistration);
 
     if (!d->available) throw std::logic_error("Service is unregistered");
 
     return d->reference;
   }
 
-  void ServiceRegistration::setProperties(const PluginContext::ServiceProperties& properties)
+  void ServiceRegistration::setProperties(const ServiceProperties& properties)
   {
 //    QMutexLocker lock(eventLock);
 //          Set before;
@@ -153,50 +83,76 @@ namespace ctk {
 
   }
 
-  void ServiceRegistration::unregister() const
+  void ServiceRegistration::unregister()
   {
-//    Q_D(ServiceRegistration);
-//
-//    if (d->unregistering) return; // Silently ignore redundant unregistration.
-//
-//        {
-//          QMutexLocker lock(eventLock);
-//          if (d->unregistering) return;
-//          d->unregistering = true;
-//
-//          if (d->available)
-//          {
-//            if (d->plugin)
-//            {
-//              d->plugin->fwCtx.services.removeServiceRegistration(this);
-//            }
-//          }
-//          else
-//          {
-//            throw std::logic_error("Service is unregistered");
-//          }
-//        }
-//
-//        if (d->plugin)
-//    {
-//          bundle.fwCtx.listeners
+    Q_D(ServiceRegistration);
+
+    if (d->unregistering) return; // Silently ignore redundant unregistration.
+    {
+      QMutexLocker lock(&d->eventLock);
+      if (d->unregistering) return;
+      d->unregistering = true;
+
+      if (d->available)
+      {
+        if (d->plugin)
+        {
+          d->plugin->fwCtx->services.removeServiceRegistration(this);
+        }
+      }
+      else
+      {
+        throw std::logic_error("Service is unregistered");
+      }
+    }
+
+    if (d->plugin)
+    {
+      //TODO
+//      bundle.fwCtx.listeners
 //            .serviceChanged(bundle.fwCtx.listeners.getMatchingServiceListeners(reference),
 //                            new ServiceEvent(ServiceEvent.UNREGISTERING, reference),
 //                            null);
-//        }
-//        synchronized (eventLock) {
-//          synchronized (properties) {
-//            available = false;
-//            if (null!=bundle)
-//              bundle.fwCtx.perm.callUnregister0(this);
-//            bundle = null;
-//            dependents = null;
-//            reference = null;
-//            service = null;
-//            serviceInstances = null;
-//            unregistering = false;
-//          }
-//        }
+    }
+
+    {
+      QMutexLocker lock(&d->eventLock);
+      {
+        QMutexLocker lock2(&d->propsLock);
+        d->available = false;
+        if (d->plugin)
+        {
+          for (QHashIterator<Plugin*, QObject*> i(d->serviceInstances); i.hasNext();)
+          {
+            QObject* obj = i.next().value();
+            try
+            {
+              // NYI, don't call inside lock
+              qobject_cast<ServiceFactory*>(d->service)->ungetService(i.key(),
+                                                         this,
+                                                         obj);
+            }
+            catch (const std::exception& ue)
+            {
+              PluginFrameworkEvent pfwEvent(PluginFrameworkEvent::ERROR, d->plugin->q_func(), ue);
+              d->plugin->fwCtx->listeners
+                  .emitFrameworkEvent(pfwEvent);
+            }
+          }
+        }
+        d->plugin = 0;
+        d->dependents.clear();
+        d->service = 0;
+        d->serviceInstances.clear();;
+        d->unregistering = false;
+      }
+    }
+  }
+
+  bool ServiceRegistration::operator<(const ServiceRegistration& o) const
+  {
+    Q_D(const ServiceRegistration);
+    return d->reference->operator <(*(o.d_func()->reference));
   }
 
 }

+ 12 - 6
Libs/PluginFramework/ctkServiceRegistration.h

@@ -26,6 +26,8 @@
 
 #include "ctkServiceReference.h"
 
+#include "CTKPluginFrameworkExport.h"
+
 namespace ctk {
 
   class ServiceRegistrationPrivate;
@@ -45,7 +47,7 @@ namespace ctk {
    * @see PluginContext#registerService()
    * @threadsafe
    */
-  class ServiceRegistration {
+  class CTK_PLUGINFW_EXPORT ServiceRegistration {
 
     Q_DECLARE_PRIVATE(ServiceRegistration)
 
@@ -63,7 +65,7 @@ namespace ctk {
      *         unregistered.
      * @return <code>ServiceReference</code> object.
      */
-    ServiceReference getReference() const;
+    ServiceReference* getReference();
 
     /**
      * Updates the properties associated with a service.
@@ -90,7 +92,7 @@ namespace ctk {
      * @throws std::invalid_argument If <code>properties</code> contains
      *         case variants of the same key name.
      */
-    void setProperties(const PluginContext::ServiceProperties& properties);
+    void setProperties(const ServiceProperties& properties);
 
     /**
      * Unregisters a service. Remove a <code>ServiceRegistration</code> object
@@ -123,12 +125,16 @@ namespace ctk {
      * @see BundleContext#ungetService
      * @see ServiceFactory#ungetService
      */
-    void unregister() const;
+    void unregister();
+
+    bool operator<(const ServiceRegistration& o) const;
 
   private:
 
-    ServiceRegistration(Plugin* plugin, QObject* service,
-                        const PluginContext::ServiceProperties& props);
+    friend class Services;
+
+    ServiceRegistration(PluginPrivate* plugin, QObject* service,
+                        const ServiceProperties& props);
 
     ServiceRegistrationPrivate * const d_ptr;
 

+ 47 - 0
Libs/PluginFramework/ctkServiceRegistrationPrivate.cxx

@@ -0,0 +1,47 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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 "ctkServiceRegistrationPrivate.h"
+
+namespace ctk {
+
+  ServiceRegistrationPrivate::ServiceRegistrationPrivate(ServiceRegistration* sr,
+                                                         PluginPrivate* plugin, QObject* service,
+                                                         const ServiceProperties& props)
+                               : q_ptr(sr), plugin(plugin), service(service), reference(new ServiceReference(this)),
+                               properties(props), available(true), unregistering(false)
+  {
+
+  }
+
+  ServiceRegistrationPrivate::~ServiceRegistrationPrivate()
+  {
+    delete reference;
+  }
+
+  bool ServiceRegistrationPrivate::isUsedByPlugin(Plugin* p)
+  {
+    QHash<Plugin*, int> deps = dependents;
+    return deps.contains(p);
+  }
+
+}
+

+ 116 - 0
Libs/PluginFramework/ctkServiceRegistrationPrivate.h

@@ -0,0 +1,116 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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.
+
+=============================================================================*/
+
+
+#ifndef CTKSERVICEREGISTRATIONPRIVATE_H
+#define CTKSERVICEREGISTRATIONPRIVATE_H
+
+#include <QHash>
+#include <QMutex>
+
+#include "ctkServiceReference.h"
+
+namespace ctk {
+
+  class PluginPrivate;
+  class ServiceRegistration;
+
+  class ServiceRegistrationPrivate
+  {
+
+  protected:
+
+    ServiceRegistration* const q_ptr;
+
+  public:
+
+    Q_DECLARE_PUBLIC(ServiceRegistration);
+
+
+    /**
+     * Plugin registering this service.
+     */
+    PluginPrivate* plugin;
+
+    /**
+     * Service or ServiceFactory object.
+     */
+    QObject* service;
+
+    /**
+     * Reference object to this service registration.
+     */
+    ServiceReference* reference;
+
+    /**
+     * Service properties.
+     */
+    ServiceProperties properties;
+
+    /**
+     * Plugins dependent on this service. Integer is used as
+     * reference counter, counting number of unbalanced getService().
+     */
+    QHash<Plugin*,int> dependents;
+
+    /**
+     * Object instances that factory has produced.
+     */
+    QHash<Plugin*, QObject*> serviceInstances;
+
+    /**
+     * Is service available. I.e., if <code>true</code> then holders
+     * of a ServiceReference for the service are allowed to get it.
+     */
+    volatile bool available;
+
+    /**
+     * Avoid recursive unregistrations. I.e., if <code>true</code> then
+     * unregistration of this service has started but is not yet
+     * finished.
+     */
+    volatile bool unregistering;
+
+    /**
+     * Lock object for synchronous event delivery.
+     */
+    QMutex eventLock;
+
+    QMutex propsLock;
+
+    ServiceRegistrationPrivate(ServiceRegistration* sr, PluginPrivate* plugin, QObject* service,
+                               const ServiceProperties& props);
+
+    ~ServiceRegistrationPrivate();
+
+    /**
+     * Check if a plugin uses this service
+     *
+     * @param p Plugin to check
+     * @return true if plugin uses this service
+     */
+    bool isUsedByPlugin(Plugin* p);
+
+  };
+
+}
+
+#endif // CTKSERVICEREGISTRATIONPRIVATE_H

+ 369 - 0
Libs/PluginFramework/ctkServices.cxx

@@ -0,0 +1,369 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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 "ctkServices_p.h"
+
+#include <QStringListIterator>
+#include <QMutexLocker>
+
+#include <algorithm>
+
+#include "ctkServiceFactory.h"
+#include "ctkPluginConstants.h"
+#include "ctkServiceRegistrationPrivate.h"
+
+namespace ctk {
+
+  //TODO: this is just a mock class. Wait for the real thing
+  class LDAPExpr
+  {
+
+  public:
+
+    LDAPExpr(const QString& filter)
+    {
+
+    }
+
+    /**
+     * Get object class set matched by this LDAP expression. This will not work
+     * with wildcards and NOT expressions. If a set can not be determined return null.
+     *
+     * @return Set of classes matched, otherwise <code>null</code>.
+     */
+    QSet<QString> getMatchedObjectClasses() const
+    {
+      QSet<QString> objClasses;
+      return objClasses;
+    }
+
+
+    /**
+     * Evaluate this LDAP filter.
+     */
+    bool evaluate(const ServiceProperties& p, bool matchCase)
+    {
+      return true;
+    }
+
+  };
+
+
+  struct ServiceRegistrationComparator
+  {
+    bool operator()(const ServiceRegistration* a, const ServiceRegistration* b) const
+    {
+      return *a < *b;
+    }
+  };
+
+  ServiceProperties Services::createServiceProperties(const ServiceProperties& in,
+                                                      const QStringList& classes,
+                                                      long sid)
+  {
+    static qlonglong nextServiceID = 1;
+    ServiceProperties props;
+
+    if (!in.isEmpty())
+    {
+      for (ServiceProperties::const_iterator it = in.begin(); it != in.end(); ++it)
+      {
+        const QString key = it.key();
+        const QString lcKey = it.key().toLower();
+        for (QListIterator<QString> i(props.keys()); i.hasNext(); )
+        {
+          if (lcKey == i.next())
+          {
+            throw std::invalid_argument(std::string("Several entries for property: ") + key.toStdString());
+          }
+        }
+
+        props.insert(lcKey, in.value(key));
+      }
+    }
+
+    if (!classes.isEmpty())
+    {
+      props.insert(PluginConstants::OBJECTCLASS, classes);
+    }
+
+    props.insert(PluginConstants::SERVICE_ID, sid != -1 ? sid : nextServiceID++);
+
+    return props;
+  }
+
+
+Services::Services(PluginFrameworkContext* fwCtx)
+  : framework(fwCtx)
+{
+
+}
+
+Services::~Services()
+{
+  clear();
+}
+
+void Services::clear()
+{
+  QList<ServiceRegistration*> serviceRegs = services.keys();
+  qDeleteAll(serviceRegs);
+  services.clear();
+  classServices.clear();
+  framework = 0;
+}
+
+ServiceRegistration* Services::registerService(PluginPrivate* plugin,
+                             const QStringList& classes,
+                             QObject* 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 (QStringListIterator i(classes); i.hasNext();)
+  {
+    QString cls = i.next();
+    if (cls.isEmpty())
+    {
+      throw std::invalid_argument("Can't register as null class");
+    }
+
+    if (!(qobject_cast<ServiceFactory*>(service)))
+    {
+      if (!checkServiceClass(service, cls))
+      {
+        throw std::invalid_argument
+            (std::string("Service object is not an instance of ") + cls.toStdString());
+      }
+    }
+  }
+
+  ServiceRegistration* res = new ServiceRegistration(plugin, service,
+                                createServiceProperties(properties, classes));
+  {
+    QMutexLocker lock(&mutex);
+    services.insert(res, classes);
+    for (QStringListIterator i(classes); i.hasNext(); )
+    {
+      QString currClass = i.next();
+      QList<ServiceRegistration*>& s = classServices[currClass];
+      QList<ServiceRegistration*>::iterator ip =
+          std::lower_bound(s.begin(), s.end(), res, ServiceRegistrationComparator());
+      s.insert(ip, res);
+    }
+  }
+
+  ServiceReference* r = res->getReference();
+  // TODO
+  //Listeners l = bundle.fwCtx.listeners;
+  //l.serviceChanged(l.getMatchingServiceListeners(r),
+  //                 new ServiceEvent(ServiceEvent.REGISTERED, r),
+  //                 null);
+  return res;
+}
+
+void Services::updateServiceRegistrationOrder(ServiceRegistration* sr,
+                                              const QStringList& classes)
+{
+  QMutexLocker lock(&mutex);
+  for (QStringListIterator i(classes); i.hasNext(); )
+  {
+    QList<ServiceRegistration*>& s = classServices[i.next()];
+    s.removeAll(sr);
+    s.insert(std::lower_bound(s.begin(), s.end(), sr, ServiceRegistrationComparator()), sr);
+  }
+}
+
+bool Services::checkServiceClass(QObject* service, const QString& cls) const
+{
+  return service->inherits(cls.toAscii());
+}
+
+
+QList<ServiceRegistration*> Services::get(const QString& clazz) const
+{
+  QMutexLocker lock(&mutex);
+  return classServices.value(clazz);
+}
+
+
+ServiceReference* Services::get(PluginPrivate* plugin, const QString& clazz) const
+{
+  QMutexLocker lock(&mutex);
+  try {
+    QList<ServiceReference*> srs = get(clazz, QString());
+    qDebug() << "get service ref" << clazz << "for plugin"
+             << plugin->location << " = " << srs;
+
+    if (!srs.isEmpty()) {
+      return srs.front();
+    }
+  }
+  catch (const std::invalid_argument& )
+  { }
+
+  return 0;
+}
+
+
+QList<ServiceReference*> Services::get(const QString& clazz, const QString& filter) const
+{
+  QMutexLocker lock(&mutex);
+
+  QListIterator<ServiceRegistration*>* s = 0;
+  LDAPExpr ldap("");
+  if (clazz.isEmpty())
+  {
+    if (!filter.isEmpty())
+    {
+      ldap = LDAPExpr(filter);
+      QSet<QString> matched = ldap.getMatchedObjectClasses();
+      if (!matched.isEmpty())
+      {
+        //TODO
+//        ArrayList v = null;
+//        boolean vReadOnly = true;;
+//        for (Iterator i = matched.iterator(); i.hasNext(); ) {
+//          ArrayList cl = (ArrayList) classServices.get(i.next());
+//          if (cl != null) {
+//            if (v == null) {
+//              v = cl;
+//            } else {
+//              if (vReadOnly) {
+//                v = new ArrayList(v);
+//                vReadOnly = false;
+//              }
+//              v.addAll(cl);
+//            }
+//          }
+//        }
+//        if (v != null) {
+//          s = v.iterator();
+//        } else {
+//          return null;
+//        }
+      }
+      else
+      {
+        s = new QListIterator<ServiceRegistration*>(services.keys());
+      }
+    }
+    else
+    {
+      s = new QListIterator<ServiceRegistration*>(services.keys());
+    }
+  }
+  else
+  {
+    QList<ServiceRegistration*> v = classServices.value(clazz);
+    if (!v.isEmpty())
+    {
+      s = new QListIterator<ServiceRegistration*>(v);
+    }
+    else
+    {
+      return QList<ServiceReference*>();
+    }
+    if (!filter.isEmpty())
+    {
+      ldap = LDAPExpr(filter);
+    }
+  }
+
+  QList<ServiceReference*> res;
+  while (s->hasNext())
+  {
+    ServiceRegistration* sr = s->next();
+    ServiceReference* sri = sr->getReference();
+
+    if (filter.isEmpty() || ldap.evaluate(sr->d_func()->properties, false))
+    {
+      res.push_back(sri);
+    }
+  }
+
+  delete s;
+
+  return res;
+}
+
+
+void Services::removeServiceRegistration(ServiceRegistration* sr)
+{
+  QMutexLocker lock(&mutex);
+
+  QStringList classes = sr->d_func()->properties.value(PluginConstants::OBJECTCLASS).toStringList();
+  services.remove(sr);
+  for (QStringListIterator i(classes); i.hasNext(); )
+  {
+    QString currClass = i.next();
+    QList<ServiceRegistration*>& s = classServices[currClass];
+    if (s.size() > 1)
+    {
+      s.removeAll(sr);
+    }
+    else
+    {
+      classServices.remove(currClass);
+    }
+  }
+}
+
+
+QList<ServiceRegistration*> Services::getRegisteredByPlugin(PluginPrivate* p) const
+{
+  QMutexLocker lock(&mutex);
+
+  QList<ServiceRegistration*> res;
+  for (QHashIterator<ServiceRegistration*, QStringList> i(services); i.hasNext(); )
+  {
+    ServiceRegistration* sr = i.next().key();
+    if (sr->d_func()->plugin = p)
+    {
+      res.push_back(sr);
+    }
+  }
+  return res;
+}
+
+
+QList<ServiceRegistration*> Services::getUsedByPlugin(Plugin* p) const
+{
+  QMutexLocker lock(&mutex);
+
+  QList<ServiceRegistration*> res;
+  for (QHashIterator<ServiceRegistration*, QStringList> i(services); i.hasNext(); )
+  {
+    ServiceRegistration* sr = i.next().key();
+    if (sr->d_func()->isUsedByPlugin(p))
+    {
+      res.push_back(sr);
+    }
+  }
+  return res;
+}
+
+}
+

+ 186 - 0
Libs/PluginFramework/ctkServices_p.h

@@ -0,0 +1,186 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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.
+
+=============================================================================*/
+
+
+#ifndef CTKSERVICES_P_H
+#define CTKSERVICES_P_H
+
+#include <QHash>
+#include <QObject>
+#include <QMutex>
+#include <QStringList>
+
+#include "ctkServiceRegistration.h"
+#include "ctkPluginPrivate_p.h"
+
+namespace ctk {
+
+/**
+ * Here we handle all the services that are registered in the framework.
+ *
+ */
+class Services {
+
+public:
+
+  mutable QMutex mutex;
+
+  /**
+   * Creates a new ServiceProperties object containing <code>in</code>
+   * 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
+   *        PluginConstants::OBJECTCLASS.
+   * @param sid A service id which will be used instead of a default one.
+   */
+  static ServiceProperties createServiceProperties(const ServiceProperties& in,
+                                 const QStringList& classes = QStringList(),
+                                 long sid = -1);
+
+  /**
+   * All registered services in the current framework.
+   * Mapping of registered service to class names under which
+   * the service is registerd.
+   */
+  QHash<ServiceRegistration*, QStringList> services;
+
+  /**
+   * Mapping of classname to registered service.
+   * The List of registered services are ordered with the highest
+   * ranked service first.
+   */
+  QHash<QString, QList<ServiceRegistration*> > classServices;
+
+
+  PluginFrameworkContext* framework;
+
+  Services(PluginFrameworkContext* fwCtx);
+
+  ~Services();
+
+  void clear();
+
+  /**
+   * Register a service in the framework wide register.
+   *
+   * @param plugin The plugin 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:
+   * <ul>
+   * <li>The service object is 0.</li>
+   * <li>The service parameter is not a ServiceFactory or an
+   * instance of all the named classes in the classes parameter.</li>
+   * </ul>
+   */
+  ServiceRegistration* registerService(PluginPrivate* plugin,
+                               const QStringList& classes,
+                               QObject* 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(ServiceRegistration* sr,
+                                      const QStringList& 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(QObject* service, const QString& 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.
+   */
+  QList<ServiceRegistration*> get(const QString& clazz) const;
+
+
+  /**
+   * Get a service implementing a certain class.
+   *
+   * @param plugin The plugin requesting reference
+   * @param clazz The class name of the requested service.
+   * @return A {@link ServiceReference} object.
+   */
+  ServiceReference* get(PluginPrivate* plugin, const QString& 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 plugin The plugin requesting reference.
+   * @return A list of {@link ServiceReference} object.
+   */
+  QList<ServiceReference*> get(const QString& clazz, const QString& filter) const;
+
+
+  /**
+   * Remove a registered service.
+   *
+   * @param sr The ServiceRegistration object that is registered.
+   */
+  void removeServiceRegistration(ServiceRegistration* sr) ;
+
+
+  /**
+   * Get all services that a plugin has registered.
+   *
+   * @param p The plugin
+   * @return A set of {@link ServiceRegistration} objects
+   */
+  QList<ServiceRegistration*> getRegisteredByPlugin(PluginPrivate* p) const;
+
+
+  /**
+   * Get all services that a plugin uses.
+   *
+   * @param p The plugin
+   * @return A set of {@link ServiceRegistration} objects
+   */
+  QList<ServiceRegistration*> getUsedByPlugin(Plugin* p) const;
+
+};
+
+}
+
+
+#endif // CTKSERVICES_P_H

+ 7 - 1
Plugins/org.commontk.cli/CMakeLists.txt

@@ -8,10 +8,15 @@ SET(PLUGIN_SRCS
 
 SET(PLUGIN_MOC_SRCS
   ctkCLIPlugin.h
+  ctkCLIRegistry.h
 )
 
 SET(PLUGIN_resources
-  org_commontk_cli.qrc
+
+)
+
+SET(PLUGIN_cached_resourcefiles
+  servicedescriptor.xml
 )
 
 SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Plugins)
@@ -24,5 +29,6 @@ ctkMacroBuildPlugin(
   SRCS ${PLUGIN_SRCS}
   MOC_SRCS ${PLUGIN_MOC_SRCS}
   RESOURCES ${PLUGIN_resources}
+  CACHED_RESOURCEFILES ${PLUGIN_cached_resourcefiles}
   TARGET_LIBRARIES ${PLUGIN_target_libraries}
 )

+ 5 - 0
Plugins/org.commontk.cli/ctkCLIPlugin.cxx

@@ -7,8 +7,11 @@
 
 #include "ctkCLIPlugin.h"
 
+#include "ctkCLIRegistry.h"
+
 #include <QtPlugin>
 #include <QServiceInterfaceDescriptor>
+#include <QStringList>
 
 #include <iostream>
 
@@ -16,6 +19,8 @@ namespace ctk {
 
   void CLIPlugin::start(PluginContext* context)
   {
+    CLIRegistry* registry = new CLIRegistry();
+    context->registerService(QStringList("ctk::CLIRegistry"), registry);
     std::cout << "Plugin A started\n";
   }
 

+ 41 - 0
Plugins/org.commontk.cli/ctkCLIRegistry.h

@@ -0,0 +1,41 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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.
+
+=============================================================================*/
+
+
+#ifndef CTKCLIREGISTRY_H
+#define CTKCLIREGISTRY_H
+
+#include <QObject>
+
+namespace ctk {
+
+  class CLIRegistry : public QObject
+  {
+    Q_OBJECT
+
+  public:
+
+
+  };
+
+}
+
+#endif // CTKCLIREGISTRY_H

+ 1 - 0
Plugins/org.commontk.cli/manifest_headers.cmake

@@ -0,0 +1 @@
+SET(Plugin-ActivationPolicy eager)