Hello, OSGi, Part 1: Bundles for beginners

Creating, executing, and managing bundles in an OSGi container

1 2 3 4 5 Page 5
Page 5 of 5

Creating a service factory

In the last section you learned how to use the OSGi framework to create a Java object and register it as service to be consumed by any other bundle. If you look at the HelloServiceActivator.start() method you will notice that we created an object of the HelloServiceImpl class in the start method and registered it under the name of HelloService interface. Thereafter, whenever any bundle asks for the HelloService service, the OSGi container will return the same object.

This implementation will work for most common use cases. But let's say you want to return a different object of the HelloServiceImpl class for every consumer bundle. Alternately, let's say your service object needs to open a connection to a database and you don't want to open the connection unless someone really needs the service.

In either case, the solution is to create a class implementing the ServiceFactory interface and register its object, instead of the actual service object, as a service. Once you do that, your ServiceFactory class will take control whenever any bundle requests the service. ServiceFactory will create a new object of Service for every bundle, and will also delay creation of the actual service until someone really needs it.

Follow these steps to change the implementation of the com.javaworld.sample.HelloService bundle developed in the last section to use a ServiceFactory:

  1. Create the factory class

    HelloServiceFactory.java

    , as shown in Listing 9.

    Listing 9. Create HelloServiceFactory.java

    public class HelloServiceFactory implements ServiceFactory{
        private int usageCounter = 0;
        public Object getService(Bundle bundle, ServiceRegistration registration) {
            System.out.println("Create object of HelloService for " + bundle.getSymbolicName());
            usageCounter++;
            System.out.println("Number of bundles using service " + usageCounter);
            HelloService helloService = new HelloServiceImpl();
            return helloService;
        }
        public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
            System.out.println("Release object of HelloService for " + bundle.getSymbolicName());
            usageCounter--;
            System.out.println("Number of bundles using service " + usageCounter);
        }
    }
    

    The

    ServiceFactory

    interface defines two methods:

    • getService(): The OSGi framework invokes this method the first time the specified bundle requests a service object using the BundleContext.getService(ServiceReference) method. In Listing 9, we use this method to create a different object of HelloServiceImpl for every bundle, and we return that object. The OSGi framework caches the value returned (unless it is null), and will return the same service object on any future calls to BundleContext.getService() from the same bundle.
    • ungetService(): The OSGi container invokes this method when a service has been released by a bundle. The service object may then be destroyed. In Listing 9, we use this method to reduce the usageCount of the service and print the number of clients for the service.
  2. Change HelloServiceActivator.java and modify the start() method of your activator so that it will register objects of ServiceFactory instead of HelloService, as shown in Listing 10:

    Listing 10. Changes to HelloServiceActivator.java

    public class HelloServiceActivator implements BundleActivator  {
        ServiceRegistration helloServiceRegistration;
        public void start(BundleContext context) throws Exception {
            HelloServiceFactory helloServiceFactory = new HelloServiceFactory();
            helloServiceRegistration =context.registerService(HelloService.class.getName(), helloServiceFactory, null);
        }
        public void stop(BundleContext context) throws Exception {
            helloServiceRegistration.unregister();
        }
    }
    

Now try running this sample code. You should notice that it prints the service usage count as one (1) when HelloWorld bundle is started and zero (0) when the HelloWorld bundle is stopped.

Tracking services

In the "OSGi services" section you learned how to search for a service using its interface name. But what happens if more than one bundle registers a service under the same interface name? In that case the OSGi container will return the service with the highest ranking -- that is, the service that is registered with the highest valued SERVICE_RANKING property. If more than one service has an equally valued SERVICE_RANKING property, then the OSGi container will return the service with the lowest PID.

But let's say that you are creating a consumer that needs to know whenever an object is registered or unregistered under a particular interface. In this situation, you should use the ServiceTracker class. Let's see what happens when we change the sample code to utilize a service tracker, as described below.

  1. Change the MANIFEST.MF for your HelloWorld bundle to import the org.osgi.util.tracker package.
  2. Create

    HelloServiceTracker.java

    as shown in Listing 11.

    Listing 11. HelloServiceTracker.java

    public class HelloServiceTracker extends ServiceTracker {
        public HelloServiceTracker(BundleContext context) {
            super(context, HelloService.class.getName(),null);
        }
        public Object addingService(ServiceReference reference) {
            System.out.println("Inside HelloServiceTracker.addingService " + reference.getBundle());
            return super.addingService(reference);
        }
        public void removedService(ServiceReference reference, Object service) {
            System.out.println("Inside HelloServiceTracker.removedService " + reference.getBundle());
            super.removedService(reference, service);
        }
    }
    

    As you can see in the constructor of the

    HelloServiceTracker

    class, we are passing the name of the

    HelloService

    interface to its super class, which is the equivalent of saying that

    HelloServiceTracker

    should track services registered under the

    HelloService

    interface name. The

    HelloServiceTracker

    class extends the

    ServiceTracker

    class and implements two methods:

    • addingService() is called when a bundle registers a service under the given interface name.
    • removedService() is called when a bundle unregisters a service under the given interface name.
  3. Change

    Activator.java

    so that it starts using the

    HelloServiceTracker

    class to manage services instead of looking them up directly, as shown in Listing 12.

    Listing 12. Activator.java uses HelloServiceTracker

    public class Activator implements BundleActivator {
        HelloServiceTracker helloServiceTracker;
        public void start(BundleContext context) throws Exception {
            System.out.println("Hello World!!");
            helloServiceTracker= new HelloServiceTracker(context);
            helloServiceTracker.open();
            HelloService helloService = (HelloService)helloServiceTracker.getService();
            System.out.println(helloService.sayHello());
    
        }
        public void stop(BundleContext context) throws Exception {
            System.out.println("Goodbye World!!");
            helloServiceTracker.close();
        }
    }
    

    In the initial

    start()

    method we first create a

    HelloServiceTracker

    object, then we ask

    HelloServiceTracker

    to start tracking the underlying service. A call to

    getService()

    will now get the

    HelloService

    object.

If you try executing this sample you will notice that whenever you start or stop the HelloService bundle it will result in a call to either the addingService() or removedService() method of the HelloServiceTracker object.

In conclusion

In this first article in the three-part Hello, OSGi series, I've introduced you to the basic concepts of modular application development with OSGi. You've learned about the three currently available open source OSGi containers and walked through the steps to develop a simple component bundle using Equinox, the fully OSGi-compliant Eclipse container. You've also learned how bundles can interact by importing and exporting each other's packages and services.

In this article you may have noticed one of the challenges of developing OSGi bundles: that each of your bundles needs to be aware of OSGi AP. In some development scenarios this could mean writing a lot of infrastructure code. In the next article in the Hello, OSGi series I'll introduce you to the Spring Dynamic Modules for OSGi Service Platforms project, which simplifies development of Spring-based OSGi bundles. See the Resources section in the meantime, to learn more about OSGi.

Learn more about this topic

OSGi open source containers

More

1 2 3 4 5 Page 5
Page 5 of 5