Home Articles OSGi Compendium Configuraion Admin
Configuration Admin Service specification explained by Example Print E-mail
Written by Valery Abu-Eid   
Wednesday, 24 September 2008 18:11

Confguration Admin Service specification of the OSGi Compendium Services defines a standard way for configuring Bundles in OSGi Environments. Like any other OSGi Compendium Service, Configuration Admin Service has a standard API and implementations provided by different vendors, in this article we will use the implementation provided by Apache Felix project.

There are two kinds of bundles from Configuration perspective: Configurable Bundles and Configurator Bundles. As the names suggest, Configurable Bundles are bundles that configured by Configurator Bundles. A bundle can be a Configurable bundle and a Configurator bundle at the same time - Such bundles usually provide a User Interface. We will go through the Config Admin Service specification by observing these two kinds of bundles.

Configuration Admin Service is also responsible for persisting configuration data. So if you restart your application (without specifying the clean start parameter of the OSGi Framework in use) your previous configuration data should be available. The example application and its source code + Implementations of the Configuration Admin Service specification are contained in config-admin-service-example.zip. The example application was tested in Equinox, Felix and Knopflerfish.

Configurable Bundles

The way you choose to make your bundle configurable largely depends on whether the bundle provides a single instance of a service, e.g., HTTP Engines, System Monitoring services, etc. or multiple instances of the same service, a service instance per configuration, e.g., a directory watcher service whose instance is created for each watched directory. The sections below explain how to configure each of these two kinds of services.

Providing a Managed Service (Single Instance)

To configure a singleton service you need to:

  • Register a service that implements the org.osgi.service.cm.ManagedService interface and set the Service Property Service PID which should have a string value that identifies your service.
  • Implement the updated(Dictionary) method of the Managed Service. This method will be invoked by the Configuration Admin service when your service is registered and when its configuration is added, updated or deleted. If there is no Configuration Admin service in the environment, the updated method will not be invoked. The dictionary parameter of the updated method contains the configuration data of your service, if no configuration for your service is present in the Configuration Admin service, the value of the dictionary parameter passed to the updated method will be null.
Service PID is a an identifier for OSGi Services that is unique inside the bundle scope, so if you want to provide other services from the same bundle, they need to have other Service PIDs (services in other bundles can have the same Service PID). The name of Service PID property can be retrieved from the constant org.osgi.framework.Constants.SERVICE_PID. Don't forget to import the org.osgi.service.cm package in all of the bundles that use the Configuration Admin Service API.

The Configurable bundle of the example application contains one Managed Service that configures a servlet that shows a greetings message. Below is a simplified version of the example code.

import java.util.Dictionary;
import java.util.Hashtable;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.Constants;
import org.osgi.service.cm.ManagedService;

public class Activator implements BundleActivator {

	public void start(BundleContext bundleContext) throws Exception {
		bundleContext.registerService(ManagedService.class.getName(),
				greetingsServletManager.getGreetingsManagedService(),
				getManagedServiceProperties());
		/// ...
	}
	
	protected Dictionary getManagedServiceProperties() {
		Dictionary result = new Hashtable();
		result.put(Constants.SERVICE_PID, "greetings_servlet_service");
		return result;
	}
	
	private GreetingsServletManager greetingsServletManager =
			new GreetingsServletManager();
	
	// ...
}

public class GreetingsServletManager {
	
	protected class GreetingsManagedService implements ManagedService {
		
		public void updated(Dictionary properties) throws ConfigurationException {
			System.out.println("Configuration Updated.");
			setProperties(properties);
			updateServletService();
		}
		
	}
	
	private final GreetingsManagedService greetingsManagedService =
			new GreetingsManagedService();
	public GreetingsManagedService getGreetingsManagedService() {
		return greetingsManagedService;
	}
	
	protected void updateServletService() {
		// Update Logic
	}
	
	// ...
}
	

You can see from the code above that the Bundle Activator registered a Managed Service with a Service PID, and the method updated of the Managed Service is used to update the configuration of the Servlet.

Providing Managed Services using a Factory (Multiple Instances)

To configure a service of which you will have an instance per configuration you need to:

  • Register a service that implements the org.osgi.service.cm.ManagedServiceFactory interface and set the Service Property Service PID which should have a string value that identifies your Managed Service Factory.
  • Implement the updated(String, Dictionary) method of the Managed Service Factory. This method will be invoked by the Configuration Admin service when the configuration of your service is added or updated. The first parameter contains the Service PID, the second configuration data.
  • Implement the deleted(String) method of the Managed Service Factory. This method will be invoked by the Configuration Admin service when the configuration of your service is deleted. The passed parameter contains the Service PID.
The Service PID parameter of the updated and deleted methods is a Service PID generated by the Configuration Admin Service for the service instance not the Service PID of your Managed Service Factory.
import java.util.Dictionary;
import java.util.Hashtable;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.Constants;
import org.osgi.service.cm.ManagedService;

public class Activator implements BundleActivator {

	public void start(BundleContext bundleContext) throws Exception {
		bundleContext.registerService(ManagedServiceFactory.class.getName(),
				greetingsServletsManager.getGreetingsManagedServiceFactory(),
				getManagedServiceFactoryProperties());
		/// ...
	}
	
	protected Dictionary getManagedServiceFactoryProperties() {
		Dictionary result = new Hashtable();
		result.put(Constants.SERVICE_PID, "greetings_servlet_service_factory");
		return result;
	}
	
	private GreetingsServletsManager greetingsServletsManager =
			new GreetingsServletsManager();
	
	// ...
}

public class GreetingsServletsManager {
	
	class GreetingsManagedServiceFactory implements ManagedServiceFactory {
		
		public void updated(String pid, Dictionary properties)
				throws ConfigurationException {
			// Depending on whether a Servlet exists with this pid or not:
			// create a new servlet or update an existing one
		}
		
		public void deleted(String pid) {
			// If a servlet has such pid, remove it.
		}
		
		// getName() method returns a descriptive name of the service factory.
		public String getName() {
			return "Greetings Servlet Service Factory";
		}
		
	}
	
	private final GreetingsManagedServiceFactory managedServiceFactory =
		new GreetingsManagedServiceFactory();
	public GreetingsManagedServiceFactory getGreetingsManagedServiceFactory() {
		return managedServiceFactory;
	}
	
	// ...
}
	

Configurator Bundles

Configuration Admin Service

Implementations of the Configuration Service specification provides a Configuration Admin service which implements the org.osgi.service.cm.ConfigurationAdmin interface. ConfigurationAdmin interface provides methods to maintain configuration data in the OSGi Environment, below are its main methods:

  • getConfiguration(String) and getConfiguration(String, String): Retrieves the Configuration object of a Managed Service. Depending on whether the Configurator bundle is the same as the Configurable bundle or not, you will have to use the first or the second method. The first method accepts the Service PID of the Managed Service as a parameter, the second accepts the Service PID and the Location Identifier of the Configurable bundle.
  • createFactoryConfiguration(String) and createFactoryConfiguration(String, String): Creates a Configuration object for a new Managed Service instance that will be created by the Managed Service Factory - The Managed Service will not be created unless you invoke the update method of the Configuration object. Depending on whether the Configurator bundle is the same as the Configurable bundle or not, you will have to use the first or the second method. The first method accepts the Service PID of the Managed Service Factory as a parameter, the second accepts the Service PID and the Location Identifier of the Configurable bundle.
  • listConfigurations(String): Returns an array of Configuration objects stored in the Configuration Admin service. It accepts an LDAP filter of the properties as a parameter.

Configuring a Managed Service

To configure a Managed Service you need to:

  • Acquire a reference to the ConfigurationAdmin service.
  • Acquire a reference to the Configuration object for the Managed Service using the getConfiguration method of the ConfigurationAdmin service. If you would like to configure a Managed Service registered from the same bundle then pass the Service PID of the Managed Service to the getConfiguration method, if you would like to configure a Managed Service registered by another bundle then pass the Service PID of the Managed Service and the Location Identifier of the Configurable bundle to the getConfiguration method.
  • To access the exiting configuration data use the getProperties() method of the Configuration object - It will return null if no data is set. To update the configuration data use the update(Dictionary) method of the Configuration object and pass it the new configuration data.

Below is a simplified version of the Configurator bundle.

import java.util.Dictionary;
import java.util.Hashtable;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleActivator;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;

public class Activator implements BundleActivator {

	public void start(BundleContext bundleContext) throws Exception {
		ServiceReference configAdminServiceRef =
			bundleContext.getServiceReference(ConfigurationAdmin.class.getName());
			
		if (configAdminServiceRef != null) {
			ConfigurationAdmin configAdmin = (ConfigurationAdmin)
					bundleContext.getService(configAdminServiceRef);
			
			Configuration config = configAdmin.getConfiguration(
					"greetings_servlet_service", getConfigurableBundleLocation());
			Dictionary properties = config.getProperties();
			if (properties == null) {
				properties = new Hashtable();
			}
			properties.put("servletAlias", "/greetings");
			config.update(properties);
		}
	}
	
	protected String getConfigurableBundleLocation() {
		// Logic to acquire the Location Identifier of the Configurable bundle
	}
	
	// ...
}
	

Configuring Managed Services provided by a Factory

To configure Managed Services provided by a Managed Service Factory you need to:

  • Acquire a reference to the ConfigurationAdmin service.
  • Use the createFactoryConfiguration method of the ConfigurationAdmin service to create a Configuration object for a new Managed Service created by the Managed Service Factory - Please note that the Managed Service will not be created unless you invoke the update method of the Configuration object. If you want to use a Managed Service Factory registered from the same bundle then pass the Service PID of the Managed Service Factory to the createFactoryConfiguration method, if you want to use a Managed Service Factory registered by another bundle then pass the Service PID of the Managed Service Factory and the Location Identifier of the Configurable bundle to the createFactoryConfiguration method.
  • Create a Dictionary that contains the configuration data and pass it as an argument to the update method of the Configuration object to create/update the Managed Service.
  • Save a reference to the Configuration object or the generated Service PID of the new service instance (using the getPid method of the Configuration object) so you could modify configuration data for that instance in the future.

Below is a simplified version of the Configurator bundle.

import java.util.List;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleActivator;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;

public class Activator implements BundleActivator {

	public void start(BundleContext bundleContext) throws Exception {
		ServiceReference configAdminServiceRef =
			bundleContext.getServiceReference(ConfigurationAdmin.class.getName());
			
		if (configAdminServiceRef != null) {
			ConfigurationAdmin configAdmin = (ConfigurationAdmin)
					bundleContext.getService(configAdminServiceRef);
			
			Configuration config = configAdmin.createFactoryConfiguration(
					"greetings_servlet_service_factory",
					getConfigurableBundleLocation());
			Dictionary properties = new Hashtable();
			properties.put("servletAlias", "/greetings");
			properties.put("friendName", "World");
			config.update(properties);
			
			configObjects.add(config);
		}
	}
	
	protected String getConfigurableBundleLocation() {
		// Logic to acquire the Location Identifier of the Configurable bundle
	}
	
	private List configObjects = new ArrayList();
	
	// ...
}
	

Configuration Service Implementations

Each project of the tree main OSGi Frameworks (Equinox, Felix and Knopflerfish) provides an implementation of the Configuration Service specification - In Equinox and Knopflerfish it's called "cm", in Felix it's "scr". The implementations provided by Felix and Knopflerfish are included in the example application - The implementation provided by Equinox wasn't included since it isn't compatible with Knopflerfish, at least using the default configuration.