/*============================================================================= Library: CTK Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef CTKPLUGINCONTEXT_H_ #define CTKPLUGINCONTEXT_H_ #include #include #include #include #include #include #include "ctkPluginFramework_global.h" #include "ctkPluginEvent.h" #include "ctkServiceException.h" #include "ctkServiceReference.h" #include "ctkServiceRegistration.h" #include "ctkPluginFrameworkExport.h" // CTK class forward declarations class ctkPlugin; class ctkPluginPrivate; class ctkPluginContextPrivate; /** * 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. * *

* ctkPluginContext methods allow a plugin to: *

    *
  • Subscribe to events published by the Framework. *
  • Register service objects with the Framework service registry. *
  • Retrieve ServiceReferences from the Framework service * registry. *
  • Get and release service objects for a referenced service. *
  • Install new plugins in the Framework. *
  • Get the list of plugins installed in the Framework. *
  • Get the {@link ctkPlugin} object for a plugin. *
  • Create QFile objects for files in a persistent storage * area provided for the plugin by the Framework. *
* *

* A ctkPluginContext object will be created and provided to the * plugin associated with this context when it is started using the * {@link ctkPluginActivator::start} method. The same ctkPluginContext * object will be passed to the plugin associated with this context when it is * stopped using the {@link ctkPluginActivator::stop} method. A * ctkPluginContext 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. * *

* The ctkPlugin object associated with a ctkPluginContext * object is called the context plugin. * *

* The ctkPluginContext object is only valid during the execution of * its context plugin; that is, during the period from when the context plugin * is in the STARTING, STOPPING, and * ACTIVE plugin states. If the ctkPluginContext * object is used subsequently, a std::logic_error must be * thrown. The ctkPluginContext object must never be reused after * its context plugin is stopped. * *

* The Framework is the only entity that can create ctkPluginContext * objects and they are only valid within the Framework that created them. * * @threadsafe */ class CTK_PLUGINFW_EXPORT ctkPluginContext { Q_DECLARE_PRIVATE(ctkPluginContext) public: ~ctkPluginContext(); /** * Returns the value of the specified property. If the key is not found in * the Framework properties, the system properties are then searched. The * method returns an invalid QVariant if the property is not found. * * @param key The name of the requested property. * @return The value of the requested property, or an invalid QVariant if * the property is undefined. */ QVariant getProperty(const QString& key) const; /** * Returns the ctkPlugin object associated with this * ctkPluginContext. This plugin is called the context plugin. * * @return The ctkPlugin object associated with this * ctkPluginContext. * @throws std::logic_error If this ctkPluginContext is no * longer valid. */ QSharedPointer getPlugin() const; /** * Returns the plugin with the specified identifier. * * @param id The identifier of the plugin to retrieve. * @return A ctkPlugin object or 0 if the * identifier does not match any installed plugin. */ QSharedPointer getPlugin(long id) const; /** * Returns a list of all installed plugins. *

* 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 ctkPlugin objects, one object per * installed plugin. */ QList > getPlugins() const; /** * Registers the specified service object with the specified properties * under the specified class names into the Framework. A * ctkServiceRegistration object is returned. The * ctkServiceRegistration 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. * *

* A plugin can register a service object that implements the * {@link ctkServiceFactory} interface to have more flexibility in providing * service objects to other plugins. * *

* The following steps are required to register a service: *

    *
  1. If service is not a ctkServiceFactory, an * std::invalid_argument is thrown if service * is not an instance of all the specified class names. *
  2. The Framework adds the following service properties to the service * properties from the specified ctkDictionary (which may be * omitted):
    * A property named {@link ctkPluginConstants#SERVICE_ID} identifying the * registration number of the service
    * A property named {@link ctkPluginConstants#OBJECTCLASS} containing all the * specified classes.
    * Properties with these names in the specified ctkDictionary will * be ignored. *
  3. The service is added to the Framework service registry and may now be * used by other plugins. *
  4. A service event of type {@link ServiceEvent#REGISTERED} is fired. *
  5. A ctkServiceRegistration object for this registration is * returned. *
* * @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 ctkPluginConstants#OBJECTCLASS}. * @param service The service object or a ctkServiceFactory * object. * @param properties The properties for this service. The keys in the * properties object must all be QString objects. See * {@link ctkPluginConstants} 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 ctkServiceRegistration::setProperties} method must be called. * The set of properties may be omitted if the service has * no properties. * @return A ctkServiceRegistration 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: *
    *
  • service is 0.
  • service * is not a ctkServiceFactory object and is not an * instance of all the named classes in clazzes.
  • * properties contains case variants of the same key * name. *
* @throws std::logic_error If this ctkPluginContext is no longer valid. * @see ctkServiceRegistration * @see ctkServiceFactory */ ctkServiceRegistration registerService(const QStringList& clazzes, QObject* service, const ctkDictionary& properties = ctkDictionary()); /** * Registers the specified service object with the specified properties * under the specified class name with the Framework. * *

* This method is otherwise identical to * registerService(const QStringList&, QObject*, const ctkDictionary&) and is provided as * a convenience when service will only be registered under a single * class name. Note that even in this case the value of the service's * ctkPluginConstants::OBJECTCLASS property will be a QStringList, rather * than just a single string. * * @param clazz The class name under which the service can be located. * @param service The service object or a ctkServiceFactory object. * @param properties The properties for this service. * @return A ctkServiceRegistration object for use by the plugin * registering the service to update the service's properties or to * unregister the service. * @throws std::logic_error If this ctkPluginContext is no longer valid. * @see registerService(const QStringList&, QObject*, const ctkDictionary&) */ ctkServiceRegistration registerService(const char* clazz, QObject* service, const ctkDictionary& properties = ctkDictionary()); template ctkServiceRegistration registerService(QObject* service, const ctkDictionary& properties = ctkDictionary()) { const char* clazz = qobject_interface_iid(); if (clazz == 0) { throw ctkServiceException(QString("The interface class you are registering your service %1 against has no Q_DECLARE_INTERFACE macro") .arg(service->metaObject()->className())); } return registerService(clazz, service, properties); } /** * Returns a list of ctkServiceReference objects. The returned * list contains services that * were registered under the specified class and match the specified filter * expression. * *

* 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. * *

* The specified filter 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 filter is * empty, all registered services are considered to match the * filter. If the specified filter expression cannot be parsed, * an {@link std::invalid_argument} will be thrown with a human readable * message where the filter became unparsable. * *

* The result is a list of ctkServiceReference objects for all * services that meet all of the following conditions: *

    *
  • If the specified class name, clazz, 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. *
  • If the specified filter is not empty, the * filter expression must match the service. *
* * @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 ctkServiceReference objects or * an empty list if no services are registered which satisfy the * search. * @throws std::invalid_argument If the specified filter * contains an invalid filter expression that cannot be parsed. * @throws std::logic_error If this ctkPluginContext is no longer valid. */ QList getServiceReferences(const QString& clazz, const QString& filter = QString()); /** * Returns a list of ctkServiceReference objects. The returned * list contains services that * were registered under the Qt interface id of the template argument S * and match the specified filter expression. * *

* This method is identical to getServiceReferences(const QString&, const QString&) except that * the class name for the service object is automatically deduced from the template argument. * * @param filter The filter expression or empty for all * services. * @return A list of ctkServiceReference objects or * an empty list if no services are registered which satisfy the * search. * @throws std::invalid_argument If the specified filter * contains an invalid filter expression that cannot be parsed. * @throws std::logic_error If this ctkPluginContext is no longer valid. * @see getServiceReferences(const QString&, const QString&) */ template QList getServiceReferences(const QString& filter = QString()) { const char* clazz = qobject_interface_iid(); if (clazz == 0) throw ctkServiceException("The service interface class has no Q_DECLARE_INTERFACE macro"); return getServiceReferences(QString(clazz), filter); } /** * Returns a ctkServiceReference object for a service that * implements and was registered under the specified class. * *

* The returned ctkServiceReference 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. * *

* This method is the same as calling * {@link ctkPluginContext::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. *

* If multiple such services exist, the service with the highest ranking (as * specified in its {@link ctkPluginConstants::SERVICE_RANKING} property) is returned. *

* If there is a tie in ranking, the service with the lowest service ID (as * specified in its {@link ctkPluginConstants::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 ctkServiceReference object, or 0 if * no services are registered which implement the named class. * @throws std::logic_error If this ctkPluginContext is no longer valid. * @throws ctkServiceException It no service was registered under the given class name. * @see #getServiceReferences(const QString&, const QString&) */ ctkServiceReference getServiceReference(const QString& clazz); /** * Returns a ctkServiceReference object for a service that * implements and was registered under the specified template class argument. * *

* This method is identical to getServiceReference(const QString&) except that * the class name for the service object is automatically deduced from the template argument. * * @return A ctkServiceReference object, or 0 if * no services are registered which implement the named class. * @throws std::logic_error If this ctkPluginContext is no longer valid. * @throws ctkServiceException It no service was registered under the given class name. * @see #getServiceReference(const QString&) * @see #getServiceReferences(const QString&) */ template ctkServiceReference getServiceReference() { const char* clazz = qobject_interface_iid(); if (clazz == 0) throw ctkServiceException("The service interface class has no Q_DECLARE_INTERFACE macro"); return getServiceReference(QString(clazz)); } /** * Returns the service object referenced by the specified * ctkServiceReference object. *

* 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(ctkServiceReference*)} the context plugin's use count for * that service is incremented by one. Each time the service is released by * {@link #ungetService(ctkServiceReference*)} the context plugin's use count * for that service is decremented by one. *

* When a plugin's use count for a service drops to zero, the plugin should * no longer use that service. * *

* This method will always return 0 when the service * associated with this reference has been unregistered. * *

* The following steps are required to get the service object: *

    *
  1. If the service has been unregistered, 0 is returned. *
  2. The context plugin's use count for this service is incremented by * one. *
  3. If the context plugin's use count for the service is currently one * and the service was registered with an object implementing the * ctkServiceFactory interface, the * {@link ctkServiceFactory::getService(ctkPlugin*, ctkServiceRegistration*)} 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.
    * If the service object returned by the ctkServiceFactory object * is not an instance of all the classes named when the service * was registered or the ctkServiceFactory object throws an * exception, 0 is returned and a Framework event of type * {@link ctkPluginFrameworkEvent::ERROR} containing a {@link ctkServiceException} * describing the error is fired. *
  4. The service object for the service is returned. *
* * @param reference A reference to the service. * @return A service object for the service associated with * reference or 0 if the service is not * registered, the service object returned by a * ctkServiceFactory does not implement the classes under * which it was registered or the ctkServiceFactory threw * an exception. * @throws std::logic_error If this ctkPluginContext is no * longer valid. * @throws std::invalid_argument If the specified * ctkServiceReference was not created by the same * framework instance as this ctkPluginContext or * if it is invalid (default constructed). * @see #ungetService(const ctkServiceReference&) * @see ctkServiceFactory */ QObject* getService(ctkServiceReference reference); /** * Returns the service object referenced by the specified * ctkServiceReference object. *

* This is a convenience method which is identical to QObject* getService(ctkServiceReference) * except that it casts the service object to the supplied template argument type * * @return A service object for the service associated with * reference or 0 if the service is not * registered, the service object returned by a * ctkServiceFactory does not implement the classes under * which it was registered, the ctkServiceFactory threw * an exception or the service could not be casted to the desired type. * @throws std::logic_error If this ctkPluginContext is no * longer valid. * @throws std::invalid_argument If the specified * ctkServiceReference was not created by the same * framework instance as this ctkPluginContext or * if it is invalid (default constructed). * @see #getService(ctkServiceReference) * @see #ungetService(const ctkServiceReference&) * @see ctkServiceFactory */ template S* getService(ctkServiceReference reference) { return qobject_cast(getService(reference)); } /** * Releases the service object referenced by the specified * ctkServiceReference object. If the context plugin's use count * for the service is zero, this method returns false. * Otherwise, the context plugins's use count for the service is decremented * by one. * *

* The service's service object should no longer be used and all references * to it should be destroyed when a bundle's use count for the service drops * to zero. * *

* The following steps are required to unget the service object: *

    *
  1. If the context plugin's use count for the service is zero or the * service has been unregistered, false is returned. *
  2. The context plugin's use count for this service is decremented by * one. *
  3. If the context plugin's use count for the service is currently zero * and the service was registered with a ctkServiceFactory object, * the * {@link ctkServiceFactory#ungetService(ctkPlugin*, ctkServiceRegistration*, QObject*)} * method is called to release the service object for the context plugin. *
  4. true is returned. *
* * @param reference A reference to the service to be released. * @return false if the context plugin's use count for the * service is zero or if the service has been unregistered; * true otherwise. * @throws std::logic_error If this ctkPluginContext is no * longer valid. * @throws std::invalid_argument If the specified * ctkServiceReference was not created by the same * framework instance as this ctkPluginContext. * @see #getService * @see ctkServiceFactory */ bool ungetService(const ctkServiceReference& reference); /** * Creates a QFileInfo object for a file or directoryin the * persistent storage area provided for the plugin by the Framework. * *

* A QFileInfo object for the base directory of the persistent * storage area provided for the context plugin by the Framework can be * obtained by calling this method with an empty string as * filename. * *

* If the permissions are enabled, the Framework will * ensure that the plugin has the ctkFilePermission with * actions read,write,delete * for all files (recursively) in the persistent storage area provided for * the context plugin. * * @param filename A relative name to the file or directory to be accessed. * @return A QFileInfo object that represents the requested file * or directory. * @throws std::logic_error If this ctkPluginContext is no longer valid. */ QFileInfo getDataFile(const QString& filename); /** * Installs a plugin from the specified QIODevice object. * *

* If the specified QIODevice is null, the * Framework must create the QIODevice from which to read the * plugin by interpreting, in an implementation dependent manner, the * specified location. * *

* The specified location identifier will be used as the * identity of the plugin. Every installed plugin is uniquely identified by * its location identifier which is typically in the form of a URL. * *

* The following steps are required to install a plugin: *

    *
  1. If a plugin containing the same location identifier is already * installed, the ctkPlugin object for that plugin is returned. * *
  2. The plugin's content is read from the input stream. If this fails, a * {@link ctkPluginException} is thrown. * *
  3. The plugin's associated resources are allocated. The associated * resources minimally consist of a unique identifier and a persistent * storage area. If this step fails, a ctkPluginException * is thrown. * *
  4. The plugin's state is set to INSTALLED. * *
  5. A plugin event of type {@link ctkPluginEvent#INSTALLED} is fired. * *
  6. The ctkPlugin object for the newly or previously installed * plugin is returned. *
* * Postconditions, no exceptions thrown *
    *
  • getState() in { INSTALLED, * RESOLVED }. *
  • Plugin has a unique ID. *
* Postconditions, when an exception is thrown *
    *
  • Plugin is not installed and no trace of the plugin exists. *
* * @param location The location identifier of the plugin to install. * @param input The QIODevice object from which this plugin * will be read or null to indicate the Framework must * create the I/O device from the specified location identifier. * The I/O device must always be closed when this method completes, * even if an exception is thrown. * @return The ctkPlugin object of the installed plugin. * @throws ctkPluginException If the I/O device cannot be read or the * installation failed. * @throws std::logic_error If this ctkPluginContext is no longer valid. */ QSharedPointer installPlugin(const QUrl& location, QIODevice* in = 0); /** * Connects the specified slot to the context * plugins's signal which is emitted when a plugin has * a lifecycle state change. The signature of the slot * must be "slotName(ctkPluginEvent)". * * @param receiver The object to connect to. * @param slot The slot to be connected. * @param type The Qt connection type. Only Qt::DirectConnection, * Qt::QueuedConnection, or Qt::BlockingQueuedConnection is allowed. * @returns true if the connection was successfull; * false otherwise. * @throws std::logic_error If this ctkPluginContext is no * longer valid. * @see ctkPluginEvent * @see ctkEventBus */ bool connectPluginListener(const QObject* receiver, const char* slot, Qt::ConnectionType type = Qt::QueuedConnection); /** * Connects the specified slot to the context * plugin's signal which emits general Framework events. The signature * of the slot must be "slotName(ctkPluginFrameworkEvent)". * * @param receiver The object to connect to. * @param slot The slot to be connected. * @param type The Qt connection type. * @returns true if the connection was successfull; * false otherwise. * @throws std::logic_error If this ctkPluginContext is no * longer valid. * @see ctkPluginFrameworkEvent * @see ctkEventBus */ bool connectFrameworkListener(const QObject* receiver, const char* slot, Qt::ConnectionType type = Qt::QueuedConnection); /** * Connects the specified slot with the * specified filter to the context plugins's signal emitting * service events when a service has a lifecycle state change. The signature * of the slot must be "slotName(const ctkServiceEvent&)", but only the name * of the slot must be provided as the argument. * See {@link ctkLDAPSearchFilter} for a description of * the filter syntax. * *

* If the object to connect to is destroyed, the slot is automatically * disconnected. To explicitly disconnect the slot, use * disconnectServiceListener(). * *

* If the context plugin's list of listeners already contains the same * slot for the given receiver, then this * method replaces that slot's filter (which may be null) * with the specified one (which may be null). * *

* The slot is called if the filter criteria is met. To filter based * upon the class of the service, the filter should reference the * {@link ctkPluginConstants#OBJECTCLASS} property. If filter is * null, all services are considered to match the filter. * *

* When using a filter, it is possible that the * ctkServiceEvents for the complete lifecycle of a service * will not be delivered to the slot. For example, if the * filter only matches when the property x has * the value 1, the listener will not be called if the * service is registered with the property x not set to the * value 1. Subsequently, when the service is modified * setting property x to the value 1, the * filter will match and the slot will be called with a * ServiceEvent of type MODIFIED. Thus, the * slot will not be called with a ServiceEvent of type * REGISTERED. * * @param receiver The object to connect to. * @param slot The name of the slot to be connected. * @param filter The filter criteria. * @throws std::invalid_argument If filter contains an * invalid filter string that cannot be parsed. * @throws std::logic_error If this ctkPluginContext is no * longer valid. * @see ctkServiceEvent * @see disconnectServiceListener() * @see ctkEventBus */ void connectServiceListener(QObject* receiver, const char* slot, const QString& filter = QString()); /** * Disconnects a slot which has been previously connected * with a call to connectServiceListener(). * * @param receiver The object containing the slot. * @param slot The slot to be disconnected. * @see connectServiceListener() */ void disconnectServiceListener(QObject* receiver, const char* slot); protected: friend class ctkPluginFrameworkPrivate; friend class ctkPlugin; friend class ctkPluginPrivate; ctkPluginContext(ctkPluginPrivate* plugin); ctkPluginContextPrivate * const d_ptr; }; #endif /* CTKPLUGINCONTEXT_H_ */