Chapter 10. Compendium Services

The OSGi Service Platform Service Compendium specification defines a number of additional services that may be supported by OSGi implementations. Spring Dynamic Modules supports an additional "compendium" namespace that provides integration with some of these services. By convention, the prefix osgix is used for this namespace:

<?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:(1)osgix="http://www.springframework.org/schema/osgi-compendium"                      (1)
   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/osgi-compendium                                    (2)
       	http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd       (3)
     http://www.springframework.org/schema/beans   
       	http://www.springframework.org/schema/beans/spring-beans.xsd">

   <!-- use the OSGi namespace elements directly -->
   <service id="simpleServiceOsgi" ref="simpleService"
       interface="org.xyz.MyService" />

   <!-- qualify compendium namespace elements -->
   <osgix:cm-properties id="cm" persistent-id="com.xyz.myapp"/>

</beans:beans>
1

Compendium namespace declaration (bound to osgix prefix)

2

Schema location (namespace URI)

3

XML schema to use for the compendium namespace

At present this namespace provides support for the Configuration Admin service. Support for other compendium services may be added in future releases.

10.1. Configuration Admin

One of the most important compendium services, is the Configuration Admin which, as a name implies, provides configuration to interested bundles through the OSGi service registry. Spring DM provides dedicated support for Configuration Admin (CM), allowing consumption and injection of the configuration data in a declarative way.

10.1.1. Exposing Configuration Admin Entries As Properties

In its simplest form, the CM can be seen as a configuration source, namely a Dictionary whose keys are always Strings. Spring DM can expose entries in the CM as a Properties object, through the cm-properties element. A minimal declaration looks as follows:

<osgix:cm-properties id="ds.cfg" persistent-id="data.source.office.1"/>

The configuration above, exposes the properties available in the CM under data.source.office.1 entry as a bean named ds.cfg.

[Note]Note

The persistent-id attribute must refer to the persistent-id of an OSGi ManagedService, it is a configuration error to specify a factory persistent id referring to a ManagedServiceFactory.

Those familiar with Spring's util namespace will find <osgi:cm-properties/> element similar to <util:properties/>.

It is possible to specify a default set of property values to be used in the event that the configuration dictionary does not contain an entry for a given key. The declaration is similar to the props element inside the Spring beans namespace:

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

   <osgix:cm-properties id="cfg.with.defaults" persistent-id="data.source.office.2">
      <beans:prop key="host">localhost</beans:prop>
      <beans:prop key="port">3306</beans:prop>
   </osgix:cm-properties>

</beans:beans>

By default, the properties found in the Configuration Admin entry will override the local properties. Thus, for the previous example, if the data.source.office.2 configuration contains a host entry, its value will override the locally defined localhost. For cases where this behaviour is undesired, the attribute local-override (default false) allows one to revert the merging algorithm, forcing the local properties to override the entries in the CM.

Since cm-properties exposes the CM entries as Properties, it can be used with Spring's PropertyPlaceholderConfigurer and PropertyOverrideConfigurer to externalize and customize environment-specific properties:

<?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:osgix="http://www.springframework.org/schema/osgi-compendium"
   xmlns:ctx="http://www.springframework.org/schema/context"
   xsi:schemaLocation="
	http://www.springframework.org/schema/beans 
	  http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context 
	  http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/osgi-compendium 
	  http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd">

   <!-- Configuration Admin entry -->
   <osgix:cm-properties id="cmProps" persistent-id="com.xyz.myapp">
      <prop key="host">localhost</prop>
   </osgix:cm-properties>

   <!-- placeholder configurer -->
   <ctx:property-placeholder properties-ref="cmProps" />

   <bean id="dataSource" ...>
      <property name="host" value="${host}"/>
      <property name="timeout" value="${timeout}"/>
   </bean>
	
</beans>

An important aspect of cm-properties is does not reflect any that any subsequent changes made to the entry it represents, made through the Configuration Admin API. That is, once resolved, the cm-properties content remains the same, regardless of any updates made the to CM entry it represents.

10.1.2. Managed Properties

Based on a configuration admin entry, Spring DM can autowire by name, the properties of a given bean. To use this feature, define a nested managed-properties inside the bean definition:

<bean id="managedComponent" class="MessageTank">
   <osgix:managed-properties persistent-id="com.xyz.messageservice"/> 
</bean>

For each key in the dictionary stored by Configuration Admin under the given persistent id, if the bean type has a property with a matching name (following JavaBeans conventions), then that component property will be dependency injected with the value stored in Configuration Admin under the key. If the definition of SomeClass from the example above is as follows:

public class MessageTank {
  private int amount;
  public int getAmount() { return this.amount; }
  public void setAmount(int amount) { this.amount = amount; }
}

and the configuration dictionary stored under the pid com.xyz.messageservice contains an entry amount=200, then the setAmount method will be invoked on the bean instance during configuration, passing in the value 200.

If a property value is defined both in the configuration dictionary stored in the Configuration Admin service and in a property element declaration nested in the component element, then the value from Configuration Admin takes precedence:

<bean id="managedComponent" class="MessageTank">
   <osgix:managed-properties persistent-id="com.xyz.messageservice"/>
   <property name="amount" value="100"/>
   <property name="threshold" value="500"/> 
</bean>

Property values specified via property elements can therefore be treated as default values to be used if none is available through Configuration Admin.

[Warning]Warning
Do not share the same persistent-id (PID) between multiple bundles or definitions, as only one of them will receive notifications. managed-properties relies on org.osgi.service.cm.ManagedService contract which mandates that each ManagedService instance must be identified with its own unique PID. Please see the Configuration Admin spec, specifically section 104.3 and 104.5

10.1.2.1. Configuration Admin Runtime Updates

A powerful feature of Configuration Admin is the ability to update (or delete) entries at runtime. That is, the configuration data stored in Configuration Admin may be updated after the bean has been created. By default, any post-creation updates will be ignored. However, one can configure managed-properties element to receive configuration updates through the update-strategy attribute, which can have a value of either bean-managed or container-managed.

bean-managed strategy will pass all the updates made to the configuration to a callback present on the bean, specified through the update-method attribute (which becomes required). The update method must have one of the following signatures:

public void anyMethodName(Map properties)
public void anyMethodName(Map<String,?> properties); // for Java 5

In contrast, the container-managed update strategy will re-inject bean properties by name based on the new properties received in the update. For container-managed updates, the component class must provide setter methods for the component properties that it wishes to have updated. Consider the following class definitions:

public class ContainerManagedBean {
  // will be reinjected (since it has a setter)
  private Integer integer;
  // will not be reinjected (no setter present)
  private Long waitTime; 
  
  public void setInteger(Integer integer) { this.integer = integer; }
}


public class SelfManagedBean {
  
  // update callback
  public void updateCallback(Map properties) {
	System.out.println("Received properties " + properties);
	System.out.println("Props can be used as a Dictionary " + (Dictionary) properties);
	// do more work ... 
  }
}

and configuraton:

<bean id="containerManaged" class="ContainerManagedBean">
   <osgix:managed-properties persistent-id="labX" update-strategy="container-managed"/>
   <property name="integer" value="23"/>
</bean>
	
<bean id="beanManaged" class="SelfManagedBean">
   <osgix:managed-properties persistent-id="labY" update-strategy="bean-managed" update-method="updateCallback"/>
</bean>

Any updates made to the CM entry labX will be automatically reinjected on existing instances of containerManaged bean while the labY updates will be passed to updateCallback method.

The update options are summarized in the table below:

Table 10.1. Managed Properties Update Options

update-strategyupdate-methodBehaviour
container-managedignoredReinjects the bean properties, using the properties present in the update. The re-injection will be applied while locking (through a synchronized instruction) the bean instance. If the locking or re-injection strategy is not suitable, consider using the bean-managed approach.
bean-managedrequiredInvokes the update-method callback on the bean instance, passing the updated configuration (as a Map object that can be safely cast to a Dictionary if needed). No locking is performed.

10.1.3. Managed Service Factories

The Configuration Admin service supports a notion of a managed service factory(see section 104.6 in the Compendium Specification). A managed service factory is identified by a factory pid which allows multiple Configuration objects to be associated with the factory. Configuration objects associated with the factory can be added or removed at any point. The main intent of a factory is to create an OSGi service for each configuration: adding a new Configuration entry results in a new OSGi service being registered, removing a Configuration, unregisters the service. Spring DM provides support for the managed service factory concept through the managed-service-factory element. Once defined, the configuration associated with the factory pid will automatically create (or remove) bean instances which will be registered (or unregistered) in the OSGi space based on a template bean definition and the CM configuration.

This might sound more complicated then it actually is, so let's look at a simplistic example:

<osgix:managed-service-factory id="simple-msf" 
   factory-pid="com.xyz.messageservice"                                                            (1)
   auto-export="all-classes">                                                                      (2)
	
   <bean class="com.xyz.MessageTank"/>                                                             (3)
</osgix:managed-service-factory>
1

factory persistent id (pid)

2

Shortcut flag used to determine under what interfaces the OSGi service is published (more info below)

3

bean definition template. For each detected configuration, a new service will be created using the bean definition template.

In its simplest form, the managed-service-factory requires the factory pid, a bean definition used as a template and some information on how possible bean instances are published as services. Basically, the definition above instructs Spring DM to to monitor the given factory pid (through a dedicated ManagedServiceFactory implementation (see the Compendium Spec for more info)) and for every Configuration object associated with the factory pid, to create a new, anonymous instance of the nested bean declared and export that instance as an OSGi service. The lifecycle of these beans instances is tied to the lifecycle of the associated Configuration objects. If a new configuration is added, a new bean is created and exported. If a configuration object is deleted or disassociated from the factory pid then the corresponding bean instance is destroyed.

In many regards, managed-service-factory acts as a specialized service exporter, similar to the service element but supporting the concept of managed properties. In fact, many of service's attributes that indicate how a bean is exported, are found in managed-service-factory (as you saw in the previous example with auto-export) as are the managed-properties attributes.

The list of attributes can be found below:

Table 10.2. Managed Service Factory Options

NameValuesDescription
interfacefully qualified class name (such as java.lang.Thread)the fully qualified name of the class under which the object will be exported
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-exportdisabled (default)interfacesclass-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.
update-strategynone (default)bean-managedcontainer-managedDefines the update strategy for configuration modifications made after the associated beans have been created.

Similar to the service element, a list of interfaces or/and registration listeners can be declared to be notified when a service is being registered/unregistered. For more information on the semantics, please see Section 7.1.1, “Controlling The Set Of Advertised Service Interfaces For An Exported Service” and Section 7.1.7, “Service Registration And Unregistration Lifecycle” chapters.

Now that the managed-service-factory options have been explained, let's look at a more complex configuration:

<bean id="queueTracker" class="org.xyz.queue.QueueTracker"/>
			
<osgix:managed-service-factory id="data-msf" 
   factory-pid="org.xyz.labX"                                                                      (1)
   update-strategy="bean-managed"                                                                  (2)
   update-method="refresh">                                                                        (3)
   <osgix:interfaces>
       <value>java.util.Collection</value>                                                         (4)
       <value>java.util.Queue</value>                                                              (4)
   </osgix:interfaces>
   <osgix:registration-listener ref="queueTracker"                                                 (5)
       registration-method="track"                                                                 (6)
       unregistration-method="untrack"/>                                                           (7)
		
   <bean class="com.xyz.ResizableQueue">                                                           (8)
       <property name="size" value="100"/>
       <property name="concurrency" value="10"/>
       <property name="fair" value="false"/>
   </bean>
</osgix:managed-service-factory>
1

ManagedServiceFactory factory persistent id

2

how should Spring DM behave when a Configuration is updated

3

the method to invoke when for bean-managed updates

4

the interfaces under which the nested beans are published as OSGi services

5

listener notified when a service (based on the CM Configuration) is registered/unregistered

6

custom (optional) service registration method

7

custom (optional) service unregistration method

8

bean definition template

The example above, creates a imaginary ResizeableQueue instance for each Configuration entry present under the org.xyz.labX factory pid. Each instance has default values assigned to size, concurrency and fair parameters. However, just like managed-properties, during the bean creation, the values received from the Configuration Admin will be injected by name, possibly overriding existing settings. Once created and configured, each nested, anonymous bean instance is registered as an OSGi service under the java.util.Collection and java.util.Queue interfaces. The OSGi service lifecycle is monitored by a registration listener, namely the bean queueTracker. Finally, due to the specified update-strategy, any updates executed to each CM configuration will cause the refresh callback to be invoked on the associated bean instance.

10.1.4. Direct Access To Configuration Data

The simplest way to work directly with the configuration data stored under a given persistent id or factory persistent id, is to register a service that implements either the ManagedService or ManagedServiceFactory interface and specify the pid that you are interested in as a service property (for more information, see the Configuration Admin chapter in the OSGi compendium spec). For example:

<osgi:service interface="org.osgi.service.cm.ManagedService" ref="myManagedService">
   <osgi:service-properties>
     <entry key="service.pid" value="my.managed.service.pid"/>
   </osgi:service-properties>
</osgi:service>

<bean id="myManagedService" class="com.xyz.MyManagedService"/>