Apache
Home » Documentation » Apache Felix Subproject Documentation » Apache Felix Dependency Manager

Dependency Manager - Configuration Dependency

A configuration dependency is always required, and allows you to depend on the availability of a valid configuration for your component. Optional configuration dependencies are not supported because in that case you can just as well register as a ManagedService yourself.

The dependency injects by default the configuration in an "updated" callback which can accept the following parameters:

If you only specify a pid, by default the callback method name is assumed to be "updated".

Configuration types are a new feature that allows you to specify a Java interface that is implemented by DM and such interface is then injected to your callback instead of the actual Dictionary. Using such configuration interface provides a way for creating type-safe configurations from a actual Dictionary that is normally injected by Dependency Manager. The callback accepts in argument an interface that you have to provide, and DM will inject a proxy that converts method calls from your configuration-type to lookups in the actual map or dictionary. The results of these lookups are then converted to the expected return type of the invoked configuration method. As proxies are injected, no implementations of the desired configuration-type are necessary!

The lookups performed are based on the name of the method called on the configuration type. The method names are "mangled" to the following form: [lower case letter] [any valid character]*. Method names starting with get or is (JavaBean convention) are stripped from these prefixes. For example: given a dictionary with the key "foo" can be accessed from a configuration-type using the following method names: foo(), getFoo() and isFoo().

The return values supported are:

When an interface is returned, it is treated equally to a configuration type, that is, a proxy is returned.

Arrays can be represented either as comma-separated values, optionally enclosed in square brackets. For example: [ a, b, c ] and a, b,c are both considered an array of length 3 with the values "a", "b" and "c". Alternatively, you can append the array index to the key in the dictionary to obtain the same: a dictionary with "arr.0" => "a", "arr.1" => "b", "arr.2" => "c" would result in the same array as the earlier examples.

Maps can be represented as single string values similarly as arrays, each value consisting of both the key and value separated by a dot. Optionally, the value can be enclosed in curly brackets. Similar to array, you can use the same dot notation using the keys. For example, a dictionary with:

"map" => "{key1.value1, key2.value2}"

and a dictionary with:

"map.key1" => "value1", "map2.key2" => "value2"

result in the same map being returned. Instead of a map, you could also define an interface with the methods getKey1() and getKey2() and use that interface as return type instead of a Map.

In case a lookup does not yield a value from the underlying map or dictionary, the following rules are applied:

Usage example where a component depends on a configuration:

public class ServiceImpl {
    void updated(Dictionary<String, Object> cnf) {
        if (cnf != null) {
            String addr = (String) cnf.get("address");
            int port = Integer.valueOf(cnf.get("port");
            ...
        }
    }
}

public class Activator extends DependencyActivatorBase {
    @Override
    public void init(BundleContext ctx, DependencyManager dm) throws Exception {
        dm.add(createComponent()
          .setImplementation(ServiceImpl.class)            
          .add(createConfigurationDependency().setPid(ServiceImpl.class.getName()));
    }
}

Here is the same example, but a custom configuration type interface is used (by default, the FQDN of the configuration type is assumed to be the configuration pid):

public interface MyConfig {
    String getAddress();
    int getPort();
}

public class ServiceImpl {
    void modified(MyConfig cnf) {
        if (cnf != null) {
            String addr = cnf.getAddress();
            int port = cnf.getPort();
            ...
        }
    }
}

public class Activator extends DependencyActivatorBase {
    @Override
    public void init(BundleContext ctx, DependencyManager dm) throws Exception {
        dm.add(createComponent()
          .setImplementation(ServiceImpl.class)            
          .add(createConfigurationDependency().setCallback("modified", MyConfig.class);
    }
}

@ConfigurationDependency

Configuration dependencies can be defined usnig the @ConfigurationDependency. Annotation attributes:

In the following example, the "Printer" component depends on a configuration with "org.apache.felix.sample.Printer" PID.

package org.apache.felix.sample;

@Component
public class Printer {
    @ConfigurationDependency
    void updated(Dictionary config) {
        // load printer ip/port from the provided dictionary.
    }
}

This other example shows how to specify a configuration dependency, as well as meta data used to customize the WebConsole GUI. Using these meta data, you can specify for example the default value for your configurations data, some descriptions, the cardinality of configuration values, etc ... (we use here standard bnd metatype annotations, see bnd metatype documentation here.

First, we define our PrinterConfiguration interface annotated with standard bndtools metatatype annotations:

 :::java
 package sample;
 import aQute.bnd.annotation.metatype.Meta.AD;
 import aQute.bnd.annotation.metatype.Meta.OCD;

 @OCD(description = "Declare here the Printer Configuration.")
 public interface PrinterConfiguration {
     @AD(description = "Enter the printer ip address")
     String ipAddress();

     @AD(description = "Enter the printer address port number.")
     int portNumber();
 }

Next, we define our Printer service with an updated method which is injected with the PrinterConfiguration type that is implemented by DM (all interface methods will lookup in the actual configuration dictionary).

 :::java
 package sample;
 import aQute.bnd.annotation.metatype.*;

 @Component
 public class Printer {
     @ConfigurationDependency // Will use pid "sample.PrinterConfiguration"
     void updated(PrinterConfiguration cnf) {
         if (cnf != null) {
             // load configuration from the provided dictionary, or throw an exception of any configuration error.
             String ip = cnf.ipAddress();
             int port = cnf.portNumber();
             ...
         }
     }
 }
Rev. 1783995 by jawi on Wed, 22 Feb 2017 09:54:59 +0000
Apache Felix, Felix, Apache, the Apache feather logo, and the Apache Felix project logo are trademarks of The Apache Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their respective owners.