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
:
-
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 ofHelloServiceImpl
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 toBundleContext.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.
- getService(): The OSGi framework invokes this method the first time the specified bundle requests a service object using the
- Change
HelloServiceActivator.java
and modify thestart()
method of your activator so that it will register objects ofServiceFactory
instead ofHelloService
, 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.
- Change the
MANIFEST.MF
for yourHelloWorld
bundle to import theorg.osgi.util.tracker
package. -
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.
-
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.
This story, "Hello, OSGi, Part 1: Bundles for beginners" was originally published by JavaWorld.