Wednesday, March 19, 2008

ServiceMediator - Mediate your OSGi services

An essential element of the OSGi platform is the lifecycle layer, which is in charge of the lifecycle of OSGi bundles. The lifecycle of a bundle as defined by the framework (>=R3) can be captured in the following UML state diagram:



Basically it means that one can install, uninstall, update, start and stop bundles at run-time, without taking down the container or the corresponding JRE. Beside the fact that this is one of the great features of OSGi, it has an important practical aspect at the framework's service layer:
Dynamic service and service model references: services may come and go, thus, references may change at any time.

To cope with this fact, it is recommendable to design your bundles to work with dynamic service references whenever it is feasible and possible.

One of the ways that is often recommended is to use a ServiceTracker, especially if a service is used more than once; see for example the Knopflerfish Service Tutorial.

While this is a good approach in some cases, there are some drawbacks for others:

  1. it is not well suited to track a bunch of different services:

    1. there is no method to get a specific service from the tracker;

    2. there is no method to wait for the reference of a specific service; and


  2. it is generic, thus needs an extra helper to avoid constant type casting.



Obviously, it is possible to construct the helpers and to use a single ServiceTracker for each service you need. However, with an increasing amount of required services, this solution is no longer easy to maintain.

In practice (i.e. the Coalevo project here), a less generic approach - that undoubtedly may require more copy & paste or a source generator as you will see later - seems to be better suited to deal with the tracking of a bunch of service references. So here the idea:

  1. we need a class that tracks a bunch of service references of different services at the same time;

  2. it should allow access to each of the service references

    1. with or without waiting timeouts; and

    2. without type casting.




Such a class actually controls and coordinates the service references required in your own bundle, which somewhat is an instance of the behavioral OO design pattern Mediator described here [1] . Thus, I will name the presented concept a ServiceMediator.


Hands-On: building a ServiceMediator


The first element we need is to track service references dynamically. The OSGi framework offers a simple tool for this task: the ServiceListener. This tool is also mentioned and introduced in the Knopflerfish Service Tutorial. A simple composition should do the job:



The ServiceMediator needs to hold service references; this can be solved straightforward by instance variables. However, to support waiting timeouts for each of this service references, an additional mechanism is needed. The easiest tool fo the job is a Latch, in particular the java.util.concurrent.CountDownLatch (note, however, that you can find a Latch class for Java <1.5 easily).

The basic pattern for a ServiceMediator implementation that tracks at least the MessageResourceService and the ExecutionService looks like this:



The method public MessageResourceService getMessageResourceService(long wait) will simply use the correspondig latch (if a waiting time is specified) or return the reference without waiting (with the danger of it being null; however, it only needs to be taken into account when using NO_WAIT. Alternatively you may add a method to verify if the ServiceMediator has a non null reference for a specfic service; e.g. isMessageResourceServiceAvailable():boolean):


public MessageResourceService getMessageResourceService(long wait) {
try {
if (wait < 0) {
m_MessageResourceServiceLatch.await();
} else if (wait > 0) {
m_MessageResourceServiceLatch.await(wait, TimeUnit.MILLISECONDS);
}
} catch (InterruptedException e) {
//log
}
return m_MessageResourceService;
}//getMessageResourceService


The methods activate(BundleContext bc):boolean and deactivate():void are basically for starting and stopping the service tracking. The first looks similar to this:


  public boolean activate(BundleContext bc) {
    //get the context
    m_BundleContext = bc;

    //prepare waits if required
//createWaitLatch() is just a factory method for return new CountDownLatch(1);
    m_MessageResourceServiceLatch = createWaitLatch();
    m_ExecutionServiceLatch = createWaitLatch();
....

    //prepare ServiceListener
    ServiceListener serviceListener = new ServiceListenerImpl();

    //prepare the filter
    String filter =
        "(|(objectclass=" + MessageResourceService.class.getName() + ")" +
        "(objectclass=" + ExecutionService.class.getName() + "))";


    try {
      //add the listener to the bundle context.
      bc.addServiceListener(serviceListener, filter);

      //ensure that already registered Service instances are registered with
      //the manager
      ServiceReference[] srl = bc.getServiceReferences(null, filter);

      for (int i = 0; srl != null && i < srl.length; i++) {
        serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, srl[i]));
      }
    } catch (InvalidSyntaxException ex) {
//log
      return false;
    }
    return true;
  }//activate


The trick here is not just to register the listener, but also to handle already registered services when the ServiceMediator is activated.

The final element is the ServiceListenerImpl:


  private class ServiceListenerImpl
      implements ServiceListener {

    public void serviceChanged(ServiceEvent ev) {
      ServiceReference sr = ev.getServiceReference();
      Object o = null;
      switch (ev.getType()) {
        case ServiceEvent.REGISTERED:
          o = m_BundleContext.getService(sr);
          if (o == null) {
//log, but should not happen?
            return;
          } else if (o instanceof MessageResourceService) {
            m_MessageResourceService = (MessageResourceService) o;
            m_MessageResourceServiceLatch.countDown();
            //log
          } else if (o instanceof ExecutionService) {
            m_ExecutionService = (ExecutionService) o;
            m_ExecutionServiceLatch.countDown();
            //log
          } else {
            m_BundleContext.ungetService(sr);
          }
          break;
        case ServiceEvent.UNREGISTERING:
          o = m_BundleContext.getService(sr);
          if (o == null) {
//log, but should not happen?
            return;
          } else if (o instanceofMessageResourceService) {
            m_MessageResourceService = null;
            m_MessageResourceServiceLatch = createWaitLatch();
//log
          } else if (o instanceof ExecutionService) {
            m_ExecutionService = null;
            m_ExecutionServiceLatch = createWaitLatch();
            //log
          } else {
            m_BundleContext.ungetService(sr);
          }
          break;
      }//switch
    }//serviceChanged

  }//inner class ServiceListenerImpl


Basically that's all, adding services to be tracked is mainly a simple copy & paste exercise. Or, write yourself a generator for it.

Wrapping it up


The above presents a simple idea how to construct a simple helper class that helps to track service references (and mediates them for the other objects in your bundle).
The Coalevo codebase has a lot of complete examples, like for example:
Bank Bundle ServiceMediator.



[1] Gamma, E., Helm, R., Johnson, R., Vlissides, J., January 1995. Design Patterns: Elements of Reusable Object-Oriented Software. Professional Computing Series. Addison-Wesley.