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/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();
     }