November 30, 2008

Building a simple OSGi Service

I have been experimenting with this technology and the main inspiration came from SpringSource's dm Server. Like other OSGi evangelist,I am beginning to see OSGi technology as technology that will transform Java development. I would like it to be more on the enterprise side. In this session, we will see how to build a simple service and consume it.

I started of my OSGi quest with Equinox and Eclipse IDE. But for the tutorial that we have today, we do not need Eclipse as it's a simple application (bundle). You may also use other OSGi runtime like Apache Felix or Knopflerfish. Knopflerfish even gives you are good GUI to work with. I will not be explaining fundamentals of OSGi technology. You may refer the technology overview, technical whitepaper and business whitepaper for more information.

What's OSGi service?
In very general terms, a service is a repeatable task. When it comes to business, any repeatable task in your business process is a service. Similarly in a application, you can have generic tasks (even specific tasks) that are repeatedly used can be represented as service. Representing and using these tasks as services is what SOA is all about! But that' at an enterprise level. When it comes to OSGi services, it is the same concept but applied at JVM level.

In OSGi, a service is a plain java object which is published to a registry. A consumer can consume the registered service through lookup. A service a be registered and unregistered at any point of time. Service is built using interface-based programming model. To implement or build a service you basically provide implementation to a interface. To consume, you only need the interface for the lookup and there is no need to know about the implementation. The service registry is the "middle man" who help producers and consumers to get in touch with each other.

Building HelloWorld service
The first step would be to create our interface or "front end" of our service. For our service, we will have a simple interface named IHelloService:
package org.tp.service.helloservice;

public interface IHelloService {
public String sayHello();
}
And here is our service implementation.
package org.tp.service.helloservice;

public class HelloService implements IHelloService {
public String sayHello() {
return "Hello World";
}
}
That's it! Our service is ready for use. But, we need to inform consumers that the service is ready to serve. For this, we will have to register our service with the OSGi service registry.

OSGi framework provides us with standard APIs to register and unregister service with the registry. We will use the registerService method to register as shown below:
serviceRegistration = context.registerService(IHelloService.class.getName(),helloService,null);
I am sure for beginners this is not enough. Let's explain the stuff little further.To register our new service, we will build a simple bundle that will call registerService method.
package org.tp.service.helloservice;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

public class Activator implements BundleActivator {

private ServiceRegistration serviceRegistration;
private IHelloService helloService;

public void start(BundleContext context) throws Exception {
System.out.println("Starting HelloService Bundle..");
helloService = new HelloService();
serviceRegistration = context.registerService(IHelloService.class.getName(),helloService,null);

}

public void stop(BundleContext context) throws Exception {
serviceRegistration.unregister();
}

}
Our Activator class implements BundleActivator. Basically, its a simple OSGi bundle with start and stop methods. We will register our service with the bundle starts up and unregister when the bundle is uninstalled from the framework.

Now lets have a closer look at start method. We create a instance of our service and then use registerService method. The first argument is service name which is obtained using InterfaceName.class.getName(). Its a best practice to use this method instead of specifying the name as string (org.tp.service.helloservice.IHelloService). The second argument is the instance of the service itself. And the final argument is Map wherein developers can pass additional properties to the service.

To unregister the service, we simple call unregister method when we stop the bundle. So now we have a running service on our OSGi runtime. Lets see how to consume it.

Consuming a service
To consume a service, we first create serviceReference object form the BundleContext. This can be achieved by calling getServiceReference method. The method takes the class name as a argument. Once you have the serviceReference object, we will use getService method to finally get the service. We will have to typecast the object returned by getService method before using it.
helloServiceRef = context.getServiceReference(IHelloService.class.getName());
IHelloService serviceObjectHelloService = (IHelloService)context.getService(helloServiceRef);
System.out.println("Service says: " + serviceObjectHelloService.sayHello());
Implementing the service and consumer is the same package is easy. Because, the interface is available. When you have your service and consumer bundle separate, there are some important points to note. OSGi provides the capability of specifying the packages they can be exported or imported. With this facility you can expose your service interface and hide its implementation from the public. The configuration details are specified in the MANIFEST file. Have a look at our HelloService's MANIFEST file:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: HelloService Plug-in
Bundle-SymbolicName: HelloService
Bundle-Version: 1.0.0
Bundle-Activator: org.tp.service.helloservice.Activator
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework;version="1.3.0"
Export-Package: org.tp.service.helloservice;uses:="org.osgi.framework"
Notice that we have exported org.tp.service.helloservice package. Similarly, we import this package in our consuming bundle.

You may download the complete code of this tutorial. I have two eclipse projects, HelloService – implementation of the service and HelloBundle – will consume the service. And to add some notes; The code used for consuming the service is not the best way. I have made the code very simple and easy to understand without involving Exceptions handling,Null pointer checks and ServiceListeners. We will have a look at ServiceListeners next.

No comments :