FELIX-4146 Add getInstances and getInstanceNames in the Factory interface

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1497007 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/runtime/composite-it/ipojo-composite-runtime-test/src/test/java/org/apache/felix/ipojo/runtime/core/FactoryManagementTest.java b/ipojo/runtime/composite-it/ipojo-composite-runtime-test/src/test/java/org/apache/felix/ipojo/runtime/core/FactoryManagementTest.java
index 93402e7..e357689 100644
--- a/ipojo/runtime/composite-it/ipojo-composite-runtime-test/src/test/java/org/apache/felix/ipojo/runtime/core/FactoryManagementTest.java
+++ b/ipojo/runtime/composite-it/ipojo-composite-runtime-test/src/test/java/org/apache/felix/ipojo/runtime/core/FactoryManagementTest.java
@@ -30,6 +30,7 @@
 import org.osgi.framework.ServiceReference;

 import org.osgi.framework.ServiceRegistration;

 

+import java.util.ArrayList;

 import java.util.Dictionary;

 import java.util.List;

 import java.util.Properties;

@@ -113,6 +114,14 @@
             return null;

         }

 

+        public List<ComponentInstance> getInstances() {

+		    return new ArrayList<ComponentInstance>();

+	    }

+

+	    public List<String> getInstancesNames() {

+		    return new ArrayList<String>();

+	    }

+

     }

 

     @Before

diff --git a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java
index 8af0c6c..f219810 100644
--- a/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java
+++ b/ipojo/runtime/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java
@@ -178,4 +178,12 @@
         return m_delegate.getComponentMetadata();

     }

 

+    public List<ComponentInstance> getInstances() {

+        return m_delegate.getInstances();

+    }

+

+    public List<String> getInstancesNames() {

+        return m_delegate.getInstancesNames();

+    }

+

 }

diff --git a/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/FiveInstances.java b/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/FiveInstances.java
new file mode 100644
index 0000000..26484c2
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/FiveInstances.java
@@ -0,0 +1,27 @@
+/*
+ * 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.ipojo.runtime.core.components;
+
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.runtime.core.services.NoService;
+
+@Component(name="FiveInstances")
+public class FiveInstances implements NoService {
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/NoInstances.java b/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/NoInstances.java
new file mode 100644
index 0000000..97c9518
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/NoInstances.java
@@ -0,0 +1,27 @@
+/*
+ * 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.ipojo.runtime.core.components;
+
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.runtime.core.services.NoService;
+
+@Component(name="NoInstances")
+public class NoInstances implements NoService {
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/OneDuplicateInstance.java b/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/OneDuplicateInstance.java
new file mode 100644
index 0000000..8bf63c2
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/OneDuplicateInstance.java
@@ -0,0 +1,27 @@
+/*
+ * 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.ipojo.runtime.core.components;
+
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.runtime.core.services.NoService;
+
+@Component(name="OneDuplicateInstance")
+public class OneDuplicateInstance implements NoService {
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/services/NoService.java b/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/services/NoService.java
new file mode 100644
index 0000000..69cec05
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-factory-test/src/main/java/org/apache/felix/ipojo/runtime/core/services/NoService.java
@@ -0,0 +1,23 @@
+/*
+ * 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.ipojo.runtime.core.services;
+
+public interface NoService {
+
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-factory-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestInstances.java b/ipojo/runtime/core-it/ipojo-core-factory-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestInstances.java
new file mode 100644
index 0000000..f2978c9
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-factory-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestInstances.java
@@ -0,0 +1,176 @@
+/*
+ * 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.ipojo.runtime.core;
+
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.junit.Test;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import static junit.framework.Assert.*;
+
+/**
+ * Checks the retrieval of instances and instance names from the Factory.
+ */
+public class TestInstances extends Common {
+
+    @Test
+    public void testNoInstances() {
+        Factory factory = ipojoHelper.getFactory("NoInstances");
+        assertNotNull(factory);
+
+        assertNotNull(factory.getInstancesNames());
+        assertNotNull(factory.getInstances());
+
+        assertEquals(0, factory.getInstancesNames().size());
+        assertEquals(0, factory.getInstances().size());
+    }
+
+    @Test
+    public void testOneDuplicateInstance() {
+        Factory factory = ipojoHelper.getFactory("OneDuplicateInstance");
+        assertNotNull(factory);
+
+        // Create one instance
+        ComponentInstance instance = createInstanceOrFail(factory, "OneDuplicateInstance-0");
+
+        assertNotNull(factory.getInstancesNames());
+        assertNotNull(factory.getInstances());
+
+        assertEquals(1, factory.getInstancesNames().size());
+        assertEquals(1, factory.getInstances().size());
+
+        assertContains("", factory.getInstancesNames().toArray(new String[1]), "OneDuplicateInstance-0");
+
+        // Create another instance with the same name
+        // The instance should not be created, and we should have only the first instance
+        try {
+            createInstance(factory, "OneDuplicateInstance-0");
+            fail("The instance OneDuplicateInstance-0 has been created with factory " + factory.getName() + ". "
+                    + "It's shouldn't have been created has it's a duplicate instance");
+        } catch (UnacceptableConfiguration e) {
+            // expected Exception
+        } catch (Exception e){
+            fail("Cannot create instance OneDuplicateInstance-0 with factory " + factory.getName() + ". "
+                    + "The wrong exception has been raised : " + e.toString());
+        }
+        assertNotNull(factory.getInstancesNames());
+        assertNotNull(factory.getInstances());
+
+        assertEquals(1, factory.getInstancesNames().size());
+        assertEquals(1, factory.getInstances().size());
+
+        // Dispose of the instance
+        instance.dispose();
+
+        assertNotNull(factory.getInstancesNames());
+        assertNotNull(factory.getInstances());
+
+        assertEquals(0, factory.getInstancesNames().size());
+        assertEquals(0, factory.getInstances().size());
+    }
+
+    @Test
+    public void testFiveInstances() {
+        Factory factory = ipojoHelper.getFactory("FiveInstances");
+        assertNotNull(factory);
+
+        // No instances for the moment
+        assertNotNull(factory.getInstancesNames());
+        assertNotNull(factory.getInstances());
+
+        assertEquals(0, factory.getInstancesNames().size());
+        assertEquals(0, factory.getInstances().size());
+
+        // Create 5 instances
+        ComponentInstance[] instances = createNInstanceOrFail(factory, "FiveInstances", 5);
+
+        assertNotNull(factory.getInstancesNames());
+        assertNotNull(factory.getInstances());
+
+        assertEquals(5, factory.getInstancesNames().size());
+        assertEquals(5, factory.getInstances().size());
+
+        assertContains("", factory.getInstancesNames().toArray(new String[5]), "FiveInstances-0");
+        assertContains("", factory.getInstancesNames().toArray(new String[5]), "FiveInstances-1");
+        assertContains("", factory.getInstancesNames().toArray(new String[5]), "FiveInstances-2");
+        assertContains("", factory.getInstancesNames().toArray(new String[5]), "FiveInstances-3");
+        assertContains("", factory.getInstancesNames().toArray(new String[5]), "FiveInstances-4");
+
+        // Dispose of instances 0, 3 and 2
+        // 2 instances left
+        instances[0].dispose();
+        instances[3].dispose();
+        instances[2].dispose();
+
+
+        assertNotNull(factory.getInstancesNames());
+        assertNotNull(factory.getInstances());
+
+        assertEquals(2, factory.getInstancesNames().size());
+        assertEquals(2, factory.getInstances().size());
+
+        assertContains("", factory.getInstancesNames().toArray(new String[2]), "FiveInstances-1");
+        assertContains("", factory.getInstancesNames().toArray(new String[2]), "FiveInstances-4");
+
+        // Dispose of instances 1 and 4
+        // No instances left
+        instances[1].dispose();
+        instances[4].dispose();
+
+        assertNotNull(factory.getInstancesNames());
+        assertNotNull(factory.getInstances());
+
+        assertEquals(0, factory.getInstancesNames().size());
+        assertEquals(0, factory.getInstances().size());
+    }
+
+    public ComponentInstance[] createNInstanceOrFail(Factory factory, String baseName, Integer n) {
+        ComponentInstance[] instances = new ComponentInstance[n];
+        for (int i = 0; i < n; i++) {
+            instances[i] = createInstanceOrFail(factory, baseName + "-" + i);
+        }
+        return instances;
+    }
+
+    public ComponentInstance createInstanceOrFail(Factory factory, String name) {
+        ComponentInstance instance = null;
+        try {
+            instance =  createInstance(factory, name);
+        } catch (Exception e) {
+            fail("Cannot create instance " + name + " with factory " + factory.getName() + ". "
+                    + "Raised exception : " + e.toString());
+        }
+        return instance;
+    }
+
+    public ComponentInstance createInstance(Factory factory, String name)
+            throws MissingHandlerException, UnacceptableConfiguration, ConfigurationException {
+        Dictionary conf = new Hashtable();
+        conf.put("instance.name", name);
+        return factory.createComponentInstance(conf);
+    }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java
index c84885e..c1b5a21 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java
@@ -18,15 +18,16 @@
  */
 package org.apache.felix.ipojo;
 
-import java.util.Dictionary;
-import java.util.List;
-
 import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
 import org.apache.felix.ipojo.metadata.Element;
 import org.osgi.framework.BundleContext;
 
+import java.util.Dictionary;
+import java.util.List;
+
 /**
  * Component Type Factory Service. This service is exposed by a instance manager factory, and allows the dynamic creation of component instance.
+ *
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public interface Factory {
@@ -36,20 +37,16 @@
      * A valid factory is a factory where all required handlers are available.
      */
     int VALID = 1;
-
     /**
      * Factory State.
      * An invalid factory is a factory where at least one required handler is
      * unavailable. Creating an instance with an invalid factory failed.
      */
     int INVALID = 0;
-
-
     /**
      * Instance configuration can set the instance name using this property.
      */
     String INSTANCE_NAME_PROPERTY = "instance.name";
-
     /**
      * Instance configuration can set the factory version they target using this property.
      */
@@ -57,35 +54,39 @@
 
     /**
      * Creates an instance manager (i.e. component type instance).
+     *
      * @param configuration the configuration properties for this component.
      * @return the created instance manager.
      * @throws UnacceptableConfiguration if the given configuration is not valid.
-     * @throws MissingHandlerException if an handler is missing.
-     * @throws ConfigurationException if the instance configuration failed.
+     * @throws MissingHandlerException   if an handler is missing.
+     * @throws ConfigurationException    if the instance configuration failed.
      */
     ComponentInstance createComponentInstance(Dictionary configuration) throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException;
 
     /**
      * Creates an instance manager (i.e. component type instance).
      * The instance is created in the scope given in argument.
-     * @param configuration the configuration properties for this component.
+     *
+     * @param configuration  the configuration properties for this component.
      * @param serviceContext the service context of the component.
      * @return the created instance manager.
      * @throws UnacceptableConfiguration if the given configuration is not valid.
-     * @throws MissingHandlerException if an handler is missing.
-     * @throws ConfigurationException if the instance configuration failed.
+     * @throws MissingHandlerException   if an handler is missing.
+     * @throws ConfigurationException    if the instance configuration failed.
      */
     ComponentInstance createComponentInstance(Dictionary configuration, ServiceContext serviceContext) throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException;
 
     /**
      * Gets the component type information containing provided service,
      * configuration properties ...
+     *
      * @return the component type information.
      */
     Element getDescription();
 
     /**
      * Gets the component type description.
+     *
      * @return the component type description object
      */
     ComponentTypeDescription getComponentDescription();
@@ -93,6 +94,7 @@
     /**
      * Checks if the given configuration is acceptable as a configuration
      * of a component instance.
+     *
      * @param conf the configuration to test
      * @return <code>true</code> if the configuration is acceptable
      */
@@ -100,6 +102,7 @@
 
     /**
      * Returns the factory name.
+     *
      * @return the name of the factory.
      */
     String getName();
@@ -107,21 +110,24 @@
     /**
      * Reconfigures an instance already created. This configuration needs to
      * have the name property to identify the instance.
+     *
      * @param conf the configuration to reconfigure the instance. The instance.name property must be set to identify
      *             the instance to reconfigure.
-     * @throws UnacceptableConfiguration  if the given configuration is not consistent for the targeted instance.
-     * @throws MissingHandlerException if an handler is missing.
+     * @throws UnacceptableConfiguration if the given configuration is not consistent for the targeted instance.
+     * @throws MissingHandlerException   if an handler is missing.
      */
     void reconfigure(Dictionary conf) throws UnacceptableConfiguration, MissingHandlerException;
 
     /**
      * Adds a factory state listener on the current factory.
+     *
      * @param listener the listener to add
      */
     void addFactoryStateListener(FactoryStateListener listener);
 
     /**
      * Removes the given factory state listener from the listener list.
+     *
      * @param listener the listener to remove
      */
     void removeFactoryStateListener(FactoryStateListener listener);
@@ -129,6 +135,7 @@
     /**
      * Gets the list of missing handlers.
      * The handlers are given under the form namespace:name
+     *
      * @return the list containing the name of missing handlers
      */
     List getMissingHandlers();
@@ -136,6 +143,7 @@
     /**
      * Get the list of required handlers.
      * The handlers are given under the form namespace:name
+     *
      * @return the list containing the name of required handlers
      */
     List getRequiredHandlers();
@@ -143,6 +151,7 @@
     /**
      * Returns the class name of the component type.
      * For factories which does not contains a class, return "composite"
+     *
      * @return the class name of the component type or "composite"
      * @deprecated
      */
@@ -150,27 +159,47 @@
 
     /**
      * Returns the state of the factory.
+     *
      * @return the state of the factory
      */
     int getState();
 
     /**
      * Gets the bundle context of the factory.
+     *
      * @return the bundle context of the factory.
      */
     BundleContext getBundleContext();
 
     /**
      * Gets the version of the component type.
+     *
      * @return the component type version or <code>null</code> if
-     * not specified.
+     *         not specified.
      */
     String getVersion();
 
     /**
      * Gets the component type metadata (Element - Attribute structure)
+     *
      * @return the root element of the component metadata. The result must <b>not</b> be modified.
      */
     Element getComponentMetadata();
 
+    /**
+     * Gets the list of instances created by the factory. The instances must be still alive.
+     *
+     * @return the list of created (and living) instances
+     * @since 1.10.2
+     */
+    List<ComponentInstance> getInstances();
+
+    /**
+     * Gets the list of the names of the instances created by the factory. The instances must be still alive.
+     *
+     * @return the list of the names of created (and living) instances
+     * @since 1.10.2
+     */
+    List<String> getInstancesNames();
+
 }
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
index daebcf3..c817093 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
@@ -368,6 +368,28 @@
     }

 

     /**

+     * Gets the list of instances created by the factory. The instances must be still alive.

+     *

+     * @return the list of created (and living) instances

+     * @since 1.10.2

+     */

+    public List<ComponentInstance> getInstances() {

+        // m_componentInstances is a concurrent hashmap, we can create retrieve values directly.

+        return new ArrayList<ComponentInstance>(m_componentInstances.values());

+    }

+

+    /**

+     * Gets the list of the names of the instances created by the factory. The instances must be still alive.

+     *

+     * @return the list of the names of created (and living) instances

+     * @since 1.10.2

+     */

+    public List<String> getInstancesNames() {

+        // m_componentInstances is a concurrent hashmap, we can create retrieve values directly.

+        return new ArrayList<String>(m_componentInstances.keySet());

+    }

+

+    /**

      * Computes the list of missing handlers.

      * @return the list of missing handlers.

      * @see org.apache.felix.ipojo.Factory#getMissingHandlers()