FELIX-5177: Committed the patch from Jan-Willem (:-)) which implements config proxy for factory conf adapters.
Updated the "dictionary.api" sample code that is now using a proxy config when using factory conf adapter.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1729508 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java
index c4cc264..13083bc 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java
@@ -33,7 +33,7 @@
     @Override
     public void init(BundleContext context, DependencyManager dm) throws Exception {
         // Create the factory configuration for our DictionaryImpl service.
-        dm.add(createFactoryConfigurationAdapterService(DictionaryConfiguration.class.getName(), "updated", true)
+        dm.add(createFactoryConfigurationAdapterService(DictionaryConfiguration.class.getName(), "updated", true, DictionaryConfiguration.class)
             .setInterface(DictionaryService.class.getName(), null)
             .setImplementation(DictionaryImpl.class)
             .add(createServiceDependency().setService(LogService.class))); // NullObject 
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java
index 4b361e1..da33f55 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java
@@ -61,16 +61,11 @@
      * Our service will be initialized from ConfigAdmin.
      * @param config The configuration where we'll lookup our words list (key=".words").
      */
-    protected void updated(Dictionary<String, ?> config) {
-        if (config != null) {
-            // We use the bnd "Configurable" helper in order to get an implementation for our DictionaryConfiguration interface.
-            DictionaryConfiguration cnf = Configurable.createConfigurable(DictionaryConfiguration.class, config);
-
-            m_lang = cnf.lang();
-            m_words.clear();
-            for (String word : cnf.words()) {
-                m_words.add(word);
-            }
+    protected void updated(DictionaryConfiguration cnf) {
+        m_lang = cnf.lang();
+        m_words.clear();
+        for (String word : cnf.words()) {
+            m_words.add(word);
         }
     }
 
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java
index 07b0b98..a3674b8 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java
@@ -364,6 +364,26 @@
     }
   
     /**
+     * Creates a new factory configuration adapter service, using a specific callback instance
+     * 
+     * @return the factory configuration adapter service
+     * @see DependencyManager#createFactoryConfigurationAdapterService(String, String, boolean, Class)
+     */
+    public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, Class<?> configType) {
+        return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, propagate, configType);
+    }
+
+    /**
+     * Creates a new factory configuration adapter service, using a specific callback instance
+     * 
+     * @return the factory configuration adapter service
+     * @see DependencyManager#createFactoryConfigurationAdapterService(String, String, boolean, Object, Class)
+     */
+    public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, Object callbackInstance, Class<?> configType) {
+        return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, propagate, callbackInstance, configType);
+    }
+
+    /**
      * Creates a new factory configuration adapter service.
      * 
      * @return the factory configuration adapter service
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 d26a10f..8a21037 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
@@ -416,7 +416,7 @@
      * @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) {
-        return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, null);
+        return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, null, null);
     }
 
     /**
@@ -439,7 +439,63 @@
      * @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) {
-        return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, callbackInstance);
+        return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, callbackInstance, null);
+    }
+
+    /**
+     * Creates a new Managed Service Factory Configuration Adapter. For each new Config Admin factory configuration matching
+     * the factoryPid, an adapter will be created based on the adapter implementation class.
+     * The adapter will be registered with the specified interface, and with the specified adapter service properties.
+     * 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. 
+     * It will also inherit all dependencies.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     *  manager.createFactoryConfigurationAdapterService("MyFactoryPid",  "update", true)
+     *         // The interface to use when registering adapter
+     *         .setInterface(AdapterService.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+     *         // the implementation of the adapter
+     *         .setImplementation(AdapterServiceImpl.class);
+     * </pre></blockquote>
+     * 
+     * @param factoryPid the pid matching the factory configuration
+     * @param update the adapter method name that will be notified when the factory configuration is created/updated.<p>
+     *  The following signatures are supported:<p>
+     *        <ul><li> updated(Dictionary)
+     *        <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.
+     * @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, Class<?> configType) {
+        return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, null, configType);
+    }
+
+    /**
+     * Creates a new Managed Service Factory Configuration Adapter using a specific update callback instance. 
+     * For each new Config Admin factory configuration matching the factoryPid, an adapter will be created 
+     * based on the adapter implementation class.
+     * The adapter will be registered with the specified interface, and with the specified adapter service properties.
+     * 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. 
+     * It will also inherit all dependencies.
+     * 
+     * @param factoryPid the pid matching the factory configuration
+     * @param update the adapter method name that will be notified when the factory configuration is created/updated.<p>
+     *  The following signatures are supported:<p>
+     *        <ul><li> updated(Dictionary)
+     *        <li> updated(Component, Dictionary)
+     *        </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.
+     * @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) {
+        return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, callbackInstance, configType);
     }
 
    /**
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java
index 92086b4..0649b95 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java
@@ -25,7 +25,6 @@
 
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentStateListener;
-import org.apache.felix.dm.Dependency;
 import org.apache.felix.dm.DependencyManager;
 import org.apache.felix.dm.ServiceDependency;
 import org.apache.felix.dm.context.DependencyContext;
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java
index 5adce5b..d89cd33 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java
@@ -27,7 +27,6 @@
 
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentStateListener;
-import org.apache.felix.dm.Dependency;
 import org.apache.felix.dm.DependencyManager;
 import org.apache.felix.dm.ServiceDependency;
 import org.apache.felix.dm.context.DependencyContext;
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java
index 166af93..b28d2e0 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java
@@ -24,7 +24,6 @@
 
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentStateListener;
-import org.apache.felix.dm.Dependency;
 import org.apache.felix.dm.DependencyManager;
 import org.apache.felix.dm.context.DependencyContext;
 import org.osgi.framework.Bundle;
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java
index 4aad446..f424e17 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java
@@ -234,7 +234,8 @@
             Bundle bundle = (Bundle) event.getEvent();
             if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
                 try {
-                    return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ Bundle.class }}, new Object[][] {{ bundle }});
+                    CallbackTypeDef callbackInfo = new CallbackTypeDef(Bundle.class, bundle);
+                    return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, callbackInfo.m_sigs, callbackInfo.m_args);
                 }
                 catch (InvocationTargetException e) {
                     m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/CallbackTypeDef.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/CallbackTypeDef.java
new file mode 100644
index 0000000..597fcb4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/CallbackTypeDef.java
@@ -0,0 +1,39 @@
+/*
+ * 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.dm.impl;
+
+/**
+ * Small wrapper to hold the various signatures and arguments for callback methods.
+ */
+public class CallbackTypeDef {
+
+    public final Class<?>[][] m_sigs;
+    public final Object[][] m_args;
+
+    public CallbackTypeDef(Class<?> sig, Object arg) {
+        this(new Class[][] { { sig } }, new Object[][] { { arg } });
+    }
+
+    public CallbackTypeDef(Class<?>[][] sigs, Object[][] args) {
+        m_sigs = sigs;
+        m_args = args;
+    }
+
+}
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 face758..d129e42 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
@@ -347,6 +347,38 @@
         }
     }
     
+    /**
+     * Creates the various signatures and arguments combinations used for the configuration-type style callbacks.
+     * 
+     * @param service the service for which the callback should be applied;
+     * @param configType the configuration type to use (can be <code>null</code>);
+     * @param settings the actual configuration settings.
+     */
+    static CallbackTypeDef createCallbackType(Logger logger, Component service, Class<?> configType, Dictionary<?, ?> settings) {
+        Class<?>[][] sigs = new Class[][] { { Dictionary.class }, { Component.class, Dictionary.class }, {} };
+        Object[][] args = new Object[][] { { settings }, { service, settings }, {} };
+
+        if (configType != null) {
+            try {
+                // if the configuration is null, it means we are losing it, and since we pass a null dictionary for other callback
+                // (that accepts a Dictionary), then we should have the same behavior and also pass a null conf proxy object when
+                // the configuration is lost.
+                Object configurable = settings != null ? Configurable.create(configType, settings) : null;
+                
+                logger.debug("Using configuration-type injecting using %s as possible configType.", configType.getSimpleName());
+
+                sigs = new Class[][] { { Dictionary.class }, { Component.class, Dictionary.class }, { Component.class, configType }, { configType }, {} };
+                args = new Object[][] { { settings }, { service, settings }, { service, configurable }, { configurable }, {} };
+            }
+            catch (Exception e) {
+                // This is not something we can recover from, use the defaults above...
+                logger.warn("Failed to create configurable for configuration type %s!", e, configType);
+            }
+        }
+
+        return new CallbackTypeDef(sigs, args);
+    }
+
     private void invokeUpdated(Dictionary<?, ?> settings) throws ConfigurationException {
         if (m_updateInvokedCache.compareAndSet(false, true)) {
             Object[] instances = super.getInstances(); // either the callback instance or the component instances
@@ -354,29 +386,11 @@
                 return;
             }
 
-            Class<?>[][] sigs = new Class[][] { { Dictionary.class }, { Component.class, Dictionary.class }, {} };
-            Object[][] args = new Object[][] { { settings }, { m_component, settings }, {} };
-
-            if (m_configType != null) {
-                Object configurable;
-                try {
-                    // if the configuration is null, it means we are losing it, and since we pass a null dictionary for other callback
-                    // (that accepts a Dictionary), then we should have the same behavior and also pass a null conf proxy object when
-                    // the configuration is lost.
-                    configurable = settings != null ? Configurable.create(m_configType, settings) : null;
-
-                    sigs = new Class[][] { { Dictionary.class }, { Component.class, Dictionary.class }, { Component.class, m_configType }, { m_configType }, {} };
-                    args = new Object[][] { { settings }, { m_component, settings }, { m_component, configurable }, { configurable }, {} };
-                }
-                catch (Exception e) {
-                    // This is not something we can recover from, use the defaults above...
-                    m_component.getLogger().warn("Failed to create configurable for method %s and configuration type %s!", e, m_add, m_configType);
-                }
-            }
+            CallbackTypeDef callbackInfo = createCallbackType(m_logger, m_component, m_configType, settings);
 
             for (int i = 0; i < instances.length; i++) {
                 try {
-                    InvocationUtil.invokeCallbackMethod(instances[i], m_add, sigs, args);
+                    InvocationUtil.invokeCallbackMethod(instances[i], m_add, callbackInfo.m_sigs, callbackInfo.m_args);
                 }
                 catch (InvocationTargetException e) {
                     // The component has thrown an exception during it's callback invocation.
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
index 6ad328d..646ffeb 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
@@ -24,11 +24,9 @@
 import java.util.Hashtable;
 
 import org.apache.felix.dm.Component;
-import org.apache.felix.dm.Dependency;
 import org.apache.felix.dm.DependencyManager;
 import org.apache.felix.dm.Logger;
 import org.apache.felix.dm.PropertyMetaData;
-import org.apache.felix.dm.context.DependencyContext;
 import org.apache.felix.dm.impl.metatype.MetaTypeProviderImpl;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
@@ -36,6 +34,8 @@
 import org.osgi.service.metatype.MetaTypeProvider;
 import org.osgi.service.metatype.ObjectClassDefinition;
 
+import static org.apache.felix.dm.impl.ConfigurationDependencyImpl.createCallbackType;
+
 /**
  * Factory configuration adapter service implementation. This class extends the FilterService in order to catch
  * some Service methods for configuring actual adapter service implementation.
@@ -49,7 +49,7 @@
     // Our logger
     protected final Logger m_logger;
     
-    public FactoryConfigurationAdapterImpl(DependencyManager dm, String factoryPid, String update, boolean propagate, Object updateCallbackInstance) {
+    public FactoryConfigurationAdapterImpl(DependencyManager dm, String factoryPid, String update, boolean propagate, Object updateCallbackInstance, Class<?> configType) {
         super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
         m_factoryPid = factoryPid;
         m_logger = ((ComponentImpl) m_component).getLogger();
@@ -58,7 +58,7 @@
         props.put(Constants.SERVICE_PID, factoryPid);
         m_component
             .setInterface(ManagedServiceFactory.class.getName(), props)
-            .setImplementation(new AdapterImpl(update, propagate, updateCallbackInstance))
+            .setImplementation(new AdapterImpl(update, propagate, updateCallbackInstance, configType))
             .setCallbacks("init", null, "stop", null);
     }
     
@@ -80,7 +80,7 @@
     public String getName() {
         return "Adapter for factory pid " + m_factoryPid;
     }
-    
+
     /**
      * Creates, updates, or removes a service, when a ConfigAdmin factory configuration is created/updated or deleted.
      */
@@ -93,6 +93,9 @@
         
         // A specific callback instance where the update callback is invoked on (or null if default component instances should be used).
         protected final Object m_updateCallbackInstance;
+        
+        // the configuration type to use as alternative to the dictionary/map when propagating the configuration to the service
+        protected final Class<?> m_configType;
 
         /**
          * Creates a new CM factory configuration adapter.
@@ -105,10 +108,11 @@
          * @param propagate
          * @param updateCallbackObject null if update should be called on all component instances (composition), or a specific update callback instance.
          */
-        public AdapterImpl(String updateMethod, boolean propagate, Object updateCallbackObject) {
+        public AdapterImpl(String updateMethod, boolean propagate, Object updateCallbackObject, Class<?> configType) {
             m_update = updateMethod;
             m_propagate = propagate;
             m_updateCallbackInstance = updateCallbackObject;
+            m_configType = configType;
         }
 
         /**
@@ -142,23 +146,23 @@
             
             // Instantiate the component, because we need to invoke the updated callback synchronously, in the CM calling thread.
             ((ComponentImpl) newService).instantiateComponent();
-                        
-            try {                
-                for (Object instance : getCompositionInstances(newService)) {
-                    InvocationUtil.invokeCallbackMethod(instance, m_update, 
-                        new Class[][] {
-                            {Dictionary.class},
-                            {Component.class, Dictionary.class},
-                            {}}, 
-                        new Object[][] {
-                            {settings}, 
-                            {newService, settings},
-                            {}});
+
+            CallbackTypeDef callbackInfo = createCallbackType(m_logger, newService, m_configType, settings);
+
+            for (Object instance : getCompositionInstances(newService)) {
+                try {
+                    InvocationUtil.invokeCallbackMethod(instance, m_update, callbackInfo.m_sigs, callbackInfo.m_args);
                 }
-            }
-            
-            catch (Throwable t) {
-               handleException(t); // will rethrow a runtime exception.
+                catch (InvocationTargetException e) {
+                    // The component has thrown an exception during it's callback invocation.
+                    handleException(e.getTargetException());
+                }
+                catch (NoSuchMethodException e) {
+                    // if the method does not exist, ignore it
+                }
+                catch (Throwable t) {
+                    handleException(t); // will rethrow a runtime exception.
+                }
             }
 
             return newService;
@@ -174,18 +178,11 @@
             Component service = (Component) properties[1];
             Object[] instances = getUpdateCallbackInstances(service);
 
+            CallbackTypeDef callbackInfo = createCallbackType(m_logger, service, m_configType, cmSettings);
+
             try {
                 for (Object instance : instances) {
-                    InvocationUtil.invokeCallbackMethod(instance, m_update, 
-                        new Class[][] {
-                            { Dictionary.class },
-                            { Component.class, Dictionary.class },
-                            {}}, 
-                        new Object[][] {
-                            { cmSettings }, 
-                            { service, cmSettings },
-                        {}
-                    });
+                    InvocationUtil.invokeCallbackMethod(instance, m_update, callbackInfo.m_sigs, callbackInfo.m_args); 
                 }
                 if (m_serviceInterfaces != null && m_propagate == true) {
                     Dictionary<String, ?> serviceProperties = getServiceProperties(cmSettings);
@@ -251,11 +248,10 @@
                     }
                 }
             }
-
             
             return props;
         }
-        
+
         private void handleException(Throwable t) {
             m_logger.log(Logger.LOG_ERROR, "Got exception while handling configuration update for factory pid " + m_factoryPid, t);
             if (t instanceof InvocationTargetException) {
@@ -285,7 +281,7 @@
                                    BundleContext bctx, Logger logger, String heading, 
                                    String description, String localization,
                                    PropertyMetaData[] properyMetaData) {
-            super(updateMethod, propagate, updateCallbackInstance);
+            super(updateMethod, propagate, updateCallbackInstance, null /* configType */);
             m_metaType = new MetaTypeProviderImpl(m_factoryPid, bctx, logger, null, this);
             m_metaType.setName(heading);
             m_metaType.setDescription(description);
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java
index 1115884..3cfc117 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java
@@ -25,7 +25,6 @@
 
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentStateListener;
-import org.apache.felix.dm.Dependency;
 import org.apache.felix.dm.DependencyManager;
 import org.apache.felix.dm.ResourceDependency;
 import org.apache.felix.dm.context.DependencyContext;
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java
index effd588..834ee55 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java
@@ -194,7 +194,8 @@
             Dictionary<String, Object> resourceProperties = re.getProperties();
             if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
                 try {
-                    return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ URL.class }}, new Object[][] {{ resource }});
+                    CallbackTypeDef callbackInfo = new CallbackTypeDef(URL.class, resource);
+                    return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, callbackInfo.m_sigs, callbackInfo.m_args);
                 }
                 catch (InvocationTargetException e) {
                     m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java
index ab4daf4..4e9df67 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java
@@ -356,9 +356,9 @@
         if (se != null) {
             if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
                 try {
-                    return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod,
-                            new Class[][]{{ServiceReference.class, Object.class}, {ServiceReference.class}}, new Object[][]{
-                                    {se.getReference(), se.getEvent()}, {se.getReference()}});
+                    CallbackTypeDef callbackInfo = new CallbackTypeDef(new Class[][] { { ServiceReference.class, Object.class }, { ServiceReference.class } },
+                        new Object[][] { { se.getReference(), se.getEvent() }, { se.getReference() } });
+                    return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, callbackInfo.m_sigs, callbackInfo.m_args);
                 } catch (InvocationTargetException e) {
                     m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
                 } catch (Throwable e) {
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java
index c455a77..af82ff0 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java
@@ -20,8 +20,7 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -29,71 +28,53 @@
 import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.felix.dm.Logger;
 import org.apache.felix.dm.context.ComponentContext;
 import org.junit.Test;
+import org.osgi.framework.BundleContext;
 import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.cm.ManagedService;
 
+import test.Ensure;
+
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public class ConfigurationDependencyImplTest {
-    public static interface MyMap {
-        String getFoo();
-
-        int getQux();
-
-        String[] getQuu();
-    }
-
     public static interface MyConfiguration {
-        boolean isTrue();
-
-        int getValue();
-
-        long getLongValue();
-
-        double getPi();
-
         String[] getArgArray();
 
         List<String> getArgList();
 
+        long getLongValue();
+
         MyMap getMap();
 
         String getMessage();
+
+        double getPi();
+
+        int getValue();
+
+        boolean isTrue();
     }
 
-    static class PlainService {
-        final CountDownLatch m_latch = new CountDownLatch(1);
+    public static interface MyMap {
+        String getFoo();
 
-        public void updated(Dictionary config) throws ConfigurationException {
-            if (config != null) {
-                m_latch.countDown();
-            }
-            assertConfiguration(config);
-        }
+        String[] getQuu();
 
-        private void assertConfiguration(Dictionary cfg) {
-            assertEquals("isTrue", "true", cfg.get("true"));
-            assertEquals("getValue", "42", cfg.get("value"));
-            assertEquals("getLongValue", "1234567890", cfg.get("longValue"));
-            assertEquals("getPi", "3.141", cfg.get("pi"));
-            assertEquals("getArgArray", "[a, b, c]", cfg.get("argArray"));
-            assertEquals("getArgList", "[d, e, f]", cfg.get("argList"));
-            assertEquals("getMap.foo", "bar", cfg.get("map.foo"));
-            assertEquals("getMap.qux", "123", cfg.get("map.qux"));
-            assertEquals("getMap.quu", "[x, y, z]", cfg.get("map.quu"));
-            assertEquals("getMessage", "hello world!", cfg.get("message"));
-        }
+        int getQux();
     }
 
     static class AManagedService extends PlainService implements ManagedService {
+        public AManagedService(Ensure ensure) {
+            super(ensure);
+        }
+
         @Override
         public void updated(Dictionary config) throws ConfigurationException {
             super.updated(config);
@@ -101,13 +82,25 @@
     }
 
     static class FancyService {
-        final CountDownLatch m_latch = new CountDownLatch(1);
+        final Ensure m_ensure;
+
+        public FancyService(Ensure ensure) {
+            m_ensure = ensure;
+        }
 
         public void updated(MyConfiguration config) throws ConfigurationException {
+            m_ensure.step();
+
             if (config != null) {
-                m_latch.countDown();
+                assertConfiguration(config);
             }
-            assertConfiguration(config);
+            else {
+                assertNull(config);
+            }
+        }
+
+        public void stop() {
+            m_ensure.step();
         }
 
         private void assertConfiguration(MyConfiguration cfg) {
@@ -126,46 +119,117 @@
         }
     }
 
-    @Test
-    public void testInvokeManagedServiceUpdatedMethodOk() throws Exception {
-        AManagedService service = new AManagedService();
+    static class PlainService {
+        final Ensure m_ensure;
 
-        ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
-        cdi.updated(createDictionary());
+        public PlainService(Ensure ensure) {
+            m_ensure = ensure;
+        }
 
-        assertTrue(service.m_latch.await(1, TimeUnit.SECONDS));
+        public void updated(Dictionary config) throws ConfigurationException {
+            m_ensure.step();
+
+            if (config != null) {
+                assertConfiguration(config);
+            }
+            else {
+                assertNull(config);
+            }
+        }
+
+        public void stop() {
+            m_ensure.step();
+        }
+
+        private void assertConfiguration(Dictionary cfg) {
+            assertEquals("isTrue", "true", cfg.get("true"));
+            assertEquals("getValue", "42", cfg.get("value"));
+            assertEquals("getLongValue", "1234567890", cfg.get("longValue"));
+            assertEquals("getPi", "3.141", cfg.get("pi"));
+            assertEquals("getArgArray", "[a, b, c]", cfg.get("argArray"));
+            assertEquals("getArgList", "[d, e, f]", cfg.get("argList"));
+            assertEquals("getMap.foo", "bar", cfg.get("map.foo"));
+            assertEquals("getMap.qux", "123", cfg.get("map.qux"));
+            assertEquals("getMap.quu", "[x, y, z]", cfg.get("map.quu"));
+            assertEquals("getMessage", "hello world!", cfg.get("message"));
+        }
     }
 
     @Test
-    public void testInvokePlainUpdatedMethodOk() throws Exception {
-        PlainService service = new PlainService();
+    public void testDoNotInvokeFancyUpdatedMethodWithWrongSignatureOk() throws Exception {
+        Ensure ensure = createEnsure();
+        FancyService service = new FancyService(ensure);
 
         ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
+        cdi.setCallback(service, "updated", Dictionary.class);
+        ensure.step(1);
+
         cdi.updated(createDictionary());
 
-        assertTrue(service.m_latch.await(1, TimeUnit.SECONDS));
+        TimeUnit.SECONDS.sleep(1L);
+
+        // Our step shouldn't be changed...
+        ensure.waitForStep(1, 1000);
     }
 
     @Test
     public void testInvokeFancyUpdatedMethodOk() throws Exception {
-        FancyService service = new FancyService();
+        Ensure ensure = createEnsure();
+        FancyService service = new FancyService(ensure);
 
         ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
         cdi.setCallback(service, "updated", MyConfiguration.class);
         cdi.updated(createDictionary());
 
-        assertTrue(service.m_latch.await(1, TimeUnit.SECONDS));
+        ensure.waitForStep(1, 1000);
+
+        cdi.updated(null);
+
+        ensure.waitForStep(2, 1000);
     }
 
     @Test
-    public void testDoNotInvokeFancyUpdatedMethodWithWrongSignatureOk() throws Exception {
-        FancyService service = new FancyService();
+    public void testInvokeManagedServiceUpdatedMethodOk() throws Exception {
+        Ensure ensure = createEnsure();
+        AManagedService service = new AManagedService(ensure);
 
         ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
-        cdi.setCallback(service, "updated", Dictionary.class);
         cdi.updated(createDictionary());
 
-        assertFalse(service.m_latch.await(1, TimeUnit.SECONDS));
+        ensure.waitForStep(1, 1000);
+
+        cdi.updated(null);
+
+        ensure.waitForStep(2, 1000);
+    }
+
+    @Test
+    public void testInvokePlainUpdatedMethodOk() throws Exception {
+        Ensure ensure = createEnsure();
+        PlainService service = new PlainService(ensure);
+
+        ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
+        cdi.updated(createDictionary());
+
+        ensure.waitForStep(1, 1000);
+
+        cdi.updated(null);
+
+        ensure.waitForStep(2, 1000);
+    }
+
+    private ConfigurationDependencyImpl createConfigurationDependency(Object service) {
+        BundleContext bc = mock(BundleContext.class);
+        Logger mockLogger = mock(Logger.class);
+
+        ComponentContext component = mock(ComponentContext.class);
+        when(component.getExecutor()).thenReturn(Executors.newSingleThreadExecutor());
+        when(component.getLogger()).thenReturn(mockLogger);
+
+        ConfigurationDependencyImpl result = new ConfigurationDependencyImpl(bc, mockLogger);
+        result.setCallback(service, "updated").setPid("does.not.matter");
+        result.setComponentContext(component);
+        return result;
     }
 
     private Dictionary createDictionary() {
@@ -183,16 +247,7 @@
         return result;
     }
 
-    private ConfigurationDependencyImpl createConfigurationDependency(Object service) {
-        Logger mockLogger = mock(Logger.class);
-
-        ComponentContext component = mock(ComponentContext.class);
-        when(component.getExecutor()).thenReturn(Executors.newSingleThreadExecutor());
-        when(component.getLogger()).thenReturn(mockLogger);
-
-        ConfigurationDependencyImpl result = new ConfigurationDependencyImpl();
-        result.setCallback(service, "updated").setPid("does.not.matter");
-        result.setComponentContext(component);
-        return result;
+    private Ensure createEnsure() {
+        return new Ensure(false);
     }
 }
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/FactoryConfigurationAdapterImplTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/FactoryConfigurationAdapterImplTest.java
new file mode 100644
index 0000000..51d6edb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/FactoryConfigurationAdapterImplTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.dm.impl;
+
+import static org.mockito.Mockito.mock;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.impl.ConfigurationDependencyImplTest.AManagedService;
+import org.apache.felix.dm.impl.ConfigurationDependencyImplTest.FancyService;
+import org.apache.felix.dm.impl.ConfigurationDependencyImplTest.MyConfiguration;
+import org.apache.felix.dm.impl.ConfigurationDependencyImplTest.PlainService;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ManagedServiceFactory;
+
+import test.Ensure;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FactoryConfigurationAdapterImplTest {
+
+    private static final String CONF_PID = "bogus";
+
+    @Test
+    public void testDoNotInvokeFancyUpdatedMethodWithWrongSignatureOk() throws Exception {
+        Ensure ensure = createEnsure();
+        FancyService service = new FancyService(ensure);
+
+        FactoryConfigurationAdapterImpl cdi = createConfigurationDependency(service, Map.class);
+        ensure.step(1);
+
+        ((ManagedServiceFactory) cdi.m_component.getInstance()).updated(CONF_PID, createDictionary());
+
+        TimeUnit.SECONDS.sleep(1L);
+
+        // Our step shouldn't be changed...
+        ensure.waitForStep(1, 1000);
+    }
+
+    @Test
+    public void testInvokeFancyUpdatedMethodOk() throws Exception {
+        Ensure ensure = createEnsure();
+        FancyService service = new FancyService(ensure);
+
+        FactoryConfigurationAdapterImpl cdi = createConfigurationDependency(service, MyConfiguration.class);
+
+        ((ManagedServiceFactory) cdi.m_component.getInstance()).updated(CONF_PID, createDictionary());
+
+        ensure.waitForStep(1, 1000);
+
+        ((ManagedServiceFactory) cdi.m_component.getInstance()).deleted(CONF_PID);
+
+        ensure.waitForStep(2, 1000);
+    }
+
+    @Test
+    public void testInvokeManagedServiceUpdatedMethodOk() throws Exception {
+        Ensure ensure = createEnsure();
+        AManagedService service = new AManagedService(ensure);
+
+        FactoryConfigurationAdapterImpl cdi = createConfigurationDependency(service);
+
+        ((ManagedServiceFactory) cdi.m_component.getInstance()).updated(CONF_PID, createDictionary());
+
+        ensure.waitForStep(1, 1000);
+
+        ((ManagedServiceFactory) cdi.m_component.getInstance()).deleted(CONF_PID);
+
+        ensure.waitForStep(2, 1000);
+    }
+
+    @Test
+    public void testInvokePlainUpdatedMethodOk() throws Exception {
+        Ensure ensure = createEnsure();
+        PlainService service = new PlainService(ensure);
+
+        FactoryConfigurationAdapterImpl cdi = createConfigurationDependency(service);
+
+        ((ManagedServiceFactory) cdi.m_component.getInstance()).updated(CONF_PID, createDictionary());
+
+        ensure.waitForStep(1, 1000);
+
+        ((ManagedServiceFactory) cdi.m_component.getInstance()).deleted(CONF_PID);
+
+        ensure.waitForStep(2, 1000);
+    }
+
+
+    private FactoryConfigurationAdapterImpl createConfigurationDependency(Object service) {
+        return createConfigurationDependency(service, null);
+    }
+    
+    private FactoryConfigurationAdapterImpl createConfigurationDependency(Object service, Class<?> configType) {
+        BundleContext bc = mock(BundleContext.class);
+
+        DependencyManager dm = new DependencyManager(bc);
+
+        Component result = dm.createFactoryConfigurationAdapterService("does.not.matter", "updated", false, service, configType);
+        
+        // Normally, when creating a factory pid adapter, you specify the class of the adapter implementation which will be instantiated
+        // for each created factory pid. To do so, you invoke the setImplementation(Object impl) method, and this methods
+        // accepts a class parameter, or an object instance. Usually, you always pass a class, because the intent of a factory pid adapter is to
+        // create a component instance for each created factory pid. But in our case, the "service" parameter represents our adapter instance, 
+        // so just use it as the factory adapter implementation instance: 
+        result.setImplementation(service); 
+        
+        // *Important note:* the semantic of the factory conf pid adapter is really similar to a ManagedServiceFactory: 
+        // - when the factory pid is created, a component is created; called in updated; and called in start().
+        // - when the factory pid is updated, the component is called in updated().
+        // - but when the factory pid is removed, updated(null) is not invoked (unlike in case of ConfigurationDependency), and the component is simply 
+        //   stopped. This is actually the same semantic as ManagedServiceFactory: when factory pid is removed, ManagedServiceFactory.deleted() is called
+        //   and the deleted() method is assumed to stop and unregister the service that was registered for the pid being removed.
+        dm.add(result);
+        return (FactoryConfigurationAdapterImpl) result;
+    }
+
+    private Dictionary<?,?> createDictionary() {
+        Dictionary<String, Object> result = new Hashtable<>();
+        result.put("true", "true");
+        result.put("value", "42");
+        result.put("longValue", "1234567890");
+        result.put("pi", "3.141");
+        result.put("argArray", "[a, b, c]");
+        result.put("argList", "[d, e, f]");
+        result.put("map.foo", "bar");
+        result.put("map.qux", "123");
+        result.put("map.quu", "[x, y, z]");
+        result.put("message", "hello world!");
+        return result;
+    }
+
+    private Ensure createEnsure() {
+        return new Ensure(false);
+    }
+}