Insight into OSGi and Eclipse Extensions

Posted by U.S. Wickramasighe | Posted in , , , , , , , , | Posted on Thursday, May 14, 2009

I've been working on OSGi(Open Statndards Gateway initiative) and Eclipse extensions for a while. Both are of course very good frameworks (infact Eclipse Extension framework depends on OSGi ) that has sprung up not very long ago and has become very popular gradually. Obviously Eclipse's popularity as a project and IDE has served immensely towards the recognition of OSGi as a framework. Technically speaking Eclipse and all it's components (except it's kernel) runs on top of an OSGi framework implementation named equinox. Hence lot of software products and platforms are now turning into OSGi to make their software more extensible or scalable.

As most of you know , within the core of OSGi lies a class-loading mechanism , where each bundle (as so called in OSGi) would take the responsibility of loading classes of their own , and would facilitate exposing of a subset of classes to the outside world while others will remain internal to the respective bundles. Although OSGi uses same Jar and Manifest mechanisms to achieve this, it is a radical change from Sun's Jar specification where all the classes and packages are implicitly exposed while clients can use any class within, they want (of course if the respective class implementation supports it). It's OSGi Runtime that deals with loading the bundles , managing dependencies(resolving) and registering and consuming services...

OSGi Service is simply an plain old java object (POJO) which can be either state-full or stateless , or any kind of a java Object that does some useful stuff. These objects should be registered under a interface key name and hence in order to register correctly as an OSGi service , service objects need to implement the specified Interfaces. Inorder to register a service you need the BundleContext object , which is almost always taken from a Bundle Activator provided by the OSGi framework to the "start(BudleContext)" at the start of each bundle.Following shows an example..

public class TestMailboxActivator implements BundleActivator{
public void start ( BundleContext context ) throws Exception {

IMailbox mbox = new TestMailbox ( ) ;
Properties mailboxProps = new Properties();
mailboxPropos.put("name","FixedTestMail");
//registering the service under it's interface name
context.registerService(IMailbox.class.getName(),mbox,mailboxProps);
}
public void stop ( BundleContext context ) throws Exception { }
}
}
For the classes to work and resolve properly ,IMailbox Interface should be exposed in the respective bundle(as declared in "export package" Manifest entry) or should be in the same bundle as TestMailbox (which is usually not the case). As in the example , services can be provided with key,value pairs as metadata which will be very useful indeed. However consuming services is little more challenging than registering them especially since we don't know when will services come and go due to their inheretly dynamic behavior.

We may not know when will a bundle register a service object and when it will be destroyed.One way to resolve this problem is to always keep querrying service eachtime it need to be accessed as shown below.

ServiceReference ref = context.getServiceReference(IMailbox.class.getName());
if(ref != null) {
IMailbox mbox = (IMailbox)
context.getService(ref);
if(mbox != null) {
int count;
try{
count = mbox.getMessageCount();
}
finally{
context.ungetService(ref);
}

return count;
}
}

The method "ungetService" will tell the framework that it's nolonger using the service.This important since framework keeps a count of a particular service and when it reaches zero it will get it disposed.However using a ServiceTracker would make the above procedures a lot easier as well.

ie:-

ServiceTracker mboxTracker = new ServiceTracker(context,IMailbox.class.getName(),null);
mboxTracker.open();
IMailbox mbox = ( IMailbox ) mboxTracker . getService ( ) ;

Also Service trackers come useful when managing particular services that depend on other services as well. Suppose a service object "servicePayroll" depends on another service "payment" , where payroll object is useless without having payment in it's hand. Here "servicePayroll" service is dependant on "payment" and hence it is pointless to consume "servicePayroll" object if system already does not have a "payment" service registered in it. To solve this problem , we have listen in for the "payment" sevice to appear (when someone registers a "payment" service) and then register "servicePayroll" object so that now payroll is able to process payments and provide necessary details for it's clients.

However things get little complicated when implementing this solution. One reason is we have to take notice of the "payment" services registered before our listener has even started as listener does not take care of this matter. So we should look up service registry to take into account the previously registered "payment" services. Also while we looking up the registry there may come a moment ,a "payment" object has been registered , however since we haven't registered our listener yet , this will go unnoticed. So we should register our service listener to listen to "payment" services first and then start lookup secondly.Again there will be some complications in overlapping and duplicate services so we should take care of them as well..

This obviously is very complex and cumbersome code and that's where Sevice Trackers come into the rescue.ServiceTracker objects handle these scenarios for us and provide us with nice and easy interface to work with..Service tracker code is almost the same , but additionaly to handle listening and registering of dependent services we have to provide service tracker object with new "ServiceTrackerCustomizer" object..it Provides very useful and important addingService(ServiceReference) method , which is invoked whenever tracked service is found in the registry or when someone registers it.


ServiceTracker paymentTracker = new ServiceTracker(context,IPayment.class.getName(),new PaymentTrackerCustomizer(context));
mboxTracker.open();

public Class PaymentTrackerCustomizer implements ServiceTrackerCustomizer(){
private BundleContext context;
.......

public Object addingService(ServiceReference ref){
payment = (Payment)context.getService(ref)
Payroll proll = new Payroll(payment);

//Returns ServiceRegistration object
return context.registerService(IPayroll.class.getName(),proll,null);
}

.........
}

addingService can return either null or any object (in this case ServiceRegistration object). Then when ever you call ServiceTracker's (ie:-paymentTracker) getService() method you will get the returned object (from addingService()) as the service object.

So this is so far for now , about OSGi and Services. I'll talk about Eclipse extensions and extension points with the next post..So keep n touch and Thank you for reading...