FELIX-2954 (annotated component factory does not allow to provide a component instance explicitly)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1124168 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/doc/changelog.txt b/dependencymanager/annotation/doc/changelog.txt
index 13f10c0..cff358b 100644
--- a/dependencymanager/annotation/doc/changelog.txt
+++ b/dependencymanager/annotation/doc/changelog.txt
@@ -1,5 +1,5 @@
-Initial Release 3.0.0
----------------------
+Changes from 3.0.0 to 3.0.1
+---------------------------
 
 ** Bug
 
@@ -7,3 +7,11 @@
 
     * removed LICENSE.json 
     * removed changelog.txt from root of project
+    * [FELIX-2954 ] - annotated component factory does not allow to provide a component instance explicitly
+
+Initial Release 3.0.0
+---------------------
+
+** Bug
+
+** Improvement
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Component.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Component.java
index 9619863..780b8f4 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Component.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Component.java
@@ -122,10 +122,15 @@
  *          // Update the first X component instance
  *          x1.put("foo", "bar1_modified");
  *          _XFactory.add(x1);
+ *          
+ *          // Instantiate a third X instance, by explicitly providing the implementation object
+ *          Dictionary x3 = new Hashtable() {{ put(Component.FACTORY_INSTANCE, new X()); }};
+ *          _XFactory.add(x3);
  *      
- *          // Destroy x1/x2 components (Notice that invoking XFactory.clear() will destroy all X component  instances).
+ *          // Destroy x1/x2/x3 components (Notice that invoking XFactory.clear() will destroy all X component  instances).
  *          _XFactory.remove(x1);
  *          _XFactory.remove(x2); 
+ *          _XFactory.remove(x3); 
  *      }
  *  }
  * </pre>
@@ -164,6 +169,9 @@
      * <p>The dictionary registered in the Set will be provided to the created component instance using a callback method that you can 
      * optionally specify in the {@link Component#factoryConfigure()} attribute. Each public properties from that dictionary 
      * (which don't start with a dot) will be propagated along with the annotated component service properties.
+     * 
+     * <p>Optionally, the dictionary registered into the factory set may provide an implementation instance for the component to be created,
+     * using the {@value #FACTORY_INSTANCE} key. 
      */
     String factorySet() default "";
 
@@ -179,4 +187,16 @@
      * Sets the static method used to create the components implementation instance.
      */
     String factoryMethod() default "";    
+    
+    /**
+     * Service property name used to match a given Factory Set.
+     * @see #factorySet() for more information about factory sets.
+     */
+    final static String FACTORY_NAME = "dm.factory.name";
+    
+    /**
+     * Key used when providing an implementation in a factory Set dictionary configuration.
+     * @see #factorySet()
+     */
+    final static String FACTORY_INSTANCE = "dm.factory.instance";
 }
diff --git a/dependencymanager/runtime/doc/changelog.txt b/dependencymanager/runtime/doc/changelog.txt
index 81bbf9e..7f3eb61 100644
--- a/dependencymanager/runtime/doc/changelog.txt
+++ b/dependencymanager/runtime/doc/changelog.txt
@@ -1,7 +1,15 @@
+Changes from 3.0.0 to 3.0.1
+---------------------------
+
+** Bug
+
+** Improvement
+    * Removed root changelog.txt
+    * [FELIX-2954 ] - annotated component factory does not allow to provide a component instance explicitly
+
 Initial Release 3.0.0
 ---------------------
 
 ** Bug
 
 ** Improvement
-    * Removed root changelog.txt
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactorySet.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactorySet.java
index 15cc1be..a035c1e 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactorySet.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactorySet.java
@@ -108,6 +108,13 @@
     private final static Object SERVICE_CREATING = new Object();
 
     /**
+     * When a Dictionary is registered in a factory Set, we use this special 
+     * property key, whose value may provide the instance to use when
+     * creating a service.
+     */
+    private final static String DM_FACTORY_INSTANCE = "dm.factory.instance";
+
+    /**
      * This class wraps <tt>Dictionary</tt>, allowing to store the dictionary into a Map, using
      * reference-equality in place of object-equality when getting the Dictionary from the Map.
      */
@@ -344,19 +351,28 @@
         {
             try
             {
-                // Create the Service / impl
+                // Create the Service / impl, unless it is explicitly provided from the
+                // configuration (using the specific key "dm.factory.instance").
                 Component s = m_dm.createComponent();
                 Class implClass = m_bundle.loadClass(m_srvMeta.getString(Params.impl));
-                String factoryMethod = m_srvMeta.getString(Params.factoryMethod, null);
-                if (factoryMethod == null)
+                Object impl = configuration.get(DM_FACTORY_INSTANCE);
+                if (impl == null)
                 {
-                    m_impl = implClass.newInstance();
+                    String factoryMethod = m_srvMeta.getString(Params.factoryMethod, null);
+                    if (factoryMethod == null)
+                    {
+                        m_impl = implClass.newInstance();
+                    }
+                    else
+                    {
+                        Method m = implClass.getDeclaredMethod(factoryMethod);
+                        m.setAccessible(true);
+                        m_impl = m.invoke(null);
+                    }
                 }
                 else
                 {
-                    Method m = implClass.getDeclaredMethod(factoryMethod);
-                    m.setAccessible(true);
-                    m_impl = m.invoke(null);
+                    m_impl = impl;
                 }
 
                 // Invoke "configure" callback
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factory/MyServiceFactory.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factory/MyServiceFactory.java
index e7fbdec..71c1ef9 100644
--- a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factory/MyServiceFactory.java
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factory/MyServiceFactory.java
@@ -34,7 +34,7 @@
 @Component
 public class MyServiceFactory
 {
-    @ServiceDependency(filter = "(dm.factory.name=MyServiceFactory)")
+    @ServiceDependency(filter = "(" + Component.FACTORY_NAME + "=MyServiceFactory)")
     Set<Dictionary> m_myServiceFactory;
     
     @ServiceDependency
@@ -50,6 +50,7 @@
         m_conf = new Hashtable();
         m_conf.put("instance.id", "instance");
         m_conf.put(".private.param", "private");
+        m_conf.put(Component.FACTORY_INSTANCE, new MyService()); // we explicitly provide the instance
         Assert.assertTrue(m_myServiceFactory.add(m_conf));
     }
 
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/FactoryServiceTestWthPublisher.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/FactoryServiceTestWthPublisher.java
index c63c5e2..488c425 100644
--- a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/FactoryServiceTestWthPublisher.java
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/FactoryServiceTestWthPublisher.java
@@ -105,7 +105,7 @@
     @Component
     public static class ProviderImplFactory 
     {
-        @ServiceDependency(filter="(dm.factory.name=MyFactory)")
+        @ServiceDependency(filter="(" + Component.FACTORY_NAME + "=MyFactory)")
         void bind(Set<Dictionary> m_providerImplFactory)
         {
             m_providerImplFactory.add(new Hashtable() {{ put("foo2", "bar2"); }});