Defining references to OSGi services

Spring Dynamic Modules supports the declaration of beans that represent services accessed via the OSGi Service Registry. In this manner references to OSGi services can be injected into application components. The service lookup is made using the service interface type that the service is required to support, plus an optional filter expression that matches against the service properties published in the registry.

For some scenarios, a single matching service that meets the application requirements is all that is needed. The reference element defines a reference to a single service that meets the required specification. In other scenarios, especially when using the OSGi whiteboard pattern, references to all available matching services are required. Spring Dynamic Modules supports the management of this set of references as a List, Set collection.

Referencing an individual service

The reference element is used to define a reference to a service in the service registry.

Since there can be multiple service matching a given description, the service returned is the service that would be returned by a call to BundleContext.getServiceReference. This means that the service with the highest ranking will be returned, or if there is a tie in ranking, the service with the lowest service id (the service registered first with the framework) is returned (please see Section 5 from the OSGi spec for more information on the service selection algorithm).

Controlling the set of advertised interfaces for the imported service

The interface attribute identifies the service interface that a matching service must implement. For example, the following declaration creates a bean messageService, which is backed by the service returned from the service registry when querying it for a service offering the MessageService interface.

<reference id="messageService" interface="com.xyz.MessageService"/>

Just like the service declaration, when specifying multiple interfaces, use the nested interfaces element instead of interface attribute:

<osgi:reference id="importedOsgiService">
  <osgi:interfaces>
     <value>com.xyz.MessageService</value>
     <value>com.xyz.MarkerInterface</value>
  </osgi:interfaces>
</osgi:reference>

It is illegal to use both interface attribute and interfaces element at the same time - use only one of them.

The bean defined by reference element implements all of the advertised interfaces of the service that are visible to the bundle (called greedy proxying). If the registered service interfaces include Java class types (as opposed to interface types) then support for these types is subject to the restrictions of Spring's AOP implementation (see the Spring Reference Guide). In short, if the specified interfaces are classes (rather then interfaces), then cglib library must be available, and final methods are not supported.

The filter attribute

The optional filter attribute can be used to specify an OSGi filter expression and constrains the service registry lookup to only those services that match the given filter.

For example:

<reference id="asyncMessageService" interface="com.xyz.MessageService"
  filter="(asynchronous-delivery=true)"/>

will match only OSGi services that advertise MessageService interface and have the property named asynchronous-delivery set to value true.

The bean-name attribute

The bean-name attribute is a convenient short-cut for specifying a filter expression that matches on the bean-name property automatically set when exporting a bean using the service element (see the section called “Exporting a Spring bean as an OSGi service”).

For example:

<reference id="messageService" interface="com.xyz.MessageService"
   bean-name="defaultMessageService"/>

will match only OSGi services that advertise MessageService interface and have the property named org.springframework.osgi.bean.name set to value defaultMessageService. In short, this means finding all Spring-DM exported beans that implement interface MessageService and are named defaultMessageService.

The cardinality attribute

The cardinality attribute is used to specify whether or not a matching service is required at all times. A cardinality value of 1..1 (the default) indicates that a matching service must always be available. A cardinality value of 0..1 indicates that a matching service is not required at all times (see section 4.2.1.6 for more details). A reference with cardinality 1..1 is also known as a mandatory service reference and, by default, application context creation is deferred until the reference is satisfied.

Note

It is an error to declare a mandatory reference to a service that is also exported by the same bundle, this behavior can cause application context creation to fail through either deadlock or timeout.

The depends-on attribute

The depends-on attribute is used to specify that the service reference should not be looked up in the service registry until the named dependent bean has been instantiated.

The context-class-loader attribute

The OSGi Service Platform Core Specification (latest version is 4.1 at time of writing) does not specify what types and resources are visible through the context class loader when an operation is invoked on a service obtained via the service registry. Since some services may use libraries that make certain assumptions about the context class loader, Spring Dynamic Modules enables you to explicitly control the context class loader during service invocation. This is achieved using the option context-class-loader attribute of the reference element.

The permissible values for the context-class-loader attribute are:

  • client - during the service invocation, the context class loader is guaranteed to be able to see types on the classpath of the invoking bundle. This is the default option.

  • service-provider - during the service invocation, the context class loader is guaranteed to be able to see types on the classpath of the bundle exporting the service.

  • unmanaged - no context class loader management will occur during the service invocation

reference element attributes

As a summary, the following table lists the reference element attributes names, possible values and a short description for each of them.

Table 5.2. OSGi <reference> attributes

NameValuesDescription
interfacefully qualified class name (such as java.lang.Thread)The fully qualified name of the class under which the object will be exported.
filterOSGi filter expression (such as ((asynchronous-delivery=true))OSGi filter expression that is used to constrain the set of matching services in the service registry.
bean-nameany string valueConvenient shortcut for specifying a filter expression that matches on the bean-name property that is automatically advertised for beans published using the <service> element.
context-class-loaderclient | service-provider | unmanagedDefines how the context class loader is managed when invoking operations on a service backing this service reference. The default value is client which means that the context class loader has visibility of the resources on this bundle's classpath. Alternate options are service-provider which means that the context class loader has visibility of resources on the bundle classpath of the bundle that exported the service, and unmanaged which does not do any management of the context class loader.
cardinality0..1 | 1..1Defines the required cardinality of the relationship to the backing service. If not specified, the default-cardinality attribute will apply. A value is '1..1' means that a backing service must exist (this is a mandatory service reference). A value of '0..1' indicates that it is acceptable to be no backing service (an optional service reference).
timeoutany positive longThe amount of time (in milliseconds) to wait for a backing service to be available when an operation is invoked. If not specified, the default-timeout attribute will apply.

reference and OSGi Service Dynamics

The bean defined by the reference element is unchanged throughout the lifetime of the application context (the object reference remains constant). However, the OSGi service that backs the reference may come and go at any time. For a mandatory service reference (cardinality 1..1), creation of the application context will block until a matching service is available. For an optional service reference (cardinality 0..1), the reference bean will be created immediately, regardless of whether or not there is currently a matching service.

When the service backing a reference bean goes away, Spring Dynamic Modules tries to replace the backing service with another service matching the reference criteria. An application may be notified of a change in backing service by registering a listener. If no matching service is available, then the reference is said to be unsatisfied. An unsatisfied mandatory service causes any exported service (service bean) that depends on it to be unregistered from the service registry until such time as the reference is satisfied again. See the section called “Relationship between the service exporter and service importer” for more information.

When an operation is invoked on an unsatisfied reference bean (either optional or mandatory), the invocation blocks until the reference becomes satisfied. The optional timeout attribute of the reference element enables a timeout value (in milliseconds) to be specified. If a timeout value is specified and no matching service becomes available within the timeout period, an unchecked ServiceUnavailableException is thrown.

Getting a hold of the managed service service reference

Spring-DM can automatically convert a managed OSGi service to service reference. That is, if the property into which a reference bean is to be injected, has type ServiceReference (instead of the service interface supported by the reference), then the managed OSGi ServiceReference for the service will be injected in place of the service itself:

public class BeanWithServiceReference {
	private ServiceReference serviceReference;
	private SomeService service;
			
	// getters/setters ommitted
}
<reference id="service" interface="com.xyz.SomeService"/>
		
<bean id="someBean" class="BeanWithServiceReference">
  <property name="serviceReference" ref="service"/> 
  <property name="service" ref="service"/>
</bean>

1

Automatic managed service to ServiceReference conversion.

2

Managed service is injected without any conversion

Note

The injected ServiceReference is managed by Spring-DM and will change at the same time as the referenced backing OSGi service instance.

Referencing a collection of services

Sometimes an application needs access not simply to any service meeting some criteria, but to all services meeting some criteria. Spring-DM allows the matching services may be held in a List or Set (optionally sorted).

The difference between using a List and a Set to manage the collection is one of equality. Two or more services published in the registry (and with distinct service ids) may be "equal" to each other, depending on the implementation of equals used by the service implementations. Only one such service will be present in a set, whereas all services returned from the registry will be present in a list. For more details on collections, see this tutorial.

The set and list schema elements are used to define collections of services with set or list semantics respectively.

These elements support the attributes interface, filter, bean-name, cardinality, and context-class-loader, with the same semantics as for the reference element. The allowable values for the cardinality attribute are 0..N and 1..N.

A cardinality value of 0..n indicates that it is permissible for their to be no matching services. A cardinality value of 1..n indicates that at least one matching service is required at all times. Such a reference is considered a mandatory reference and any exported services from the same bundle (service defined beans) that depend on a mandatory reference will automatically be unregistered when the reference becomes unsatisfied, and reregistered when the reference becomes satisfied again.

The bean defined by a list element is of type java.util.List. The bean defined by a set element is of type java.util.Set.

The following example defines a bean of type List that will contain all registered services supporting the EventListener interface:

<list id="myEventListeners" 
  interface="com.xyz.EventListener"/>

The members of the collection defined by the bean are managed dynamically by Spring. As matching services are registered and unregistered in the service registry, the collection membership will be kept up to date. Each member of the collection supports the service interfaces that the corresponding service was registered with and that are visible to the bundle.

Spring-DM supports sorted collections as well, both for set and list.

It is possible to specify a sorting order using either the comparator-ref attribute, or the nested comparator element. The comparator-ref attribute is used to refer to a named bean implementing java.util.Comparator. The comparator element can be used to define an inline bean. For example:

<set id="myServices" interface="com.xyz.MyService"
  comparator-ref="someComparator"/>

<list id="myOtherServices" 
  interface="com.xyz.OtherService">
  <comparator>
     <beans:bean class="MyOtherServiceComparator"/>
  </comparator>
</list>

To sort using a natural ordering instead of an explicit comparator, you can use the natural-ordering element inside of comparator. You need to specify the basis for the natural ordering: based on the service references, following the ServiceReference natural ordering defined in the OSGi Core Specification section 6.1.2.3; or based on the services themselves (in which case the services must be Comparable).

<list id="myServices" interface"com.xyz.MyService">
  <comparator><natural-ordering basis="services"/></comparator>
</list>

<set id="myOtherServices"interface="com.xyz.OtherService">
  <comparator><natural-ordering basis="service-references"/></comparator>
</set>

For a sorted set, a SortedSet implementation will be created. However, since JDK API do not provide a dedicated SortedListinterface, the sorted list will implement only the List interface.

Collection (list and set) element attributes

list and set elements support all the attributes available to reference element except the timeout attribute. See the following table as a summary of the list and set element attribute names, possible values and a short description for each of them.

Table 5.3. <list>/<set> attributes

NameValuesDescription
interfacefully qualified class name (such as java.lang.Thread)The fully qualified name of the class under which the object will be exported.
filterOSGi filter expression (such as ((asynchronous-delivery=true))OSGi filter expression that is used to constrain the set of matching services in the service registry.
bean-nameany string valueConvenient shortcut for specifying a filter expression that matches on the bean-name property that is automatically advertised for beans published using the <service> element.
context-class-loaderclient | service-provider | unmanagedDefines how the context class loader is managed when invoking operations on a service backing this service reference. The default value is client which means that the context class loader has visibility of the resources on this bundle's classpath. Alternate options are service-provider which means that the context class loader has visibility of resources on the bundle classpath of the bundle that exported the service, and unmanaged which does not do any management of the context class loader.
cardinality0..N | 1..NDefines the required cardinality of the relationship to the backing service. If not specified, the default-cardinality attribute will apply. A value is '1..N' means that a backing service must exist (this is a mandatory service reference). A value of '0..N' indicates that it is acceptable to be no backing service (an optional service reference).
comparator-refany string valueNamed reference to a bean acting as comparator for the declaring collection. Declaring a comparator automatically makes the declaring collection sorted.

The table below lists the attributes available for the comparator/natural sub element.

Table 5.4. collection <comparator> attributes

NameValuesDescription
basisservice | service-referenceIndicate the element on which natural ordering should apply - service for considering the service instance and service-reference for considering the service reference instead of the service.

list / set and OSGi Service Dynamics

A collection of OSGi services will change its content during the lifetime of the application context since it needs to reflect the state of the OSGi space. As service are registered and unregistered, they will be added or removed from the collection.

While a reference declaration will try to find a replacement if the backing service is unregistered, the collection will simply remove the service from the collection. Like reference, a collection with cardinality 1..N is said to be mandatory while a collection with cardinality 0..N is referred to as being optional. If no matching service is available then only mandatory collections become unsatisfied. That is if no service is available invoking an operation on:

  • mandatory collection - will throw an unchecked ServiceUnavailableException.
  • optional collection - will not throw any exceptions (however the collection will be empty).

Just like reference, mandatory collections will trigger the unregistration of any exported service that depends upon it. See the section called “Relationship between the service exporter and service importer” for more information.

Iterator contract and service collections

The recommend way of traversing a collection is by using an Iterator. However, since OSGi services can come and go, the content of the managed service collection will be adjusted accordingly. Spring-DM will transparently update all Iterators held by the user so it is possible to safely traverse the collection while it is being modified. Moreover, the Iterators will reflect all the changes made to the collection, even if they occurred after the Iterators were created (that is during the iteration). Consider a case where a collection shrinks significantly (for example a big number of OSGi services are shutdown) right after an iteration started. To avoid dealing with the resulting 'dead' service references, Spring-DM iterators do not take collection snapshots (that can be inaccurate) but rather are updated on each service event so they reflect the latest collection state, no matter how fast or slow the iteration is.

It is important to note that a service update will only influence Iterator operations that are executed after the event occurred. Services already returned by the iterator will not be updated even if the backing service has been unregistered. As a side note, if an operation is invoked on such a service that has been unregistered, a ServiceUnavailableException will be thrown.

To conclude, while a reference declaration will search for candidates in case the backing service has been unregistered, a service collections will not replace unregistered services returned to the user. However, it will remove the unregistered services from the collection so future iterations will not encounter them.

Please note that the Iterator contract is guaranteed meaning that next() method always obey the result of the previous hasNext() invocation.

Table 5.5. Dynamic service collection Iterator contract

hasNext() returned valuenext() behaviour
trueAlways return a non-null value, even when the collection has shrunk as services when away.
falseper Iterator contract, NoSuchElementException is thrown. This applies even if other services are added to the collection

The behaviour described above, offers a consistent view over the collection even if its structure changes during iteration. To simply refresh the iterator, call hasNext() again. This will force the Iterator to check again the collection status for its particular entry in the iteration.

In addition, any elements added to the collection during iteration over a sorted collection will only be visible if the iterator has not already passed their sort point.

Dealing with the dynamics of OSGi imported services

Whether you are using reference or set or list, Spring Dynamic Modules will manage the backing service. However there are cases where the application needs to be aware when the backing service is updated.

Such applications, that need to be aware of when the service backing a reference bean is bound and unbound, can register one or more listeners using the nested listener element. This element is available on both reference and set, list declarations. In many respects, the service importer listener declaration is similar to the service exporter listener declaration (the section called “Service registration and unregistration lifecycle”). The listener element refers to a bean (either by name, or by defining one inline) that will receive bind and unbind notifications. If this bean implements Spring-DM's org.springframework.osgi.service.importer.OsgiServiceLifecycleListener interface, then the bind and unbind operations in this interface will be invoked. Instead of implementing this interface (or in addition), custom bind and unbind callback methods may be named.

An example of declaring a listener that implements OsgiServiceLifecycleListener:

<reference id="someService" interface="com.xyz.MessageService">
  <listener ref="aListenerBean"/>
</reference>

An example of declaring an inline listener bean with custom bind and unbind methods:

<reference id="someService" interface="com.xyz.MessageService">
  <listener bind-method="onBind" unbind-method="onUnbind">
     <beans:bean class="MyCustomListener"/>
  </listener>
</reference>

If the listener bean implements the OsgiServiceLifecycleListener interface and the listener definition specifies custom bind and unbind operations then both the OsgiServiceLifecycleListener operation and the custom operation will be invoked, in that order.

The signature of a custom bind or unbind method must be one of:

public void anyMethodName(ServiceType service, Dictionary properties);

public void anyMethodName(ServiceType service, Map properties);

public void anyMethodName(ServiceReference ref);

where ServiceType can be any type. Please note that bind and unbind callbacks are invoked only if the backing service matches the type declared in the method signature( ServiceType). If you want the callbacks to be called no matter the type, use java.lang.Object as a ServiceType.

The properties parameter contains the set of properties that the service was registered with.

If the method signature has a single argument of type ServiceReference then the ServiceReference of the service will be passed to the callback in place of the service object itself.

When the listener is used with a reference declaration:

  • A bind callback is invoked when the reference is initially bound to a backing service, and whenever the backing service is replaced by a new backing service.
  • An unbind callback is only invoked when the current backing service is unregistered, and no replacement service is immediately available (i.e., the reference becomes unsatisfied).

When the listener is used with a collection declaration (set or list):

  • A bind callback is invoked when a new service is added to the collection.
  • An unbind callback is invoked when a service is unregistered and is removed from the collection.

Again note that service collections there is no notion of service rebind: services are added or removed from the collection.

Bind and unbind callbacks are made synchronously as part of processing an OSGi serviceChanged event for the backing OSGi service, and are invoked on the OSGi thread that delivers the corresponding OSGi ServiceEvent.

The table below lists the attributes available for the reference listener sub element.

Table 5.6. OSGi <listener> attributes

NameValuesDescription
refbean name referenceName based reference to another bean acting as listener.
bind-methodstring representing a valid method nameThe name of the method to be invoked when a backing service is bound.
unbind-methodstring representing a valid method nameThe name of the method to be invoked when a backing service is bound.

Listener and service proxies

While the importer listener provides access to the OSGi service bound at a certain point, it is important to note that the given argument is not the actual service but a proxy. This can have subtle side effects especially with regards to service class name and identity. The reason behind using a proxy is to prevent the listener from holding strong reference to the service (which can disappear at any point). Listeners interested in tracking certain services should not rely on instance equality (==). Object equality (equals/hashcode) can be used but only if the backing service has exposed the aforementioned methods as part of its contract (normally by declaring them on a certain published interface/class). If these methods are not published, the proxy will invoke its own method, not the targets. This is on purpose since, while the proxy tries to be as transparent as possible, it is up to the developer to define the desired semantics.

Thus, it is recommended (especially for reference importers) to do tracking based on just the service interface/contract (not identity), service properties (see org.osgi.framework.Constants#SERVICE_ID) or service notification (bind/unbind).

Accessing the caller BundleContext

It is sometime useful for an imported service to know which bundle is using it at a certain time. To help with this scenarion, in Spring-DM imported services publish the importing bundle BundleContext through LocalBundleContext class. Each time a method on the importer is invoked, the caller BundleContext will be made available, using a ThreadLocal, through getInvokerBundleContext().

Please be careful when using this class since it ties your code to Spring-DM API.