Example 2 - Dictionary Service Bundle

This example creates a bundle that implements an OSGi service. Implementing an OSGi service is a two-step process, first we must define the interface of the service and then we must define an implementation of the service interface. In this particular example, we will create a dictionary service that we can use to check if a word exists, which indicates if the word is spelt correctly or not. First, we will start by defining a simple dictionary service interface in a file called DictionaryService.java:

/*
 * OSGi and Gravity Service Binder tutorial.
 * Copyright (c) 2003  Richard S. Hall
 * http://oscar-osgi.sourceforge.net
**/

package tutorial.example2.service;

/**
 * A simple service interface that defines a dictionary service.
 * A dictionary service simply verifies the existence of a word.
**/
public interface DictionaryService
{
    /**
     * Check for the existence of a word.
     * @param word the word to be checked.
     * @return true if the word is in the dictionary,
     *         false otherwise.
    **/
    public boolean checkWord(String word);
}

The service interface is quite simple, with only one method that needs to be implemented. Notice that we put the service interface in the package tutorial.example2.service, instead of just putting it in tutorial.example2. We did this because we need to share the interface definition with other bundles, therefore it is better to separate service interfaces that need to be shared from code that does not need to be shared. Such an approach ensures a strong separation between interface and implementation.

In the following source code, the bundle uses its bundle context to register the dictionary service. We implement the dictionary service as an inner class of the bundle activator class, but we could have also put it in a separate file. The source code for our bundle is as follows in a file called Activator.java:

/*
 * OSGi and Gravity Service Binder tutorial.
 * Copyright (c) 2003  Richard S. Hall
 * http://oscar-osgi.sourceforge.net
**/

package tutorial.example2;

import java.util.Properties;

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

import tutorial.example2.service.DictionaryService;

/**
 * This class implements a simple bundle that uses the bundle
 * context to register an English language dictionary service
 * with the OSGi framework. The dictionary service interface is
 * defined in a separate class file and is implemented by an
 * inner class.
**/
public class Activator implements BundleActivator
{
    /**
     * Implements BundleActivator.start(). Registers an
     * instance of a dictionary service using the bundle context;
     * attaches properties to the service that can be queried
     * when performing a service look-up.
     * @param context the framework context for the bundle.
    **/
    public void start(BundleContext context)
    {
        Properties props = new Properties();
        props.put("Language", "English");
        context.registerService(
            DictionaryService.class.getName(), new DictionaryImpl(), props);
    }

    /**
     * Implements BundleActivator.stop(). Does nothing since
     * the framework will automatically unregister any registered services.
     * @param context the framework context for the bundle.
    **/
    public void stop(BundleContext context)
    {
        // NOTE: The service is automatically unregistered.
    }

    /**
     * A private inner class that implements a dictionary service;
     * see DictionaryService for details of the service.
    **/
    private static class DictionaryImpl implements DictionaryService
    {
        // The set of words contained in the dictionary.
        String[] m_dictionary =
            { "welcome", "to", "the", "osgi", "tutorial" };

        /**
         * Implements DictionaryService.checkWord(). Determines
         * if the passed in word is contained in the dictionary.
         * @param word the word to be checked.
         * @return true if the word is in the dictionary,
         *         false otherwise.
        **/
        public boolean checkWord(String word)
        {
            word = word.toLowerCase();

            // This is very inefficient
            for (int i = 0; i < m_dictionary.length; i++)
            {
                if (m_dictionary[i].equals(word))
                {
                    return true;
                }
            }
            return false;
        }
    }
}

Note that we do not need to unregister the service in the stop() method, because the OSGi framework will automatically do so for us. The dictionary service that we have implemented is very simple; its dictionary is a static array of only five words, so this solution is not optimal and is only intended for educational purposes. We must create a manifest.mf file that contains the metadata for our bundle; the manifest file contains the following:

Bundle-Activator: tutorial.example2.Activator
Export-Package: tutorial.example2.service
Bundle-Name: English dictionary
Bundle-Description: A bundle that registers an English dictionary service
Bundle-Vendor: Richard Hall
Bundle-Version: 1.0.0

As before, much of the metadata in the manifest file above is for human consumption. We specify which class implements the Bundle-Activator interface and also specify that our bundle exports a shared package using the Export-Package attribute. This attribute makes it possible for other bundles to import our dictionary service interface.

To compile our source code, we need to have the osgi.jar file (found in Oscar's lib 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\example2, 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 metadata to the JAR file. To create the JAR file, we issue the command:

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

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

To run Oscar, we follow the instructions described in usage.html. When we start Oscar, it asks for a profile name, we will put all of our bundles in a profile named tutorial. After running Oscar, we should make sure that the bundle from Example 1 is active. We can use the ps Oscar shell command to get a list of all bundles, their state, and their bundle identifier number. If the Example 1 bundle is not active, we should start the bundle using the start command and the bundle's identifier number that is displayed by the ps command. Now we can install and start our dictionary service bundle. Assuming that we created our bundle in the directory c:\tutorial, we can install and start it in Oscar's shell using the following command:

    start file:/c:/tutorial/example2.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 install and start Oscar shell commands. To stop the bundle, use the stop Oscar shell command. If the bundle from Example 1 is still active, then we should see it print out the details of the service event it receives when our new bundle registers its dictionary service. Using the Oscar shell ps command to get the bundle identifier number for our dictionary service bundle and we can stop and restart it at will using the stop and start commands, respectively. Each time we start and stop our dictionary service bundle, we should see the details of the associated service event printed from the bundle from Example 1. In Example 3, we will create a client for our dictionary service. To exit Oscar, we use the shutdown command.