The Plugin Lifecycle
In This Topic
Plugin versus OSGi bundle
A plugin is an OSGi bundle that XperienCentral is capable of dealing with. A plugin implements the OSGi specification but also implements XperienCentral-specific logic not defined by the OSGi specification. It is important to understand the differences between a plugin and OSGi bundle:
- A plugin is an OSGi bundle, however, an OSGi bundle not necessarily a plugin. A plugin is a specific type of OSGi bundle.
- An OSGi bundle is unaware of the concept of components, unlike a plugin.
- A plugin exists only within the active state of the OSGi bundle. In other words, the API that makes the OSGi bundle a plugin can only be accessed as long as the OSGi bundle is in an active state.
Lifecycle of an OSGi Bundle
OSGi bundle states
An OSGi bundle can be in one of the following states:
Property | Purpose |
---|---|
Installed | The bundle has been successfully installed. |
Resolved | All Java classes that the bundle needs are available. This state indicates that the bundle is either ready to be started or has stopped. |
Starting | The bundle is being started, the |
Active | The bundle has successfully started and is running. |
Stopping | The bundle is being stopped. The |
Uninstalled | The bundle has been uninstalled. It cannot move into any other state. |
OSGi Bundle Lifecycle
The lifecycle of an OSGi bundle is depicted in the figure below. The lifecycle starts with the installation of the bundle. If all Java classes that the bundle depends on are available, the OSGi bundle proceeds to the resolved state. From there the bundle can be started or uninstalled. When started, the bundle becomes active unless some runtime error occurs during the starting of the bundle. If that happens, the bundle remains resolved. An active bundle can be stopped in which case the bundle proceeds to the resolved state. When a resolved bundle is uninstalled it proceeds to the uninstalled state and stays there.
Lifecycle of a Plugin
A plugin exists only within the active state of the OSGi bundle, therefore the API that makes an OSGi bundle a plugin can only be accessed during the active state of the OSGi bundle. The API methods of a plugin that affect the lifecycle of the plugin are available on a component bundle, which is only available when the bundle is active. These methods are:
Property | Purpose |
---|---|
Start() | Registers services that each component within the bundle exposes if all required service dependencies are available. For each component, at least a component service is registered. |
Stop() | Stops all services registered by the component. |
Update() | Updates a plugin to a new version. |
Uninstall() | Removes all content that was created during the installation of the component and uninstalls the plugin. |
Uninstall(doPurge) | Removes all content that was created during and after the installation of the component and uninstalls the plugin.
|
Purge() | The bundle has been uninstalled. It cannot move into any other state. |
The lifecycle of a plugin is depicted below.
Bundle Manager Workflow
The workflow for each of the Bundle Manager methods is described below.
installBundle()
installBundle()
is invoked.- The validity of the plugin is checked. If the platform version is not within the plugin’s platform version range (if specified), the install stops. If the platform is valid, the OSGi
bundleContext.installBundle
method is invoked. - The bundle is installed.
update()
update()
is invoked.A check is done to see whether the version number of the plugin or bundle has been incremented and is higher than the currently installed version. If the version number is not correctly incremented, the update stops. If the version number is correctly incremented, the platform validity is checked.
If the setting
enable_wcb_development_mode
in the XperienCentral Setup Tool is set to “false”, this check is skipped.- If the platform version is not valid, the update stops. If the platform is valid, the OSGi method
bundle.update
is invoked
start()
start()
is invoked.start()
invokes the OSGi bundle.start
method.- The Dependency Manager invokes the
ComponentBundleImpl.start()
method. - A check is done to determine whether the plugin is being started for the very first time. If it is the very first time the plugin has been started, the ComponentManager method
installBundle
is invoked. Proceed to step 6. - If the plugin is not being started for the very first time, a check is done to see whether the plugin has been modified (updated). If the plugin has been modified, the ComponentManager method
updateBundle
is invoked. - A check is done to see whether the service dependencies for the bundle are available.
- The services for the bundle are registered with the OSGi.
stop()
stop()
is invoked.- The OSGi method bundle.
stop
is invoked. - The bundle’s services are unregistered from the OSGi.
uninstall()
is invoked.- Should the plugin be purged? If yes, the
ComponentManager.purgeBundle
method is invoked. - If the plugin is not purged, the
ComponentManager.uninstallBundle
method is invoked. - The OSGi method
bundle.uninstall
is invoked.
uninstall()
uninstall()
is invoked.- Should the plugin be purged? If yes, the
ComponentManager.purgeBundle
method is invoked. - If the plugin is not purged, the
ComponentManager.uninstallBundle
method is invoked. - The OSGi method
bundle.uninstall
is invoked.
purge()
purge()
is invoked.- The
ComponentManager.purgeBundle
method is invoked.
Plugin Lifecycle Methods
The plugin lifecycle methods make it possible for a plugin to respond to OSGi bundle.install, bundle.update, bundle.start, bundle.stop and bundle.uninstall methods. These methods are invoked within the plugin after the OSGi method is executed. This gives you extra flexibility to perform other tasks in response to a change in a plugin’s lifecycle.
The plugin lifecycle methods are:
Lifecycle | Purpose |
---|---|
onStart() | Allows a component to attach logic to a start event. For example: public void onStart() { LOG.log(Level.INFO, “onStart() invoked!"); }
Dependencies:
|
onInstall() | Allows a component to attach logic to an install event. For example: public void onInstall() { LOG.log(Level.INFO, “onInstall() invoked!"); }
Dependencies:
|
onStop() | Allows a component to attach logic to a stop event. For example: public void onStop() { LOG.log(Level.INFO, “onStop() invoked!"); }
Dependencies:
|
onUpdate() | Allows a component to attach logic to an update event. For example: public void onUpdate() { LOG.log(Level.INFO, “onUpdate() invoked!"); }
Dependencies:
|
onUninstall() | Allows a component to attach logic to an uninstall event. The plugin containing the component must be in an active state in order for the public void onUninstall() { LOG.log(Level.INFO, “onUninstall() invoked!"); }
Dependencies:
|
onPurge() | Allows a component to attach logic to a purge event. The plugin containing the component must be in an active state in order for the public void onPurge() { LOG.log(Level.INFO, “onPurge() invoked!"); }
Dependencies: |
onInit() | Allows a component to attach logic to an init event. For example: public void onInit() { LOG.log(Level.INFO, “onInit() invoked!"); }
Dependencies: |
onDestroy() | Allows a component to attach logic to a destroy event. For example: public void onDestroy() { LOG.log(Level.INFO, “onDestroy() invoked!"); }
Dependencies:
onDestroy() |
Plugin Management Console
Another XperienCentral specific tool for plugin management is the Plugin Management console. This is a plugin available from the Configuration > Plugins menu item in XperienCentral. While the Felix Gogo shell provides access to the OSGi bundles only (and is unaware of plugin specific functionality), the Plugins Management Console provides access to plugin-specific lifecycle methods, like purge
. The Plugins Management Console appears as follows:
Felix Gogo Shell
Felix, the OSGi implementation that XperienCentral uses, comes with a shell tool which integrates with Apache Tomcat in order to provide command line access to the OSGi bundles. The tool can be installed the same way you install other plugins.
When the Felix Gogo shell ss running, you can enter commands in the Tomcat console. The most commonly used command is ps
which shows an overview of all installed OSGi bundles and their state. An example of this console is displayed below.
Managing Plugins from the Felix Gogo Shell
In addition to the functionality available for managing plugins from the XperienCentral Plugins Management Console, there are equivalent commands available in the Apache Felix Gogo shell that you can invoke directly from the command line. To use the commands on a plugin in the Apache Felix Gogo shell, you must determine the ID of a plugin by issuing the ps
command. After you issue the ps
command, you will see a list of the plugins as shown in the figure above.
Command | Purpose |
---|---|
wmstart [id] | Registers services that each component within the bundle exposes if all required service dependencies are available. For each component, at the very least a component service is registered |
wmstop | Stops all services registered by the component. |
wmupdate | Updates the plugin to a newer version. If a problem is encountered during the update, the plugin is automatically rolled back to the existing version. The following describes a typical plugin update scenario:
|
wmpurge | Removes all content that was created during and after the installation of the component. |
wmuninstall | Removes all content that was created during the installation of the component.
|
Service Dependencies
A plugin may depend on services exposed by the platform or other plugins. Examples are using the Entity Manager, Data Source Manager or Configuration Management service in a plugin. To use these services a service dependency must be defined in the Activator in order to get the service injected into the component of the plugin. In the dependency you can define whether the availability of the service is required or not. When a component defines a required dependency on a service that is not available, the component will not be started at all. When the dependency is not required, the component will be started but the service will not be injected.
As an example the code snippet below shows how to define a service dependency with the Configuration Management service:
ComponentDependencyImpl serviceDependency = new ComponentDependencyImpl(); serviceDependency.setServiceName(ConfigurationManagement.class.getName()); serviceDependency.setRequired(true); definition.setDependencies(new ComponentDependency[]{serviceDependency});
The definition in the code snippet above is the component definition of the component that uses the service.
The exact classes in which the services are injected and the way they are injected depends on the component that defines the dependency. There are two ways the service might be injected into the component’s classes:
- Using Felix by accessing the field directly. Felix injects the service in the class marked as implementation class (
ComponentDefinitionImpl.setImplementationClassName
) by manipulating a class member of which the type is the same as the one defined by the service dependency. Even if this field is private, Felix will still assign the service to this private field. - Using XperienCentral using a setter for the field in the class marked as instance class in the component definition (
ComponentDefinitionImpl.setInstanceClassName
). XperienCentral will try to find a setter for the field in this class with the same type as the service. If the setter exists, it will invoke that setter with the service.
The table below shows an overview on the service injections for each component type.
Component | Service is injected in | Injection type |
---|---|---|
Element |
| Field access |
Element |
| Setter method |
Panel
|
| Field access |
Panel |
| Setter method |
Media item |
| Field access |
Media item |
| Setter method |
Form |
| Field access |
Form |
| Setter method |
Page metadata |
| Field access |
Page metadata |
| Setter method |
Presentation |
| Field access |
Service |
| Field access |
Servlet |
| Field access |
Servlet | HttpServlet | Setter method |
Using service dependencies as described here is a required guideline for level 1 certification, see guideline G023 of the development guidelines.
Using Services in Static Methods
The service dependencies described above in this topic are always injected on instance fields (non-static). In static methods however, a non-static field cannot be accessed and so such a service dependency cannot be used. Static service injection is not supported, therefore using services acquired from a service dependency cannot be used in a static method.
For this particular use case, the service can be retrieved from the static framework instance.
ConfigurationManagement service = FrameworkFactory.getInstance() .getFramework().getService(ConfigurationManagement.class.getName());
This is only allowed for static methods or any other use case the service cannot be acquired using a service dependency. In all other use cases this is a bad practice and guideline violation (see G023 of the development guidelines).