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" => "a", "arr.1" => "b", "arr.2" => "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();
}