This section describes how to obtain and use references to OSGi services that
have been exported to the OSGi service registry. Essentially, you can use either
the reference
element or the reference-list
element to
import an OSGi service. The key difference between these elements is
not (as you might at first be tempted to think) that
reference
returns a single service reference, while
reference-list
returns a list of service references. Rather,
the real difference is that the reference
element is suitable for
accessing stateless services, while the
reference-list
element is suitable for accessing
stateful services.
The following models for obtaining OSGi services references are supported:
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 shows an overview of the
model for accessing a stateless service using the reference manager.
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.
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 shows an overview of
the model for accessing a stateful service using the reference list
manager.
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.
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), 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; } ... }
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), 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; } ... }
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"/>
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)"/>
By default, a reference to an OSGi service is assumed to be mandatory (see
Mandatory dependencies). It is
possible, however, 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; and 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"/>
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.