Tuesday, June 19, 2018

OSGi R7 Highlights: Configuration Admin and Configurator

The OSGi Compendium Release 7 specification contains an update to the Configuration Admin Service specification which includes a number of new features and also introduces the new Configurator specification.

Configuration Admin


One of the most used but on the other hand barely noticed services from the OSGi compendium specification is the Configuration Admin. This service allows to create, update and delete configurations. It is up to the implementation where these configurations are stored. A configuration has a unique persistent identifier (PID) and a dictionary of properties.

Usually, configurations are tied to an implementation of an OSGi service but configurations can be used for any purpose like database connections, current temperature or the set of available nodes in a cloud setup. While the Configuration Admin service has an API to find configurations or create them, it supports a more inversion of control like behavior by supporting a callback mechanism. The callback (known as the ManagedService interface) gets invoked for existing/created configurations of a certain PID and also if that configuration is deleted.

While this callback exists, again it's not that common to use it directly. The most common and easiest way to develop OSGi components and services is to use Declarative Services. Declarative Services (DS) provides build-in support for configurations. Simply by implementing an activation method the component can get its configuration. The implementor of that component does not have to worry whether such a configuration exists, gets deleted or is modified. DS takes care of all of this and invokes the right actions on the component.

In addition to single configurations, Configuration Admin provides support for factory configurations: multiple configurations of the same type, like for example different logger configurations for different categories or different database connection configurations. These factory configurations have a factory PID which is the same for all configurations of that type and a configuration PID to distinguish those configurations.

This should explain the big picture. Let's start talking about the new things in Release 7.

Configurator Specification


The Configurator specification is a new specification in the R7 release. It basically consists of two parts. The first part describes a common textual representation of a set of configurations. Previous to this specification each and every tool was using its own format for provisioning configurations. For example, the famous Apache Felix File Install uses a properties like format. Other tools use slightly different formats. One problem is that you can't simply switch from one tool to another and the other major problem is that some of the formats do not allow to specify the real type of a property value. For example, the value for the service ranking property must be of type Integer. Or you might have a special implementation that is expecting (for whatever reason) a value to be of type Byte. However, some tools are simply always using a Long to represent numbers or a String to represent anything else.

Therefore a common definition eliminates these problems and allows interchangeability of configurations between various tools. The format is JSON based and uses the PIDs of a configuration as the keys. The value is the configuration object with the properties:

{
    "my.component.pid": {
        "port:Integer" : 300, 
        "array:int[]" : [2, 3, 4], 
        "collection:Collection<Integer>" : [2,3,4], 
        "complex": { 
            "a" : 1, 
            "b" : "two"
        }
    }
}

As you can see in the example, it is possible to specify the runtime type of a configuration property by separating the property name from the type using a colon. For example, the "port" property value is of type Integer, the "array" property value is an array of ints and the "collection" property value is of type Collection<Integer>. You can specify all allowed types for a configuration and the configurator implementation uses converting rules as defined by the Converter specification - another one of the new specifications of Release 7.

In addition, a configuration property can hold structured JSON as a string value. In the example above "complex" contains at runtime a string value of the specified JSON.

Factory configurations can be specified by using the following syntax: FACTORY_PID~NAME. With the updated Configuration Admin it is possible to use a meaningful name to address factory components. The tilde separates the factory PID from the name:

{
    "my.factory.component~foo" : {
        ...
    },
    "my.factory.component~bar" : {
        ...
    }
}

Please note the errata for the published specification.

OSGi Bundle Configurations


The second part of the configurator specification describes a new extender based mechanism that picks up configurations from within a bundle and applies them. A bundle can contain one or more JSON files with configurations and once the bundle is started the configurations will be put into Configuration Admin by the Configurator. The configurator manages the state handling and ordering in a deterministic way. For example, if two bundles contain a configuration for the same PID, a ranking mechanism is used to specify which configuration is put into Configuration Admin, regardless of their installation or start order.

In addition to provide configurations through bundles, the Configurator supports providing initial configurations through system properties on startup of the OSGi framework. This is especially useful for customising an application without changing the distributable for the application. By specifying the system property configurator.initial with either a JSON document as described above or a list of URLs pointing to such JSON documents, the Configurator will apply the contained configurations in the same manner as if they would have been provided through a bundle.

With this new feature, provisioning of configurations through bundles and allowing to override them on startup becomes part of the OSGi specifications. You will find an example application using the Configurator at the OSGi enRoute website. The specification of the Configurator has driven the update of the Configuration Admin specification. So let's talk about the most important new features in Configuration Admin.

Improved Factory Configuration Handling


The handling of factory configurations has been greatly improved. With previous versions, when you create a factory configuration, the PID part is randomly generated which makes identifying a particular factory configuration later on much harder. In addition, as the PID is auto-generated, it has no meaning. With the updated Configuration Admin, it is now possible to specify the PID of a factory configuration, eliminating those problems.

New methods on the Configuration Admin allow to create and retrieve factory configurations based on the factory PID and a name. These methods behave the same as the already existing methods for plain configurations. The PID for those factory components is generated by appending the name to the factory PID separated by a tilde. The Configurator uses this syntax to specify factory configurations as shown above.

Improved Configuration Plugin Handling


When a configuration is delivered to a managed service, the configuration is passed through registered configuration plugin services. Such a service can manipulate the configuration. One common use case is to handle placeholders in the configuration properties and replace them with real values when delivered. For example, a property of a database connection configuration could just contain the value "${database.url}" which is replaced with the actual URL when this configuration is passed to the component processing the configuration. Or if you have sensitive configuration data, you can store it encrypted in the configuration and just decrypt it in a configuration plugin before it is passed to the managed service.

While this mechanism sounds useful, it is only useful if you register a managed service. However, when you are using Declarative Services (or other component frameworks) for your components, the plugins are not called at all. This gap is closed now and the DS implementation uses a new functionality of the Configuration Admin service and calls the plugins before it is passing the configuration to its components. This ensures plugins will be called regardless how you get your configuration. And this is making those use cases mentioned above possible.

Conclusion


The standard format for OSGi configurations is a great step forward for tooling and the Configurator implementation allows to deploy configurations through bundles in a standardized and well-specified way. The update of the Configuration Admin resolves some long outstanding issues and allows for new use cases around configuration management. For all the new features of the Configuration Admin service, have a look at the specification and make sure to also read the new Configuration specification.


Want to find out more about OSGi R7?

This is one post in a series of 12 focused on OSGi R7 Highlights.  Previous posts are:
  1. Proposed Final Draft Now Available
  2. Java 9 Support
  3. Declarative Services
  4. The JAX-RS Whiteboard
  5. The Converter
  6. Cluster Information
  7. Transaction Control
  8. The Http Whiteboard Service
  9. Push Streams and Promises 1.1
Be sure to follow us on Twitter or LinkedIn or subscribe to our Blog feed to find out when it's available.

2 comments:

  1. I have not read it in detail, but this Configurator stuff seems to be very useful. I would ask for a couple of rules that should be applied on the configuration JSON (at least in implementation):

    - The Maps within the JSON should be sorted by ABC.
    - In case of arrays, the starting bracket, each value and the end bracket should be on a new line

    Felix ConfigAdmin got this feature in its config file format a while ago and it became so easy to run diff tools on two separate configurations. E.g.: If the JSON files are stored in Git, one can check the config changes during a code review.

    ReplyDelete
    Replies
    1. I am not sure I understand your comment. Are you asking we edit the JSON examples in this blog post to look a certain way? Or are you asking about a change to the OSGi Configurator specification?

      Delete