Apache Felix Tutorial Example 1 - Service Event Listener Bundle

This example creates a simple bundle that listens for OSGi service events. This example does not do much at first, because it only prints out the details of registering and unregistering services. In the next example we will create a bundle that implements a service, which will cause this bundle to actually do something. For now, we will just use this example to help us understand the basics of creating a bundle.

A bundle gains access to the OSGi framework using a unique instance of BundleContext. In order for a bundle to get its unique bundle context, it must implement the BundleActivator interface; this interface has two methods, start() and stop(), that both receive the bundle’s context and are called when the bundle is started and stopped, respectively. In the following source code, our bundle implements the BundleActivator interface and uses the context to add itself as a listener for service events (the file for our bundle is called Activator.java):

/*
 * Apache Felix OSGi tutorial.
**/

package tutorial.example1;

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

/**
 * This class implements a simple bundle that utilizes the OSGi
 * framework's event mechanism to listen for service events. Upon
 * receiving a service event, it prints out the event's details.
**/
public class Activator implements BundleActivator, ServiceListener
{
    /**
     * Implements BundleActivator.start(). Prints
     * a message and adds itself to the bundle context as a service
     * listener.
     * @param context the framework context for the bundle.
    **/
    public void start(BundleContext context)
    {
        System.out.println("Starting to listen for service events.");
        context.addServiceListener(this);
    }

    /**
     * Implements BundleActivator.stop(). Prints
     * a message and removes itself from the bundle context as a
     * service listener.
     * @param context the framework context for the bundle.
    **/
    public void stop(BundleContext context)
    {
        context.removeServiceListener(this);
        System.out.println("Stopped listening for service events.");

        // Note: It is not required that we remove the listener here,
        // since the framework will do it automatically anyway.
    }

    /**
     * Implements ServiceListener.serviceChanged().
     * Prints the details of any service event from the framework.
     * @param event the fired service event.
    **/
    public void serviceChanged(ServiceEvent event)
    {
        String[] objectClass = (String[])
            event.getServiceReference().getProperty("objectClass");

        if (event.getType() == ServiceEvent.REGISTERED)
        {
            System.out.println(
                "Ex1: Service of type " + objectClass[0] + " registered.");
        }
        else if (event.getType() == ServiceEvent.UNREGISTERING)
        {
            System.out.println(
                "Ex1: Service of type " + objectClass[0] + " unregistered.");
        }
        else if (event.getType() == ServiceEvent.MODIFIED)
        {
            System.out.println(
                "Ex1: Service of type " + objectClass[0] + " modified.");
        }
    }
}

After implementing the Java source code for the bundle, we must also define a manifest file that contains meta-data needed by the OSGi framework for manipulating the bundle. The manifest is packaged into a JAR file along with the Java class file associated with our bundle; the whole JAR package is actually referred to as a bundle. We create a file called manifest.mf that contains the following:

Bundle-Name: Service listener example
Bundle-Description: A bundle that displays messages at startup and when service events occur
Bundle-Vendor: Apache Felix
Bundle-Version: 1.0.0
Bundle-Activator: tutorial.example1.Activator
Import-Package: org.osgi.framework

Most of the above meta-data is for human consumption and does not affect the OSGi framework. Only the Bundle-Activator and Import-Package meta-data is used by the framework. The Bundle-Activator attribute tells the framework which class implements the BundleActivator interface. With this information, when the OSGi framework starts the bundle, an instance of the specified class is created and its start() method is invoked. The created instance will also have its stop() method called when the framework stops the bundle. The Import-Package attribute informs the framework of the bundle’s dependencies on external packages; all bundles with an activator must import org.osgi.framework since it contains the core OSGi class definitions. Any packages dependencies will be verified and resolved by the OSGi framework. (Note: Make sure your manifest file ends in a trailing carriage return or else the last line will be ignored.)

Now we need to compile the source code. To compile we must have the felix.jar file (found in Felix' bin directory) in our class path. We compile the source file using a command like:

javac -d c:\classes *.java

This command compiles all source files and outputs the generated classes into a subdirectory of the c:\classes directory; this subdirectory is tutorial\example1, named after the package we specified in the source file. For the above command to work, the c:\classes directory must exist. After compiling, we need to create a JAR file containing the generated package directories. We will also add our manifest file that contains the bundle’s meta-data to the JAR file. To create the JAR file, we issue the command:

jar cfm example1.jar manifest.mf -C c:\classes tutorial\example1

This command creates a JAR file using the manifest file we created and includes all of the classes in the tutorial\example1 directory inside of the c:\classes directory. Once the JAR file is created, we are ready to install and start the bundle.

To run Felix, we follow the instructions described in usage.html. When we start Felix, it asks for a profile name, we will put all of our bundles in a profile named tutorial. Now we will install and start our bundle. Assuming that we created our bundle in the directory c:\tutorial, we can install and start it in Felix' shell using the following command:

start file:/c:/tutorial/example1.jar

The above command installs and starts the bundle in a single step; it is also possible to install and start the bundle in two steps by using the Felix install and start shell commands. To stop the bundle, use the Felix stop shell command. Keep in mind, that this bundle will not do much at this point since it only listens for service events and we are not registering any services. In the next example we will register a service that will generate an event for this bundle to receive. To exit Felix, we use the shutdown command.