Writing about technology, traveling, politics and more

Friday, January 02, 2009

Binding declarative services - Tutorial 3

This is the next entry in this series of tutorials, where we will show how a simple component can be bound to a service. In the first tutorial, we learned how to install and start the Equinox OSGi framework and in the second one, we learned how to construct a simple component declaring an offered service.

Assuming you have followed all the steps described in the first two tutorials, then your OSGi runtime should have four components installed and started, as illustrated in the following screen:
osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.2.R34x_v20080826-1230
1 ACTIVE org.eclipse.equinox.ds_1.0.0.v20080427-0830
2 ACTIVE org.eclipse.equinox.util_1.0.0.v20080414
3 ACTIVE org.eclipse.osgi.services_3.1.200.v20071203
4 ACTIVE RunnableProvider_1.0.0
Developing the client side of the Runnable service

In this part, we describe how to develop a component that can be automatically bound to the java.lang.Runnable service, exported by the Runnable Provider component. We will name this component "RunnableConsumer".

The bundle structure is quite similar to the one of the "RunnableProvider", described in the second tutorial:
+- META-INF
| +- MANIFEST.MF
|
+- OSGI-INF
| +- RunnableConsumer.xml
|
+- cy
+- ac
+- ucy
+- cs
+- osgi
+- tutorial3
+- RunnableConsumer.class
The MANIFEST file is also quite similar to the one defined for the "RunnableProvider":
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tutorial 3: Runnable consumer
Bundle-SymbolicName: RunnableConsumer
Bundle-Version: 1.0.0
Service-Component: OSGI-INF/RunnableConsumer.xml

The main differences of the client side from the service provider are in the Java source code implementing the component, and in the service descriptor. As opposed to the "RunnableProvider", the "RunnableConsumer" does not define a provided service, but rather just a reference to a needed service, as follows:
<?xml version="1.0"?>

<component name="RunnableConsumer">

<implementation class="cy.ac.ucy.cs.osgi.tutorial3.RunnableConsumer"/>

<reference name="runnable_service"
interface="java.lang.Runnable"
bind="setRunnableService"
unbind="unsetRunnableService"
cardinality="0..1"
policy="dynamic"/>

</component>
The "reference" element in the service descriptor specifies that:
  • The component has a binding to a service, and the name of the binding is set to "runnable_service"
  • The needed service is one which implements the "java.lang.Runnable" interface
  • To bind this component with a provider of the service, the framework must invoke the "setRunnableService" method
  • Similarly, to unbind the component from a provider, the framework must invoke the "undetRunnableService" method
  • The cardinality parameter has the form "x..y", where x can be either '0' or '1' and y can be either '1' or 'n'. The first parameter specifies optionality, and the second one specifies multiplicity. For instance, x='0' means that a binding to the service is optional, while x='1' means that the binding is (otherwise the component can not be resolved and activated). On the other hand, y='1' implies single binding only, where the component can only be bound to just one service provider at a time, while y='n' implies that multiple service providers can be bound to the component simultaneously.
  • The last parameter, policy, can be set to either "dynamic" or "static". This controls the binding to new service providers at runtime. When the value is set to "static", then it is implied that the component cannot handle dynamic switching of service providers and thus the framework constructs a new instance of the component when the service provider changes. On the other hand, when the policy is set to "dynamic", then the binding and unbinding of service providers takes place dynamically on the same component instance. As the former is more heavy-weight, it is recommended that the components are designed and implemented so that they support the dynamic policy. The default value is "static", which means that unless you specify the policy parameter as "dynamic", it is going to be set as "static".
Next, we show the class source code, which is quite straightforward:
package cy.ac.ucy.cs.osgi.tutorial3;

public class RunnableConsumer
{
private Runnable runnableService = null;

public void setRunnableService(Runnable runnableService)
{
this.runnableService = runnableService;
}

public void unsetRunnableService(Runnable runnableService)
{
this.runnableService = null;
}

public RunnableConsumer()
{
super();
}
}
This class is also a POJO object, just like the Service Provider. It specifies a reference to a "java.lang.Runnable" type of service, along with straightfoward setter methods (complying to the signatures defined in the service descriptor). Evidently, this code is not too fascinating, but combined with the service descriptor it reveals the power of OSGi and Declarative Services.

The last step is to put everything together and construct the JAR file of the bundle. Similar to the "RunnableProvider", we construct the "RunnableConsumer" by compiling the Java class and adding the metadata as defined in the bundle structure. The source code, the metadata and an optional ANT build script are included in the RunnableConsumer JAR file.

Deployment and activation

We first install the newly constructed component using the, now familiar, "install" command of the console:
osgi> install file:RunnableConsumer.jar
Bundle id is 5

osgi> bundle 5
file:RunnableConsumer.jar [5]
Id=5, Status=INSTALLED Data Root=C:\eclipse\configuration\org.eclipse.osgi\bundles\5\data
No registered services.
No services in use.
No exported packages
No imported packages
No fragment bundles
No named class spaces
No required bundles
As shown in this listing, at the time the component is simply installed, assigned the ID #5, and has no services in use. As soon as we start the service, using the "start 5" command, the component is bound to the "java.lang.Runnable" service provided by the "RunnableProvider" component, as shown here:
osgi> start 5

osgi> bundle 5
file:RunnableConsumer.jar [5]
Id=5, Status=ACTIVE Data Root=C:\eclipse\configuration\org.eclipse.osgi\bundles\5\data
No registered services.
Services in use:
{java.lang.Runnable}={component.name=RunnableProvider, component.id=0, service.id=26}
No exported packages
No imported packages
No fragment bundles
Named class space
RunnableConsumer; bundle-version="1.0.0"[provided]
No required bundles
Later on, if we stop the "ServiceProvider" bundle, then the component is unbound from the service, although its instance is still around, deployed in the framework and awaiting for a new service provider:
osgi> stop 4

osgi> bundle 5
file:RunnableConsumer.jar [5]
Id=5, Status=ACTIVE Data Root=C:\eclipse\configuration\org.eclipse.osgi\bundles\5\data
No registered services.
No services in use.
No exported packages
No imported packages
No fragment bundles
Named class space
RunnableConsumer; bundle-version="1.0.0"[provided]
No required bundles
In the next tutorial, we will develop a more powerful client for the "java.lang.Runnable" service, featuring user-interaction via the command-line.

Homework 3.1: Read the third chapter (Introduction to Services) of Neil Bartlett's book. You can read just the first four sections for now, i.e., 4.1, 4.2, 4.3 and 4.4.

No comments: