Chapter 7. The Service Registry

The OSGi service registry enables a bundle to publish objects to a shared registry, advertised via a given set of Java interfaces. Published services also have service properties associated with them in the registry.

Spring Dynamic Modules provides an osgi namespace for Spring (see Appendix H, Spring Dynamic Modules Schema) that can be used to export Spring beans as OSGi services, and to define references to services obtained via the service registry. The namespace elements may be used nested inside another top-level namespace (typically the Spring beans namespace), or within the top-level osgi element.

The following example shows the use of the osgi namespace within the familiar Spring beans element:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"                               (1)
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:osgi="http://www.springframework.org/schema/osgi"                               (2)
   xsi:schemaLocation="http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans.xsd                      (3)
       http://www.springframework.org/schema/osgi  
       http://www.springframework.org/schema/osgi/spring-osgi.xsd">

    <osgi:service id="simpleServiceOsgi" ref="simpleService"                             (4)
      interface="org.xyz.MyService" />
</beans>
1

Use Spring Framework beans schema as the default namespace.

2

Import Spring Dynamic Modules schema and associate a prefix with its namespace (osgi in this example).

3

Make sure to import Spring beans schema version 2.5.

4

Use Spring Dynamic Modules elements using the declared namespace prefix (in this example osgi).

Using the OSGi namespace as a top-level namespace, the same service would be declared as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans                                                                             (1)
   xmlns="http://www.springframework.org/schema/osgi"                                    (2)
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:beans="http://www.springframework.org/schema/beans"                             (3)
   xsi:schemaLocation="http://www.springframework.org/schema/osgi  
       http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans.xsd">                    (4)

    <service id="simpleServiceOsgi" ref="simpleService"                                  (5)
       interface="org.xyz.MyService" />

</beans:beans>                                                                           (1)
1

beans root element has to be prefixed with Spring Framework beans schema prefix (beans in this example).

2

Use Spring Dynamic Modules schema as the default namespace.

3

Import Spring Framework beans schema and associate a prefix with its namespace (beans in this example).

4

Make sure to import Spring beans schema version 2.5.

5

Use Spring Dynamic Modules elements without any prefix.

Using the OSGi namespace as a top-level namespace is particularly convenient when following the recommendation of Section 6.1, “Bundle Format And Manifest Headers” to use a dedicated configuration file for all OSGi-related declarations.

7.1. Exporting A Spring Bean As An OSGi Service

The service element is used to define a bean representing an exported OSGi service. At a minimum you must specify the bean to be exported, and the service interface that the service advertises.

For example, the declaration

<service ref="beanToPublish" interface="com.xyz.MessageService"/>

exports the bean with name beanToPublish with interface com.xyz.MessageService. The published service will have a service property with the name org.springframework.osgi.bean.name set to the name of the target bean being registered (beanToPublish in this case).

The bean defined by the service element is of type org.osgi.framework.ServiceRegistration and is the ServiceRegistration object resulting from registering the exported bean with the OSGi service registry. By giving this bean an id you can inject a reference to the ServiceRegistration object into other beans if needed. For example:

<service id="myServiceRegistration" ref="beanToPublish"
    interface="com.xyz.MessageService"/>

As an alternative to exporting a named bean, the bean to be exported to the service registry may be defined as an anonymous inner bean of the service element. Typically the top-level namespace would be the beans namespace when using this style:

<osgi:service interface="com.xyz.MessageService">
  <bean class="SomeClass">
     ...
  </bean>
</osgi:service>

If the bean to be exported implements the org.osgi.framework.ServiceFactory interface then the ServiceFactory contract is honored as per section 5.6 of the OSGi Service Platform Core Specification. As an alternative to implementing this OSGi API, Spring Dynamic Modules introduces a new bean scope, the bundle scope. When a bean with bundle scope is exported as an OSGi service then one instance of the bean will be created for each unique client (service importer) bundle that obtains a reference to it through the OSGi service registry. When a service importing bundle is stopped, the bean instance associated with it is disposed. To declare a bean with bundle scope simply use the scope attribute of the bean element:

<osgi:service ref="beanToBeExported" interface="com.xyz.MessageService"/>

<bean id="beanToBeExported" scope="bundle" class="com.xyz.MessageServiceImpl"/>

7.1.1. Controlling The Set Of Advertised Service Interfaces For An Exported Service

The OSGi Service Platform Core Specification defines the term service interface to represent the specification of a service's public methods. Typically this will be a Java interface, but the specification also supports registering service objects under a class name, so the phrase service interface can be interpreted as referring to either an interface or a class.

There are several options for specifying the service interface(s) under which the exported service is registered. The simplest mechanism, shown above, is to use the interface attribute to specify a fully-qualified interface name. To register a service under multiple interfaces the nested interfaces element can be used in place of the interface attribute.

<osgi:service ref="beanToBeExported">
  <osgi:interfaces>
     <value>com.xyz.MessageService</value>
     <value>com.xyz.MarkerInterface</value>
  </osgi:interfaces>
</osgi:service>

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

7.1.1.1. Detecting The Advertised Interfaces At Runtime

Using the auto-export attribute you can avoid the need to explicitly declare the service interfaces at all by analyzing the object class hierarchy and its interfaces.

The auto-export attribute can have one of four values:

  • disabled : the default value; no auto-detected of service interfaces is undertaken and the interface attribute or interfaces element must be used instead.

  • interfaces : the service will be registered using all of the Java interface types implemented by the bean to be exported

  • class-hierarchy : the service will be registered using the exported bean's implementation type and super-types

  • all-classes : the service will be registered using the exported bean's implementation type and super-types plus all interfaces implemented by the bean.

auto-export and interface(s) option are not exclusive; both can be used at the same time for fine grained control over the advertised interfaces if there is such a need. However, the former option should be enough for most cases.

For example, to automatically register a bean under all of the interfaces that it supports you would declare:

<service ref="beanToBeExported" auto-export="interfaces"/>

Given the interface hierarchy:

public interface SuperInterface {}

public interface SubInterface extends SuperInterface {}

then a service registered as supporting the SubInterface interface is not considered a match in OSGi when a lookup is done for services supporting the SuperInterface interface. For this reason it is a best practice to export all interfaces supported by the service being registered explicitly, using either the interfaces element or auto-export="interfaces".

7.1.2. Controlling The Set Of Advertised Properties For An Exported Service

As previously described, an exported service is always registered with the service property org.springframework.osgi.bean.name set to the name of the bean being exported. Additional service properties can be specified using the nested service-properties element. The service-properties element contains key-value pairs to be included in the advertised properties of the service. The key must be a string value, and the value must be a type recognized by OSGi Filters. See section 5.5 of the OSGi Service Platform Core Specification for details of how property values are matched against filter expressions.

The service-properties element must contain at least one nested entry element from the Spring beans namespace. For example:

<service ref="beanToBeExported" interface="com.xyz.MyServiceInterface">
  <service-properties>
    <beans:entry key="myOtherKey" value="aStringValue"/>
    <beans:entry key="aThirdKey" value-ref="beanToExposeAsProperty"/>
  </service-properties>
</service>

The Spring Dynamic Modules roadmap includes support for exporting properties registered in the OSGi Configuration Administration service as properties of the registered service. See Appendix F, Roadmap for more details.

7.1.3. The depends-on Attribute

Spring will manage explicit dependencies of a service element, ensuring for example that the bean to be exported as a service is fully constructed and configured before exporting it. If a service has implicit dependencies on other components (including other service elements) that must be fully initialized before the service can be exported, then the optional depends-on attribute can be used to express these dependencies.

<service ref="beanToBeExported" interface="com.xyz.MyServiceInterface"
     depends-on="myOtherComponent"/>

7.1.4. The context-class-loader Attribute

The OSGi Service Platform Core Specification (most current 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 execution. This is achieved using the option context-class-loader attribute of the service element.

The permissible values for the context-class-loader attribute are unmanaged (the default) and service-provider. When the service-provider value is specified, Spring Dynamic Modules ensures that the context class loader can see all of the resources on the class path of the bundle exporting the service.

When setting context-class-loader to service-provider, the service object will be proxied to handle the class loader. If the service advertises any concrete class then CGLIB library is required .

7.1.5. The ranking Attribute

When registering a service with the service registry, you may optionally specify a service ranking (see section 5.2.5 of the OSGi Service Platform Core Specification). When a bundle looks up a service in the service registry, given two or more matching services the one with the highest ranking will be returned. The default ranking value is zero. To explicitly specify a ranking value for the registered service, use the optional ranking attribute.

<service ref="beanToBeExported" interface="com.xyz.MyServiceInterface"
  ranking="9"/>

7.1.6. service Element Attributes

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

Table 7.1. OSGi <service> 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
refany bean nameReference to the named bean to be exported as a service in the service registry.
context-class-loaderunmanagedservice-providerDefines how the context class loader will be managed when an operation is invoked on the exported service. The default value is unmanaged which means that no management of the context class loader is attempted. A value of service-provider guarantees that the context class loader will have visibility of all the resources on the class path of bundle exporting the service.
auto-exportdisabledinterfacesclass-hierarchyall-classesEnables Spring to automatically manage the set of service interfaces advertised for the service. By default this facility is disabled. A value of interfaces advertises all of the Java interfaces supported by the exported service. A value of class-hierarchy advertises all the Java classes in the hierarchy of the exported service. A value of all-classes advertises all Java interfaces and classes.
rankingany integer valueSpecify the service ranking to be used when advertising the service. Default value is 0.

7.1.7. Service Registration And Unregistration Lifecycle

The service defined by a service element is registered with the OSGi service registry when the application context is first created. It will be unregistered automatically when the bundle is stopped and the application context is disposed.

If you need to take some action when a service is unregistered because its dependencies are not satisfied (or when it is registered), then you can define a listener bean using the nested registration-listener element.

The declaration of a registration listener must use either the ref attribute to refer to a top-level bean definition, or declare an anonymous listener bean inline. For example:

<service ref="beanToBeExported" interface="SomeInterface">
  <registration-listener ref="myListener"                                                (1)
    registration-method="serviceRegistered"                                              (2)
    unregistration-method="serviceUnregistered"/>                                        (2)
  <registration-listener
     registration-method="register">                                                     (3)
     <bean class="SomeListenerClass"/>                                                   (4)
  </registration-listener>
</service>
1

Listener declaration referring to a top-level bean declaration.

2

Indicate the registration and unregistration methods.

3

Declare only a registration custom method for this listener.

4

Nested listener bean declaration.

The optional registration-method and unregistration-method attributes specify the names of the methods defined on the listener bean that are to be invoked during registration and unregistration. A registration and unregistration callback methods must have a signature matching one of the following formats:

public void anyMethodName(ServiceType serviceInstance, Map serviceProperties);
public void anyMethodName(ServiceType serviceInstance, Dictionary serviceProperties);

where ServiceType can be any type compatible with the exported service interface of the service.

The register callback is invoked when the service is initially registered at startup, and whenever it is subsequently re-registered. The unregister callback is invoked during the service unregistration process, no matter the cause (such as the owning bundle stopping).

Spring DM will use the declared ServiceType argument type and invoke the registration/unregistration method only when a service of a compatible type will be registered/unregistered.

serviceProperties represents a map holding all the properties of the registered/unregistered service. To preserve compatibility with the OSGi specification this argument can be cast, if needed, to a java.util.Dictionary.

7.1.7.1. Using OsgiServiceRegistrationListener Interface

While we discourage, it is possible to implement a Spring DM specific interface, namely org.springframework.osgi.service.exporter.OsgiServiceRegistrationListener which avoids the need to declare the registration-method and unregistration-method. However, by implementing OsgiServiceRegistrationListener, your code becomes Spring DM aware (which goes against the POJO philosophy).

It is possible for a listener to implement OsgiServiceRegistrationListener interface and declare custom methods. In this case, the Spring DM interface methods will be called first, followed by the custom methods.

7.2. 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.

7.2.1. 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).

7.2.1.1. 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.

7.2.1.2. 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.

7.2.1.3. 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 Section 7.1, “Exporting A Spring Bean As An OSGi Service”).

Consider the following exporter/importer declarations:

<bean id="(1)messageServiceBean" scope="bundle" class="com.xyz.MessageServiceImpl"/>
<!-- service exporter -->
<osgi:service id="messageServiceExporter" ref="(1)messageServiceBean" interface="com.xyz.MessageService"/>
<osgi:reference id="messageService" interface="com.xyz.MessageService"
   bean-name="(1)messageServiceBean"/>
1

the name used with bean-name attribute

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

7.2.1.4. 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 7.2.1.8, “reference And OSGi Service Dynamics” 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. More information about context creation and mandatory dependencies is available at Section 7.2.1.8, “reference And OSGi Service Dynamics”

[Note]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.

7.2.1.5. 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.

7.2.1.6. 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

7.2.1.7. 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 7.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-loaderclientservice-providerunmanagedDefines 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..11..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.

7.2.1.8. 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 Section 7.5, “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.

7.2.1.9. Getting A Hold Of The Managed 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"/>                                      (1)
  <property name="service" ref="service"/>                                               (2)
</bean>
1

Automatic managed service to ServiceReference conversion.

2

Managed service is injected without any conversion

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

There are cases when the managed ServiceReference is needed to get a hold of the OSGi service. Unfortunately, most of the OSGi frameworks expect their own ServiceReference classes and will fail when the Spring DM managed reference is used. For such cases, one can get a hold of the native ServiceReference bound at that moment, by casting the reference object to ServiceReferenceProxy and then calling getTargetServiceReference. Using the example context above, one might use the following code:

ServiceReference nativeReference = ((ServiceReferenceProxy)serviceReference).getTargetServiceReference()

The returned nativeReference can be safely passed to the OSGi framework however, since it is not managed by Spring DM, in time, it might refer to a service different then the one backing the imported OSGi service.

To avoid this desynchronization, consider using managed ServiceReference objects mainly for reading the bound OSGi service properties rather then getting a hold of OSGi services (which can be simply injected by Spring DM).

7.2.2. 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 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.

[Note]Note
Make sure the Spring DM collections are injected into properties of compatible types ( for example set into Set or Collection) since otherwise the container will automatically perform type conversion, transforming the Spring DM managed collection into a 'normal' one, unaware of the OSGi dynamics.

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 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 release 4, version 4.1, section 6.1.23; or based on the services themselves (in which case the services must be Comparable).

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

<set id="myOtherServices"interface="com.xyz.OtherService">
  <comparator><natural basis="service-references"/></comparator>
</set>
[Note]Note
For a sorted set, a SortedSet implementation will be created. However, since the JDK API does not provide a dedicated SortedListinterface, the sorted list will implement only the List interface.

7.2.2.1. Greedy Proxying

All OSGi services imported by a Spring DM service collection publish and are type-compatible with the classes declared by the interfaces property. However, some services might expose additional (optional) classes that could be relevant to your application.

For these cases, Spring DM collections offer a dedicated attribute called greedy-proxying which will cause the creates proxies to use all the classes advertised by the imported services, visible to the consuming importing bundle. Thus, it is possible to cast the imported proxies to classes different then those specified in the interfaces. For example, with the following list definition:

<list id="services" interface="com.xyz.SomeService" greedy-proxying="true"/>

one can do the following iteration (assuming MessageDispatcher type is imported by the bundle):

for (Iterator iterator = services.iterator(); iterator.hasNext();) {
	SomeService service = (SomeService) iterator.next();
	service.executeOperation();
	// if the service implements an additional type
	// do something extra
	if (service instanceof MessageDispatcher) {
		((MessageDispatcher)service).sendAckMessage();
	}
}
[Note]Note
Before using greedy proxies and instanceof statements, consider using a different interface/class for your services which provides better polymorphism and is more object-oriented.

7.2.2.2. 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 7.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-loaderclientservice-providerunmanagedDefines 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..N1..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.
greedy-proxyingtruefalseIndicates whether the proxies created for the imported OSGi services will be generated using just the classes specified (false) or all the classes exported by the service and visible to the importing bundle (true). The default value is false.

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

Table 7.4. collection <comparator> attributes

NameValuesDescription
basisserviceservice-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.

7.2.2.3. 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 Section 7.5, “Relationship Between The Service Exporter And Service Importer” for more information.

7.2.2.4. 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 7.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.

7.2.3. 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 (Section 7.1.7, “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 7.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.

7.2.4. 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).

7.2.5. 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.

7.3. Exporter/Importer Listener Best Practices

As mentioned above, Spring DM exporter and importer allow listeners to be used for receiving notifications on when services are bound, unbound, registered or unregistered. Below you can find some guidance advices when working with listeners:

  • Do not execute long activity tasks inside the listener. If you really have to, use a separate thread for executing the work. The listener are called synchronously and so try to be as fast as possible. Doing work inside the listener prevents other the event to be sent to other listeners and the OSGi service to resume activity.
  • Use listener custom declaration as much as possible - it doesn't tie your code to Spring DM API and it doesn't enforce certain signature names.
  • If find yourself repeating bind/unbind method declarations for your listener definitions, consider using Spring bean definition inheritance to define a common definition that can be reused and customized accordingly.
  • Prefer java.util.Map instead of java.util.Dictionary class. The first is an interface while the latter is a deprecated, abstract class. To preserve compatibility, Spring DM will pass to the listeners a Map implementation that can be casted, if needed, to a Dictionary.
  • Be careful when using overloaded methods: all methods matching a certain service type will be called which is not always expected. Consider the following listener:
    public class MyListener {
        void register((1)Object service, Map properties);
        void register((2)Collection dataService, Map properties);
        void register((3)SortedSet orderedDataService , Map properties);
    }
    1

    Object type - will match all services for which the listener is triggered. This method will be always called.

    2

    Collection type - if this method is called, the Object method is also called.

    3

    SortedSet type - if this method is called, then both the Object and Collection methods are called.

7.3.1. Listener And Cyclic Dependencies

There are cases where an exporter/importer listener needs a reference back to the bean it is defined on:

<bean id="listener" class="cycle.Listener">                                              (1)
    <property name="target" ref="importer" />                                            (2)
</bean>
	
<osgi:reference id="importer" interface="SomeService">                                   (3)
    <osgi:listener bind-method="bind" ref="listener" />                                  (4)
</osgi:reference>
1

Listener bean

2

Dependency listener -> importer

3

Importer declaration

4

Dependency importer -> listener

The declaration above while valid, creates a dependecy between the listener and the importer it is defined upon. In order to create the importer, the listener has to be resolved and created but in order to do that, the importer called service needs to be retrieved (instantiated and configured). This cycle needs to be broken down so that at least one bean can be fully created and configured. This scenario is supported by Spring DM for both exporter and importers however, if the listener is defined as a nested bean, the cycle cannot be resolved:

<osgi:reference id="importer" interface="SomeService">                                   (1)
    <osgi:listener bind-method="bind">                                                   (2)
        <bean class="cycle.Listener">                                                    (3)
            <property name="target" ref="importer" />                                    (4)
        </bean>
    </osgi:listener>
</osgi:reference>
1

OSGi service importer

2

Dependency between importer -> listener

3

Nested listener declaration

4

Dependency nested listener -> importer

The example above will fail since service bean cannot be initialized as it depends on the listener. The same cycle was seen before but in this case there is subtle yet big different from the container perspective - the listener is declared as a nested/inner-bean (hence the missing bean id). Inner beans have the same life cycle as their declaring parents and do not have any name. By definition, they are not tracked by the container and are simply created on demand. Since the importer cannot be partially created and the nested listener cannot be cached, the container cannot break the cycle and create the beans. While the two configurations shown above seem similar, one works while the other does not. Another reason to not use cycles unless you really, really have to.

To conclude, if you need inside the listener to hold a reference to the exporter/importer on which the listener is declared, either declare the listener as a top-level bean (as shown before) or consider doing dependency lookup. However, the latter approach requires extra contextual information such as the BeanFactory to use and the bean name and is more fragile then dependency injection.

[Note]Note

For those interested in the technical details, neither the exporter and importer cannot be partially initialized since they require the application context ClassLoader which is requested through the BeanClassLoaderAware which relies on a buit-in BeanPostProcessor which is applied only after the bean has been configured and is ready for initialization. If the ClassLoader was not required then the exporter/importer could have been partially initialized and the case above supported.

7.4. Service Importer Global Defaults

The osgi namespace offers two global attributes for specifying default behaviours for all importers declared in that file.

Thus, when using the osgi namespace to enclose set, list or reference elements, one can use:

  • default-timeout - can be used to specify the default timeout (in milliseconds) for all importer elements that do not explicitly specify one. For example:

    <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:osgi="http://www.springframework.org/schema/osgi"                            (1)
              osgi:default-timeout="5000">                                                   (2)
    
      <reference id="someService" interface="com.xyz.AService"/>                             (3)
    
      <reference id="someOtherService" interface="com.xyz.BService"
           timeout="1000"/>                                                                  (4)
    
    </beans:beans>
    1

    Declare osgi namespace prefix.

    2

    Declare default-timeout(in miliseconds) on the root element. If the default is not set, it will have a value of 5 minutes. In this example, the default value is 5 seconds.

    3

    This reference will inherit the default timeout value since it does not specify one. This service reference will have a timeout of 5 seconds.

    4

    This reference declares a timeout, overriding the default value. This service reference will have a timeout of 1 second.

  • default-cardinality - can be used to specify the default cardinality for all importer elements that do not explicitly specify one. Possible values are 0..X and 1..X where X is substituted at runtime to 1 for reference elements or N for collection types such as set or list.

    Consider the following example:

    <beans:beans
          xmlns="http://www.springframework.org/schema/osgi"                                 (1)
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:beans="http://www.springframework.org/schema/beans"                          (2)
          xmlns:osgi="http://www.springframework.org/schema/osgi"                            (3)
             osgi:default-cardinality="0..X"                                                 (4)
             default-lazy-init="false">                                                      (5)
    
      <reference id="someService" interface="com.xyz.AService"/>                             (6)
    
      <set id="someSetOfService" interface="com.xyz.BService"/>                              (7)
    
      <list id="anotherListOfServices" interface="com.xyz.CService" cardinality="1..N"/>     (8)
    
    </beans:beans>
    1

    Declare Spring Dynamic Modules schema as the default namespace.

    2

    Import Spring Framework beans schema and associate a prefix with its namespace (beans in this example).

    3

    Import Spring Dynamic Modules schema and associate a prefix with its namespace (osgi in this example). This is required since the global attributes have to be declared to an element (beans) belonging to another schema. To avoid ambiguity, the Spring DM schema is imported under a specified prefix as well.

    4

    Declare default-cardinality on the root element. If the default is not set, it will have a value of 1..N. In this example, the default value is 0..N. Note the osgi prefix added to the global attribute.

    5

    beans element attributes (such as default-lazy-init) do not need a prefix since they are declared as being local and unqualified (see the beans schema for more information).

    6

    The reference declaration will inherit the default cardinality value since it does not specify one. As reference represents a single service, its cardinality will be 0..1.

    7

    The set declaration will inherit the default cardinality value since it does not specify one. As set (or list) represents a collection of service, its cardinality will be 0..N.

    8

    The list declaration specifies its cardinality (1..N), overriding the default value.

The default-* attributes allow for concise and shorter declarations as well as easy propagation of changes (such as increasing or decreasing the timeout).

7.5. Relationship Between The Service Exporter And Service Importer

An exported service may depend, either directly or indirectly, on other services in order to perform its function. If one of these services is considered a mandatory dependency (has cardinality 1..x) and the dependency can no longer be satisfied (because the backing service has gone away and there is no suitable replacement available) then the exported service that depends on it will be automatically unregistered from the service registry - meaning that it is no longer available to clients. If the mandatory dependency becomes satisfied once more (by registration of a suitable service), then the exported service will be re-registered in the service registry.

This automatic unregistering and re-registering of exported services based on the availability of mandatory dependencies only takes into account declarative dependencies. If exported service S depends on bean A, which in turn depends on mandatory imported service M, and these dependencies are explicit in the Spring configuration file as per the example below, then when M becomes unsatisfied S will be unregistered. When M becomes satisfied again, S will be re-registered.

<osgi:service id="S" ref="A" interface="SomeInterface"/>

<bean id="A" class="SomeImplementation">
   <property name="helperService" ref="M"/>
</bean> 

<!-- the reference element is used to refer to a service
     in the service registry -->
<osgi:reference id="M" interface="HelperService"
     cardinality="1..1"/>

If however the dependency from A on M is not established through configuration as shown above, but instead at runtime through for example passing a reference to M to A without any involvement from the Spring container, then Spring Dynamic Modules will not track this dependency.