Chapter 10. OSGi Services
Abstract
The OSGi core framework defines the OSGi Service Layer, which provides a simple mechanism for bundles to interact by registering Java objects as services in the OSGi service registry. One of the strengths of the OSGi service model is that any Java object can be offered as a service: there are no particular constraints, inheritance rules, or annotations that must be applied to the service class. This chapter describes how to deploy an OSGi service using the OSGi Blueprint container.
10.1. The Blueprint Container
Abstract
The Blueprint container is a dependency injection framework that simplifies interaction with the OSGi container. The Blueprint container supports a configuration-based approach to using the OSGi service registry—for example, providing standard XML elements to import and export OSGi services.
10.1.1. Blueprint Configuration
Location of Blueprint files in a JAR file
Relative to the root of the bundle JAR file, the standard location for Blueprint configuration files is the following directory:
OSGI-INF/blueprint
Any files with the suffix, .xml
, under this directory are interpreted as Blueprint configuration files; in other words, any files that match the pattern, OSGI-INF/blueprint/*.xml
.
Location of Blueprint files in a Maven project
In the context of a Maven project, ProjectDir, the standard location for Blueprint configuration files is the following directory:
ProjectDir/src/main/resources/OSGI-INF/blueprint
Blueprint namespace and root element
Blueprint configuration elements are associated with the following XML namespace:
http://www.osgi.org/xmlns/blueprint/v1.0.0
The root element for Blueprint configuration is blueprint
, so a Blueprint XML configuration file normally has the following outline form:
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> ... </blueprint>
In the blueprint
root element, there is no need to specify the location of the Blueprint schema using an xsi:schemaLocation
attribute, because the schema location is already known to the Blueprint framework.
Blueprint Manifest configuration
Some aspects of Blueprint configuration are controlled by headers in the JAR’s manifest file, META-INF/MANIFEST.MF
, as follows:
Custom Blueprint file locations
If you need to place your Blueprint configuration files in a non-standard location (that is, somewhere other than OSGI-INF/blueprint/*.xml
), you can specify a comma-separated list of alternative locations in the Bundle-Blueprint
header in the manifest file—for example:
Bundle-Blueprint: lib/account.xml, security.bp, cnf/*.xml
Mandatory dependencies
Dependencies on an OSGi service are mandatory by default (although this can be changed by setting the availability
attribute to optional
on a reference
element or a reference-list
element). Declaring a dependency to be mandatory means that the bundle cannot function properly without that dependency and the dependency must be available at all times.
Normally, while a Blueprint container is initializing, it passes through a grace period, during which time it attempts to resolve all mandatory dependencies. If the mandatory dependencies cannot be resolved in this time (the default timeout is 5 minutes), container initialization is aborted and the bundle is not started. The following settings can be appended to the Bundle-SymbolicName
manifest header to configure the grace period:
blueprint.graceperiod
-
If
true
(the default), the grace period is enabled and the Blueprint container waits for mandatory dependencies to be resolved during initialization; iffalse
, the grace period is skipped and the container does not check whether the mandatory dependencies are resolved. blueprint.timeout
- Specifies the grace period timeout in milliseconds. The default is 300000 (5 minutes).
For example, to enable a grace period of 10 seconds, you could define the following Bundle-SymbolicName
header in the manifest file:
Bundle-SymbolicName: org.fusesource.example.osgi-client; blueprint.graceperiod:=true; blueprint.timeout:= 10000
The value of the Bundle-SymbolicName
header is a semi-colon separated list, where the first item is the actual bundle symbolic name, the second item, blueprint.graceperiod:=true
, enables the grace period and the third item, blueprint.timeout:= 10000
, specifies a 10 second timeout.
10.1.2. Defining a Service Bean
Overview
The Blueprint container enables you to instantiate Java classes using a bean
element. You can create all of your main application objects this way. In particular, you can use the bean
element to create a Java object that represents an OSGi service instance.
Blueprint bean element
The Blueprint bean
element is defined in the Blueprint schema namespace, http://www.osgi.org/xmlns/blueprint/v1.0.0
.
Sample beans
The following example shows how to create a few different types of bean using Blueprint’s bean
element:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <bean id="label" class="java.lang.String"> <argument value="LABEL_VALUE"/> </bean> <bean id="myList" class="java.util.ArrayList"> <argument type="int" value="10"/> </bean> <bean id="account" class="org.fusesource.example.Account"> <property name="accountName" value="john.doe"/> <property name="balance" value="10000"/> </bean> </blueprint>
Where the Account
class referenced by the last bean example could be defined as follows:
package org.fusesource.example; public class Account { private String accountName; private int balance; public Account () { } public void setAccountName(String name) { this.accountName = name; } public void setBalance(int bal) { this.balance = bal; } ... }
References
For more details on defining Blueprint beans, consult the following references:
- Spring Dynamic Modules Reference Guide v2.0, Blueprint chapter.
- Section 121 Blueprint Container Specification, from the OSGi Compendium Services R4.2 specification.
10.1.3. Using properties to configure Blueprint
Overview
This section describes how to configure Blueprint using properties held in a file which is outside the Camel context.
Configuring Blueprint beans
Blueprint beans can be configured by using variables that can be substitued with properties from an external file. You need to declare the ext
namespace and add the property placeholder
bean in your Blueprint xml. Use the Property-Placeholder
bean to declare the location of your properties file to Blueprint.
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.2.0"> <ext:property-placeholder> <ext:location>file:etc/ldap.properties</ext:location> </ext:property-placeholder> ... <bean ...> <property name="myProperty" value="${myProperty}" /> </bean> </blueprint>
The specification of property-placeholder
configuration options can be found at http://aries.apache.org/schemas/blueprint-ext/blueprint-ext.xsd.
10.2. Exporting a Service
Overview
This section describes how to export a Java object to the OSGi service registry, thus making it accessible as a service to other bundles in the OSGi container.
Exporting with a single interface
To export a service to the OSGi service registry under a single interface name, define a service
element that references the relevant service bean, using the ref
attribute, and specifies the published interface, using the interface
attribute.
For example, you could export an instance of the SavingsAccountImpl
class under the org.fusesource.example.Account
interface name using the Blueprint configuration code shown in Example 10.1, “Sample Service Export with a Single Interface”.
Example 10.1. Sample Service Export with a Single Interface
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="savings" class="org.fusesource.example.SavingsAccountImpl"/>
<service ref="savings" interface="org.fusesource.example.Account"/>
</blueprint>
Where the ref
attribute specifies the ID of the corresponding bean instance and the interface
attribute specifies the name of the public Java interface under which the service is registered in the OSGi service registry. The classes and interfaces used in this example are shown in Example 10.2, “Sample Account Classes and Interfaces”
Example 10.2. Sample Account Classes and Interfaces
package org.fusesource.example public interface Account { ... } public interface SavingsAccount { ... } public interface CheckingAccount { ... } public class SavingsAccountImpl implements SavingsAccount { ... } public class CheckingAccountImpl implements CheckingAccount { ... }
Exporting with multiple interfaces
To export a service to the OSGi service registry under multiple interface names, define a service
element that references the relevant service bean, using the ref
attribute, and specifies the published interfaces, using the interfaces
child element.
For example, you could export an instance of the SavingsAccountImpl
class under the list of public Java interfaces, org.fusesource.example.Account
and org.fusesource.example.SavingsAccount
, using the following Blueprint configuration code:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <bean id="savings" class="org.fusesource.example.SavingsAccountImpl"/> <service ref="savings"> <interfaces> <value>org.fusesource.example.Account</value> <value>org.fusesource.example.SavingsAccount</value> </interfaces> </service> ... </blueprint>
The interface
attribute and the interfaces
element cannot be used simultaneously in the same service
element. You must use either one or the other.
Exporting with auto-export
If you want to export a service to the OSGi service registry under all of its implemented public Java interfaces, there is an easy way of accomplishing this using the auto-export
attribute.
For example, to export an instance of the SavingsAccountImpl
class under all of its implemented public interfaces, use the following Blueprint configuration code:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <bean id="savings" class="org.fusesource.example.SavingsAccountImpl"/> <service ref="savings" auto-export="interfaces"/> ... </blueprint>
Where the interfaces
value of the auto-export
attribute indicates that Blueprint should register all of the public interfaces implemented by SavingsAccountImpl
. The auto-export
attribute can have the following valid values:
disabled
- Disables auto-export. This is the default.
interfaces
- Registers the service under all of its implemented public Java interfaces.
class-hierarchy
-
Registers the service under its own type (class) and under all super-types (super-classes), except for the
Object
class. all-classes
-
Like the
class-hierarchy
option, but including all of the implemented public Java interfaces as well.
Setting service properties
The OSGi service registry also allows you to associate service properties with a registered service. Clients of the service can then use the service properties to search for or filter services. To associate service properties with an exported service, add a service-properties
child element that contains one or more beans:entry
elements (one beans:entry
element for each service property).
For example, to associate the bank.name
string property with a savings account service, you could use the following Blueprint configuration:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:beans="http://www.springframework.org/schema/beans" ...> ... <service ref="savings" auto-export="interfaces"> <service-properties> <beans:entry key="bank.name" value="HighStreetBank"/> </service-properties> </service> ... </blueprint>
Where the bank.name
string property has the value, HighStreetBank
. It is possible to define service properties of type other than string: that is, primitive types, arrays, and collections are also supported. For details of how to define these types, see Controlling the Set of Advertised Properties. in the Spring Reference Guide.
The entry
element ought to belong to the Blueprint namespace. The use of the beans:entry
element in Spring’s implementation of Blueprint is non-standard.
Default service properties
There are two service properties that might be set automatically when you export a service using the service
element, as follows:
-
osgi.service.blueprint.compname
—is always set to theid
of the service’sbean
element, unless the bean is inlined (that is, the bean is defined as a child element of theservice
element). Inlined beans are always anonymous. -
service.ranking
—is automatically set, if the ranking attribute is non-zero.
Specifying a ranking attribute
If a bundle looks up a service in the service registry and finds more than one matching service, you can use ranking to determine which of the services is returned. The rule is that, whenever a lookup matches multiple services, the service with the highest rank is returned. The service rank can be any non-negative integer, with 0
being the default. You can specify the service ranking by setting the ranking
attribute on the service
element—for example:
<service ref="savings" interface="org.fusesource.example.Account" ranking="10"/>
Specifying a registration listener
If you want to keep track of service registration and unregistration events, you can define a registration listener callback bean that receives registration and unregistration event notifications. To define a registration listener, add a registration-listener
child element to a service
element.
For example, the following Blueprint configuration defines a listener bean, listenerBean
, which is referenced by a registration-listener
element, so that the listener bean receives callbacks whenever an Account
service is registered or unregistered:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" ...> ... <bean id="listenerBean" class="org.fusesource.example.Listener"/> <service ref="savings" auto-export="interfaces"> <registration-listener ref="listenerBean" registration-method="register" unregistration-method="unregister"/> </service> ... </blueprint>
Where the registration-listener
element’s ref
attribute references the id
of the listener bean, the registration-method
attribute specifies the name of the listener method that receives the registration callback, and unregistration-method
attribute specifies the name of the listener method that receives the unregistration callback.
The following Java code shows a sample definition of the Listener
class that receives notifications of registration and unregistration events:
package org.fusesource.example; public class Listener { public void register(Account service, java.util.Map serviceProperties) { ... } public void unregister(Account service, java.util.Map serviceProperties) { ... } }
The method names, register
and unregister
, are specified by the registration-method
and unregistration-method
attributes respectively. The signatures of these methods must conform to the following syntax:
-
First method argument—any type T that is assignable from the service object’s type. In other words, any supertype class of the service class or any interface implemented by the service class. This argument contains the service instance, unless the service bean declares the
scope
to beprototype
, in which case this argument isnull
(when the scope isprototype
, no service instance is available at registration time). -
Second method argument—must be of either
java.util.Map
type orjava.util.Dictionary
type. This map contains the service properties associated with this service registration.
10.3. Importing a Service
Overview
This section describes how to obtain and use references to OSGi services that have been exported to the OSGi service registry. You can use either the reference
element or the reference-list
element to import an OSGi service. The reference
element is suitable for accessing stateless services, while the reference-list
element is suitable for accessing stateful services.
Managing service references
The following models for obtaining OSGi services references are supported:
Reference manager
A reference manager instance is created by the Blueprint reference
element. This element returns a single service reference and is the preferred approach for accessing stateless services. Figure 10.1, “Reference to Stateless Service” shows an overview of the model for accessing a stateless service using the reference manager.
Figure 10.1. Reference to Stateless Service
Beans in the client Blueprint container get injected with a proxy object (the provided object), which is backed by a service object (the backing service) from the OSGi service registry. This model explicitly takes advantage of the fact that stateless services are interchangeable, in the following ways:
-
If multiple services instances are found that match the criteria in the
reference
element, the reference manager can arbitrarily choose one of them as the backing instance (because they are interchangeable). - If the backing service disappears, the reference manager can immediately switch to using one of the other available services of the same type. Hence, there is no guarantee, from one method invocation to the next, that the proxy remains connected to the same backing service.
The contract between the client and the backing service is thus stateless, and the client must not assume that it is always talking to the same service instance. If no matching service instances are available, the proxy will wait for a certain length of time before throwing the ServiceUnavailable
exception. The length of the timeout is configurable by setting the timeout
attribute on the reference
element.
Reference list manager
A reference list manager instance is created by the Blueprint reference-list
element. This element returns a list of service references and is the preferred approach for accessing stateful services. Figure 10.2, “List of References to Stateful Services” shows an overview of the model for accessing a stateful service using the reference list manager.
Figure 10.2. List of References to Stateful Services
Beans in the client Blueprint container get injected with a java.util.List
object (the provided object), which contains a list of proxy objects. Each proxy is backed by a unique service instance in the OSGi service registry. Unlike the stateless model, backing services are not considered to be interchangeable here. In fact, the lifecycle of each proxy in the list is tightly linked to the lifecycle of the corresponding backing service: when a service gets registered in the OSGi registry, a corresponding proxy is synchronously created and added to the proxy list; and when a service gets unregistered from the OSGi registry, the corresponding proxy is synchronously removed from the proxy list.
The contract between a proxy and its backing service is thus stateful, and the client may assume when it invokes methods on a particular proxy, that it is always communicating with the same backing service. It could happen, however, that the backing service becomes unavailable, in which case the proxy becomes stale. Any attempt to invoke a method on a stale proxy will generate the ServiceUnavailable
exception.
Matching by interface (stateless)
The simplest way to obtain a stateles service reference is by specifying the interface to match, using the interface
attribute on the reference
element. The service is deemed to match, if the interface
attribute value is a super-type of the service or if the attribute value is a Java interface implemented by the service (the interface
attribute can specify either a Java class or a Java interface).
For example, to reference a stateless SavingsAccount
service (see Example 10.1, “Sample Service Export with a Single Interface”), define a reference
element as follows:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <reference id="savingsRef" interface="org.fusesource.example.SavingsAccount"/> <bean id="client" class="org.fusesource.example.client.Client"> <property name="savingsAccount" ref="savingsRef"/> </bean> </blueprint>
Where the reference
element creates a reference manager bean with the ID, savingsRef
. To use the referenced service, inject the savingsRef
bean into one of your client classes, as shown.
The bean property injected into the client class can be any type that is assignable from SavingsAccount
. For example, you could define the Client
class as follows:
package org.fusesource.example.client; import org.fusesource.example.SavingsAccount; public class Client { SavingsAccount savingsAccount; // Bean properties public SavingsAccount getSavingsAccount() { return savingsAccount; } public void setSavingsAccount(SavingsAccount savingsAccount) { this.savingsAccount = savingsAccount; } ... }
Matching by interface (stateful)
The simplest way to obtain a stateful service reference is by specifying the interface to match, using the interface
attribute on the reference-list
element. The reference list manager then obtains a list of all the services, whose interface
attribute value is either a super-type of the service or a Java interface implemented by the service (the interface
attribute can specify either a Java class or a Java interface).
For example, to reference a stateful SavingsAccount
service (see Example 10.1, “Sample Service Export with a Single Interface”), define a reference-list
element as follows:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <reference-list id="savingsListRef" interface="org.fusesource.example.SavingsAccount"/> <bean id="client" class="org.fusesource.example.client.Client"> <property name="savingsAccountList" ref="savingsListRef"/> </bean> </blueprint>
Where the reference-list
element creates a reference list manager bean with the ID, savingsListRef
. To use the referenced service list, inject the savingsListRef
bean reference into one of your client classes, as shown.
By default, the savingsAccountList
bean property is a list of service objects (for example, java.util.List<SavingsAccount>
). You could define the client class as follows:
package org.fusesource.example.client; import org.fusesource.example.SavingsAccount; public class Client { java.util.List<SavingsAccount> accountList; // Bean properties public java.util.List<SavingsAccount> getSavingsAccountList() { return accountList; } public void setSavingsAccountList( java.util.List<SavingsAccount> accountList ) { this.accountList = accountList; } ... }
Matching by interface and component name
To match both the interface and the component name (bean ID) of a stateless service, specify both the interface
attribute and the component-name
attribute on the reference
element, as follows:
<reference id="savingsRef" interface="org.fusesource.example.SavingsAccount" component-name="savings"/>
To match both the interface and the component name (bean ID) of a stateful service, specify both the interface
attribute and the component-name
attribute on the reference-list
element, as follows:
<reference-list id="savingsRef" interface="org.fusesource.example.SavingsAccount" component-name="savings"/>
Matching service properties with a filter
You can select services by matching service properties against a filter. The filter is specified using the filter
attribute on the reference
element or on the reference-list
element. The value of the filter
attribute must be an LDAP filter expression. For example, to define a filter that matches when the bank.name
service property equals HighStreetBank
, you could use the following LDAP filter expression:
(bank.name=HighStreetBank)
To match two service property values, you can use &
conjunction, which combines expressions with a logical and
.For example, to require that the foo
property is equal to FooValue
and the bar
property is equal to BarValue
, you could use the following LDAP filter expression:
(&(foo=FooValue)(bar=BarValue))
For the complete syntax of LDAP filter expressions, see section 3.2.7 of the OSGi Core Specification.
Filters can also be combined with the interface
and component-name
settings, in which case all of the specified conditions are required to match.
For example, to match a stateless service of SavingsAccount
type, with a bank.name
service property equal to HighStreetBank
, you could define a reference
element as follows:
<reference id="savingsRef" interface="org.fusesource.example.SavingsAccount" filter="(bank.name=HighStreetBank)"/>
To match a stateful service of SavingsAccount
type, with a bank.name
service property equal to HighStreetBank
, you could define a reference-list
element as follows:
<reference-list id="savingsRef" interface="org.fusesource.example.SavingsAccount" filter="(bank.name=HighStreetBank)"/>
Specifying whether mandatory or optional
By default, a reference to an OSGi service is assumed to be mandatory (see Mandatory dependencies). It is possible to customize the dependency behavior of a reference
element or a reference-list
element by setting the availability
attribute on the element.
There are two possible values of the availability
attribute:
-
mandatory
(the default), means that the dependency must be resolved during a normal Blueprint container initialization -
optional
, means that the dependency need not be resolved during initialization.
The following example of a reference
element shows how to declare explicitly that the reference is a mandatory dependency:
<reference id="savingsRef" interface="org.fusesource.example.SavingsAccount" availability="mandatory"/>
Specifying a reference listener
To cope with the dynamic nature of the OSGi environment—for example, if you have declared some of your service references to have optional
availability—it is often useful to track when a backing service gets bound to the registry and when it gets unbound from the registry. To receive notifications of service binding and unbinding events, you can define a reference-listener
element as the child of either the reference
element or the reference-list
element.
For example, the following Blueprint configuration shows how to define a reference listener as a child of the reference manager with the ID, savingsRef
:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <reference id="savingsRef" interface="org.fusesource.example.SavingsAccount" > <reference-listener bind-method="onBind" unbind-method="onUnbind"> <bean class="org.fusesource.example.client.Listener"/> </reference-listener> </reference> <bean id="client" class="org.fusesource.example.client.Client"> <property name="savingsAcc" ref="savingsRef"/> </bean> </blueprint>
The preceding configuration registers an instance of org.fusesource.example.client.Listener
type as a callback that listens for bind
and unbind
events. Events are generated whenever the savingsRef
reference manager’s backing service binds or unbinds.
The following example shows a sample implementation of the Listener
class:
package org.fusesource.example.client; import org.osgi.framework.ServiceReference; public class Listener { public void onBind(ServiceReference ref) { System.out.println("Bound service: " + ref); } public void onUnbind(ServiceReference ref) { System.out.println("Unbound service: " + ref); } }
The method names, onBind
and onUnbind
, are specified by the bind-method
and unbind-method
attributes respectively. Both of these callback methods take an org.osgi.framework.ServiceReference
argument.
10.4. Publishing an OSGi Service
10.4.1. Overview
This section explains how to generate, build, and deploy a simple OSGi service in the OSGi container. The service is a simple Hello World Java class and the OSGi configuration is defined using a Blueprint configuration file.
10.4.2. Prerequisites
In order to generate a project using the Maven Quickstart archetype, you must have the following prerequisites:
- Maven installation—Maven is a free, open source build tool from Apache. You can download the latest version from http://maven.apache.org/download.html (minimum is 2.0.9).
- Internet connection—whilst performing a build, Maven dynamically searches external repositories and downloads the required artifacts on the fly. In order for this to work, your build machine must be connected to the Internet.
10.4.3. Generating a Maven project
The maven-archetype-quickstart
archetype creates a generic Maven project, which you can then customize for whatever purpose you like. To generate a Maven project with the coordinates, org.fusesource.example:osgi-service
, enter the following command:
mvn archetype:create -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=org.fusesource.example -DartifactId=osgi-service
The result of this command is a directory, ProjectDir/osgi-service
, containing the files for the generated project.
Be careful not to choose a group ID for your artifact that clashes with the group ID of an existing product! This could lead to clashes between your project’s packages and the packages from the existing product (because the group ID is typically used as the root of a project’s Java package names).
10.4.4. Customizing the POM file
You must customize the POM file in order to generate an OSGi bundle, as follows:
- Follow the POM customization steps described in Section 3.1, “Generating a Bundle Project”.
In the configuration of the Maven bundle plug-in, modify the bundle instructions to export the
org.fusesource.example.service
package, as follows:<project ... > ... <build> ... <plugins> ... <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <extensions>true</extensions> <configuration> <instructions> <Bundle-SymbolicName>${pom.groupId}.${pom.artifactId}</Bundle-SymbolicName> <Export-Package>org.fusesource.example.service</Export-Package> </instructions> </configuration> </plugin> </plugins> </build> ... </project>
10.4.5. Writing the service interface
Create the ProjectDir/osgi-service/src/main/java/org/fusesource/example/service
sub-directory. In this directory, use your favorite text editor to create the file, HelloWorldSvc.java
, and add the code from Example 10.3, “The HelloWorldSvc Interface” to it.
Example 10.3. The HelloWorldSvc Interface
package org.fusesource.example.service; public interface HelloWorldSvc { public void sayHello(); }
10.4.6. Writing the service class
Create the ProjectDir/osgi-service/src/main/java/org/fusesource/example/service/impl
sub-directory. In this directory, use your favorite text editor to create the file, HelloWorldSvcImpl.java
, and add the code from Example 10.4, “The HelloWorldSvcImpl Class” to it.
Example 10.4. The HelloWorldSvcImpl Class
package org.fusesource.example.service.impl; import org.fusesource.example.service.HelloWorldSvc; public class HelloWorldSvcImpl implements HelloWorldSvc { public void sayHello() { System.out.println( "Hello World!" ); } }
10.4.7. Writing the Blueprint file
The Blueprint configuration file is an XML file stored under the OSGI-INF/blueprint
directory on the class path. To add a Blueprint file to your project, first create the following sub-directories:
ProjectDir/osgi-service/src/main/resources ProjectDir/osgi-service/src/main/resources/OSGI-INF ProjectDir/osgi-service/src/main/resources/OSGI-INF/blueprint
Where the src/main/resources
is the standard Maven location for all JAR resources. Resource files under this directory will automatically be packaged in the root scope of the generated bundle JAR.
Example 10.5, “Blueprint File for Exporting a Service” shows a sample Blueprint file that creates a HelloWorldSvc
bean, using the bean
element, and then exports the bean as an OSGi service, using the service
element.
Under the ProjectDir/osgi-service/src/main/resources/OSGI-INF/blueprint
directory, use your favorite text editor to create the file, config.xml
, and add the XML code from Example 10.5, “Blueprint File for Exporting a Service”.
Example 10.5. Blueprint File for Exporting a Service
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <bean id="hello" class="org.fusesource.example.service.impl.HelloWorldSvcImpl"/> <service ref="hello" interface="org.fusesource.example.service.HelloWorldSvc"/> </blueprint>
10.4.8. Running the service bundle
To install and run the osgi-service
project, perform the following steps:
Build the project—open a command prompt and change directory to
ProjectDir/osgi-service
. Use Maven to build the demonstration by entering the following command:mvn install
If this command runs successfully, the
ProjectDir/osgi-service/target
directory should contain the bundle file,osgi-service-1.0-SNAPSHOT.jar
.Install and start the osgi-service bundle—at the Red Hat Fuse console, enter the following command:
Jkaraf@root()> bundle:install -s file:ProjectDir/osgi-service/target/osgi-service-1.0-SNAPSHOT.jar
Where ProjectDir is the directory containing your Maven projects and the
-s
flag directs the container to start the bundle right away. For example, if your project directory isC:\Projects
on a Windows machine, you would enter the following command:karaf@root()> bundle:install -s file:C:/Projects/osgi-service/target/osgi-service-1.0-SNAPSHOT.jar
NoteOn Windows machines, be careful how you format the
file
URL—for details of the syntax understood by thefile
URL handler, see Section 12.1, “File URL Handler”.Check that the service has been created—to check that the bundle has started successfully, enter the following Red Hat Fuse console command:
karaf@root()> bundle:list
Somewhere in this listing, you should see a line for the
osgi-service
bundle, for example:[ 236] [Active ] [Created ] [ ] [ 60] osgi-service (1.0.0.SNAPSHOT)
10.5. Accessing an OSGi Service
10.5.1. Overview
This section explains how to generate, build, and deploy a simple OSGi client in the OSGi container. The client finds the simple Hello World service in the OSGi registry and invokes the sayHello()
method on it.
10.5.2. Prerequisites
In order to generate a project using the Maven Quickstart archetype, you must have the following prerequisites:
- Maven installation—Maven is a free, open source build tool from Apache. You can download the latest version from http://maven.apache.org/download.html (minimum is 2.0.9).
- Internet connection—whilst performing a build, Maven dynamically searches external repositories and downloads the required artifacts on the fly. In order for this to work, your build machine must be connected to the Internet.
10.5.3. Generating a Maven project
The maven-archetype-quickstart
archetype creates a generic Maven project, which you can then customize for whatever purpose you like. To generate a Maven project with the coordinates, org.fusesource.example:osgi-client
, enter the following command:
mvn archetype:create -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=org.fusesource.example -DartifactId=osgi-client
The result of this command is a directory, ProjectDir/osgi-client
, containing the files for the generated project.
Be careful not to choose a group ID for your artifact that clashes with the group ID of an existing product! This could lead to clashes between your project’s packages and the packages from the existing product (because the group ID is typically used as the root of a project’s Java package names).
10.5.4. Customizing the POM file
You must customize the POM file in order to generate an OSGi bundle, as follows:
- Follow the POM customization steps described in Section 3.1, “Generating a Bundle Project”.
Because the client uses the
HelloWorldSvc
Java interface, which is defined in theosgi-service
bundle, it is necessary to add a Maven dependency on theosgi-service
bundle. Assuming that the Maven coordinates of theosgi-service
bundle areorg.fusesource.example:osgi-service:1.0-SNAPSHOT
, you should add the following dependency to the client’s POM file:<project ... > ... <dependencies> ... <dependency> <groupId>org.fusesource.example</groupId> <artifactId>osgi-service</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> ... </project>
10.5.5. Writing the Blueprint file
To add a Blueprint file to your client project, first create the following sub-directories:
ProjectDir/osgi-client/src/main/resources ProjectDir/osgi-client/src/main/resources/OSGI-INF ProjectDir/osgi-client/src/main/resources/OSGI-INF/blueprint
Under the ProjectDir/osgi-client/src/main/resources/OSGI-INF/blueprint
directory, use your favorite text editor to create the file, config.xml
, and add the XML code from Example 10.6, “Blueprint File for Importing a Service”.
Example 10.6. Blueprint File for Importing a Service
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <reference id="helloWorld" interface="org.fusesource.example.service.HelloWorldSvc"/> <bean id="client" class="org.fusesource.example.client.Client" init-method="init"> <property name="helloWorldSvc" ref="helloWorld"/> </bean> </blueprint>
Where the reference
element creates a reference manager that finds a service of HelloWorldSvc
type in the OSGi registry. The bean
element creates an instance of the Client
class and injects the service reference as the bean property, helloWorldSvc
. In addition, the init-method
attribute specifies that the Client.init()
method is called during the bean initialization phase (that is, after the service reference has been injected into the client bean).
10.5.6. Writing the client class
Under the ProjectDir/osgi-client/src/main/java/org/fusesource/example/client
directory, use your favorite text editor to create the file, Client.java
, and add the Java code from Example 10.7, “The Client Class”.
Example 10.7. The Client Class
package org.fusesource.example.client; import org.fusesource.example.service.HelloWorldSvc; public class Client { HelloWorldSvc helloWorldSvc; // Bean properties public HelloWorldSvc getHelloWorldSvc() { return helloWorldSvc; } public void setHelloWorldSvc(HelloWorldSvc helloWorldSvc) { this.helloWorldSvc = helloWorldSvc; } public void init() { System.out.println("OSGi client started."); if (helloWorldSvc != null) { System.out.println("Calling sayHello()"); helloWorldSvc.sayHello(); // Invoke the OSGi service! } } }
The Client
class defines a getter and a setter method for the helloWorldSvc
bean property, which enables it to receive the reference to the Hello World service by injection. The init()
method is called during the bean initialization phase, after property injection, which means that it is normally possible to invoke the Hello World service within the scope of this method.
10.5.7. Running the client bundle
To install and run the osgi-client
project, perform the following steps:
Build the project—open a command prompt and change directory to
ProjectDir/osgi-client
. Use Maven to build the demonstration by entering the following command:mvn install
If this command runs successfully, the
ProjectDir/osgi-client/target
directory should contain the bundle file,osgi-client-1.0-SNAPSHOT.jar
.Install and start the osgi-service bundle—at the Red Hat Fuse console, enter the following command:
karaf@root()> bundle:install -s file:ProjectDir/osgi-client/target/osgi-client-1.0-SNAPSHOT.jar
Where ProjectDir is the directory containing your Maven projects and the
-s
flag directs the container to start the bundle right away. For example, if your project directory isC:\Projects
on a Windows machine, you would enter the following command:karaf@root()> bundle:install -s file:C:/Projects/osgi-client/target/osgi-client-1.0-SNAPSHOT.jar
NoteOn Windows machines, be careful how you format the
file
URL—for details of the syntax understood by thefile
URL handler, see Section 12.1, “File URL Handler”.Client output—f the client bundle is started successfully, you should immediately see output like the following in the console:
Bundle ID: 239 OSGi client started. Calling sayHello() Hello World!
10.6. Integration with Apache Camel
10.6.1. Overview
Apache Camel provides a simple way to invoke OSGi services using the Bean language. This feature is automatically available whenever a Apache Camel application is deployed into an OSGi container and requires no special configuration.
10.6.2. Registry chaining
When a Apache Camel route is deployed into the OSGi container, the CamelContext
automatically sets up a registry chain for resolving bean instances: the registry chain consists of the OSGi registry, followed by the Blueprint registry. Now, if you try to reference a particular bean class or bean instance, the registry resolves the bean as follows:
- Look up the bean in the OSGi registry first. If a class name is specified, try to match this with the interface or class of an OSGi service.
- If no match is found in the OSGi registry, fall back on the Blueprint registry.
10.6.3. Sample OSGi service interface
Consider the OSGi service defined by the following Java interface, which defines the single method, getGreeting()
:
package org.fusesource.example.hello.boston; public interface HelloBoston { public String getGreeting(); }
10.6.4. Sample service export
When defining the bundle that implements the HelloBoston
OSGi service, you could use the following Blueprint configuration to export the service:
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="hello" class="org.fusesource.example.hello.boston.HelloBostonImpl"/>
<service ref="hello" interface="org.fusesource.example.hello.boston.HelloBoston"/>
</blueprint>
Where it is assumed that the HelloBoston
interface is implemented by the HelloBostonImpl
class (not shown).
10.6.5. Invoking the OSGi service from Java DSL
After you have deployed the bundle containing the HelloBoston
OSGi service, you can invoke the service from a Apache Camel application using the Java DSL. In the Java DSL, you invoke the OSGi service through the Bean language, as follows:
from("timer:foo?period=5000")
.bean(org.fusesource.example.hello.boston.HelloBoston.class, "getGreeting")
.log("The message contains: ${body}")
In the bean
command, the first argument is the OSGi interface or class, which must match the interface exported from the OSGi service bundle. The second argument is the name of the bean method you want to invoke. For full details of the bean
command syntax, see Apache Camel Development Guide Bean Integration .
When you use this approach, the OSGi service is implicitly imported. It is not necessary to import the OSGi service explicitly in this case.
10.6.6. Invoking the OSGi service from XML DSL
In the XML DSL, you can also use the Bean language to invoke the HelloBoston
OSGi service, but the syntax is slightly different. In the XML DSL, you invoke the OSGi service through the Bean language, using the method
element, as follows:
<beans ...>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="timer:foo?period=5000"/>
<setBody>
<method ref="org.fusesource.example.hello.boston.HelloBoston" method="getGreeting"/>
</setBody>
<log message="The message contains: ${body}"/>
</route>
</camelContext>
</beans>
When you use this approach, the OSGi service is implicitly imported. It is not necessary to import the OSGi service explicitly in this case.