Refactor nullable object system.
Add default-implementation attribute allowing to configure the default implementation of a required service instead of the default nullable object.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@587421 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
index 13d8c02..6b343d9 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
@@ -318,40 +318,14 @@
                 }
             } else {
                 if (m_references.size() == 0) {
-                    // Load the nullable class
-                    String className = m_specification + "Nullable";
-                    Class nullableClazz = m_handler.getNullableClass(className);
+                    Object nullable = m_handler.getNullableObject(this);
 
-                    if (nullableClazz == null) {
+                    if (nullable == null) {
                         m_handler.log(Logger.WARNING, "[" + m_handler.getInstanceManager().getClassName() + "] Cannot load the nullable class to return a dependency object for " + m_field + " -> " + m_specification);
                         return null;
                     }
 
-                    // The nullable class is loaded, create the object and add it
-                    Object instance = null;
-                    try {
-                        instance = nullableClazz.newInstance();
-                    } catch (IllegalAccessException e) {
-                        // There is a problem in the dependency resolving (like in stopping method)
-                        if (m_isAggregate) {
-                            m_handler.log(Logger.ERROR, "[" + m_handler.getInstanceManager().getInstanceName() + "] Return an empty array, an exception was throwed in the get method", e);
-                            return Array.newInstance(m_clazz, 0);
-                        } else {
-                            m_handler.log(Logger.ERROR, "[" + m_handler.getInstanceManager().getInstanceName() + "] Return null, an exception was throwed in the get method", e);
-                            return null;
-                        }
-                    } catch (InstantiationException e) {
-                        // There is a problem in the dependency resolving (like in stopping
-                        // method)
-                        if (m_isAggregate) {
-                            m_handler.log(Logger.ERROR, "[" + m_handler.getInstanceManager().getInstanceName() + "] Return an empty array, an exception was throwed in the get method", e);
-                            return Array.newInstance(m_clazz, 0);
-                        } else {
-                            m_handler.log(Logger.ERROR, "[" + m_handler.getInstanceManager().getInstanceName() + "] Return null, an exception was throwed in the get method", e);
-                            return null;
-                        }
-                    }
-                    m_usage.getObjects().add(instance);
+                    m_usage.getObjects().add(nullable);
                 } else {
                     ServiceReference ref = (ServiceReference) m_references.get(0);
                     m_usage.getReferences().add(ref); // Get the first one
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
index 2e38641..275f265 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
@@ -21,7 +21,9 @@
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Dictionary;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.felix.ipojo.ComponentInstance;
 import org.apache.felix.ipojo.ConfigurationException;
@@ -48,9 +50,9 @@
     private Dependency[] m_dependencies = new Dependency[0];
 
     /**
-     * List of nullable class for optional dependencies.
+     * Map of dependency - nullable objects for optional dependencies.
      */
-    private Class[] m_nullableClasses = new Class[0];
+    private Map m_nullableObjects;
 
     /**
      * State of the handler.
@@ -65,7 +67,6 @@
 
     /**
      * Add a dependency.
-     * 
      * @param dep : the dependency to add
      */
     private void addDependency(Dependency dep) {
@@ -83,25 +84,6 @@
     }
 
     /**
-     * Add a nullable class.
-     * 
-     * @param clazz : the class to add
-     */
-    private void addNullableClass(Class clazz) {
-        for (int i = 0; (m_nullableClasses != null) && (i < m_nullableClasses.length); i++) {
-            if (m_nullableClasses[i] == clazz) { return; }
-        }
-        if (m_nullableClasses.length > 0) {
-            Class[] newClass = new Class[m_nullableClasses.length + 1];
-            System.arraycopy(m_nullableClasses, 0, newClass, 0, m_nullableClasses.length);
-            newClass[m_nullableClasses.length] = clazz;
-            m_nullableClasses = newClass;
-        } else {
-            m_nullableClasses = new Class[] { clazz };
-        }
-    }
-
-    /**
      * Get the list of managed dependency.
      * @return the dependency list
      */
@@ -234,7 +216,7 @@
      */
     public void configure(Element componentMetadata, Dictionary configuration) throws ConfigurationException {
         m_dependencies = new Dependency[0];
-        m_nullableClasses = new Class[0];
+        m_nullableObjects = new HashMap();
 
         ManipulationMetadata manipulation = new ManipulationMetadata(componentMetadata);
         List fl = new ArrayList();
@@ -252,6 +234,7 @@
             String filter = deps[i].getAttribute("filter");
             String opt = deps[i].getAttribute("optional");
             boolean optional = opt != null && opt.equalsIgnoreCase("true");
+            
             String agg = deps[i].getAttribute("aggregate");
             boolean aggregate = agg != null && agg.equalsIgnoreCase("true");
             String id = deps[i].getAttribute("id");
@@ -314,7 +297,13 @@
                     fl.add(manipulation.getField(dep.getField()));
                 }
             }
-
+            
+            if (optional) {
+                String defaultImpl = deps[i].getAttribute("default-implementation");
+                if (defaultImpl != null) {
+                    m_nullableObjects.put(dep, defaultImpl);
+                }
+            }
         }
 
         if (deps.length > 0) {
@@ -326,30 +315,58 @@
 
     /**
      * Create a nullable class for the given dependency.
-     * @param dep : the dependency
+     * @param dep : the service dependency
      */
-    private void createNullableClass(Dependency dep) {
-
-        String className = dep.getSpecification() + "Nullable";
-        String resource = dep.getSpecification().replace('.', '/') + ".class";
+    private void createNullableObject(Dependency  dep) {
+        String spec = dep.getSpecification();
+        String className = spec + "Nullable";
+        String resource = spec.replace('.', '/') + ".class";
         URL url = getInstanceManager().getContext().getBundle().getResource(resource);
 
-        byte[] b = NullableObjectWriter.dump(url, dep.getSpecification());
+        byte[] b = NullableObjectWriter.dump(url, spec);
         Class c = getInstanceManager().getFactory().defineClass(className, b, null);
-        addNullableClass(c);
+        try {
+            Object o = c.newInstance();
+            m_nullableObjects.put(dep, o);
+        } catch (InstantiationException e) {
+            log(Logger.ERROR, "The nullable object for " + dep.getSpecification() + " cannot be instantiate : " + e.getMessage());
+            getInstanceManager().stop(); 
+        } catch (IllegalAccessException e) {
+            log(Logger.ERROR, "The nullable object for " + dep.getSpecification() + " cannot be instantiate : " + e.getMessage());
+            getInstanceManager().stop();
+        }
     }
 
     /**
      * Return the nullable class corresponding to the given name.
-     * @param name the needed type
-     * @return the class correspondig to the name, or null if the class does not exist.
+     * @param dep the dependency which require the nullable class.
+     * @return the class corresponding to the name, or null if the class does not exist.
      */
-    protected Class getNullableClass(String name) {
-        for (int i = 0; i < m_nullableClasses.length; i++) {
-            Class c = m_nullableClasses[i];
-            if (c.getName().equals(name)) { return c; }
+    protected Object getNullableObject(Dependency dep) {
+        Object obj = m_nullableObjects.get(dep);
+        if (obj == null) { return null; } // Should not happen
+        if (obj instanceof String) { 
+            try {
+                Class c = getInstanceManager().getContext().getBundle().loadClass((String) obj);
+                obj = c.newInstance();
+                m_nullableObjects.put(dep, obj);
+                return obj;
+            } catch (ClassNotFoundException e) {
+                // A default-implementation class cannot be loaded
+                log(Logger.ERROR, "The default-implementation class " + obj + " cannot be loaded : " + e.getMessage());
+                getInstanceManager().stop();
+                return null;
+            } catch (InstantiationException e) {
+                log(Logger.ERROR, "The default-implementation class " + obj + " cannot be instantiated : " + e.getMessage());
+                getInstanceManager().stop();
+            } catch (IllegalAccessException e) {
+                log(Logger.ERROR, "The default-implementation class " + obj + " cannot be instantiated : " + e.getMessage());
+                getInstanceManager().stop();
+            }
+            return null;
+        } else {
+            return obj;
         }
-        return null;
     }
 
     /**
@@ -406,12 +423,11 @@
      * @see org.apache.felix.ipojo.Handler#start()
      */
     public void start() {
-        // Start the dependencies, for optional dependencies create Nullable
-        // class
+        // Start the dependencies, for optional dependencies create Nullable class
         for (int i = 0; i < m_dependencies.length; i++) {
             Dependency dep = m_dependencies[i];
-            if (dep.isOptional() && !dep.isAggregate()) {
-                createNullableClass(dep);
+            if (dep.isOptional() && !dep.isAggregate() && ! m_nullableObjects.containsKey(dep)) {
+                createNullableObject(dep);
             }
             dep.start();
         }