/*============================================================================= 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 CTKPLUGIN_H #define CTKPLUGIN_H #include #include #include #include #include "ctkVersion.h" #include "ctkPluginLocalization.h" #include "ctkPluginConstants.h" #include "service/log/ctkLogStream.h" class ctkPluginContext; class ctkPluginArchive; class ctkPluginFrameworkContext; class ctkPluginPrivate; /** * \ingroup PluginFramework * * An installed plugin in the Framework. * *

* A %ctkPlugin object is the access point to define the lifecycle of * an installed plugin. Each plugin installed in the plugin environment must have * an associated %ctkPlugin object. * *

* A plugin must have a unique identity, a long, chosen by the * Framework. This identity must not change during the lifecycle of a plugin, * even when the plugin is updated. Uninstalling and then reinstalling the * plugin must create a new unique identity. * *

* A plugin can be in one of six states: *

    *
  • {@link #UNINSTALLED} *
  • {@link #INSTALLED} *
  • {@link #RESOLVED} *
  • {@link #STARTING} *
  • {@link #STOPPING} *
  • {@link #ACTIVE} *
*

* They can be ORed together using the States type to * determine if a plugin is in one of the valid states. * *

* A plugin should only execute code when its state is one of * STARTING, ACTIVE, or STOPPING. * An UNINSTALLED plugin can not be set to another state; it is a * zombie and can only be reached because references are kept somewhere. * *

* The Framework is the only entity that is allowed to create * %ctkPlugin objects, and these objects are only valid within the * Framework that created them. * * @remarks This class is thread safe. */ class CTK_PLUGINFW_EXPORT ctkPlugin { Q_DECLARE_PRIVATE(ctkPlugin) Q_DISABLE_COPY(ctkPlugin) public: enum State { /** * The plugin is uninstalled and may not be used. * *

* The UNINSTALLED state is only visible after a plugin is * uninstalled; the plugin is in an unusable state but references to the * %ctkPlugin object may still be available and used for * introspection. */ UNINSTALLED = 0x00000001, /** * The plugin is installed but not yet resolved. * *

* A plugin is in the INSTALLED state when it has been * installed in the Framework but is not or cannot be resolved. *

* This state is visible if the plugin's code dependencies are not resolved. * The Framework may attempt to resolve an INSTALLED plugin's * code dependencies and move the plugin to the RESOLVED * state. */ INSTALLED = 0x00000002, /** * The plugin is resolved and is able to be started. * *

* A plugin is in the RESOLVED state when the Framework has * successfully resolved the plugin's code dependencies. These dependencies * include: *

    *
  • The plugin's required plugin dependencies from its * {@link ctkPluginConstants::REQUIRE_PLUGIN} Manifest header. *
*

* Note that the plugin is not active yet. A plugin must be put in the * RESOLVED state before it can be started. The Framework may * attempt to resolve a plugin at any time. */ RESOLVED = 0x00000004, /** * The plugin is in the process of starting. * *

* A plugin is in the STARTING state when its * {@link #start(const StartOptions&) start} method is active. A plugin must be in this * state when the plugin's {@link ctkPluginActivator::start} method is called. If the * ctkPluginActivator::start method completes without exception, * then the plugin has successfully started and must move to the * ACTIVE state. *

* If the plugin does not have a * {@link ctkPluginConstants#ACTIVATION_EAGER eager activation policy}, then the * plugin may remain in this state for some time until the activation is * triggered. */ STARTING = 0x00000008, /** * The plugin is in the process of stopping. * *

* A plugin is in the STOPPING state when its * {@link #stop(const StopOptions&) stop} method is active. A plugin must be in this state * when the plugin's {@link ctkPluginActivator::stop} method is called. When the * ctkPluginActivator::stop method completes the plugin is * stopped and must move to the RESOLVED state. */ STOPPING = 0x00000010, /** * The plugin is now running. * *

* A plugin is in the ACTIVE state when it has been * successfully started and activated. */ ACTIVE = 0x00000020 }; /** * Represents an ORed combination of plugin states. * * @see #State */ Q_DECLARE_FLAGS(States, State) enum StartOption { /** * The plugin start operation is transient and the persistent autostart * setting of the plugin is not modified. * *

* This option may be set when calling {@link #start(const StartOptions&)} to notify the * framework that the autostart setting of the plugin must not be modified. * If this option is not set, then the autostart setting of the plugin is * modified. * * @see #start(const StartOptions&) */ START_TRANSIENT = 0x00000001, /** * The plugin start operation must activate the plugin according to the * plugin's declared * {@link ctkPluginConstants#PLUGIN_ACTIVATIONPOLICY activation policy}. * *

* This bit may be set when calling {@link #start(const StartOptions&)} to notify the * framework that the plugin must be activated using the plugin's declared * activation policy. * * @see ctkPluginConstants#PLUGIN_ACTIVATIONPOLICY * @see #start(const StartOptions&) */ START_ACTIVATION_POLICY = 0x00000002 }; /** * Represents an ORed combination of start options. * * @see #StartOption */ Q_DECLARE_FLAGS(StartOptions, StartOption) enum StopOption { /** * The plugin stop is transient and the persistent autostart setting of the * plugin is not modified. * *

* This option may be set when calling {@link #stop(const StopOptions&)} to notify the * framework that the autostart setting of the plugin must not be modified. * If this option is not set, then the autostart setting of the plugin is * modified. * * @see #stop(const StopOptions&) */ STOP_TRANSIENT = 0x00000001 }; /** * Represents an ORed combination of stop options. * * @see #StopOption */ Q_DECLARE_FLAGS(StopOptions, StopOption) virtual ~ctkPlugin(); /** * Returns this plugin's current state. * *

* A plugin can be in only one state at any time. * * @return An element of UNINSTALLED,INSTALLED, * RESOLVED,STARTING, * STOPPING,ACTIVE. */ State getState() const; /** * Starts this plugin. * *

* If this plugin's state is UNINSTALLED then an * ctkIllegalStateException is thrown. *

* The following steps are required to start this bundle: *

    *
  1. If this plugin is in the process of being activated or deactivated * then this method must wait for activation or deactivation to complete * before continuing. If this does not occur in a reasonable time, a * ctkPluginException is thrown to indicate this plugin was unable * to be started. * *
  2. If this plugin's state is ACTIVE then this method * returns immediately. * *
  3. If the {@link #START_TRANSIENT} option is not set then set this * plugin's autostart setting to Started with declared activation * if the {@link #START_ACTIVATION_POLICY} option is set or * Started with lazy activation if not set. When the Framework is * restarted and this plugin's autostart setting is not Stopped, * this plugin must be automatically started. * *
  4. If this plugin's state is not RESOLVED, an attempt is * made to resolve this plugin. If the Framework cannot resolve this plugin, * a ctkPluginException is thrown. * *
  5. If the {@link #START_ACTIVATION_POLICY} option is not set then: *
      *
    • If this plugin's state is STARTING then this method * returns immediately. *
    • This plugin's state is set to STARTING. *
    • A plugin event of type {@link ctkPluginEvent::LAZY_ACTIVATION} is fired. *
    • This method returns immediately and the remaining steps will be * followed when this plugin's activation is later triggered. *
    * If the {@link #START_ACTIVATION_POLICY} option is set and this * plugin's declared activation policy is {@link ctkPluginConstants#ACTIVATION_EAGER * eager} then: * *
  6. This plugin's state is set to STARTING. * *
  7. A plugin event of type {@link ctkPluginEvent::STARTING} is fired. * *
  8. The {@link ctkPluginActivator::start} method of this plugin's * ctkPluginActivator, is called. If the * ctkPluginActivator throws an exception then: *
      *
    • This plugin's state is set to STOPPING. *
    • A plugin event of type {@link ctkPluginEvent::STOPPING} is fired. *
    • Any services registered by this plugin must be unregistered. *
    • Any services used by this plugin must be released. *
    • Any listeners registered by this plugin must be removed. *
    • This plugin's state is set to RESOLVED. *
    • A plugin event of type {@link ctkPluginEvent::STOPPED} is fired. *
    • A ctkPluginException is then thrown. *
    * *
  9. If this plugin's state is UNINSTALLED, because this * plugin was uninstalled while the ctkPluginActivator::start * method was running, a ctkPluginException is thrown. * *
  10. This plugin's state is set to ACTIVE. * *
  11. A plugin event of type {@link ctkPluginEvent::STARTED} is fired. *
* * Preconditions *
    *
  • getState() in { INSTALLED, * RESOLVED, STARTING } * or { INSTALLED, RESOLVED } * if this plugin has a eager activation policy. *
* Postconditions, no exceptions thrown *
    *
  • Plugin autostart setting is modified unless the * {@link #START_TRANSIENT} option was set. *
  • getState() in { ACTIVE } * if the eager activation policy was used. *
  • ctkPluginActivator::start() has been called and did not * throw an exception if the eager policy was used. *
* Postconditions, when an exception is thrown *
    *
  • Depending on when the exception occurred, plugin autostart setting is * modified unless the {@link #START_TRANSIENT} option was set. *
  • getState() not in { STARTING, * ACTIVE }. *
* * @param options The options for starting this plugin. See * {@link #START_TRANSIENT} and {@link #START_ACTIVATION_POLICY}. The * Framework must ignore unrecognized options. * @throws ctkPluginException If this plugin could not be started. This could * be because a code dependency could not be resolved or the * ctkPluginActivator could not be loaded or * threw an exception. * @throws ctkIllegalStateException If this plugin has been uninstalled or this * plugin tries to change its own state. */ virtual void start(const StartOptions& options = START_ACTIVATION_POLICY); /** * Stops this plugin. * *

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

    *
  1. If this plugin's state is UNINSTALLED then an * ctkIllegalStateException is thrown. * *
  2. If this plugin is in the process of being activated or deactivated * then this method must wait for activation or deactivation to complete * before continuing. If this does not occur in a reasonable time, a * ctkPluginException is thrown to indicate this plugin was unable * to be stopped. *
  3. If the {@link #STOP_TRANSIENT} option is not set then then set this * plugin's persistent autostart setting to Stopped. When the * Framework is restarted and this plugin's autostart setting is * Stopped, this bundle must not be automatically started. * *
  4. If this plugin's state is not STARTING or * ACTIVE then this method returns immediately. * *
  5. This plugin's state is set to STOPPING. * *
  6. A plugin event of type {@link ctkPluginEvent::STOPPING} is fired. * *
  7. If this plugin's state was ACTIVE prior to setting the * state to STOPPING, the {@link ctkPluginActivator#stop} method * of this plugin's ctkPluginActivator is * called. If that method throws an exception, this method must continue to * stop this plugin and a ctkPluginException must be thrown after * completion of the remaining steps. * *
  8. Any services registered by this plugin must be unregistered. *
  9. Any services used by this plugin must be released. *
  10. Any listeners registered by this plugin must be removed. * *
  11. If this plugin's state is UNINSTALLED, because this * plugin was uninstalled while the ctkPluginActivator::stop method * was running, a ctkPluginException must be thrown. * *
  12. This plugin's state is set to RESOLVED. * *
  13. A plugin event of type {@link ctkPluginEvent::STOPPED} is fired. *
* * Preconditions *
    *
  • getState() in { ACTIVE }. *
* Postconditions, no exceptions thrown *
    *
  • Plugin autostart setting is modified unless the * {@link #STOP_TRANSIENT} option was set. *
  • getState() not in { ACTIVE, * STOPPING }. *
  • ctkPluginActivator::stop has been called and did not throw * an exception. *
* Postconditions, when an exception is thrown *
    *
  • Plugin autostart setting is modified unless the * {@link #STOP_TRANSIENT} option was set. *
* * @param options The options for stoping this bundle. See * {@link #STOP_TRANSIENT}. * @throws ctkPluginException If this plugin's ctkPluginActivator * threw an exception. * @throws ctkIllegalStateException If this plugin has been uninstalled or this * plugin tries to change its own state. */ virtual void stop(const StopOptions& options = 0); /** * Updates this plugin from a QUrl. * *

* If the specified QURl is empty, the * Framework creates the QUrl from which to read the * updated plugin by interpreting, in an implementation dependent manner, * this plugin's {@link ctkPluginConstants#PLUGIN_UPDATELOCATION * Plugin-UpdateLocation} Manifest header, if present, or this plugin's * original location. * *

* If this plugin's state is ACTIVE, it must be stopped before * the update and started after the update successfully completes. * *

* If this plugin has exported any classes that are used by another * plugin, these classes remain available until the Framework is relaunched. * *

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

    *
  1. If this plugin's state is UNINSTALLED then an * ctkIllegalStateException is thrown. * *
  2. If this plugin's state is ACTIVE, STARTING * or STOPPING, this plugin is stopped as described in the * ctkPlugin::stop() method. If ctkPlugin::stop() throws an * exception, the exception is rethrown terminating the update. * *
  3. The updated version of this plugin is read from the URL and * installed. If the Framework is unable to install the updated version of * this plugin, the original version of this plugin is restored and a * ctkPluginException is thrown after completion of the * remaining steps. * *
  4. This plugin's state is set to INSTALLED. * *
  5. If the updated version of this plugin was successfully installed, a * plugin event of type {@link ctkPluginEvent#UPDATED} is fired. * *
  6. If this plugin's state was originally ACTIVE, the * updated plugin is started as described in the ctkPlugin::start() * method. If ctkPlugin::start() throws an exception, a Framework * event of type {@link ctkPluginFrameworkEvent#PLUGIN_ERROR} is fired containing the * exception. *
* * Preconditions *
    *
  • getState() not in { UNINSTALLED * }. *
* Postconditions, no exceptions thrown *
    *
  • getState() in { INSTALLED, * RESOLVED, ACTIVE }. *
  • This plugin has been updated. *
* Postconditions, when an exception is thrown *
    *
  • getState() in { INSTALLED, * RESOLVED, ACTIVE }. *
  • Original plugin is still used; no update occurred. *
* * @param updateLocation The QUrl from which to read the new * plugin or null to indicate the Framework must create * the URL from this plugin's * {@link ctkPluginConstants#PLUGIN_UPDATELOCATION Plugin-UpdateLocation} * Manifest header, if present, or this plugin's original location. * @throws ctkPluginException If the update location cannot be read or the update * fails. * @throws ctkIllegalStateException If this plugin has been uninstalled or this * plugin tries to change its own state. * @see #stop() * @see #start() */ void update(const QUrl &updateLocation = QUrl()); /** * Uninstalls this plugin. * *

* This method causes the Plugin Framework to notify other plugins that this * plugin is being uninstalled, and then puts this plugin into the * UNINSTALLED state. The Framework must remove any resources * related to this plugin that it is able to remove. * *

* If this plugin is required by other plugins which are already resolved, * the Framework must keep this plugin loaded until the * Framework is relaunched. * *

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

    *
  1. If this plugin's state is UNINSTALLED then an * ctkIllegalStateException is thrown. * *
  2. If this plugin's state is ACTIVE, STARTING * or STOPPING, this plugin is stopped as described in the * ctkPlugin::stop method. If ctkPlugin::stop throws an * exception, a Framework event of type ctkPluginFrameworkEvent::PLUGIN_ERROR is * fired containing the exception. * *
  3. This plugin's state is set to UNINSTALLED. * *
  4. A plugin event of type ctkPluginEvent::UNINSTALLED is fired. * *
  5. This plugin and any persistent storage area provided for this plugin * by the Framework are removed. *
* * Preconditions *
    *
  • getState() not in { UNINSTALLED * }. *
* Postconditions, no exceptions thrown *
    *
  • getState() in { UNINSTALLED * }. *
  • This plugin has been uninstalled. *
* Postconditions, when an exception is thrown *
    *
  • getState() not in { UNINSTALLED * }. *
  • This plugin has not been uninstalled. *
* * @throws ctkPluginException If the uninstall failed. This can occur if * another thread is attempting to change this plugin's state and * does not complete in a timely manner. * @throws ctkIllegalStateException If this plugin has been uninstalled or this * plugin tries to change its own state. * @see #stop() */ virtual void uninstall(); /** * Returns this plugin's {@link ctkPluginContext}. The returned * ctkPluginContext can be used by the caller to act on behalf * of this plugin. * *

* If this plugin is not in the {@link #STARTING}, {@link #ACTIVE}, or * {@link #STOPPING} states, then this * plugin has no valid ctkPluginContext. This method will * return 0 if this plugin has no valid * ctkPluginContext. * * @return A ctkPluginContext for this plugin or * 0 if this plugin has no valid * ctkPluginContext. */ ctkPluginContext* getPluginContext() const; /** * Returns this plugin's unique identifier. This plugin is assigned a unique * identifier by the Framework when it was installed in the plugin * environment. * *

* A plugin's unique identifier has the following attributes: *

    *
  • Is unique and persistent. *
  • Is a long. *
  • Its value is not reused for another plugin, even after a plugin is * uninstalled. *
  • Does not change while a plugin remains installed. *
  • Does not change when a plugin is updated. *
* *

* This method must continue to return this plugin's unique identifier while * this plugin is in the UNINSTALLED state. * * @return The unique identifier of this plugin. */ long getPluginId() const; /** * Returns this plugin's location identifier. * *

* The location identifier is the location passed to * ctkPluginContext::installPlugin when a plugin is installed. * The location identifier does not change while this plugin remains * installed, even if this plugin is updated. * *

* This method must continue to return this plugin's location identifier * while this plugin is in the UNINSTALLED state. * * @return The string representation of this plugin's location identifier. */ QString getLocation() const; /** * Returns this plugin's Manifest headers and values. This method returns * all the Manifest headers and values from the main section of this * bundle's Manifest file; that is, all lines prior to the first named section. * * TODO: documentation about manifest value internationalization * *

* For example, the following Manifest headers and values are included if * they are present in the Manifest file: * *

   *     Plugin-Name
   *     Plugin-Vendor
   *     Plugin-ctkVersion
   *     Plugin-Description
   *     Plugin-DocURL
   *     Plugin-ContactAddress
   * 
* *

* This method must continue to return Manifest header information while * this plugin is in the UNINSTALLED state. * * @return A QHash object containing this plugin's * Manifest headers and values. */ virtual QHash getHeaders(); /** * Returns the symbolic name of this plugin as specified by its * Plugin-SymbolicName manifest header. The plugin symbolic * name together with a version must identify a unique plugin. The plugin * symbolic name should be based on the reverse domain name naming * convention like that used for java packages. * *

* This method must continue to return this plugin's symbolic name while * this plugin is in the UNINSTALLED state. * * @return The symbolic name of this plugin or a null QString if this * plugin does not have a symbolic name. */ QString getSymbolicName() const; /** * Returns a list of all the files and directories * within this plugin whose longest sub-path matches the * specified path. *

* The specified path is always relative to the root of this plugin * (the plugins symbolic name) and may begin with a "/". * A path value of "/" indicates the root of this plugin. *

* Returned paths indicating subdirectory paths end with a "/". * The returned paths are all relative to the root of this plugin and must * not begin with "/". *

* * @param path The path name for which to return resource paths. * @return A list of the resource paths (QString * objects) or an empty list if no entry could be found. * @throws ctkIllegalStateException If this plugin has been * uninstalled. */ virtual QStringList getResourceList(const QString& path) const; /** * Returns a list of resources in this plugin. * *

* This method is intended to be used to obtain configuration, setup, * localization and other information from this plugin. This method can * either return only entries in the specified path or recurse into * subdirectories returning entries in the directory tree beginning at the * specified path. * *

* Examples: * * \code * // List all XML files in the OSGI-INF directory and below * QStringList r = b->findResources("OSGI-INF", "*.xml", true); * * // Find a specific localization file * QStringList r = b->findResources("OSGI-INF/l10n", "plugin_nl_DU.tm", false); * \endcode * * @param path The path name in which to look. The path is always relative * to the root of this plugin and may begin with "/". A * path value of "/" indicates the root of this plugin. * @param filePattern The file name pattern for selecting entries in the * specified path. The pattern is only matched against the last * element of the entry path. Substring matching is supported, as * specified in the Filter specification, using the wildcard * character ("*"). If a null QString is specified, this * is equivalent to "*" and matches all files. * @param recurse If true, recurse into subdirectories. * Otherwise only return entries from the specified path. * @return A list of QString objects for each matching entry, or * an empty list if an entry could not be found or if the caller * does not have the appropriate * AdminPermission[this,RESOURCE], and the Plugin * Framework supports permissions. * @throws ctkIllegalStateException If this plugin has been uninstalled. */ virtual QStringList findResources(const QString& path, const QString& filePattern, bool recurse) const; /** * Returns a QByteArray containing a Qt resource located at the * specified path in this plugin. *

* The specified path is always relative to the root of this plugin * (the plugins symbolic name) and may * begin with "/". A path value of "/" indicates the * root of this plugin. *

* * @param path The path name of the resource. * @return A QByteArray to the resource, or a null QByteArray if no resource could be * found. * @throws ctkIllegalStateException If this plugin has been * uninstalled. */ virtual QByteArray getResource(const QString& path) const; /** * Returns a ctkPluginLocalization object for the * specified locale. The translations are loaded from a * .qm file starting with base. * * You can use the returned ctkPluginLocalization * object to dynamically translate text without changing the current * locale of the application. This can be used for example to * provide localized messages to multiple users which use the application * (maybe some kind of server) simultaneously but require different * localizations. * * @param locale The locale to be used by the returned * ctkPluginLocalization object. * @param base The base name of the .qm message file which contains * translated messages. Defaults to * ctkPluginConstants::PLUGIN_LOCALIZATION_DEFAULT_BASENAME. * @return A locale specific ctkPluginLocalization instance. */ ctkPluginLocalization getPluginLocalization(const QLocale& locale, const QString& base = ctkPluginConstants::PLUGIN_LOCALIZATION_DEFAULT_BASENAME) const; /** * Returns the version of this plugin as specified by its * Plugin-Version manifest header. If this plugin does not have a * specified version then {@link ctkVersion#emptyVersion} is returned. * *

* This method must continue to return this plugin's version while * this plugin is in the UNINSTALLED state. * * @return The version of this plugin. */ ctkVersion getVersion() const; protected: friend class ctkPluginFramework; friend class ctkPluginFrameworkPrivate; friend class ctkPluginFrameworkContext; friend class ctkPlugins; friend class ctkServiceReferencePrivate; // Do NOT change this to QScopedPointer! // We would need to include ctkPlugin.h (and ctkPluginPrivate_p.h) // at a lot of places... ctkPluginPrivate* d_ptr; ctkPlugin(); void init(ctkPluginPrivate* dd); void init(const QWeakPointer& self, ctkPluginFrameworkContext* fw, QSharedPointer ba); }; /** * \ingroup PluginFramework * @{ */ Q_DECLARE_METATYPE(ctkPlugin*) Q_DECLARE_METATYPE(QSharedPointer) Q_DECLARE_OPERATORS_FOR_FLAGS(ctkPlugin::States) Q_DECLARE_OPERATORS_FOR_FLAGS(ctkPlugin::StartOptions) Q_DECLARE_OPERATORS_FOR_FLAGS(ctkPlugin::StopOptions) CTK_PLUGINFW_EXPORT QDebug operator<<(QDebug debug, ctkPlugin::State state); CTK_PLUGINFW_EXPORT QDebug operator<<(QDebug debug, const ctkPlugin& plugin); CTK_PLUGINFW_EXPORT QDebug operator<<(QDebug debug, ctkPlugin const * plugin); CTK_PLUGINFW_EXPORT ctkLogStream& operator<<(ctkLogStream& stream, ctkPlugin const * plugin); CTK_PLUGINFW_EXPORT ctkLogStream& operator<<(ctkLogStream& stream, const QSharedPointer& plugin); /** @}*/ #endif // CTKPLUGIN_H