reworked FactoryConfigurationAdapter API

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@947624 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivatorBase.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivatorBase.java
index 53156a0..57e3e5d 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivatorBase.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivatorBase.java
@@ -173,74 +173,14 @@
         return m_manager.createBundleAdapterService(bundleStateMask, bundleFilter, propagate);
     }
 
-    /**
-     * 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.
-     * 
-     * @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.
-     * @param adapterInterface the interface to use when registering adapters (can be either a String, String array) 
-     * @param adapterImplementation the implementation of the adapter (can be a Class or an Object instance)
-     * @param adapterProperties additional properties to use with the service registration
-     * @param propagate true if public factory configuration should be propagated to the adapter service properties
-     * @return a service that acts as a factory for generating the managed service factory configuration adapter
-     */
-    public Service createFactoryConfigurationAdapterService(String factoryPid, String update, Object adapterImplementation, String adapterInterface, Dictionary adapterProperties, boolean propagate) {
-        return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, adapterImplementation, adapterInterface, adapterProperties, propagate);
+    public Service createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate) {
+        return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, propagate);
     }
     
-    /**
-     * 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.
-     * 
-     * @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.
-     * @param adapterInterfaces the interfaces to use when registering adapters (can be either a String, String array) 
-     * @param adapterImplementation the implementation of the adapter (can be a Class or an Object instance)
-     * @param adapterProperties additional properties to use with the service registration
-     * @param propagate true if public factory configuration should be propagated to the adapter service properties
-     * @return a service that acts as a factory for generating the managed service factory configuration adapter
-     */
-   public Service createFactoryConfigurationAdapterService(String factoryPid, String update, Object adapterImplementation, String[] adapterInterfaces, Dictionary adapterProperties, boolean propagate) {
-        return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, adapterImplementation, adapterInterfaces, adapterProperties, propagate);
-    }
-
-   /**
-    * Creates a new Managed Service Factory Configuration Adapter with meta type support. 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.
-    * @param adapterInterfaces the interfaces to use when registering adapters (can be either a String, String array) 
-    * @param adapterImplementation the implementation of the adapter (can be a Class or an Object instance)
-    * @param adapterProperties additional properties to use with the service registration
-    * @param propagate true if public factory configuration should be propagated to the adapter service properties
-    * @param heading The label used to display the tab name (or section) where the properties are displayed. Example: "Printer Service"
-    * @param desc A human readable description of the factory PID this configuration is associated with. Example: "Configuration for the PrinterService bundle"
-    * @param localization Points to the basename of the Properties file that can localize the Meta Type informations.
-    *        The default localization base name for the properties is OSGI-INF/l10n/bundle, but can
-    *        be overridden by the manifest Bundle-Localization header (see core specification, in section Localization on page 68).
-    *        You can specify a specific localization basename file using this parameter (e.g. <code>"person"</code> 
-    *        will match person_du_NL.properties in the root bundle directory).
-    * @param propertiesMetaData Array of MetaData regarding configuration properties
-    * @return a service that acts as a factory for generating the managed service factory configuration adapter
-    */
-   public Service createFactoryConfigurationAdapterService(String factoryPid, String update, Object adapterImplementation, String[] adapterInterfaces, Dictionary adapterProperties, boolean propagate, 
+   public Service createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, 
        String heading, String desc, String localization, PropertyMetaData[] propertiesMetaData) 
    {
-       return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, adapterImplementation, adapterInterfaces, adapterProperties, propagate,
+       return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, propagate,
            heading, desc, localization, propertiesMetaData);
    }
 
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
index 2e60ec5..15f8719 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
@@ -20,8 +20,6 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Hashtable;
 import java.util.List;
 
 import org.apache.felix.dm.dependencies.BundleDependency;
@@ -33,8 +31,7 @@
 import org.apache.felix.dm.impl.AdapterServiceImpl;
 import org.apache.felix.dm.impl.AspectServiceImpl;
 import org.apache.felix.dm.impl.BundleAdapterServiceImpl;
-import org.apache.felix.dm.impl.FactoryConfigurationAdapterImpl;
-import org.apache.felix.dm.impl.FactoryConfigurationAdapterMetaTypeImpl;
+import org.apache.felix.dm.impl.FactoryConfigurationAdapterServiceImpl;
 import org.apache.felix.dm.impl.Logger;
 import org.apache.felix.dm.impl.ResourceAdapterServiceImpl;
 import org.apache.felix.dm.impl.ServiceImpl;
@@ -47,8 +44,6 @@
 import org.apache.felix.dm.resources.Resource;
 import org.apache.felix.dm.service.Service;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.service.cm.ManagedServiceFactory;
 
 /**
  * The dependency manager manages all services and their dependencies. Using 
@@ -303,81 +298,55 @@
      * (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>
+     *  manager.createFactoryConfigurationAdapterService("MyFactoryPid",  "update", true)
+     *         // The interface to use when registering adapter
+     *         .setInterface(AdapterService.class, new Hashtable() {{ put("foo", "bar"); }})
+     *         // the implementation of the adapter
+     *         .setImplementation(AdapterServiceImpl.class);
+     * <pre>
+     * </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.
-     * @param adapterInterface the interface to use when registering adapters (can be either a String, String array) 
-     * @param adapterImplementation the implementation of the adapter (can be a Class or an Object instance)
-     * @param adapterProperties additional properties to use with the service registration
      * @param propagate true if public factory configuration should be propagated to the adapter service properties
      * @return a service that acts as a factory for generating the managed service factory configuration adapter
      */
-    public Service createFactoryConfigurationAdapterService(String factoryPid, String update, Object adapterImplementation, String adapterInterface, Dictionary adapterProperties, boolean propagate) {
-        Hashtable props = new Hashtable();
-        props.put(Constants.SERVICE_PID, factoryPid);
-        return createService()
-            .setInterface(ManagedServiceFactory.class.getName(), props)
-            .setImplementation(new FactoryConfigurationAdapterImpl(factoryPid, update, adapterImplementation, adapterInterface, adapterProperties, propagate));
+    public Service createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate) {
+        return new FactoryConfigurationAdapterServiceImpl(this, factoryPid, update, propagate);
     }
     
     /**
-     * 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 
+     * Creates a new Managed Service Factory Configuration Adapter with meta type support. 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.
-     * @param adapterInterfaces the interfaces to use when registering adapters (can be either a String, String array) 
-     * @param adapterImplementation the implementation of the adapter (can be a Class or an Object instance)
-     * @param adapterProperties additional properties to use with the service registration
      * @param propagate true if public factory configuration should be propagated to the adapter service properties
-     * @return a service that acts as a factory for generating the managed service factory configuration adapter
-     */
-    public Service createFactoryConfigurationAdapterService(String factoryPid, String update, Object adapterImplementation, String[] adapterInterfaces, Dictionary adapterProperties, boolean propagate) {
-        Hashtable props = new Hashtable();
-        props.put(Constants.SERVICE_PID, factoryPid);
-        return createService()
-            .setInterface(ManagedServiceFactory.class.getName(), props)
-            .setImplementation(new FactoryConfigurationAdapterImpl(factoryPid, update, adapterImplementation, adapterInterfaces, adapterProperties, propagate));
-    }
-
-    /**
-     * Creates a new Managed Service Factory Configuration Adapter with meta type support. 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.
-     * @param adapterInterfaces the interfaces to use when registering adapters (can be either a String, String array) 
-     * @param adapterImplementation the implementation of the adapter (can be a Class or an Object instance)
-     * @param adapterProperties additional properties to use with the service registration
-     * @param propagate true if public factory configuration should be propagated to the adapter service properties
-     * @param heading The label used to display the tab name (or section) where the properties are displayed. Example: "Printer Service"
-     * @param desc A human readable description of the factory PID this configuration is associated with. Example: "Configuration for the PrinterService bundle"
+     * @param heading The label used to display the tab name (or section) where the properties are displayed. 
+     *        Example: "Printer Service"
+     * @param desc A human readable description of the factory PID this configuration is associated with. 
+     *        Example: "Configuration for the PrinterService bundle"
      * @param localization Points to the basename of the Properties file that can localize the Meta Type informations.
      *        The default localization base name for the properties is OSGI-INF/l10n/bundle, but can
-     *        be overridden by the manifest Bundle-Localization header (see core specification, in section Localization on page 68).
-     *        You can specify a specific localization basename file using this parameter (e.g. <code>"person"</code> 
-     *        will match person_du_NL.properties in the root bundle directory).
+     *        be overridden by the manifest Bundle-Localization header (see core specification, in section Localization 
+     *        on page 68). You can specify a specific localization basename file using this parameter 
+     *        (e.g. <code>"person"</code> will match person_du_NL.properties in the root bundle directory).
      * @param propertiesMetaData Array of MetaData regarding configuration properties
      * @return a service that acts as a factory for generating the managed service factory configuration adapter
      */
-    public Service createFactoryConfigurationAdapterService(String factoryPid, String update, Object adapterImplementation, String[] adapterInterfaces, Dictionary adapterProperties, boolean propagate, 
-                                                            String heading, String desc, String localization, PropertyMetaData[] propertiesMetaData) 
+    public Service createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, 
+                                                            String heading, String desc, String localization,
+                                                            PropertyMetaData[] propertiesMetaData) 
     {
-        Hashtable props = new Hashtable();
-        props.put(Constants.SERVICE_PID, factoryPid);
-        FactoryConfigurationAdapterMetaTypeImpl impl = 
-            new FactoryConfigurationAdapterMetaTypeImpl(factoryPid, update, adapterImplementation, adapterInterfaces, adapterProperties, propagate,
-                                                        m_context, m_logger, heading, desc, localization, propertiesMetaData);
-        return createService()
-            .setInterface(ManagedServiceFactory.class.getName(), props)
-            .setImplementation(impl);
+        return new FactoryConfigurationAdapterServiceImpl(this, factoryPid, update, propagate, m_context, m_logger, 
+                                                          heading, desc, localization, propertiesMetaData);        
     }
 
     /**
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
deleted file mode 100644
index a70e70f..0000000
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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 java.lang.reflect.InvocationTargetException;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
-
-import org.apache.felix.dm.DependencyManager;
-import org.apache.felix.dm.service.Service;
-import org.osgi.service.cm.ManagedServiceFactory;
-
-/**
- * Creates, updates, or removes a service, when a ConfigAdmin factory configuration is created/updated or deleted.
- */
-public class FactoryConfigurationAdapterImpl extends AbstractDecorator implements ManagedServiceFactory
-{
-    // The Adapter Service (we need to inherit all its dependencies).
-    protected volatile Service m_service;
-
-    // Our injected dependency manager
-    protected volatile DependencyManager m_dm;
-    
-    // Our adapter implementation (either a Class, or an Object instance)
-    protected final Object m_adapterImplementation;
-
-    // Our adapter interface(s) (either null, a String, or a String array)
-    protected final Object m_adapterInterface;
-    
-    // Our adapter service properties (may be null)
-    protected final Dictionary m_adapterProperties;
-    
-    // Our Managed Service Factory PID
-    protected String m_factoryPid;
-    
-    // The adapter "update" method used to provide the configuration
-    protected String m_update;
-
-    // Tells if the CM config must be propagated along with the adapter service properties
-    protected boolean m_propagate;
-
-    /**
-     * Creates a new CM factory configuration adapter.
-     * 
-     * @param factoryPid
-     * @param updateMethod
-     * @param adapterInterface
-     * @param adapterImplementation
-     * @param adapterProperties
-     * @param propagate
-     */
-    public FactoryConfigurationAdapterImpl(String factoryPid, String updateMethod, Object adapterImplementation, Object adapterInterface, Dictionary adapterProperties, boolean propagate)
-    {
-        m_factoryPid = factoryPid;
-        m_update = updateMethod;
-        m_adapterImplementation = adapterImplementation;
-        m_adapterInterface = adapterInterface;
-        m_adapterProperties = adapterProperties;
-        m_propagate = propagate;
-    }
-
-    /**
-     * Returns the managed service factory name.
-     */
-    public String getName()
-    {
-        return m_factoryPid;
-    }
-  
-    /**
-     * Method called from our superclass, when we need to create a service.
-     */
-    public Service createService(Object[] properties) {
-        Dictionary settings = (Dictionary) properties[0];     
-        Service newService = m_dm.createService();        
-        Object impl;
-        
-        try {
-            impl = (m_adapterImplementation instanceof Class) ? 
-                ((Class) m_adapterImplementation).newInstance() : m_adapterImplementation;
-            InvocationUtil.invokeCallbackMethod(impl, m_update, 
-                new Class[][] {{ Dictionary.class }, {}}, 
-                new Object[][] {{ settings }, {}});
-        }
-        
-        catch (InvocationTargetException e)
-        {
-            // Our super class will check if the target exception is itself a ConfigurationException.
-            // In this case, it will simply re-thrown.
-            throw new RuntimeException(e.getTargetException());
-        }
-        
-        catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else {
-                throw new RuntimeException(t);
-            }
-        }
-
-        // Merge adapter service properties, with CM settings 
-        Dictionary serviceProperties = m_propagate ? mergeSettings(m_adapterProperties, settings) : m_adapterProperties;
-   
-        if (m_adapterInterface instanceof String)
-        {
-            newService.setInterface((String) m_adapterInterface, serviceProperties);
-        }
-        else if (m_adapterInterface instanceof String[])
-        {
-            newService.setInterface((String[]) m_adapterInterface, serviceProperties);
-        }
-
-        newService.setImplementation(impl);
-        List dependencies = m_service.getDependencies();
-        newService.add(dependencies);
-        return newService;
-    }
-
-    /**
-     * Method called from our superclass, when we need to update a Service, because 
-     * the configuration has changed.
-     */
-    public void updateService(Object[] properties) 
-    {
-        Dictionary settings = (Dictionary) properties[0];
-        Service service = (Service) properties[1];
-        Object impl = service.getService();
-       
-        try
-        {
-            InvocationUtil.invokeCallbackMethod(impl, m_update, 
-                new Class[][] {{ Dictionary.class }, {}}, 
-                new Object[][] {{ settings }, {}});
-            if (m_adapterInterface != null && m_propagate == true) {
-                settings = mergeSettings(m_adapterProperties, settings);
-                service.setServiceProperties(settings);
-            }
-        }
-        
-        catch (InvocationTargetException e)
-        {
-            // Our super class will check if the target exception is itself a ConfigurationException.
-            // In this case, it will simply re-thrown.
-            throw new RuntimeException(e.getTargetException());
-        }
-        
-        catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else {
-                throw new RuntimeException(t);
-            }
-        }
-    }   
-
-    /**
-     * Merge CM factory configuration setting with the adapter service properties. The private CM factory configuration 
-     * settings are ignored. A CM factory configuration property is private if its name starts with a dot (".").
-     * 
-     * @param adapterProperties
-     * @param settings
-     * @return
-     */
-    private Dictionary mergeSettings(Dictionary adapterProperties, Dictionary settings)
-    {
-        Dictionary props = new Hashtable();
-        
-        if (adapterProperties != null) {
-            Enumeration keys = adapterProperties.keys();
-            while (keys.hasMoreElements()) {
-                Object key = keys.nextElement();
-                Object val = adapterProperties.get(key);
-                props.put(key, val);
-            }
-        }
-        
-        Enumeration keys = settings.keys();
-        while (keys.hasMoreElements()) {
-            Object key = keys.nextElement();
-            if (! key.toString().startsWith(".")) {
-                // public properties are propagated
-                Object val = settings.get(key);
-                props.put(key, val);
-            }
-        }
-        return props;
-    }
-}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FactoryConfigurationAdapterMetaTypeImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FactoryConfigurationAdapterMetaTypeImpl.java
deleted file mode 100644
index 4f4dd57..0000000
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FactoryConfigurationAdapterMetaTypeImpl.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.apache.felix.dm.impl;
-
-import java.util.Dictionary;
-
-import org.apache.felix.dm.dependencies.PropertyMetaData;
-import org.apache.felix.dm.impl.metatype.MetaTypeProviderImpl;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.metatype.MetaTypeProvider;
-import org.osgi.service.metatype.ObjectClassDefinition;
-
-public class FactoryConfigurationAdapterMetaTypeImpl extends FactoryConfigurationAdapterImpl implements MetaTypeProvider
-{
-    // Our MetaType Provider for describing our properties metadata
-    private MetaTypeProviderImpl m_metaType;
-    
-   /**
-     * Creates a new CM factory configuration adapter.
-     * 
-     * @param factoryPid
-     * @param updateMethod
-     * @param adapterInterface
-     * @param adapterImplementation
-     * @param adapterProperties
-     * @param propagate
-     */
-    public FactoryConfigurationAdapterMetaTypeImpl(String factoryPid, String updateMethod, Object adapterImplementation, Object adapterInterface, Dictionary adapterProperties, boolean propagate,
-        BundleContext bctx, Logger logger, String heading, String description, String localization, PropertyMetaData[] properyMetaData)
-    {
-        super(factoryPid, updateMethod, adapterImplementation, adapterInterface, adapterProperties, propagate);
-        m_metaType = new MetaTypeProviderImpl(m_factoryPid, bctx, logger, null, this);
-        m_metaType.setName(heading);
-        m_metaType.setDescription(description);
-        if (localization != null) 
-        {
-            m_metaType.setLocalization(localization);
-        }
-        for (int i = 0; i < properyMetaData.length; i ++) 
-        {
-            m_metaType.add(properyMetaData[i]);
-        }
-    }
-
-    public String[] getLocales()
-    {
-        return m_metaType.getLocales();
-    }
-
-    public ObjectClassDefinition getObjectClassDefinition(String id, String locale)
-    {
-        return m_metaType.getObjectClassDefinition(id, locale);
-    }
-}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FactoryConfigurationAdapterServiceImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FactoryConfigurationAdapterServiceImpl.java
new file mode 100644
index 0000000..cbc6ac3
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FactoryConfigurationAdapterServiceImpl.java
@@ -0,0 +1,296 @@
+/*
+ * 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 java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.service.Service;
+import org.apache.felix.dm.service.ServiceStateListener;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.apache.felix.dm.dependencies.PropertyMetaData;
+import org.apache.felix.dm.impl.metatype.MetaTypeProviderImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.metatype.MetaTypeProvider;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * Factory configuration adapter service implementation. This class extends the FilterService in order to catch
+ * some Service methods for configuring actual adapter service implementation.
+ */
+public class FactoryConfigurationAdapterServiceImpl extends FilterService
+{
+    public FactoryConfigurationAdapterServiceImpl(DependencyManager dm, String factoryPid, String update, boolean propagate)
+    {
+        super(dm.createService()); // This service will be filtered by our super class, allowing us to take control.
+        Hashtable props = new Hashtable();
+        props.put(Constants.SERVICE_PID, factoryPid);
+        m_service
+            .setInterface(ManagedServiceFactory.class.getName(), props)
+            .setImplementation(new AdapterImpl(factoryPid, update, propagate));
+    }
+    
+    public FactoryConfigurationAdapterServiceImpl(DependencyManager dm, String factoryPid, String update, boolean propagate,
+                                                  BundleContext bctx, Logger logger, String heading, String description, String localization, PropertyMetaData[] properyMetaData)
+    {
+        super(dm.createService()); // This service will be filtered by our super class, allowing us to take control.
+        Hashtable props = new Hashtable();
+        props.put(Constants.SERVICE_PID, factoryPid);
+        String[] interfaces = new String[] {
+            ManagedServiceFactory.class.getName(),
+            MetaTypeProvider.class.getName()                                
+        };
+        m_service
+            .setInterface(interfaces, props)
+            .setImplementation(new MetaTypeAdapterImpl(factoryPid, update, propagate,
+                                                       bctx, logger, heading, description,
+                                                       localization, properyMetaData));
+    }
+    
+    /**
+     * Creates, updates, or removes a service, when a ConfigAdmin factory configuration is created/updated or deleted.
+     */
+    public class AdapterImpl extends AbstractDecorator implements ManagedServiceFactory
+    {
+        // Our injected dependency manager
+        protected volatile DependencyManager m_dm;
+        
+        // Our Managed Service Factory PID
+        protected String m_factoryPid;
+        
+        // The adapter "update" method used to provide the configuration
+        protected String m_update;
+
+        // Tells if the CM config must be propagated along with the adapter service properties
+        protected boolean m_propagate;
+
+        /**
+         * Creates a new CM factory configuration adapter.
+         * 
+         * @param factoryPid
+         * @param updateMethod
+         * @param adapterInterface
+         * @param adapterImplementation
+         * @param adapterProperties
+         * @param propagate
+         */
+        public AdapterImpl(String factoryPid, String updateMethod, boolean propagate)
+        {
+            m_factoryPid = factoryPid;
+            m_update = updateMethod;
+            m_propagate = propagate;
+        }
+
+        /**
+         * Returns the managed service factory name.
+         */
+        public String getName()
+        {
+            return m_factoryPid;
+        }
+      
+        /**
+         * Method called from our superclass, when we need to create a service.
+         */
+        public Service createService(Object[] properties) {
+            Dictionary settings = (Dictionary) properties[0];     
+            Service newService = m_dm.createService();        
+            Object impl = null;
+            
+            try {
+                if (m_serviceImpl != null) {
+                    impl = (m_serviceImpl instanceof Class) ? 
+                        ((Class) m_serviceImpl).newInstance() : m_serviceImpl;
+                } else {
+                    impl = instantiateFromFactory(m_factory, m_factoryCreateMethod);
+                }
+                InvocationUtil.invokeCallbackMethod(impl, m_update, 
+                    new Class[][] {{ Dictionary.class }, {}}, 
+                    new Object[][] {{ settings }, {}});
+            }
+            
+            catch (Throwable t)
+            {
+               handleException(t);
+            }
+
+            // Merge adapter service properties, with CM settings 
+            Dictionary serviceProperties = m_propagate ? mergeSettings(m_serviceProperties, settings) : m_serviceProperties;
+            newService.setInterface(m_serviceInterfaces, serviceProperties);
+            newService.setImplementation(impl);
+            List dependencies = m_service.getDependencies();
+            newService.add(dependencies);
+            newService.setComposition(m_compositionInstance, m_compositionMethod); // if not set, no effect
+            newService.setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy); // if not set, no effect
+            for (int i = 0; i < m_stateListeners.size(); i ++) {
+                newService.addStateListener((ServiceStateListener) m_stateListeners.get(i));
+            }
+
+            return newService;
+        }
+
+        /**
+         * Method called from our superclass, when we need to update a Service, because 
+         * the configuration has changed.
+         */
+        public void updateService(Object[] properties) 
+        {
+            Dictionary settings = (Dictionary) properties[0];
+            Service service = (Service) properties[1];
+            Object impl = service.getService();
+           
+            try
+            {
+                InvocationUtil.invokeCallbackMethod(impl, m_update, 
+                    new Class[][] {{ Dictionary.class }, {}}, 
+                    new Object[][] {{ settings }, {}});
+                if (m_serviceInterfaces != null && m_propagate == true) {
+                    settings = mergeSettings(m_serviceProperties, settings);
+                    service.setServiceProperties(settings);
+                }
+            }
+            
+            catch (Throwable t)
+            {
+                handleException(t);
+            }
+        }   
+
+        /**
+         * Merge CM factory configuration setting with the adapter service properties. The private CM factory configuration 
+         * settings are ignored. A CM factory configuration property is private if its name starts with a dot (".").
+         * 
+         * @param adapterProperties
+         * @param settings
+         * @return
+         */
+        private Dictionary mergeSettings(Dictionary adapterProperties, Dictionary settings)
+        {
+            Dictionary props = new Hashtable();
+            
+            if (adapterProperties != null) {
+                Enumeration keys = adapterProperties.keys();
+                while (keys.hasMoreElements()) {
+                    Object key = keys.nextElement();
+                    Object val = adapterProperties.get(key);
+                    props.put(key, val);
+                }
+            }
+            
+            Enumeration keys = settings.keys();
+            while (keys.hasMoreElements()) {
+                Object key = keys.nextElement();
+                if (! key.toString().startsWith(".")) {
+                    // public properties are propagated
+                    Object val = settings.get(key);
+                    props.put(key, val);
+                }
+            }
+            return props;
+        }
+    
+        private Object instantiateFromFactory(Object mFactory, String mFactoryCreateMethod)
+        {
+            Object factory = null;
+            if (m_factory instanceof Class) {
+                try {
+                    factory = createInstance((Class) m_factory);
+                }
+                catch (Throwable t) {
+                    handleException(t);
+                }
+            }
+            else {
+                factory = m_factory;
+            }
+
+            try {
+                return InvocationUtil.invokeMethod(factory, factory.getClass(), m_factoryCreateMethod,
+                                                   new Class[][] {{}}, new Object[][] {{}}, false);
+            }
+            catch (Throwable t) {
+                handleException(t);
+                return null;
+            }
+        }
+
+        private Object createInstance(Class clazz) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException {
+            Constructor constructor = clazz.getConstructor(new Class[] {});
+            constructor.setAccessible(true);
+            return clazz.newInstance();
+        }
+    
+        private void handleException(Throwable t) {
+            if (t instanceof InvocationTargetException)
+            {
+                // Our super class will check if the target exception is itself a ConfigurationException.
+                // In this case, it will simply re-thrown.
+                throw new RuntimeException(((InvocationTargetException) t).getTargetException());
+            } else if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            } else {
+                throw new RuntimeException(t);
+            }
+        }
+    }
+
+    
+    /**
+     * Extends AdapterImpl for MetaType support.
+     */
+    class MetaTypeAdapterImpl extends AdapterImpl implements MetaTypeProvider {
+        // Our MetaType Provider for describing our properties metadata
+        private MetaTypeProviderImpl m_metaType;
+        
+        public MetaTypeAdapterImpl(String factoryPid, String updateMethod, boolean propagate,
+                                   BundleContext bctx, Logger logger, String heading, 
+                                   String description, String localization,
+                                   PropertyMetaData[] properyMetaData)
+        {
+            super(factoryPid, updateMethod, propagate);
+            m_metaType = new MetaTypeProviderImpl(m_factoryPid, bctx, logger, null, this);
+            m_metaType.setName(heading);
+            m_metaType.setDescription(description);
+            if (localization != null) 
+            {
+                m_metaType.setLocalization(localization);
+            }
+            for (int i = 0; i < properyMetaData.length; i ++) 
+            {
+                m_metaType.add(properyMetaData[i]);
+            }
+        }
+        
+        public String[] getLocales()
+        {
+            return m_metaType.getLocales();
+        }
+
+        public ObjectClassDefinition getObjectClassDefinition(String id, String locale)
+        {
+            return m_metaType.getObjectClassDefinition(id, locale);
+        }
+    }    
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
index dc8ce75..b499dec 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
@@ -47,7 +47,9 @@
         String[] services = serviceMetaData.getStrings(Params.service, null);
         Dictionary<String, Object> properties = serviceMetaData.getDictionary(Params.properties, null);
         boolean propagate = "true".equals(serviceMetaData.getString(Params.propagate, "false"));
-        Service srv = dm.createFactoryConfigurationAdapterService(factoryPid, updated, impl, services, properties, propagate);
+        Service srv = dm.createFactoryConfigurationAdapterService(factoryPid, updated, propagate)
+                        .setInterface(services, properties)
+                        .setImplementation(impl);
         setCommonServiceParams(srv, serviceMetaData);
         for (MetaData dependencyMetaData: serviceDependencies)
         {
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/FactoryConfigurationAdapterTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/FactoryConfigurationAdapterTest.java
index 2429e63..1a05804 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/FactoryConfigurationAdapterTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/FactoryConfigurationAdapterTest.java
@@ -71,12 +71,10 @@
 
         // Create an Adapter that will be instantiated, once the configuration is created.
         // This Adapter provides an AdapterService, and depends on an AdapterExtraDependency service.
-        Service s2 = m.createFactoryConfigurationAdapterService("MyFactoryPid", 
-                                                                 "updated", 
-                                                                 Adapter.class, 
-                                                                 AdapterService.class.getName(), 
-                                                                 new Properties() {{ put("foo", "bar"); }},
-                                                                 true /* propagate CM settings */);
+        Service s2 = m.createFactoryConfigurationAdapterService("MyFactoryPid", "updated", true /* propagate CM settings */)
+                      .setInterface(AdapterService.class.getName(), new Properties() {{ put("foo", "bar"); }})
+                      .setImplementation(Adapter.class);
+
         s2.add(m.createServiceDependency()
             .setService(AdapterExtraDependency.class)
             .setRequired(true)