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.
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 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.
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.
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.
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.
Bundle-SymbolicName:
The Bundle-SymbolicName header specifies a unique, non-localizable name for the bundle. This is the name you will use while referring a given bundle from other bundles.
Bundle-Name:
The Bundle-Name header defines a short, human-readable name for the bundle. I have set this to SimpleBundle
in our pom.xml
file.
Bundle-Version:
The Bundle-Version header specifies the version of the bundle.
Bundle-Activator:
The Bundle-Activator header specifies the name of the optional listener class to be notified of bundle start and stop events. To get our programme working, we should set this to org.wso2.simplebundle.Activator
.
Private-Package, Export-Package:
The Export-Package and Private-Package instructions tell the plugin about the contents of the resulting bundle JAR file 8. Export-Package tells the plugin which packages to copy into the bundle and export while Private-Package instruction indicates which of the available packages to copy into the bundle but not export 8. In our example, I have asked the plugin to add org.wso2.simplebundle
as a Private-Package. Otherwise, it won’t be copied to the final bundle as the default values are empty 6.
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>
Now we can build the Maven project by typing the following command at the project root.
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.
An OSGi bundle lifecycle consists of six states. Below is a state diagram showing the different states and transitions.
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.
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.
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.
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
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,
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,
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,
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,
Well that’s all! Go through the references to find out more about OSGi bundles.