Enterprise Java

OSGi: An Introduction

OSGi, created for Java-based systems, provides a framework for modular systems. OSGi makes it possible to define the dependencies of each individual module with the others and enables users to control the lifecycle and dynamically change each component of the system.

OSGi is a specification and the most common implementations can be counted as Equinox, Apache Felix and Knoplerfish. In this article, I will try to give an example of creation of a simple OSGi bundle in Equinox.

OSGi Structure
 
Basic OSGi structure can be seen in figure on the right side. OSGi implementations sits on the top of the JVM and provide mechanisms for service management, component definition, execution, management and life cycle control of modules. The most essential OSGi concepts are described below:

Bundle

In OSGi systems, the components that builds the structure are given the name, “Bundle“. On the deployment stage, each OSGi bundle is a jar file. But the main difference of bundle jar files with regular ones can be counted as the OSGi specific manifest definition and some OSGi specific classes. We will discuss these differences in oncoming sections and in the example.

Services

Services provide the interaction between the bundles of the structure. Services are exposed as interfaces and registered with an implementation that performs that interface. In parallel with SOA structure, the being of access through OSGi services makes OSGi based systems more loose-coupled compared to regular jar based java structures. These structure also makes it possible to change change components of the system in runtime.

OSGi implements a service directory for services to be registered and accessed through. OSGi also provides mechanisms for management of the services.

Life Cycle

OSGi provides a platform which allows to control the life cycle of bundles. In this structure, each bundle has its own OSGi configuration mainly in terms of dependencies and exposed parts and the system is run by the OSGi itself. OSGi knows the bundles that compose the system which is given with a configuration file with an order and the life cycle management is applied for each of the components with given order. The bundle side of the life cycle management is controlled by the “Activator” class which implements and OSGi interface that has to exist in every “regular” OSGi bundle. (Not for “fragment” ones, but this is beyond the scope of this article for now, forget about that)

Bundles

As mentioned above, a bundle is a jar file which has at least an Activator class and a MANIFEST file that has OSGi specific headers and informations in. A sample MANIFEST file can be seen below. Let’s look at the meaning of each part in definition.

Bundle-Name: Our Bundle
Bundle-SymbolicName: us.elron.bundles.ours
Bundle-Description: Very own bundle of ours
Bundle-ManifestVersion: 1
Bundle-Version: 1.0.0
Bundle-Activator: us.elron.bundles.ours.BundleActivator
Export-Package: us.elron.bundles.ours.exported; version = "1.0.0"
Import-Package: us.elron.bundles.yours.exported; version = "1.3.0"
  • Bundle-Name: The “appealing to the public” name of the bundle.
  • Bundle-SymbolicName: As the only mandatory definition in the MANIFEST file, symbolic name defines the unique name of the bundle in OSGi ecosystem. As this definition should be unique, it is generally defined as the base package name of the bundle by convention.
  • Bundle-Description: The description about the “raison d’être” of the bundle.
  • Bundle-ManifestVersion: The manifest version of the bundle.
  • Bundle-Version: OSGi bundle version.
  • Bundle-Activator: This class is used to control the bundle’s life cycle. This class is called by the OSGi to start or stop the bundle.
  • Export-Package: Packages that is wanted to be used by other bundles are defined in this section.
  • Import-Package: Packages that is needed for the execution of current bundle are defined in this section.

Life Cycle

OSGi structure provides the necessary mechanisms to control the life cycle of the bundles. Bundles are subject to the OSGi for control of their life cycles in accordance with the configuration given. This life cycle steps are explained in detail below :

COMPONENT STATUS
DESCRIPTION
INSTALLED
This state indicates that the installation step has been successfully completed. In this case, neither dependency analysis nor the class loading is made. Only required steps are performed, such as defining bundle properties analysing its Manifest file.
RESOLVED
Bundle is found in this state when OSGi resolves and satisfies all of its dependencies and makes class loading operations. This is the state that comes before starting and after stopping.
STARTING
This is the state that bundle is found when the “start” method of the Activator of the bundle is called, but not yet as successfully or unsuccessfully finished.
ACTIVE
The bundle is successfully started and running meaning the “start” method of the Activator resulted success.
STOPPING
This is the state that bundle is found when the “stop” method of the Activator of the bundle is called, but not yet as successfully or unsuccessfully finished.
UNINSTALLED
This is the state when the bundle is removed from the system. In this situation, there is no transition to another state. The component must be installed again.
The transitions between described life cycle steps can be seen in the figure above.

Let’s make a simple example to clarify mentioned concepts and steps above. In our example, there will be two bundles, one of which gives a random number generator service to generate random numbers, and the other bundle will use this service to print a random number each second with a seperate process. (Doesn’t make sense ? Same for me, but enough to grasp the concepts :) )

Now let’s develop this sample project (preferably) together using Eclipse and Equinox.
In Eclipse, OSGi bundles are developed using New Plug-in Project wizard as seen below:

Using the wizard create two projects (us.elron.osgi.random and us.elron.osgi.user), following required steps and name your bundles and Activators (RandomActivator, UserActivator) as seen below. The ultimate result of the project also should be like that :

The service definitions, implementations and MANIFEST definitions of the Random number generating bundle (us.elron.osgi.random) are given below.
Interface (IRandomGenerator):

package us.elron.osgi.random;

public interface IRandomGenerator {

    int generate ();

    int generate(int upperBound);

}

Service (RandomGenerator):

package us.elron.osgi.random.generator;

import java.util.Random;

import us.elron.osgi.random.IRandomGenerator;

public class RandomGenerator implements IRandomGenerator {

    private final Random random;

    public RandomGenerator () {
        this.random = new Random();
    }

    @ Override
    public int generate () {
        return this.random.nextInt();
    }

    @ Override
    public int generate (final int upperBound) {
        return this.random.nextInt (upperBound);
    }

}

Activator (RandomActivator) :

package us.elron.osgi.random;

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

import us.elron.osgi.random.generator.RandomGenerator;

public class RandomActivator implements BundleActivator {

    public void start(final BundleContext context) throws Exception {
        System.out.println("[Random] Let's 'Random'!");
        RandomGenerator randomGenerator = new RandomGenerator();
        context.registerService(IRandomGenerator.class.getName (), randomGenerator, null);
        System.out.println("[Random] Random services were registered.");
    }

    public void stop(final BundleContext context) throws Exception {
        System.out.println("[Random] Bundle is being stopped !");
    }

}

MANIFEST.MF description of the component will be as follows. A bundle should at least export packages that have its service interfaces in order to make other bundles use of them. Because loose coupling is one of the most important goals of SOA and OSGi systems, only the minimum required set of classes should be exported from any bundle.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Random
Bundle-SymbolicName: us.elron.osgi.random
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: us.elron.osgi.random.RandomActivator
Bundle-Vendor: ELRON.US
Require-Bundle: org.eclipse.core.runtime
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Export-Package: us.elron.osgi.random

As can be seen, a service is an implementation of a java interface registered as an OSGi service. Activator class is the OSGi access point of a bundle. OSGi uses Activator class of the bundle in order to manage its life-cycle. While doing that, OSGi sends an implementation of “org.osgi.framework.BundleContext” interface to the bundle. This interface enables bundle to interact with the OSGi layer and as can be seen in code, to make operations such as registering and getting an OSGi service.
Now let’s look at the user bundle classes:
This is the class which prints random numbers that are generated by the random generator service.

package us.elron.osgi.user;

import us.elron.osgi.random.IRandomGenerator;

public class RandomPrinter extends Thread {

    private final IRandomGenerator random;
    private volatile boolean run = true;

    public RandomPrinter (final IRandomGenerator random) {
        this.random = random;
    }

    @ Override
    public void run () {
        while (this.run) {
            System.out.println ("[User] new random number: " + this.random.generate (300));
            try {
                Thread.sleep (1000);
            } catch (final InterruptedException e) {
                break;
            }
        }
        System.out.println ("[User] The process was terminated.");
    }

    public void close () {
        this.run = false;
    }

}

and this is the Activator implementation:

package us.elron.osgi.user;

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

import us.elron.osgi.random.IRandomGenerator;

public class UserActivator implements BundleActivator {

    private RandomPrinter randomPrinter;

    public void start (final BundleContext context) throws Exception {
        System.out.println ("[User] Here we go ..");
        ServiceReference randSrvRef = context.getServiceReference (IRandomGenerator.class.getName ());
        IRandomGenerator randService = (IRandomGenerator) context.getService (randSrvRef);
        if (randService == null) {
            throw new Exception ("[User] Error! Random service could not be found!");
        }
        this.randomPrinter = new RandomPrinter(randService);
        this.randomPrinter.start();
    }

    public void stop (final BundleContext bundleContext) throws Exception {
        System.out.println ("[User] finish ..");
        this.randomPrinter.close ();
    }

}

The MANIFEST.MF description of the “user” bundle will be as follows. We should define the dependency with the “us.elron.osgi.random” package of random generator bundle in which random service interface resides. Dependencies can be defined at the level of bundle or package, however, to reduce the dependencies between bundles, package level dependency is better be preferred as much as possible.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: User
Bundle-SymbolicName: us.elron.osgi.user
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: us.elron.osgi.user.UserActivator
Bundle-Vendor: ELRON.US
Require-Bundle: org.eclipse.core.runtime
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Import-Package: us.elron.osgi.random

To run these projects on OSGi using Eclipse, a run configuration should be defined as can be seen below. From “Run (or Debug) Configurations” step, under OSGi Framework, a new configuration should be created (with a right click) and our new bundles should be selected in this configuration. To supply the required dependencies for selected bundles, we can use “Add Required Bundles ” button. In this way, Eclipse will resolve the dependency hierarchy and add the required bundles for selected ones. We should also define the start order of the bundles. This order should be defined in accordance with the dependencies of the bundles. Dependent bundles should start after the bundles they depend. So, in our example, we will set the level of “us.elron.osgi.random” as 1, while setting the “us.elron.osgi.user” as 2.

Running the project with this shape generates an output like as follows :

OSGi> [Random] Let's 'Random'!
[Random] Random services were registered.
[User] Here we go ..
[User] new random number: 38
[User] new random number: 250
[User] new random number: 94
[User] new random number: 150
[User] new random number: 215
[User] new random number: 124
[User] new random number: 195
[User] new random number: 260
[User] new random number: 276
[User] new random number: 129

OSGi runtime provides a console interface for us to interact with itself. When running the Console application window, we see a “osgi>” the script, says we can access the console. After mentioning about a few important commands that you can execute in console, I will leave you alone with the console for you to discover what can be done there, starting with the “help” command.

“ss” command, shows all the components registered to the OSGi with their id, state and bundle name values along with version part. id value indicates a unique identifier given by OSGi to every bundle. This number stays the same in a JVM execution even if the bundle uninstalled and installed again (one thing to discover) but can be changed in a new execution. Status values indicates the status of the bundle (detailed and explained in the table above), and the name and version values indicates what their names evokes to us. For the current system, the output of “ss” the command is as follows:

OSGi> ss

Framework is launched.

id State Bundle
0  ACTIVE org.eclipse.osgi_3.6.0.v20100517
          Fragments = 4
2  ACTIVE org.eclipse.core.jobs_3.5.0.v20100515
3  ACTIVE javax.servlet_2.5.0.v200910301333
          Resolved javax.transaction_1.1.1.v201006150915 4
          Master = 0
5  ACTIVE org.eclipse.core.runtime_3.6.0.v20100505
6  ACTIVE org.eclipse.equinox.preferences_3.3.0.v20100503
7  ACTIVE org.eclipse.osgi.services_3.2.100.v20100503
8  ACTIVE org.eclipse.core.runtime.compatibility.auth_3.2.200.v20100517
9  ACTIVE us.elron.osgi.random_1.0.0.qualifier
          Resolved org.eclipse.core.runtime.compatibility.registry_3.3.0.v20100520 10
          Master = 11
11 ACTIVE org.eclipse.equinox.registry_3.5.0.v20100503
          Fragments = 10
12 ACTIVE org.eclipse.equinox.app_1.3.0.v20100512
13 ACTIVE org.eclipse.equinox.common_3.6.0.v20100503
14 ACTIVE org.eclipse.core.contenttype_3.4.100.v20100505 14-1235
15 ACTIVE us.elron.osgi.user_1.0.0.qualifier
OSGi>

Let’s suppose that we want to turn off our User bundle. In this case, we need to execute “stop” command with the id of the bundle we want to stop (in this case 15).

[User] a new random number is: 48
[User] a new random number is: 49
OSGi> stop 15
[User] finish ..
[User] The process was terminated.

When we look at the output of “ss” command again,

Framework is launched.

id State Bundle
0  ACTIVE org.eclipse.osgi_3.6.0.v20100517
          Fragments = 4
2  ACTIVE org.eclipse.core.jobs_3.5.0.v20100515
3  ACTIVE javax.servlet_2.5.0.v200910301333
          Resolved javax.transaction_1.1.1.v201006150915 4
          Master = 0
5  ACTIVE org.eclipse.core.runtime_3.6.0.v20100505
6  ACTIVE org.eclipse.equinox.preferences_3.3.0.v20100503
7  ACTIVE org.eclipse.osgi.services_3.2.100.v20100503
8  ACTIVE org.eclipse.core.runtime.compatibility.auth_3.2.200.v20100517
9  ACTIVE us.elron.osgi.random_1.0.0.qualifier
          Resolved org.eclipse.core.runtime.compatibility.registry_3.3.0.v20100520 10
          Master = 11
11 ACTIVE org.eclipse.equinox.registry_3.5.0.v20100503 Fragments = 10
12 ACTIVE org.eclipse.equinox.app_1.3.0.v20100512
13 ACTIVE org.eclipse.equinox.common_3.6.0.v20100503
14 ACTIVE org.eclipse.core.contenttype_3.4.100.v20100505-1235
15 RESOLVED us.elron.osgi.user_1.0.0.qualifier

we see that the state of the User bundle with id 15 is Resolved (see Life Cycle section). Likewise, we can execute start command (start 15) to start the bundle and observe the process began to work again, or execute “s” command to see all services registered to OSGi or use uninstall command to remove a bundle from OSGi. You are free to discover !

In this article, I tried to simply explain what OSGi is, how it works and what can be done with it. Hope you enjoy it. You can download sources here.

Feel free to comment or contact via elron[at]elron.us. I will be glad to hear from you.

Reference: OSGi: An Introduction  from our JCG partner Elron at the Ender Ayd?n Orak blog.

Related Articles :

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

9 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Wojciech Owczarczyk
Wojciech Owczarczyk
12 years ago

Very nice! Keep up the good stuff!

Monis Iqbal
Monis Iqbal
12 years ago

Thanks for the nice and easy explanation :)

I was following the example and found some inconsistencies:
1. In the package explorer RandomPrinter is under the 
us.elron.osgi.user package whereas in the code for the class its package is us.elron.osgi.random.2. Method parameter of the method ‘start’ for both the Activators is  ‘bundleContext’ whereas variable ‘context’ is being used in the method’s body.3. Catch in the RandomPrinter class is with capital C.

Am I correctly identifying these? because I am unable to run the example.

José Luís Sanchez
José Luís Sanchez
12 years ago

Quite interesting introduction to OSGI, yes .. it really deserves a look :-)

Btw, i believe there’s a typo in the code .. everywhere you reference ‘context’ , i believe you are using the ‘bundlecontext’ object sent as parameter .. am i right ?

Thanks a lot !

Ajay Kumar
12 years ago

I believe there is an error in RandomActivator.java:14, it should be just:

 this.randomGenerator = new RandomGenerator();

Shahzada Hatim
12 years ago

I had some unwanted exception, namely java.lang.IllegalStateException: Workbench has not been created yet. coming up in my eclipse console when following this example. I followed this (http://stackoverflow.com/questions/3447419/starting-osgi-bundle) to make it work.

Sk Tanbir Ahmed
Sk Tanbir Ahmed
9 years ago

Good article for new bee….

Ömer Bayram
Ömer Bayram
8 years ago

Very nice explanation, Thanks.

Could you please update the links to the images.
The first two images were easy to find, but I couldn’t find the others…

Munish Gupta
Munish Gupta
8 years ago

good information in precise manner..

Munish Gupta
Munish Gupta
8 years ago

BTW, images I could not find out. can you please publish the links for the same.

Back to top button