Building applications with OSGI framework

OSGI is a framework used in building complex Java applications that contain multiple components. With OSGI, you can build a Java application that can load dynamically. When you are building a basic Java application, you might create a jar file for each module and load it with the classpath. But, with the OSGI framework, you can load OSGI jar files dynamically whenever you needed. Let's discuss some of the terminologies and concepts that you should know about OSGI framework:

Bundle

Bundle is the OSGI module that contains all your logic. The bundle is a jar file that contains your business logic that provides some service. Other than a regular jar file, it also contains information about how it is supposed to work in OSGI runtime in MANIFEST.MF file. This information includes a bundle name, version, exported and imported libraries. Following is a sample definition of OSGI bundle definition:

 Bundle-Name: Hello World
 Bundle-SymbolicName: org.dhanushka.helloworld
 Bundle-Description: A Hello World bundle
 Bundle-ManifestVersion: 2
 Bundle-Version: 1.0.0
 Bundle-Activator: org.dhanushka.Activator
 Export-Package: org.dhanushka.helloworld;version="1.0.0"
 Import-Package: org.osgi.framework;version="1.3.0"
  • Bundle-name is used to identify the bundle. This is just an optional string value.
  • Bundle-SymbolicName is a unique identifier for the bundle. This follows the common java package naming standard.
  • Bundle-Description is a description of what this bundle does.
  • Bundle-ManifestVersion is versioning used by OSGI framework to read bundle manifest.
  • Bundle-Version is the version number you allocated for the OSGI bundle.
  • Bundle-Activator is the starting point of your module. A bundle can have multiple class implementations. OSGI uses this property to mark what is the class that used as the starting point of the bundle. We will discuss the bundle activator more later.
  • Export-Package is a set of packages that you need to expose to other OSGI bundles. If you didn't specify export packages, then other OSGI bundles are unable to access classes in your OSGI bundle.
  • Import-Package is a set of packages required for your OSGI bundle. You should import packages here if you need to refer to any other OSGI bundles.

The bundle is simply a module that contains all of your programming logic. You can import another bundle functionality into it and expose your own functionality to other bundles.

Services

Services are the way how each of the bundles are connected together. As I have already explained, multiple bundles can be connected together to provide some service. Check the following sample bundle implementation that has activation method on it:

public class Activator implements BundleActivator {
    private BundleContext context;

    @Override
    public void start(BundleContext context) throws Exception {
        System.out.println("Starting");
        this.context = context;
    }

    @Override
    public void stop(BundleContext context) throws Exception {
        System.out.println("Stopping");
        this.context = null;
    }
}

In this source code, the bundle activator is the Activator class. When the OSGI starting up it starts executing the start method. This is much similar to the main method. When the OSGI framework stopping, it triggers the stop method. Whenever you add this bundle into the OSGI runtime, it will start execution by calling the start method and end its execution by calling the stop method. As you can see, this bundle does not have any dependency on other OSGI bundles. It can start its execution independently. In some scenarios, you might need to start this bundle when some conditions are met. Until the condition satisfies, OSGI runtime does not call the start method. This can be achieved with references. References let you call start method once given conditions are met. Check the following modification done to the Activator class:

public class Activator implements BundleActivator {
    private BundleContext context;

    @Override
    public void start(BundleContext context) throws Exception {
        System.out.println("Starting");
        this.context = context;
    }
        @Reference(
            name = "activator.service",
            service = ActivatorService.class,
            cardinality = ReferenceCardinality.MANDATORY,
            policy = ReferencePolicy.DYNAMIC,
            unbind = "unsetActivatorService")
    protected void setActivatorService(ActivatorService activatorService) {
            // Set values
     }

    protected void unsetActivatorService(ActivatorService activatorService) {
            // Unset values
    }

    @Override
    public void stop(BundleContext context) throws Exception {
        System.out.println("Stopping");
        this.context = null;
    }
}

In this code, other than the previous start and stop methods, there are another two methods. @Reference annotation is used here to mark these dependencies. Here, we have given a service name and service class. Under the reference annotation, we defined the cardinality as mandatory. Since the cardinality is marked as mandatory, to trigger start function, setActivatorService should be satisfied. If the cardinality is optional, then it is not mandatory to call setActivatorService to start this bundle. This method can be triggered from another bundle. Following is a sample code, how to trigger setActivatorService method.

context.getBundleContext().registerService(ActivatorService.class.getName(), activatorService, null);

Here, the activatorService is the object that contains details that need to send to the setActivatorService. Once this method gets called, OSGI bundle will start the ActivatorService start method.

You can build more advanced applications by using the OSGI framework. You build dynamically pluggable components with dependency injection design patterns.