Eclipse OSGi Services - Tutorial

1. Prerequisite

The following assumes that you are familiar with the OSGi runtime and its modularity layer as described in OSGi modularity .

2. OSGi Services

2.1. Overview

A bundle can register and use OSGi services. An OSGi service registered Java class provides a certain functionality.

OSGi provides the central OSGi Service Registry for this purpose.

A service in OSGi is defined by a standard Java class or interface. Typically a Java interface is used to define the service interface.

A service in OSGi can be dynamically started and stopped, therefore the bundles must manage these dependencies themselves. The bundles can register listeners to be informed, if a service is started or stopped.

2.2. Best practices for defining services

It is common practice to define the service via a bundle which only contains the interface definition. Another bundle would provide the implementation for this service. This allows you to change the implementation of the service via a different bundle.

2.3. Selection of OSGi services

If several services are available and valid then the OSGi runtime will by default select the service with the lowest bundle ID. You can also set a service ranking for your services via a property. OSGi assigns by default a value of zero as the service ranking and selects the service with the highest ranking.

The Constants class from the org.osgi.framework package defines via Constants.SERVICE_RANKING the service.ranking constant. This constant can be used to set the integer property of the service ranking.

In the registerService() method from the BundleContext class you can specify arbitrary properties in the Dictionary.

You can use the getProperty() method of the org.osgi.framework.ServiceReference class, to access a specific property.

5. Tutorial: Using services via declarative services

Of course you can also define the consumption of services via DS.

Create a new plug-in "de.vogella.osgi.ds.quoteconsumer". Do not use a template, do not create an activator. Import the package "de.vogella.osgi.quote" in MANIFEST.MF on the Dependencies tab.

Create the following class.

package de.vogella.osgi.ds.quoteconsumer;

import de.vogella.osgi.quote.IQuoteService;

public class QuoteConsumer {
  private IQuoteService service;

  public void quote() {
    System.out.println(service.getQuote());
  }
  
  // Method will be used by DS to set the quote service
  public synchronized void setQuote(IQuoteService service) {
    System.out.println("Service was set. Thank you DS!");
    this.service = service;
    // I know I should not use the service here but just for demonstration
    System.out.println(service.getQuote());
  }

  // Method will be used by DS to unset the quote service
  public synchronized void unsetQuote(IQuoteService service) {
    System.out.println("Service was unset. Why did you do this to me?");
    if (this.service == service) {
      this.service = null;
    }
  }
} 

Tip

Note that this class has no dependency to OSGi.

Create the OSGi-INF folder and create a new Component Definition in this folder.

This time we will use a service. Maintain the "Referenced Services".

Make the relationship to the bind() and bind() method by selecting your entry can by pressing the Edit button.

The result component.xml should look like:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="de.vogella.osgi.ds.quoteconsumer">
   <implementation class="de.vogella.osgi.ds.quoteconsumer.QuoteConsumer"/>
   <reference bind="setQuote" cardinality="1..1" interface="de.vogella.osgi.quote.IQuoteService" name="IQuoteService" policy="static" unbind="unsetQuote"/>
</scr:component> 

The result MANIFEST.MF should look like:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Quoteconsumer
Bundle-SymbolicName: de.vogella.osgi.ds.quoteconsumer
Bundle-Version: 1.0.4
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: de.vogella.osgi.quote
Service-Component: OSGI-INF/component.xml 

Export your plug-in and install it via: install file:c:\temp\bundles\plugins \de.vogella.osgi.ds.quoteconsumer.jar

"If you start the bundle now with "start id_of_your_bundle" you should get the feedback that the service was set and one quote should be returned

6. OSGi Services low-level API

6.1. Using the service API

In defining and using OSGi you should prefer higher level abstractions as OSGi declarative services as these simplify the handling of OSGi services. This chapter describes the API to work directly with OSGi services. It is included in this tutorial as reference.

6.2. BundleContext

Access to the service registry is performed via the BundleContext class.

A bundle can define a Bundle-Activator (Activator) in its declaration. This class must extend the BundleActivator interface.

If defined, OSGi injects the BundleContext into the start() and stop() methods of the implementing Activator class.

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


public class Activator implements BundleActivator {

  public void start(BundleContext context) throws Exception {
    System.out.println("Starting bundle");
    // Do something with the context, e.g. 
    // register services
  }

  public void stop(BundleContext context) throws Exception {
    System.out.println("Stopping bundle");
    serviceTracker.close();
    // Do something with the context, e.g. 
    // deregister service
  }

} 

If you do not have an Activator, you can use the FrameworkUtil class from the OSGi framework which allows you to retrieve the BundleContext for a class.

BundleContext bundleContext = 
        FrameworkUtil.
        getBundle(this.getClass()).
        getBundleContext(); 

6.3. Registering services via API

A bundle can also register itself for the events (ServiceEvents) of the BundleContext. These are, for example, triggered if a new bundle is installed or de-installed or if a new service is registered.

To publish a service in your bundle use:

public class Activator implements BundleActivator {
  // ...
  public void start(BundleContext context) throws Exception {
    context.
      registerService(IMyService.class.getName(), 
         new ServiceImpl(), null);

  }
  // ...
} 

Once the service is no longer used you must unregister the service with OSGi. OSGi counts the usage of services to enable the dynamic replacement of services.

context.ungetService(serviceReference); 

6.4. Accessing a service via API

A bundle can acquire a service via the BundleContext class. The following example demonstrates that.

ServiceReference<?> serviceReference = context.
  getServiceReference(IMyService.class.getName());
IMyService service = (IMyService) context.
  getService(serviceReference); 

6.5. Problems with the low-level API

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

OSGi declarative services simplify the handling of OSGi services.

7. Tutorial: Using the OSGi service API

In the following we will define and consume a service. Our service will return "famous quotes".

7.1. Define the service interface

Create a plug-in project "de.vogella.osgi.quote" and the package "de.vogella.osgi.quote". Do not use a template. You do not need an activator. Afterwards select the MANIFEST.MF and the 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();
} 

7.2. Create service

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

Create a plug-in 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 {
  }
} 

7.3. 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.

7.4. Use your service

Create a new plug-in "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.

7.5. Use your service with a service tracker

Declare a package dependency to the package "org.osgi.util.tracker" in your bundle.

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.

8. Bndtools

Eclipse use the PDE tooling to manage bundles. Alternatively you can use Bndtools hosted at http://bndtools.org/ .

Please see Bndtools tutorial for an introduction.

9. Thank you

Please help me to support this article:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值