Version 4.6
Copyright © 2008, 2009, 2010, 2012, 2013 Lars Vogel
22.03.2013
| Revision History | |||
|---|---|---|---|
| Revision 0.1 | 03.09.2007 | Lars Vogel |
Created |
| Revision 0.2 - 4.6 | 25.10.2008 - 22.03.2013 | Lars Vogel |
bugfixes and enhancements |
OSGi Services with Eclipse Equinox
This tutorial gives an overview of the usage and declaration of OSGi services. It explains the creation and consumption of OSGi services via OSGi declarative services. Eclipse Equinox is used as an stand-alone OSGi server. For this tutorial Eclipse 4.2 (Juno) is used.
Table of Contents
The following assumes that you are familiar with the OSGi runtime and its modularity layer as described in OSGi modularity .
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 bundle can register and use OSGi services. OSGi provides the central OSGi service registry for this purpose.
A service can be dynamically started and stopped, and bundles which use services must be able to handle this dynamic. The bundles can register listeners to be informed, if a service is started or stopped.
It is common practice to define a 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.
If several service 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 the
Constants.SERVICE_RANKING
constant
the
service.ranking
constant. This constant can be used to
set the
integer property of the
service ranking.
The OSGi declarative services (DS) functionality allows you to define and consume services via metadata (XML). Via DS you can define OSGi services without extending or implementing OSGi classes. This allows for these services to be tested independently of the OSGi runtime.
The OSGi service component is responsible for starting the service (service component). For the service consumer it is not visible if the service has been created via declarative service or via other means.
Service Components consists of an XML description (Component Description) and an object (Component Instance). The component description contains all information about the service component, e.g. the class name of the component instance and the service interface.
A reference to the
component description file is entered in the
MANIFEST.MF
file. This file is read by the OSGi runtime. If a
component
description is found,
the corresponding service is
created.
The
component
description in the
MANIFEST.MF
file looks for example
like the
following:
Service-Component: OSGI-INF/component.xml
To run declarative services you require the following bundles.
org.eclipse.equinox.util
org.eclipse.equinox.ds - Declarative service
Typically component definitions are created in the project
OSGI-INF
folder via
→ → → . The wizard will also add the
Service-Component
entry to the
MANIFEST.MF
file.

On the next page of the wizard, you can maintain the name of the component definition file, a name and the class which implements the services (or consumes the services).

If you press finish, the service editor opens.

On the Services tab you can enter the provided services or the referred services. For example to provide a service you would press the button under Provided Services and select the service interface you want to implement.

As a final step you would implement the class which would provide the service.
A correctly maintained
component.xml
XML file
would look like the following.
<?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.quoteservice"> <implementation class="de.vogella.osgi.ds.quoteservice.QuoteService"/> <service> <provide interface="de.vogella.osgi.quote.IQuoteService"/> </service> </scr:component>
This means that there is a component called
de.vogella.osgi.ds.quoteservice
which provides a service to the
OSGi
Service Registry under the
IQuoteService
interface. This component is
implemented
by
the
QuoteService
class.
After the definition of the component your
MANIFEST.MF
file
would contain an entry to the service component.
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Quoteservice Bundle-SymbolicName: de.vogella.osgi.ds.quoteservice Bundle-Version: 1.0.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Import-Package: de.vogella.osgi.quote Service-Component: OSGI-INF/component.xml
The
org.eclipse.core.runtime
bundle defined the OSGi runtime.
The
org.eclipse.equinox.ds
bundle is the bundle which is responsible for reading the component
meta data and registering the services based on the component
definition files.
Therefore these two bundle needs to start before the service becomes available.
You can ensure this in your run configuration by setting
.ds
bundle to auto start and using a start level lower than 4, which is
the default.

This requires that the service plug-in has the
Activate this plug-in when one of its classes is loaded
flag set in the
MANIFEST.MF
file. Via this flag it is ensured that the service is avaiable
after
the
org.eclipse.equinox.ds
has started.

If you face issues with starting your services, ensure that the core and ds plug-in is auto-started and have a start level lower than a bundle which want to consume the service. Also ensure that the Activate this plug-in when one of its classes is loaded flag is set
The following will define a DS service based on the quote example. It is therefore required that you have created the "de.vogella.osgi.quote" project which contains the interface definition.
Create a new plug-in project "de.vogella.osgi.ds.quoteservice". Do not use a template, do not create an activator. Import package "de.vogella.osgi.quote" in MANIFST.MF on the tab Dependencies.
Create the OSGI-INF folder in your project. Create a new
component definition as described earlier. The implementing class is
de.vogella.osgi.ds.quoteservice.QuoteService which provides the
service for IQuoteService.
Create the class "QuoteService" which implements the interface IQuoteService.
package de.vogella.osgi.ds.quoteservice; 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 "Ds: Tell them I said something"; case 1: return "Ds: I feel better already"; default: return "Ds: Hubba Bubba, Baby!"; } } }
Open component.xml and select the tab "Source". The final result should look like the following.
<?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.quoteservice"> <implementation class="de.vogella.osgi.ds.quoteservice.QuoteService"/> <service> <provide interface="de.vogella.osgi.quote.IQuoteService"/> </service> </scr:component>
Copy the "org.eclipse.equinox.ds*.jar", "org.eclipse.osgi.services.jar" and "org.eclipse.equinox.util*.jar" from your Eclipse/plugin installation directory into a folder, e.g. "C:\temp\bundles\plugins" and install the bundle into your OSGi runtime via.
install file:c:\temp\bundles\plugins\org.eclipse.equinox.ds.jar install file:c:\temp\bundles\plugins\org.eclipse.equinox.util.jar install file:c:\temp\bundles\plugins\org.eclipse.osgi.services.jar
Start the bundles manually so that declarative services are available.
Export your own bundle to "de.vogella.osgi.ds.quoteservice.jar". and install it via:
install file:c:\temp\bundles\plugins\de.vogella.osgi.ds.quoteservice.jar
To check if your service was registered use the command "services". This will list all installed and available services.
If you stop / uninstall the old service provider and start the new one your service should be picked up by the consumer.

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; } } }
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
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
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.
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();
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);
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);
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.
In the following we will define and consume a service. Our service will return "famous quotes".
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(); }
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 { } }
Export your bundles and install them on your server. Start the service bundle.

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

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.
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.
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.
Before posting questions, please see the vogella FAQ. If you have questions or find an error in this article please use the www.vogella.com Google Group. I have created a short list how to create good questions which might also help you.
http://www.osgi.org OSGi Homepage
http://www.eclipse.org/equinox Equinox Homepage
http://www.eclipse.org/equinox/documents/quickstart.php Equinox Quickstart guide
http://www.ibm.com/developerworks/opensource/library/os-osgiblueprint/ OSGi Blueprint services
vogella Training Android and Eclipse Training from the vogella team
Android Tutorial Introduction to Android Programming
GWT Tutorial Program in Java and compile to JavaScript and HTML
Eclipse RCP Tutorial Create native applications in Java
JUnit Tutorial Test your application
Git Tutorial Put everything you have under distributed version control system