FELIX-5177:

- added a configType attribute in FactoryConfigurationAdapterService annotation.
- when using a config type with ConfigurationDependency, then assume that pid is set to the fqdn of the provided config type, in case
no pid has already been set using setPid method.
- code cleanup.
- adapted samples to use configuration type.
- added javadocs.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1730934 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
index 6ef1f60..d747c59 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
@@ -22,6 +22,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Map;
 
 
 /**
@@ -29,11 +32,21 @@
  * is always required, and allows you to depend on the availability of a valid configuration 
  * for your component. This dependency requires the OSGi Configuration Admin Service.
  * 
+ * The annotation can be applied on a callback method which accepts the following parameters:
+ * 
+ * <p><ul>
+ * <li>callback(Dictionary) 
+ * <li>callback(Component, Dictionary) 
+ * <li>callback(Configuration interface) // type safe configuration
+ * <li>callback(Component, Configuration interface) // type safe configuration
+ * </ul>
+ * 
  * <h3>Usage Examples</h3>
  * 
  * <p> In the following example, the "Printer" component depends on a configuration
  * whose PID name is "sample.PrinterConfiguration". This service will initialize
  * its ip/port number from the provided configuration.
+ * 
  * <p> First, we define the configuration metadata, using standard bndtools metatatype annotations 
  * (see http://www.aqute.biz/Bnd/MetaType):
  * 
@@ -63,10 +76,8 @@
  *
  * &#64;Component
  * public class Printer {
- *     &#64;ConfigurationDependency(pidClass = PrinterConfiguration.class) // Will use pid "sample.PrinterConfiguration"
- *     void updated(Dictionary props) {
- *         // load configuration from the provided dictionary, or throw an exception of any configuration error.
- *         PrinterConfig cnf = Configurable.createConfigurable(PrinterConfig.class, props);
+ *     &#64;ConfigurationDependency // Will use the fqdn of the  PrinterConfiguration interface as the pid.
+ *     void updated(PrinterConfiguration cnf) {
  *         String ip = cnf.ipAddress();
  *         int port = cnf.portNumber();
  *         ...
@@ -75,6 +86,59 @@
  * </pre>
  * </blockquote>
  * 
+ * In the above example, the updated callback accepts a type-safe configuration type (and its fqdn is used as the pid).
+ * <p> Configuration type is a new feature that allows you to specify an 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 {@link 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.<br>
+ * As proxies are injected, no implementations of the desired configuration-type are necessary!
+ * </p>
+ * <p>
+ * 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: <tt>[lower case letter] [any valid character]*</tt>. Method names starting with
+ * <tt>get</tt> or <tt>is</tt> (JavaBean convention) are stripped from these prefixes. For example: given a dictionary
+ * with the key <tt>"foo"</tt> can be accessed from a configuration-type using the following method names:
+ * <tt>foo()</tt>, <tt>getFoo()</tt> and <tt>isFoo()</tt>.
+ * </p>
+ * <p>
+ * The return values supported are: primitive types (or their object wrappers), strings, enums, arrays of
+ * primitives/strings, {@link Collection} types, {@link Map} types, {@link Class}es and interfaces. When an interface is
+ * returned, it is treated equally to a configuration type, that is, it is returned as a proxy.
+ * </p>
+ * <p>
+ * Arrays can be represented either as comma-separated values, optionally enclosed in square brackets. For example:
+ * <tt>[ a, b, c ]</tt> and <tt>a, b,c</tt> 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" =&gt; "a", "arr.1" =&gt; "b", "arr.2" =&gt; "c" would result in the same array as the earlier examples.
+ * </p>
+ * <p>
+ * 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 
+ * 
+ * <pre>{@code "map" => "{key1.value1, key2.value2}"}</pre> 
+ * 
+ * and a dictionary with <p>
+ * 
+ * <pre>{@code "map.key1" => "value1", "map2.key2" => "value2"}</pre> 
+ * 
+ * result in the same map being returned.
+ * Instead of a map, you could also define an interface with the methods <tt>getKey1()</tt> and <tt>getKey2</tt> and use
+ * that interface as return type instead of a {@link Map}.
+ * </p>
+ * <p>
+ * In case a lookup does not yield a value from the underlying map or dictionary, the following rules are applied:
+ * <ol>
+ * <li>primitive types yield their default value, as defined by the Java Specification;
+ * <li>string, {@link Class}es and enum values yield <code>null</code>;
+ * <li>for arrays, collections and maps, an empty array/collection/map is returned;
+ * <li>for other interface types that are treated as configuration type a null-object is returned.
+ * </ol>
+ * </p>
+ * 
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 @Retention(RetentionPolicy.CLASS)
@@ -92,9 +156,10 @@
      * You can use this method when you use an interface annotated with standard bndtols metatype annotations.
      * (see http://www.aqute.biz/Bnd/MetaType).
      * @return the pid class
+     * @deprecated just define an updated callback which accepts as argument a configuration type.
      */
     Class<?> pidClass() default Object.class;
-
+    
     /**
      * Returns true if the configuration properties must be published along with the service. 
      * Any additional service properties specified directly are merged with these.
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java
index 245a12c..a95689e 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java
@@ -31,6 +31,13 @@
  * Depending on the <code>propagate</code> parameter, every public factory configuration properties 
  * (which don't start with ".") will be propagated along with the adapter service properties.
  * 
+ * <p> If you specify a configuration type, then the fqdn of the configuration interface is used as the factory pid,
+ * else you can specify the factory pid explicitly using the factoryPid attribute.
+ * If no configuration type is used and no factoryPid attribute is specified, then the factory pid will be set to the fqdn of
+ * the class on which this annotation is applied.
+ * 
+ * <p> (see javadoc from {@link ConfigurationDependency} for more informations about configuration types).
+ * 
  * <h3>Usage Examples</h3>
  * Here, a "Dictionary" service instance is created for each existing "sample.DictionaryConfiguration" factory pids.
  * 
@@ -55,19 +62,16 @@
  * </pre>
  * </blockquote>
  *
- * And here is the Dictionary service:
- *
+ * And here is the factory pid adapter service, which is instantiated for each instance of the "sample.DictionaryConfiguration" factory pid:
+ * 
  * <blockquote>
  * <pre>
  * import java.util.List;
  * import aQute.bnd.annotation.metatype.Configurable;
  *
- * &#64;FactoryConfigurationAdapterService(factoryPidClass=DictionaryConfiguration.class)  
+ * &#64;FactoryConfigurationAdapterService(configType=DictionaryConfiguration.class)  
  * public class DictionaryImpl implements DictionaryService {
- *     protected void updated(Dictionary&#60;String, ?&#62; props) {
- *         // load configuration from the provided dictionary, or throw an exception of any configuration error.
- *         DictionaryConfiguration cnf = Configurable.createConfigurable(DictionaryConfiguration.class, props);
- * 
+ *     protected void updated(DictionaryConfiguration config) {
  *         m_lang = config.lang();
  *         m_words.clear();
  *         for (String word : conf.words()) {
@@ -79,6 +83,7 @@
  * </pre>
  * </blockquote>
  * 
+ * 
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 @Retention(RetentionPolicy.CLASS)
@@ -100,6 +105,15 @@
     Property[] properties() default {};
 
     /**
+     * Returns the type safe configuration class which will be injected in the updated callback.
+     * By default, the factory pid is assumed to match the fqdn of the configuration type.
+     * see javadoc from {@link ConfigurationDependency} for more informations about configuration types.
+     * @return the configuration type to pass in the "updated" callback argument.
+     * @see ConfigurationDependency
+     */
+    Class<?> configType() default Object.class;
+    
+    /**
      * Returns the factory pid whose configurations will instantiate the annotated service class. (By default, the pid is the 
      * service class name).
      * @return the factory pid
@@ -111,6 +125,8 @@
      * You can use this method when you use an interface annoted with standard bndtols metatype annotations.
      * (see http://www.aqute.biz/Bnd/MetaType).
      * @return the factory pid class
+     * @deprecated use {@link #configType()} and accept a configuration type parameter from your updated callback. The pid
+     * is then assumed to match the fqdn of the configuration type.
      */
     Class<?> factoryPidClass() default Object.class;
 
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
index 5a9497f..978571c 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
@@ -640,7 +640,7 @@
             if (! Dictionary.class.getName().equals(confProxyType)) 
             {
                 // It's a conf proxy type.
-                writer.put(EntryParam.confProxyType, confProxyType);
+                writer.put(EntryParam.configType, confProxyType);
             }
             else
             {
@@ -655,7 +655,7 @@
             if (! Dictionary.class.getName().equals(confProxyType)) 
             {
                 // It's a conf proxy type.
-                writer.put(EntryParam.confProxyType, confProxyType);
+                writer.put(EntryParam.configType, confProxyType);
             }
             else
             {
@@ -922,12 +922,19 @@
         // factory pid attribute (can be specified using the factoryPid attribute, or using the factoryPidClass attribute)
         String factoryPidClass = parseClassAttrValue(annotation.get(EntryParam.factoryPidClass.toString()));
         
-        // If a factory pid class is specified, consider it as a possible candidate for a configuration proxy.
-        if (factoryPidClass != null) {
-            writer.put(EntryParam.confProxyType, factoryPidClass);
+        // Test if a type safe configuration type is provided.
+        String configType = parseClassAttrValue(annotation.get(EntryParam.configType.toString()));
+        
+        if (configType != null) {
+            writer.put(EntryParam.configType, configType);
         }
         
-        String factoryPid = factoryPidClass != null ? factoryPidClass : get(annotation, EntryParam.factoryPid.toString(), m_className);
+        String factoryPid = null;
+        
+        factoryPid = get(annotation, EntryParam.factoryPid.toString(), factoryPidClass);
+        if (factoryPid == null) {
+            factoryPid = configType != null ? configType : m_className;
+        }
         
         writer.put(EntryParam.factoryPid, factoryPid);
 
diff --git a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
index bff3b7a..2835f70 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
@@ -44,7 +44,7 @@
     autoConfig, 
     pid,
     pidClass,
-    confProxyType, // inject a proxy configuration type 
+    configType, // inject a proxy configuration type 
     factoryPid,
     factoryPidClass,
     propagate, 
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java
index 7171103..d724df2 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/DependencyBuilder.java
@@ -139,7 +139,7 @@
 
     private Dependency createConfigurationDependency(Bundle b, DependencyManager dm) throws Exception
     {
-        String confProxyType = m_metaData.getString(Params.confProxyType, null);
+        String confProxyType = m_metaData.getString(Params.configType, null);
         String pid = m_metaData.getString(Params.pid);
         boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
         String callback = m_metaData.getString(Params.updated, "updated");
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
index 9f13923..b74108c 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
@@ -51,7 +51,7 @@
         String[] provides = srvMeta.getStrings(Params.provides, null);
         Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
         boolean propagate = "true".equals(srvMeta.getString(Params.propagate, "false"));
-        String configProxyClassName = srvMeta.getString(Params.confProxyType, null);
+        String configProxyClassName = srvMeta.getString(Params.configType, null);
         Component c = null;
         
         if (configProxyClassName != null)
diff --git a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java
index ee2dd86..44868c8 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.runtime/src/org/apache/felix/dm/runtime/Params.java
@@ -65,5 +65,5 @@
     componentField,
     registered, 
     unregistered,
-    confProxyType
+    configType
 }
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/hello.api.bnd b/dependencymanager/org.apache.felix.dependencymanager.samples/hello.api.bnd
index 79a44d6..7c5262f 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/hello.api.bnd
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/hello.api.bnd
@@ -14,8 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-Private-Package:  \
-	org.apache.felix.dependencymanager.samples.hello.api
 Bundle-Activator: org.apache.felix.dependencymanager.samples.hello.api.Activator
 Bundle-Description: Dependency Manager hello example with API
-Bundle-Name: Dependency Manager Hello Example
\ No newline at end of file
+Bundle-Name: Dependency Manager Hello Example
+Private-Package: org.apache.felix.dependencymanager.samples.hello.api
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java
index afc1809..727f284 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.util.Hashtable;
 
+import org.apache.felix.dependencymanager.samples.hello.api.ServiceConsumerConf;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.log.LogService;
@@ -41,13 +42,13 @@
             System.out.println("Configuring sample components ... please consult log messages to see example output, like this:");
             System.out.println("\"log warn\"");
             // Provide configuration to the hello.ServiceConsumer component
-            m_serviceConsumerConf = m_ca.getConfiguration("org.apache.felix.dependencymanager.samples.hello.api.ServiceConsumer", null);
+            m_serviceConsumerConf = m_ca.getConfiguration("org.apache.felix.dependencymanager.samples.hello.api.ServiceConsumerConf", null);
             Hashtable<String, String> props = new Hashtable<>();
             props.put("key", "value");
             m_serviceConsumerConf.update(props);
             
             // Provide configuration to the hello.annot.ServiceConsumer component
-            m_serviceConsumerAnnotConf = m_ca.getConfiguration("org.apache.felix.dependencymanager.samples.hello.annot.ServiceConsumer", null);
+            m_serviceConsumerAnnotConf = m_ca.getConfiguration("org.apache.felix.dependencymanager.samples.hello.annot.ServiceConsumerConf", null);
             props = new Hashtable<>();
             props.put("key", "value");
             m_serviceConsumerAnnotConf.update(props);
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java
index 4093371..7d3e5bf 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java
@@ -39,15 +39,16 @@
     @ServiceDependency
     volatile LogService log;
 
-    Dictionary<?, ?> conf;
+    ServiceConsumerConf conf;
 
-    @ConfigurationDependency
-    protected void update(Dictionary<?, ?> conf) {
+    @ConfigurationDependency 
+    protected void update(ServiceConsumerConf conf) { // type safe config
         this.conf = conf;
     }
 
     @Start
     public void start() {
+        log.log(LogService.LOG_WARNING, "ServiceConsumer.start: configured key=" + conf.getKey());
         log.log(LogService.LOG_WARNING, "ServiceConsumer.start: calling service.hello() ...");
         this.service.hello();
     }
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumerConf.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumerConf.java
new file mode 100644
index 0000000..a26e5cc
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumerConf.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.samples.hello.annot;
+
+/**
+ * This is our type-safe configuration for the ServiceConsumer component.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceConsumerConf {
+    String getKey();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/Activator.java
index 8dbec58..f688d94 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/Activator.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/Activator.java
@@ -37,8 +37,7 @@
         dm.add(createComponent()
             .setImplementation(ServiceConsumer.class)            
             .add(createServiceDependency().setService(LogService.class).setRequired(true))
-            .add(createConfigurationDependency()
-                .setPid(ServiceConsumer.class.getName()).setCallback("updated"))
+            .add(createConfigurationDependency().setCallback("updated", ServiceConsumerConf.class))
             .add(createServiceDependency().setService(ServiceProvider.class).setRequired(true)));
     }
 }
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java
index 058a5db..a5097b7 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java
@@ -30,13 +30,14 @@
 public class ServiceConsumer {
     volatile ServiceProvider service;
     volatile LogService log;
-    Dictionary<?, ?> conf;
-
-    protected void update(Dictionary<?, ?> conf) {
+    ServiceConsumerConf conf;
+    
+    protected void updated(ServiceConsumerConf conf) {
         this.conf = conf;
     }
 
     public void start() {
+        log.log(LogService.LOG_WARNING, "ServiceConsumer.start: configured key = " + conf.getKey());
         log.log(LogService.LOG_WARNING, "ServiceConsumer.start: calling service.hello()");
         this.service.hello();
     }
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumerConf.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumerConf.java
new file mode 100644
index 0000000..a464ff7
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumerConf.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.samples.hello.api;
+
+/**
+ * This is our type-safe configuration for the ServiceConsumer component.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ServiceConsumerConf {
+    String getKey();
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
index a4a97fc..d34015d 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
@@ -18,6 +18,10 @@
  */
 package org.apache.felix.dm;
 
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Map;
+
 import aQute.bnd.annotation.ProviderType;
 
 /**
@@ -48,8 +52,61 @@
  * The callback invoked when a configuration dependency is updated can supports the following signatures:<p>
  * <ul><li> updated(Dictionary)
  *     <li> updated(Component, Dictionary)
+ *     <li> updated(Configuration interface)
+ *     <li> updated(Component, Configuration interface)
  * </ul>
  * 
+ * <p> Configuration interface is a new feature that allows you to specify an 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 {@link 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.<br>
+ * As proxies are injected, no implementations of the desired configuration-type are necessary!
+ * </p>
+ * <p>
+ * 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: <tt>[lower case letter] [any valid character]*</tt>. Method names starting with
+ * <tt>get</tt> or <tt>is</tt> (JavaBean convention) are stripped from these prefixes. For example: given a dictionary
+ * with the key <tt>"foo"</tt> can be accessed from a configuration-type using the following method names:
+ * <tt>foo()</tt>, <tt>getFoo()</tt> and <tt>isFoo()</tt>.
+ * </p>
+ * <p>
+ * The return values supported are: primitive types (or their object wrappers), strings, enums, arrays of
+ * primitives/strings, {@link Collection} types, {@link Map} types, {@link Class}es and interfaces. When an interface is
+ * returned, it is treated equally to a configuration type, that is, it is returned as a proxy.
+ * </p>
+ * <p>
+ * Arrays can be represented either as comma-separated values, optionally enclosed in square brackets. For example:
+ * <tt>[ a, b, c ]</tt> and <tt>a, b,c</tt> 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" =&gt; "a", "arr.1" =&gt; "b", "arr.2" =&gt; "c" would result in the same array as the earlier examples.
+ * </p>
+ * <p>
+ * 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 
+ * 
+ * <pre>{@code "map" => "{key1.value1, key2.value2}"}</pre> 
+ * 
+ * and a dictionary with <p>
+ * 
+ * <pre>{@code "map.key1" => "value1", "map2.key2" => "value2"}</pre> 
+ * 
+ * result in the same map being returned.
+ * Instead of a map, you could also define an interface with the methods <tt>getKey1()</tt> and <tt>getKey2</tt> and use
+ * that interface as return type instead of a {@link Map}.
+ * </p>
+ * <p>
+ * In case a lookup does not yield a value from the underlying map or dictionary, the following rules are applied:
+ * <ol>
+ * <li>primitive types yield their default value, as defined by the Java Specification;
+ * <li>string, {@link Class}es and enum values yield <code>null</code>;
+ * <li>for arrays, collections and maps, an empty array/collection/map is returned;
+ * <li>for other interface types that are treated as configuration type a null-object is returned.
+ * </ol>
+ * </p>
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 @ProviderType
@@ -98,6 +155,7 @@
      * is available. The contract for this method is identical to that of
      * <code>ManagedService.updated(Dictionary) throws ConfigurationException</code> with the difference that 
      * instead of a Dictionary it accepts an interface of the given configuration type.<br>
+     * By default, the pid is assumed to match the fqdn of the configuration type.
      * 
      * <p>The callback is invoked on the instantiated component.
      * 
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
index 8a21037..87ea8df 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
@@ -467,8 +467,10 @@
      *        <li> updated(Component, Dictionary)
      *        </ul>
      * @param propagate true if public factory configuration should be propagated to the adapter service properties
-     * @param configType the configuration type to use instead of a dictionary or map.
+     * @param configType the configuration type to use instead of a dictionary. See the javadoc from {@link ConfigurationDependency} for
+     * more informations about type-safe configuration.
      * @return a service that acts as a factory for generating the managed service factory configuration adapter
+     * @see ConfigurationDependency
      */
     public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, Class<?> configType) {
         return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, null, configType);
@@ -491,7 +493,8 @@
      *        </ul>
      * @param propagate true if public factory configuration should be propagated to the adapter service properties
      * @param callbackInstance the object on which the updated callback will be invoked.
-     * @param configType the configuration type to use instead of a dictionary or map.
+     * @param configType the configuration type to use instead of a dictionary.  See the javadoc from {@link ConfigurationDependency} for
+     * more informations about type-safe configuration.
      * @return a service that acts as a factory for generating the managed service factory configuration adapter
      */
     public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, Object callbackInstance, Class<?> configType) {
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/ComponentContext.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/ComponentContext.java
index b3aa039..82b9a0e 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/ComponentContext.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/context/ComponentContext.java
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.dm.context;
 
+import java.util.Dictionary;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -156,4 +157,15 @@
      * @return all the available dependency services for a given dependency
      */
     public Set<Event> getDependencyEvents(DependencyContext dc);
+    
+    /**
+     * Creates a configuration for a given type backed by a given dictionary.
+     * This method can be used by any custom Dependency Manager dependency that
+     * needs to expose some configuration through a dynamic proxy interface.
+     * 
+     * @param type the configuration class, cannot be <code>null</code>;
+     * @param config the configuration to wrap, cannot be <code>null</code>.
+     * @return an instance of the given type that wraps the given configuration.
+     */
+    public <T> T createConfigurationProxy(Class<T> type, Dictionary<?, ?> config);
 }
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java
index 0a7a49f..13ff38e 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java
@@ -341,6 +341,11 @@
     }
 
     @Override
+    public <T> T createConfigurationProxy(Class<T> type, Dictionary<?, ?> config) {
+        return Configurable.create(type,  config);
+    }
+    
+    @Override
     public Executor getExecutor() {
         return m_executor;
     }
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
index 1122d44..5725161 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
@@ -21,6 +21,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.util.Arrays;
 import java.util.Dictionary;
+import java.util.Objects;
 import java.util.Properties;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -126,8 +127,10 @@
      * Sets a type-safe callback method invoked on the instantiated component.
      */
     public ConfigurationDependency setCallback(String callback, Class<?> configType) {
+        Objects.nonNull(configType);
         setCallback(callback);
         m_configType = configType;
+        m_pid = (m_pid == null) ? configType.getName() : m_pid;
         return this;
     }
 
@@ -136,8 +139,10 @@
      * The component is not yet instantiated at the time the callback is invoked.
      */
     public ConfigurationDependency setCallback(Object instance, String callback, Class<?> configType) {
+        Objects.nonNull(configType);
         setCallback(instance, callback);
         m_configType = configType;
+        m_pid = (m_pid == null) ? configType.getName() : m_pid;
         return this;
     }
     
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FilterComponent.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FilterComponent.java
index f7ebb98..5c27564 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FilterComponent.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FilterComponent.java
@@ -67,6 +67,11 @@
     }
     
     @Override
+    public <T> T createConfigurationProxy(Class<T> type, Dictionary<?, ?> config) {
+        return m_component.createConfigurationProxy(type, config);
+    }
+    
+    @Override
     public Executor getExecutor() {
         return m_component.getExecutor();
     }