Last updated Apr 19, 2024

Detecting the presence or absence of classes in different OSGI bundles

Problems have been found with this approach and it is not recommended. We are currently working on a better solution.

This page is about 'Plugins 2' plugins, i.e. plugins written for version 2 of the Atlassian Plugin Framework. The procedure below will not work with plugins written against version 1 of the plugin framework.

Occasionally you will need to write a plugin that behaves differently depending on the presence or absence of another plugin. An example where we at Atlassian confront this problem is with the AppLinks plugin: if the AppLinks plugin is available, we want to use it to auto-detect known servers and connect to them automatically, but we still want our plugin to behave normally if AppLinks is not present.

There are a few ways to detect whether a specific plugin is present in your host application or not. On this page, we will show you how to add a listener to track when the availability of that plugin changes.

To start with, there is a bit of housekeeping. Add a dependency on spring-extender in your pom.xml:

1
2
<dependency>
  <groupId>org.springframework.osgi</groupId>
  <artifactId>spring-osgi-extender</artifactId>
  <version>1.2.0</version>
  <scope>provided</scope> <!-- Provided by application -->
</dependency>

Add a DynamicImport-Package element to the plugin-info element in atlassian-plugin.xml:

1
2
<plugin-info>
...
  <bundle-instructions>
    ...
    <DynamicImport-Package>com.example.plugin.package*;version="1.2.3",com.example.another.plugins.package*;version="3.1.4"</DynamicImport-Package>
    <!-- packages are separated by commas (,) and package information is separated by semicolons (;) -->
  </bundle-instructions>
</plugin-info>

Wire up the class you will use, in atlassan-plugin.xml:

1
2
<component key="exampleKey" name="Example Name" class="com.example.somepackage.ExampleClass">
  <interface>com.example.somepackage.ExampleInterface</interface>
</component>
    

Now to the meat of it all. The class should implement BundleContextAware and DisposableBean as follows:

ExampleClass.java

1
2
package com.example.somepackage;

import org.springframework.osgi.context.BundleContextAware;
import org.springframework.beans.factory.DisposableBean;

public class ExampleClass implements ExampleInterface, BundleContextAware, DisposableBean
{
        private ServiceTracker tracker = null;
    private boolean exampleIsPresent = false;
    public boolean isExamplePresent() { return exampleIsPresent; }

    /**
     * Set the {@link BundleContext} that this bean runs in. Normally this can
     * be used to initialize an object.
     * 
     * @param bundleContext the <code>BundleContext</code> object to be used
     * by this object
     */
    public void setBundleContext(final BundleContext bundleContext) // comes from BundleContextAware
    {
        ServiceTracker tracker = new ServiceTracker(bundleContext, "com.example.plugin.package.ExampleDependency", new ServiceTrackerCustomizer(){

            public Object addingService(ServiceReference serviceReference)
            {
                final ExampleDependency exampleDependency = (ExampleDependency) bundleContext.getService(serviceReference);
                exampleIsPresent = true;
                return exampleDependency;
                
            }

            public void modifiedService(ServiceReference serviceReference, Object o)
            {
                /* Do nothing for this simple case */
            }

            public void removedService(ServiceReference serviceReference, Object o)
            {
                exampleIsPresent = false;
            }
        });
        
        // Start the tracker. 
        tracker.open();
        this.tracker = tracker;
    }

    /**
     * Invoked by a BeanFactory on destruction of a singleton.
     *
     * @throws Exception in case of shutdown errors.
     *                   Exceptions will get logged but not rethrown to allow
     *                   other beans to release their resources too.
     */
    public void destroy() throws Exception // comes from DisposableBean
    {
        // stick your cleanup code here
        tracker.close();
    }

}

ExampleInterface.java

1
2
package com.example.somepackage;

public interface ExampleInterface{}

Just query the method isExamplePresent to find out if the example dependency is available, and execute different code based on the result.

Advanced Plugin Development FAQ
Set up the Atlassian Plugin SDK and Build a Project

Rate this page: