As a bit of an experiment I tried to create an OSGI Bundle by hand and run it with Apache Felix.

OSGI is a specification for building modular Java applications. Like many things Java it is based on a specification with multiple implementations. Equinox is the implementation that the Eclipse framework is built upon to provide an extensible IDE. Alternatively, there are other implementations such as Felix and Knopflerfish.

As of 2021, OSGI has been around for quite a while. However, my interest has been piqued over the last year since I’ve been working with Eclipse plugins. When working on Eclipse plugins it always felt like Eclipse is doing a lot of the background work. As a result, I thought I would throw out all the tooling and give it a try from scratch.

The Project

This project is meant as a light introduction to OSGI in which we are going to :

  • Create a simple Activator class that is
  • Packaged into an OSGI bundle which is then
  • Loaded into an OSGI Container and then
  • Started and
  • Stopped and finally
  • Uninstalled

If any of those terms seem strange, I’ll be explaining them as I go. Firstly, a bundle is just a special jar file that contains some additional content in the MANIFEST.MF file. This extra metadata allows OSGI containers to install the bundle, check that all dependencies are available and notify the activator that it has started. The activator is a class that implements the BundleActivator interface and is called when the bundle is started and stopped.

First we will create a directory with our project in:

mkdir osgiTest
cd osgiTest
mkdir -p uk/co/andygibson/osgi/helloworld/
mkdir META-INF
<edit> uk/co/andygibson/osgi/helloworld/Activator.java

Substitute <edit> with the editor (atom, code, gedit, nano, notepad, vim etc) of your choice to edit a new file called Activator.java.

Additionally, you should have the java, jar and javac tools on the command line path to be easily called.

The Source

Once you have the Activator.java file in an editor, add the following code:

package uk.co.andygibson.osgi.helloworld;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

    public void start(BundleContext context) throws Exception {
        System.out.println("Starting my activator");
    }
    public void stop(BundleContext context) throws Exception {
        System.out.println("Stopping my activator");
    }
}

This is our activator completed. It is called by the OSGI container when our bundle is started and stopped. The activator is referenced from the META-INF/MANIFEST.MF file which we’ll edit next:

Bundle-Bundle-RequiredExecutionEnvironment: JavaSE-1.8	
Bundle-SymbolicName: uk.co.andygibson.osgi.helloworld
Bundle-Bundle-Version: 1.0.0
Bundle-Name: Hello World App
Bundle-ManifestVersion: 2
Bundle-Classpath: .
Bundle-Activator: uk.co.andygibson.osgi.helloworld.Activator
Export-Package: uk.co.andygibson.osgi.helloworld
Import-Package: org.osgi.framework;version=1.3.0

These attributes define our Bundle, the version of java it requires and any dependencies we need and also the location of our activator class.

The Build

We are going to use plain old command line tools to compile our Bundle – I did say it was OSGI by hand. We need to :

  • Compile our activator
  • Package our activator into a jar file with our MANIFEST.MF

In order to compile, we’ll need a copy of the org.osgi.framework jar dependency. There are a couple of ways of doing this, you can download it from somewhere, or search for a local copy. You might even have one in your local maven repository which is where I got mine. Once you have a copy, you can add it to the following line to compile your activator:

javac -cp org.osgi.framework-1.9.0.jar uk/co/andygibson/osgi/helloworld/Activator.java

Once completed, we have class file in the uk/co/andygibson/osgi/helloworld folder. To jar it up use the jar command to create a jar file containing our class and the MANIFEST.MF file.

jar cvfm uk.co.andygibson.osgi.helloworld.1.0.jar META-INF/MANIFEST.MF uk/co/andygibson/osgi/helloworld/Activator.class 

The Execution

In our directory , we have a jar file that contains our class and the MANIFEST.MF. This is now a bundle which can be loaded into an OSGI container. For this, we will use Felix. Go to the Downloads page and download a zipped version of the the framework. It should be at the top of the page under “Felix Framework Distribution”. Download the zip binary, and unzip it in a folder.

Open a terminal and go to the folder containing Felix and start it by typing:

java -jar bin/felix.jar

You should end up with a felix console that will let you query the OSGI container. Try typing lb:

g! lb
START LEVEL 1
   ID|State      |Level|Name
    0|Active     |    0|System Bundle (7.0.1)|7.0.1
    1|Active     |    1|jansi (1.18.0)|1.18.0
    2|Active     |    1|JLine Bundle (3.13.2)|3.13.2
    3|Active     |    1|Apache Felix Bundle Repository (2.0.10)|2.0.10
    4|Active     |    1|Apache Felix Gogo Command (1.1.2)|1.1.2
    5|Active     |    1|Apache Felix Gogo JLine Shell (1.1.8)|1.1.8
    6|Active     |    1|Apache Felix Gogo Runtime (1.1.4)|1.1.4

Starting the Bundle

Now lets install and start our bundle. In the Felix console, type the following:

g! felix:install /home/andy/dev/projects/osgiTest/uk.co.andygibson.osgi.helloworld.1.0.jar
Bundle ID: 12
g! start 12
Starting my activator

Finally, we’ve installed the bundle, for which we received a bundle id (12). We then used that to start the bundle which caused the activator message to display.

If you type lb again, you will see the similar list of bundles, but our bundle will be included there now:

START LEVEL 1
   ID|State      |Level|Name
    0|Active     |    0|System Bundle (7.0.1)|7.0.1
    1|Active     |    1|jansi (1.18.0)|1.18.0
    2|Active     |    1|JLine Bundle (3.13.2)|3.13.2
    3|Active     |    1|Apache Felix Bundle Repository (2.0.10)|2.0.10
    4|Active     |    1|Apache Felix Gogo Command (1.1.2)|1.1.2
    5|Active     |    1|Apache Felix Gogo JLine Shell (1.1.8)|1.1.8
    6|Active     |    1|Apache Felix Gogo Runtime (1.1.4)|1.1.4
   12|Active     |    1|Hello World App (0.0.0)|0.0.0

If you type felix:stop 12 you will see the close message in the console. This is because it stops our bundle and causes the Activator stop method to be called.

You can start and stop the bundle as many times as you want. Additionally, you can even start and stop Felix and see that the start amd stop activator methods are called when we start and stop the container.

g! exit 0
Stopping my activator
...
...
java -jar bin/felix.jar 
Starting my activator
____________________________
Welcome to Apache Felix Gogo

g!

The Tooling

Without a doubt, its easy to see why we use tooling to create our java apps. This is just a couple of files and is quite a task. Traditional tools like Maven or Gradle use repositories to supply us with dependencies on demand based on the contents of their source files, pom.xml for example.

However, if we were to use maven we would specify our dependencies in the pom file. At the same time, it would mean we need to manually keep dependencies in the MANIFEST.MF file up to date. Unsurprisingly, this is where special tools like Tycho, BND and Eclipse plugin projects come in. They work with the manifest file and use that to provide the list of dependencies (so called Manifest first tools like Tycho or Eclipse). Others can build up the manifest file based on the configuration of the tool (i.e. BND) to build dependencies that way.