RSS

Search Engine

Sunday, July 18, 2010

Define a Service and service consumption

6.1. Overview

OSGi platform provides a flexible mechanism for provisioning functionality via services. In the following we will define and consume a service.

Our service will return "famous quote".

6.2. How to build services

A service in OSGi is defined by a standard Java class or interface. It is common practice to define the service via a bundle which only contains the interface definition. This allows to change the implementation of the service via a different bundle.

6.3. Define the service interface

Create a plugin project "de.vogella.osgi.quote". Do not use a template. You do not need an activator for this example.

Select the MANIFEST.MF and runtime tab. Add "de.vogella.osgi.quote" to the exported packages.

Create the following interface "IQuoteService".

    
package de.vogella.osgi.quote;

public interface IQuoteService {
String getQuote();
}

6.4. Create service

We will now define a bundle which will provide the service.

Create a plugin project "de.vogella.osgi.quoteservice". Do not use a template.

Select the MANIFEST.MF and dependecy tab. Add "de.vogella.osgi.quote" to the required plugins.

Create the following class "QuoteService".

    
package de.vogella.osgi.quoteservice.internal;

import java.util.Random;

import de.vogella.osgi.quote.IQuoteService;

public class QuoteService implements IQuoteService {

@Override
public String getQuote() {
Random random = new Random();
// Create a number between 0 and 2
int nextInt = random.nextInt(3);
switch (nextInt) {
case 0:
return "Tell them I said something";
case 1:
return "I feel better already";
default:
return "Hubba Bubba, Baby!";
}

}
}

Register the service in the class Activator.

    
package de.vogella.osgi.quoteservice;

import java.util.Hashtable;

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

import de.vogella.osgi.quote.IQuoteService;
import de.vogella.osgi.quoteservice.internal.QuoteService;

public class Activator implements BundleActivator {

public void start(BundleContext context) throws Exception {
IQuoteService service = new QuoteService();
// Third parameter is a hashmap which allows to configure the service
// Not required in this example
context.registerService(IQuoteService.class.getName(), service,
null);
System.out.println("IQuoteService is registered");
}

public void stop(BundleContext context) throws Exception {
}
}

6.5. Install service bundles

Export your bundles and install them on your server. Start the service bundle.

Tip

Nothing fancy happens, as we are not yet consuming our service.

6.6. Use your service

Create a new plugin "de.vogella.osgi.quoteconsumer". Add also a dependency to the package "de.vogella.osgi.quote".

Tip

Please note that we have added the dependency against the package NOT against the plugin. This way we later replace the service with a different implementation.

Lets register directly to the service and use it.

    
package de.vogella.osgi.quoteconsumer;

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

import de.vogella.osgi.quote.IQuoteService;

public class Activator implements BundleActivator {

private BundleContext context;
private IQuoteService service;

public void start(BundleContext context) throws Exception {
this.context = context;
// Register directly with the service
ServiceReference reference = context
.getServiceReference(IQuoteService.class.getName());
service = (IQuoteService) context.getService(reference);
System.out.println(service.getQuote());
}

public void stop(BundleContext context) throws Exception {
System.out.println(service.getQuote());
}

}

Export this bundle, install it and start and stop it. Everything work. But if you stop the service bundle then your receive an error.

The reason for this is that OSGi is a very dynamic environment and service may be registered and de-registered any time. The next chapter will use a service tracker to improve this.

6.7. Use your service with a service tracker

OSGi provides a service to track service events (new service / stop services / started service).

Import for this the "org.osgi.util.tracker" package into the de.vogella.osgi.quoteconsumer project

To use this define the following class "MyQuoteServiceTrackerCustomizer"

    
package de.vogella.osgi.quoteconsumer;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTrackerCustomizer;

import de.vogella.osgi.quote.IQuoteService;

public class MyQuoteServiceTrackerCustomizer implements
ServiceTrackerCustomizer {

private final BundleContext context;

public MyQuoteServiceTrackerCustomizer(BundleContext context) {
this.context = context;
}

private MyThread thread;

@Override
public Object addingService(ServiceReference reference) {
IQuoteService service = (IQuoteService) context.getService(reference);
thread = new MyThread(service);
thread.start();
return service;
}

@Override
public void modifiedService(ServiceReference reference, Object service) {
// removedService(reference, service);
// addingService(reference);
}

@Override
public void removedService(ServiceReference reference, Object service) {
context.ungetService(reference);
System.out.println("How sad. Service for quote is gone");
thread.stopThread();
}

public static class MyThread extends Thread {

private volatile boolean active = true;
private final IQuoteService service;

public MyThread(IQuoteService service) {
this.service = service;
}

public void run() {
while (active) {
System.out.println(service.getQuote());
try {
Thread.sleep(5000);
} catch (Exception e) {
System.out.println("Thread interrupted " + e.getMessage());
}
}
}

public void stopThread() {
active = false;
}
}

}

You also need to register a service tracker in your activator of your serviceconsumer.

    
package de.vogella.osgi.quoteconsumer;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;

import de.vogella.osgi.quote.IQuoteService;

public class Activator implements BundleActivator {

private ServiceTracker serviceTracker;

public void start(BundleContext context) throws Exception {
System.out.println("Starting quoteconsumer bundles");
// Register directly with the service
MyQuoteServiceTrackerCustomizer customer = new MyQuoteServiceTrackerCustomizer(
context);
serviceTracker = new ServiceTracker(context, IQuoteService.class
.getName(), customer);
serviceTracker.open();
}

public void stop(BundleContext context) throws Exception {
System.out.println("Stopping quoteconsumer bundles");
serviceTracker.close();
}

}

Export your bundle again. Start the OSGI console. Use the update command or the install command to get the new version of your bundle and start it. Once you start your service the tracker will be called and the consumer bundle will start writing messages to the console. Stop the service and verify that the consumer does not use the service anymore.

6.8. Problems with service tracker

The problem with service trackers it they still obey Java rules. In case your service consumer keeps a reference of the service, this service can not get removed via the OSGi framework. The other disadvantage is that the service tracker require a lot of boilerplate code. To solve these issues declarative services were developed.

0 comments:

Post a Comment