瀏覽代碼

ENH: PluginFramework: added docu and more mork on the service framework

Sascha Zelzer 15 年之前
父節點
當前提交
52e5e775e0

+ 3 - 0
Libs/PluginFramework/CMakeLists.txt

@@ -48,6 +48,7 @@ SET(KIT_SRCS
   ctkPluginContext.cxx
   ctkPluginDatabase.cxx
   ctkPluginDatabaseException.cxx
+  ctkPluginEvent.cxx
   ctkPluginException.cxx
   ctkPluginFramework.cxx
   ctkPluginFrameworkContext.cxx
@@ -71,6 +72,8 @@ SET(KIT_SRCS
 
 # Headers that should run through moc
 SET(KIT_MOC_SRCS
+  ctkPluginContext.h
+  ctkPluginEvent.h
 )
 
 # UI files

+ 8 - 1
Libs/PluginFramework/ctkPluginContext.cxx

@@ -44,13 +44,18 @@ namespace ctk {
     /**
      * Check that the plugin is still valid.
      */
-    void isPluginContextValid()
+    void isPluginContextValid() const
     {
       if (!plugin) {
         throw std::logic_error("This plugin context is no longer valid");
       }
     }
 
+    void invalidate()
+    {
+      plugin = 0;
+    }
+
   };
 
 
@@ -67,12 +72,14 @@ namespace ctk {
   Plugin* PluginContext::getPlugin(int id) const
   {
     Q_D(const PluginContext);
+    d->isPluginContextValid();
     return d->plugin->fwCtx->plugins->getPlugin(id);
   }
 
   QList<Plugin*> PluginContext::getPlugins() const
   {
     Q_D(const PluginContext);
+    d->isPluginContextValid();
     return d->plugin->fwCtx->plugins->getPlugins();
   }
 

+ 92 - 2
Libs/PluginFramework/ctkPluginContext.h

@@ -27,20 +27,72 @@
 #include <QVariant>
 #include <QUrl>
 
+#include "ctkPluginEvent.h"
+
 #include "CTKPluginFrameworkExport.h"
 
 
 namespace ctk {
 
+  // CTK class forward declarations
   class Plugin;
   class PluginPrivate;
   class ServiceRegistration;
   class ServiceReference;
   class PluginContextPrivate;
 
-  class CTK_PLUGINFW_EXPORT PluginContext
+  /**
+   * A plugin's execution context within the Framework. The context is used to
+   * grant access to other methods so that this plugin can interact with the
+   * Framework.
+   *
+   * <p>
+   * <code>PluginContext</code> methods allow a plugin to:
+   * <ul>
+   * <li>Subscribe to events published by the Framework.
+   * <li>Register service objects with the Framework service registry.
+   * <li>Retrieve <code>ServiceReferences</code> from the Framework service
+   * registry.
+   * <li>Get and release service objects for a referenced service.
+   * <li>Install new plugins in the Framework.
+   * <li>Get the list of plugins installed in the Framework.
+   * <li>Get the {@link Plugin} object for a plugin.
+   * <li>Create <code>QFile</code> objects for files in a persistent storage
+   * area provided for the plugin by the Framework.
+   * </ul>
+   *
+   * <p>
+   * A <code>PluginContext</code> object will be created and provided to the
+   * plugin associated with this context when it is started using the
+   * {@link PluginActivator#start} method. The same <code>PluginContext</code>
+   * object will be passed to the plugin associated with this context when it is
+   * stopped using the {@link PluginActivator#stop} method. A
+   * <code>PluginContext</code> object is generally for the private use of its
+   * associated plugin and is not meant to be shared with other plugins in the
+   * plugin environment.
+   *
+   * <p>
+   * The <code>Plugin</code> object associated with a <code>PluginContext</code>
+   * object is called the <em>context plugin</em>.
+   *
+   * <p>
+   * The <code>PluginContext</code> object is only valid during the execution of
+   * its context plugin; that is, during the period from when the context plugin
+   * is in the <code>STARTING</code>, <code>STOPPING</code>, and
+   * <code>ACTIVE</code> plugin states. If the <code>PluginContext</code>
+   * object is used subsequently, a <code>std::logic_error</code> must be
+   * thrown. The <code>PluginContext</code> object must never be reused after
+   * its context plugin is stopped.
+   *
+   * <p>
+   * The Framework is the only entity that can create <code>PluginContext</code>
+   * objects and they are only valid within the Framework that created them.
+   *
+   * @threadsafe
+   */
+  class CTK_PLUGINFW_EXPORT PluginContext : public QObject
   {
-
+    Q_OBJECT
 	  Q_DECLARE_PRIVATE(PluginContext)
 
   public:
@@ -49,8 +101,28 @@ namespace ctk {
 
     ~PluginContext();
 
+    /**
+     * Returns the <code>Plugin</code> object associated with this
+     * <code>PluginContext</code>. This plugin is called the context plugin.
+     *
+     * @return The <code>Plugin</code> object associated with this
+     *         <code>PluginContext</code>.
+     * @throws std::logic_error If this PluginContext is no
+     *         longer valid.
+     */
     Plugin* getPlugin(int id) const;
 
+    /**
+     * Returns a list of all installed plugins.
+     * <p>
+     * This method returns a list of all plugins installed in the plugin
+     * environment at the time of the call to this method. However, since the
+     * Framework is a very dynamic environment, plugins can be installed or
+     * uninstalled at anytime.
+     *
+     * @return A QList of <code>Plugin</code> objects, one object per
+     *         installed plugin.
+     */
     QList<Plugin*> getPlugins() const;
 
     ServiceRegistration registerService(const QStringList& clazzes, QObject* service, const ServiceProperties& properties = ServiceProperties());
@@ -63,6 +135,24 @@ namespace ctk {
 
     Plugin* installPlugin(const QUrl& location, QIODevice* in = 0);
 
+
+  signals:
+
+    /**
+     *
+     */
+    void pluginChanged(const PluginEvent& event);
+
+    /**
+     *
+     */
+    //void frameworkEvent(const FrameworkEvent& event);
+
+    /**
+     *
+     */
+    //void serviceChanged(const ServiceEvent& event);
+
   protected:
 
     friend class PluginFrameworkPrivate;

+ 49 - 0
Libs/PluginFramework/ctkPluginEvent.cxx

@@ -0,0 +1,49 @@
+/*=============================================================================
+
+  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 "ctkPluginEvent.h"
+
+
+namespace ctk {
+
+  PluginEvent::PluginEvent(Type type, Plugin* plugin)
+    : d(new PluginEventData(type, plugin))
+  {
+
+  }
+
+  PluginEvent::PluginEvent(const PluginEvent& other)
+    : d(other.d)
+  {
+
+  }
+
+  Plugin* PluginEvent::getPlugin() const
+  {
+    return d->plugin;
+  }
+
+  PluginEvent::Type PluginEvent::getType() const
+  {
+    return d->type;
+  }
+
+}

+ 132 - 0
Libs/PluginFramework/ctkPluginEvent.h

@@ -0,0 +1,132 @@
+/*=============================================================================
+
+  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 CTKPLUGINEVENT_H
+#define CTKPLUGINEVENT_H
+
+#include <QObject>
+#include <QSharedDataPointer>
+
+#include "CTKPluginFrameworkExport.h"
+
+namespace ctk {
+
+  class Plugin;
+  class PluginEventData;
+
+  /**
+   * An event from the Framework describing a plugin lifecycle change.
+   * <p>
+   * <code>PluginEvent</code> objects are delivered to slots connected
+   * to PluginContext::pluginChanged() or to registerd event handlers
+   * for the topic "org.commontk/framework/pluginChanged"
+   * when a change occurs in a plugins's lifecycle. A type code is used to identify
+   * the event type for future extendability.
+   *
+   * @see PluginContext#pluginChanged
+   * @see EventBus
+   */
+  class CTK_PLUGINFW_EXPORT PluginEvent : public QObject
+  {
+    Q_OBJECT
+    Q_PROPERTY(Type type READ getType CONSTANT)
+    Q_PROPERTY(Plugin* plugin READ getPlugin CONSTANT)
+    Q_ENUMS(Type)
+
+    QSharedDataPointer<PluginEventData> d;
+
+  public:
+
+    enum Type {
+      INSTALLED,
+      STARTED,
+      STOPPED,
+      UPDATED,
+      UNINSTALLED,
+      RESOLVED,
+      UNRESOLVED,
+      STARTING,
+      STOPPING,
+      LAZY_ACTIVATION
+    };
+
+    /**
+     * Creates a plugin event of the specified type.
+     *
+     * @param type The event type.
+     * @param plugin The plugin which had a lifecycle change.
+     */
+    PluginEvent(Type type, Plugin* plugin);
+
+    PluginEvent(const PluginEvent& other);
+
+    /**
+     * Returns the plugin which had a lifecycle change.
+     *
+     * @return The plugin that had a change occur in its lifecycle.
+     */
+    Plugin* getPlugin() const;
+
+    /**
+     * Returns the type of lifecyle event. The type values are:
+     * <ul>
+     * <li>{@link #INSTALLED}
+     * <li>{@link #RESOLVED}
+     * <li>{@link #LAZY_ACTIVATION}
+     * <li>{@link #STARTING}
+     * <li>{@link #STARTED}
+     * <li>{@link #STOPPING}
+     * <li>{@link #STOPPED}
+     * <li>{@link #UPDATED}
+     * <li>{@link #UNRESOLVED}
+     * <li>{@link #UNINSTALLED}
+     * </ul>
+     *
+     * @return The type of lifecycle event.
+     */
+    Type getType() const;
+
+
+  };
+
+  class PluginEventData : public QSharedData
+  {
+  public:
+
+    PluginEventData(PluginEvent::Type type, Plugin* plugin)
+      : type(type), plugin(plugin)
+    {
+
+    }
+
+    PluginEventData(const PluginEventData& other)
+      : QSharedData(other), type(other.type), plugin(other.plugin)
+    {
+
+    }
+
+    PluginEvent::Type type;
+    Plugin* plugin;
+  };
+
+}
+
+#endif // CTKPLUGINEVENT_H

+ 115 - 0
Libs/PluginFramework/ctkServiceRegistration.cxx

@@ -21,15 +21,130 @@
 
 #include "ctkServiceRegistration.h"
 
+#include <QMutex>
+
 namespace ctk {
 
+  class ServiceRegistrationPrivate
+  {
+
+  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(plugint), 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
   {
+    if (!available) throw std::logic_error("Service is unregistered");
 
+    return reference;
   }
 
   void ServiceRegistration::setProperties(const PluginContext::ServiceProperties& properties)
   {
+    QMutexLocker lock(eventLock);
+          Set before;
+          // TBD, optimize the locking of services
+          synchronized (bundle.fwCtx.services) {
+            synchronized (properties) {
+              if (available) {
+                // NYI! Optimize the MODIFIED_ENDMATCH code
+                Object old_rank = properties.get(Constants.SERVICE_RANKING);
+                before = bundle.fwCtx.listeners.getMatchingServiceListeners(reference);
+                String[] classes = (String[])properties.get(Constants.OBJECTCLASS);
+                Long sid = (Long)properties.get(Constants.SERVICE_ID);
+                properties = new PropertiesDictionary(props, classes, sid);
+                Object new_rank = properties.get(Constants.SERVICE_RANKING);
+                if (old_rank != new_rank && new_rank instanceof Integer &&
+                    !((Integer)new_rank).equals(old_rank)) {
+                  bundle.fwCtx.services.updateServiceRegistrationOrder(this, classes);
+                }
+              } else {
+                throw new IllegalStateException("Service is unregistered");
+              }
+            }
+          }
+          bundle.fwCtx.listeners
+            .serviceChanged(bundle.fwCtx.listeners.getMatchingServiceListeners(reference),
+                            new ServiceEvent(ServiceEvent.MODIFIED, reference),
+                            before);
+          bundle.fwCtx.listeners
+            .serviceChanged(before,
+                            new ServiceEvent(ServiceEvent.MODIFIED_ENDMATCH, reference),
+                            null);
 
   }
 

+ 94 - 0
Libs/PluginFramework/ctkServiceRegistration.h

@@ -28,16 +28,110 @@
 
 namespace ctk {
 
+  class ServiceRegistrationPrivate;
+
+  /**
+   * A registered service.
+   *
+   * <p>
+   * The Framework returns a <code>ServiceRegistration</code> object when a
+   * <code>PluginContext#registerService()</code> method invocation is successful.
+   * The <code>ServiceRegistration</code> object is for the private use of the
+   * registering plugin and should not be shared with other plugins.
+   * <p>
+   * The <code>ServiceRegistration</code> object may be used to update the
+   * properties of the service or to unregister the service.
+   *
+   * @see PluginContext#registerService()
+   * @threadsafe
+   */
   class ServiceRegistration {
 
+    Q_DECLARE_PRIVATE(ServiceRegistration)
+
   public:
 
+    /**
+     * Returns a <code>ServiceReference</code> object for a service being
+     * registered.
+     * <p>
+     * The <code>ServiceReference</code> object may be shared with other
+     * plugins.
+     *
+     * @throws std::logic_error If this
+     *         <code>ServiceRegistration</code> object has already been
+     *         unregistered.
+     * @return <code>ServiceReference</code> object.
+     */
     ServiceReference getReference() const;
 
+    /**
+     * Updates the properties associated with a service.
+     *
+     * <p>
+     * The {@link PluginConstants#OBJECTCLASS} and {@link PluginConstants#SERVICE_ID} keys
+     * cannot be modified by this method. These values are set by the Framework
+     * when the service is registered in the environment.
+     *
+     * <p>
+     * The following steps are required to modify service properties:
+     * <ol>
+     * <li>The service's properties are replaced with the provided properties.
+     * <li>A service event of type {@link ServiceEvent#MODIFIED} is fired.
+     * </ol>
+     *
+     * @param properties The properties for this service. 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 this method should be called again.
+     *
+     * @throws std::logic_error If this <code>ServiceRegistration</code>
+     *         object has already been unregistered.
+     * @throws std::invalid_argument If <code>properties</code> contains
+     *         case variants of the same key name.
+     */
     void setProperties(const PluginContext::ServiceProperties& properties);
 
+    /**
+     * Unregisters a service. Remove a <code>ServiceRegistration</code> object
+     * from the Framework service registry. All <code>ServiceReference</code>
+     * objects associated with this <code>ServiceRegistration</code> object
+     * can no longer be used to interact with the service once unregistration is
+     * complete.
+     *
+     * <p>
+     * The following steps are required to unregister a service:
+     * <ol>
+     * <li>The service is removed from the Framework service registry so that
+     * it can no longer be obtained.
+     * <li>A service event of type {@link ServiceEvent#UNREGISTERING} is fired
+     * so that plugins using this service can release their use of the service.
+     * Once delivery of the service event is complete, the
+     * <code>ServiceReference</code> objects for the service may no longer be
+     * used to get a service object for the service.
+     * <li>For each plugin whose use count for this service is greater than
+     * zero: <br>
+     * The plugin's use count for this service is set to zero. <br>
+     * If the service was registered with a {@link ServiceFactory} object, the
+     * <code>ServiceFactory#ungetService</code> method is called to release
+     * the service object for the plugin.
+     * </ol>
+     *
+     * @throws std::logic_error If this
+     *         <code>ServiceRegistration</code> object has already been
+     *         unregistered.
+     * @see BundleContext#ungetService
+     * @see ServiceFactory#ungetService
+     */
     void unregister() const;
 
+  private:
+
+    ServiceRegistration(Plugin* plugin, QObject* service,
+                        const PluginContext::ServiceProperties& props);
+
+    ServiceRegistrationPrivate * const d_ptr;
+
   };
 
 }