Creating a simple OSGi bundle and running it with Equinox OSGi container

OSGi is a framework for describing a modular system and a service platform for the Java programming language. It implements a complete and dynamic component model 1. This article explains how to create a simple OSGi bundle and run it using Equinox OSGi container: a popular OSGi framework implementation. Before we start, we need to clearly understand what OSGi is and why we need it.

Software components, APIs and dependencies

Normally, a larger application is composed by combining more smaller parts known as software components. Each component is responsible for a cohesive subset of overall application logic. To get the overall application working, these components interact with each other. To do so, each component publishes its own API exposing a set of classes and methods which can be used by other components. Apart from the public API, components may also have other internal classes and methods which are private.

Since there is a dependency between the components, we need to maintain the public API stable and at the same time change the internal implementations freely to address changing requirements. Java by default (at the time of writing) does not provide a structured way to describe software component dependencies and APIs 2.

OSGi and OSGi containers

OSGi framework is described as a set of specifications which enables software components (known as bundles in OSGi specification) to define its own API and dependencies. Apart from this, there are a lot of advantages of using OSGi framework like reduced complexity, reuse, easy deployment, dynamic updates, etc 3.

OSGi framework specification has been implemented in various open-source projects like Equinox, Knopflerfish and Apache Felix 4. These are known as OSGi containers and are used for running the OSGi bundles we create. Eclipse Equinox is used in the Eclipse application development and we will use it during this article as well. Equinox implements all the required features of the latest (at the time of writing) OSGi core framework specification.

OSGi bundles

An OSGi bundle is technically a .jar file with additional meta information 2. This meta information is stored in a manifest file. So, in order to create a bundle we need to create a .jar file with a manifest file inside it. Normally, there exists tools that can be used to generate this manifest file rather than writing it by hand. Maven bundle plugin is one such tool that is used to generate manifest files. In this tutorial we’ll use Maven with the bundle plugin to generate a complete OSGi bundle.

Creating a simple bundle (1/3): The Java file and the Maven project structure

OK, let’s start coding! We are going to create a bundle which prints “Hello World !!” when the bundle is started by the OSGi framework (in our case, Equinox) and “Bye World !!” when the bundle is stopped by the OSGi framework.

To do this, we can create a class which implements the BundleActivator interface in org.osgi.framework and pack it into an OSGi bundle with an appropriate manifest file. BundleActivator interface has two methods: start() and stop(). These two methods are invoked by the OSGi framework when we start and stop the bundle. Below is a very simple Java file (named Activator.java) which implements the BundleActivator interface.

    package org.wso2.simplebundle;

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

    public class Activator implements BundleActivator {

        @Override
        public void start(BundleContext context) throws Exception {
            System.out.println("Hello World!!");
        }

        @Override
        public void stop(BundleContext context) throws Exception {
            System.out.println("Bye World!!");
        }
    }

Note: For the time being we are ignoring the BundleContext object passed to the methods by the OSGi container.

We have finished coding our simple bundle. Now it is all about generating the bundle with the manifest file. Since we are going to do it with Maven, next we will create a Maven project with our Activator.java file.

All Maven projects are required to have a file called pom.xml at the project root. It is an XML file that contains information about the project and configuration details used by Maven to build the project 5. Also, Maven has a standard directory structure that is recommended to be used when creating Maven projects. Read the Maven Getting Started Guide for more details. Below is the directory structure of the Maven project I created with pom.xml (empty for the time being, we will fill it later) and Activator.java files.

    .
    ├── pom.xml
    └── src
        └── main
            └── java
                └── org
                    └── wso2
                        └── simplebundle
                            └── Activator.java

    6 directories, 2 files

Here, src/main/java directory structure comes from the standard Maven project structure and the org/wso2/simplebundle comes from the package definition I used in the Activator.java file. “.” is the project root.

Creating a simple bundle (2/3): Creating the pom file

We have to do two things while creating the pom file: including configuration details for building the project and generating the manifest file correctly. Configuration information is included using standard maven tags and manifest file is generated by setting manifest headers under the Maven bundle plugin section. Following list, extracted from 4 gives a brief introduction to the most commonly used manifest headers.

Here is an example pom file with comments describing what each part does.

    <!-- Mark the start and defines the namespace and schema used in the pom -->
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      
      <!-- POM version, required -->
      <modelVersion>4.0.0</modelVersion>
      
      <!-- Naming the project, required. Normally its a good practice to follow the
      package structure although it is not required -->
      <!-- groupId is unique among an organization -->
      <groupId>org.wso2</groupId>
      <!-- name of the project -->
      <artifactId>simplebundle</artifactId>
      <!-- version of the project -->
      <version>1.0.0</version>
      
      <!-- configure maven to build an OSGi bundle instead of the default .jar -->
      <packaging>bundle</packaging>
      
      <!-- Dependency for our BundleActivator interface -->
      <dependencies>
          <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>6.0.0</version>
          </dependency>
      </dependencies>
      
      <!-- Build the project -->
      <build>
          <plugins>
              <plugin>
                  <!-- Tell maven about the maven-bundle-plugin -->
                  <groupId>org.apache.felix</groupId>
                  <artifactId>maven-bundle-plugin</artifactId>
                  <version>1.4.0</version>      
                  <extensions>true</extensions>
                  <configuration>
                      <!-- When creating the bundle, maven-bundle-plugin decides the classes and
                      resources which should be copied to the bundle depending on this information.
                      Also, we need to state the manifest headers and their values which should be
                      written to the manifest file -->
                      <instructions>
                          <Bundle-SymbolicName>${pom.groupId}.${pom.artifactId}</Bundle-SymbolicName>
                          <Bundle-Name>SimpleBundle</Bundle-Name>
                          <Bundle-Version>${pom.version}</Bundle-Version>
                          <!-- fully qualified class name of the bundle activator -->
                          <Bundle-Activator>org.wso2.simplebundle.Activator</Bundle-Activator>
                          <!-- Ask the maven-bundle-plugin to include the following
                          package as a private package in the bundle -->
                          <Private-Package>org.wso2.simplebundle</Private-Package>
                      </instructions>
                  </configuration>
              </plugin>
          </plugins>
      </build>
      
    </project>

Creating a simple bundle (3/3): Building the Maven project

Now we can build the Maven project by typing the following command at the project root.

    mvn clean install

After successful execution of the above command, You’ll see a directory structure similar to the following in your project root folder.

    .
    ├── pom.xml
    ├── src
    │   └── main
    │       └── java
    │           └── org
    │               └── wso2
    │                   └── simplebundle
    │                       └── Activator.java
    └── target
        ├── classes
        │   ├── META-INF
        │   │   └── MANIFEST.MF
        │   └── org
        │       └── wso2
        │           └── simplebundle
        │               └── Activator.class
        ├── simplebundle-1.0.0.jar
        └── surefire

Note the generated manifest file MANIFEST.MF and the bundle simplebundle-1.0.0.jar under the target folder. If you take a look inside the bundle, you can see that the compiled class file Activator.class and MANIFEST.MF are inside it. We can now install this bundle in an OSGi container and then run it. But before that we need to get an idea about the lifecycle of a bundle.

OSGi Bundle lifecycle

An OSGi bundle lifecycle consists of six states. Below is a state diagram showing the different states and transitions.

OSGi Lifecycle – Source: Wikipedia

Note that there is only a single entrance to the start state and a single end state. Lifecycle of an OSGi bundle starts when someone installs it in an OSGi container. Bundle is in the INSTALLED state after the installation. It is moved to the RESOLVED state after the OSGi container has successfully resolved its code dependencies. A Bundle is in the STARTING state when its BundleActivator.start() method is being called. If the start method finishes without any exceptions, then the bundle is moved to the ACTIVE state. Similarly, a bundle is in STOPPING state when the BundleActivator.stop() method is being called. When the stop method completes, bundle is stopped and moved back to the RESOLVED state. Finally, a bundle is moved to the UNINSTALLED state when someone uninstalls from the OSGi container 7.

Running the simple bundle using Equinox

Lets try to go through the above lifecycle with our simple bundle. First we need to download Equinox OSGi container (or any other container of your choice). You can download Equinox from this site. If you downloaded an Equinox version prior to Equinox 3.8, then the only thing you have to do is to locate the jar file org.eclipse.osgi_<version>.jar and run it using the following command.

    java -jar org.eclipse.osgi_<version>.jar -console

Here <version> is the specific number you find at the end of the downloaded jar file.

If you downloaded Equinox 3.8 or a newer version, you will have to run through some trouble before getting it started. Follow the instructions found here.

if you succeed you’ll be able to see the OSGi prompt as follows.

    osgi>

Type ss at the prompt. You’ll see a list of currently existing bundles with bundle id, state and symbolic name. Now let’s install our bundle. To do this, you have to type

    install file:<path_to_the_simplebundle-1.0.0.jar>

at the OSGi prompt. Replace <path_to_the_simplebundle-1.0.0.jar> with the actual path of the bundle. If you do another ss after this, you will see our simplebundle in installed state.

    osgi> ss
    "Framework is launched."


    id  State       Bundle
    0   ACTIVE      org.eclipse.osgi_3.10.100.v20150529-1857
    1   ACTIVE      org.eclipse.equinox.console_1.1.100.v20141023-1406
    2   ACTIVE      org.apache.felix.gogo.command_0.10.0.v201209301215
    3   ACTIVE      org.apache.felix.gogo.runtime_0.10.0.v201209301036
    4   ACTIVE      org.apache.felix.gogo.shell_0.10.0.v201212101605
    7   INSTALLED   org.wso2.simplebundle_1.0.0

Let’s resolve the bundle by typing,

    resolve <bundle_id>

Replace <bundle_id> with the actual bundle id (7 in this case). You can do a ss to verify that now our bundle is in the RESOLVED state.

Now we can start our bundle by typing,

    start <bundle_id>

You will see the output “Hello World!!”. You can also verify that now our bundle is in ACTIVE state by doing another ss.

Let’s stop the bundle by typing,

    stop <bundle_id>

You will see the bundle printing “Bye World!!”. Again use ss to verify that the bundle is back in the RESOLVED state.

Finally, let’s uninstall our bundle by typing,

    uninstall <bundle_id>

Well that’s all! Go through the references to find out more about OSGi bundles.

References

  1. http://en.wikipedia.org/wiki/OSGi
  2. http://www.vogella.com/tutorials/OSGi/article.html
  3. http://www.osgi.org/Technology/WhyOSGi
  4. http://www.javaworld.com/article/2077837/java-se/hello–osgi–part-1–bundles-for-beginners.html
  5. https://maven.apache.org/guides/introduction/introduction-to-the-pom.html
  6. http://wso2.com/library/tutorials/develop-osgi-bundles-using-maven-bundle-plugin/#bundleplugin
  7. https://osgi.org/javadoc/r4v43/core/org/osgi/framework/Bundle.html
  8. http://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html