Using OSGi Services in an existing RCP Application

Summary
How to integrate declared OSGi services into an existing RCP application.

Background
Traditionally you register and consume OSGi services using Java code. However in a truly dynamic system with bundles coming and going, your code must explicitly track the services. This can get very complicated with multiple services and multiple dependencies between them. As of OSGi 4.1 (and Equinox in Eclipse 3.3) we can use declarative services to deal with dependencies. There are great links to deal with all of this, in particular those supplied by Neil Bartlett. However it is not at all clear how to use declaratively declared services in an existing GUI application. That’s the purpose of this post.

Caveat
Would you even want to do this? As of 3.4, GUI RCP apps are fundamentally not dynamic as the extension point based UI is not itself dynamic. Hence any service-based dynamism should be hidden behind some static facade that the UI accesses. However, this is good thought experiment to better understand declarative services in OSGI.

Solution Theory
The declarative services are all defined and intantiated via entries in xml files within their bundles. However how does an RCP application that is defined and instantiated via extension points (in particular org.eclipse.core.runtime.applications) then access these?
The answer is the reference element in declarative services. This allows OSGI components to define dependencies and, crucially, have methods that are passed explicit Java references to these dependencies. These methods are defined in the bind and unbind attributes, and can be used as callbacks to provide your Java code with references to the declared services.

Let us say use have declared a new OSGi service based in an interface IMyService. This is now running in your VM but you have no Java reference to it. So:

  1. In your Java code create another interface IMyServiceConsumer with a setService(IMyService) method.
  2. Create an implementation of IMyServiceConsumer and programmatically register this as an OSGi service. This is now running in OSGi but of course its setService has not yet been called.
  3. Now create an OSGi component – MyServiceMediator which has an addService(IMyService) and an addConsumer(IMyServiceConsumer) methods. The addService will simply store the IMyService reference whilst the addConsumer will use this in the callback IMyServiceConsumer.setService()
  4. The final step is to declare this component adding reference elements for both IMyService and IMyServiceConsumer.  Each reference element has bind and unbind attributes that refer to methods that will be supplied with a reference to the service.  Each method is called whenever an instance of the service is registered or unregistered. This reference can then be passed on to your client code.

Solution Practice
These are steps to go through in practice:

  1. Create a bundle containing the IMyService interface and an implementation;
  2. Define this as a declarative service with an xml file in the OSGI-INF directory: screenhunter_06-jan-23-1450
  3. Ensure the xml file is picked up by OSGi declarative services by adding a line to the bundle’s MANIFEST.MF file: screenhunter_03-jan-23-1432
  4. Create the IMyServiceConsumer interface and an implementation;
  5. Whenever the implementation class requires a reference to IMyService it should programmatically register itself as an OSGi service, hence obtaining the IMyService via the callback, and then deregister itself as soon as possible: screenhunter_04-jan-23-1440
  6. Create the MyServiceMediator implementation:
  7. screenhunter_05-jan-23-1446

  8. Declare this as an OSGi component, adding the essential reference elements to ensure all the bind methods are called: screenhunter_09-jan-23-1514
  9. Finally split this up into seperate bundles as suits your purposes – it is likely you will want the actual IMyService implementation in a seperate, swapable bundle.

Notes and Further Caveats

  • Why use a mediator at all? Can the core service be set up with reference elements direct? Absolutely! However the mediator allows you to keep the main service interface completely ‘clean’ – with no knowlede of consumers etc.
  • You will still need to do some coding to account for dynamic behaviour as IMyService might be unregistered at any time. Ensure either the mediator and/or the consumer can deal with this situation – most probably using a method defined by the unbind attribute on the service declaration.

Hullo

Here I am going to scratch some of the personal itches I have with Eclipse related technologies. I hope I scratch some of yours too.



Follow

Get every new post delivered to your Inbox.