Chapter 5. The Service Registry

Table of Contents

Exporting a Spring bean as an OSGi service
Controlling the set of advertised service interfaces for an exported service
Controlling the set of advertised properties for an exported service
The depends-on attribute
The context-class-loader attribute
The ranking attribute
service element attribute
Service registration and unregistration lifecycle
Defining references to OSGi services
Referencing an individual service
Referencing a collection of services
Dealing with the dynamics of OSGi imported services
Listener and service proxies
Accessing the caller BundleContext
Exporter/Importer listener best practices
Listener and cyclic dependencies
Service importer global defaults
Relationship between the service exporter and service importer

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 E, 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"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:osgi="http://www.springframework.org/schema/osgi"
   xsi:schemaLocation="http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi  
       http://www.springframework.org/schema/osgi/spring-osgi.xsd">

    <osgi:service id="simpleServiceOsgi" ref="simpleService"
      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 
   xmlns="http://www.springframework.org/schema/osgi"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:beans="http://www.springframework.org/schema/beans"
   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">

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

</beans:beans>

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 4.1 to use a dedicated configuration file for all OSGi-related declarations.

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"/>

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.

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

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 this appendix for more details.

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"/>

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 .

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"/>

service element attribute

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

Table 5.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-loaderunmanaged | service-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-exportdisabled | interfaces | class-hierarchy | all-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.

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"
    registration-method="serviceRegistered"
    unregistration-method="serviceUnregistered"/>
  <registration-listener
     registration-method="register">
     <bean class="SomeListenerClass"/>
  </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.

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.