Last updated Dec 8, 2017

Converting from Version 1 to Version 2 (OSGi) Plugins

This page contains guidelines on migrating an existing version 1 plugin to the Atlassian Plugin Framework 2. Version 2 and later of the plugin framework is based on OSGi and Spring Dynamic Modules. The plugin framework has all the advantages of OSGi, plus it will be included into all Atlassian applications. This means that writing plugins for different applications will be more consistent.

Plugins that were written for the old framework will continue to work, but to leverage the new functionality you will need to convert your plugin to the new framework. This page describes how to migrate an existing plugin to the new plugin framework.

Prerequisites

First upgrade the host application to a version which supports version 2 of the plugin framework. For example, upgrade to Confluence 2.10 or later, or JIRA 4.0 or later. See the Plugin Framework Version Matrix.

Summary of How to Convert your Plugin

Summary of the steps required to convert your plugin:

  1. Add the plugins-version="2" attribute in atlassian-plugin.xml:

    1
    2
    <atlassian-plugin name="plugin name" key="plugin key" enabled="true" plugins-version="2">
    
  2. Check that packages used by your plugin are available to OSGi plugins. 

  3. Check that components used by your plugin are available to OSGi plugins. See OSGi, Spring and the Plugin Framework for more information.

The plugin framework handles all the OSGi requirements, such as bundle instructions, manifest, etc. See Going from Plugin to OSGi Bundle. For more complex plugins, you may find it useful to do more complex configuration, such as supplying complete Spring configuration files.

Details of How to Convert your Plugin

1. Set your plugin 'plugins-version' flag to version 2

As described in the documentation there are two types of plugins in the Atlassian Plugin Framework 2:

So the first step of migration is to make your plugin a Version 2 plugin by setting plugins-version="2" attribute in atlassian-plugin.xml:

1
2
<atlassian-plugin name="plugin name" key="plugin key" enabled="true" plugins-version="2">

For the remainder of this document, please consider the following terms synonymous (they mean the same thing): Version 2 plugin and OSGi plugin.

2. Check that packages used by your plugin are available to OSGi plugins

OSGi plugins - plugins with 'plugins-version' set to 2 - are subject to certain restrictions. In particular, an OSGi plugin can access only those external classes that the host application (or other plugins) explicitly exposes. This means that you can no longer assume that all classes on the application's classpath will be accessible to your plugin.

Refer to the list of packages that your host application exposes, and ensure that all classes used by your plugin are covered by this list. Inside the application, this list is configured as a parameter to the packageScanningConfiguration component in the pluginServiceContext.xml file.

It is very important to ensure that plugin code does not depend on packages that are not exposed, as the problem will only manifest itself during runtime.

2.1 Rely on automatic package imports

With the plugin framework, the required packages are imported transparently for OSGi plugins. You do not need to do anything to have required packages imported, but it may help to understand how this works.

Normally, an OSGi bundle needs to explicitly import all packages it needs. To work around this requirement, the plugin framework generates the list of required packages by scanning the class files inside your plugin and determining which packages they use. Once this list of packages is determined, the plugin system generates an OSGi manifest and inserts it into your plugin prior to loading it into the OSGi container. For more information, refer to OSGi Basics and how OSGI bundles are generated.

2.2 Customise package imports and exports with a bundle manifest

If you want to have a full control over what packages are imported by your plugin, you can package the plugin as an OSGi bundle. To do this, you need to specify all necessary entries in a manifest file inside your plugin JAR file. Using the Bundle Plugin for Maven makes the process of generating a valid OSGi manifest much simpler.

You might also want to configure a bundle manifest yourself if you want expose a set of packages as being exported from your plugin.

See Managing Dependencies.

3. Check that components used by your plugin are available to OSGi plugins

The other important restriction for OSGi plugins is that they are only allowed to access those Spring components that are explicitly exposed by the host application or by other plugins.

  • In Confluence, you can find the list of all components that are available to OSGi plugins under http://<baseURL>/admin/pluginexports.action. In this list, each Spring component is listed against the interfaces that it provides. In OSGi, every component must specify the interfaces it provides.
  • Using the OSGi Browser to determine the packages and components exposed by an application.

As with the exposed packages, the list of exposed components attempts to cover all the host application functionality but not to expose all the internals of the application. If your plugin uses the beans that are not exposed you should be able to find an exposed bean that provides the same functionality. As with the packages, this list is intentionally limited to try to improve plugin compatibility across releases of the host application.

It is very important to ensure that plugin code does not depend on beans that are not exposed, as the problem will only manifest itself during runtime. The easiest way to ensure that there are no dependencies on beans which are not exposed is to use constructor injection. Using constructor injection will ensure that the plugin fails during the loading if any of the dependencies are not satisfied.

3.1 Specify qualifiers on ambiguous Spring dependencies

In some cases, the host application exposes more than one bean under the same interface. When this happens, Spring can't determine exactly which bean to use to satisfy a dependency on that interface. For example, there may be two exposed beans that implement the PluginController interface. Spring will fail to inject the right dependency unless you provide a Spring @Qualifier annotation.

The most common example is in Confluence:

1
2
// Confluence has two beans that implement PluginController, so we add a qualifier to specify which one we want
public void setPluginController(@Qualifier("pluginController") PluginController pluginController)
{
this.pluginController = pluginController;
}

3.2 Expose your plugin components to other plugins

In order to make a component in your plugin available to other plugins you can simply add the public="true" attribute to the component in your plugin descriptor file. You will need to specify one or more interfaces under which this bean will be exposed.

1
2
<component key="pluginScheduler" class="com.atlassian.sal.core.scheduling.TimerPluginScheduler" public="true" >

<interface>com.atlassian.sal.api.scheduling.PluginScheduler</interface>

</component>

3.3 Import components exposed by other plugins

Components that are exposed by other plugins are treated a little differently to beans that are exposed by the host application. Your plugin needs to specifically import components which come from other plugins. To do this, include a <component-import> tag inside your atlassian-plugin.xml file.

1
2
<component-import key="loc" interface="com.atlassian.sal.api.license.LicenseHandler" />

You will also need to ensure that the component class is imported, which usually happens transparently.

4. Advanced configuration with Spring configuration files

The new plugin framework provides the ability to create plugin components using complete Spring configuration files. If you provide Spring Dynamic Modules (Spring DM) configuration files in META-INF/spring/, these will be loaded into your plugin OSGi bundle by the Spring DM loader. Using this option for configuration provides you with a lot of flexibility about how your plugin components are created and managed.

1
2
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:osgi="http://www.springframework.org/schema/osgi"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"
  default-autowire="autodetect">

  <beans:bean id="repositoryManager" class="com.atlassian.plugin.repository.logic.ConfluenceRepositoryManager"/>
  ...
</beans:beans>

To include a Spring configuration file, ensure it is included in the META-INF/spring/ directory inside your plugin JAR file. All files matching *.xml inside this directory will be loaded as Spring configuration files when your plugin is loaded. For more details on Spring configuration, see the Spring documentation.

Notes

Useful External Guides

Managing Dependencies
OSGi, Spring and the Plugin Framework
Common Coding Tasks
Confluence Developer Documentation: Converting a Plugin to Plugin Framework 2

Rate this page: