Add the onTimeout support to the temporal dependencies.
It's now possible to specify the action to do when the timeout occurs. Several possibilities are offered :
- nullable injects a nullable object
- default-implementation  injects a default-implementation
- empty-array injects an empty array (must be an aggregate dependency)
- null injects null

When no onTimeout action are provided, the handler throws a runtime exception.

Moreover, now it is possible to indicate an infinite timeout with timeout="infinite" or timeout=-1" in the handler description. 

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@674506 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/temporal.dependency.handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalDependency.java b/ipojo/temporal.dependency.handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalDependency.java
index 5b3c853..6df96d1 100644
--- a/ipojo/temporal.dependency.handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalDependency.java
+++ b/ipojo/temporal.dependency.handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalDependency.java
@@ -19,8 +19,12 @@
 package org.apache.felix.ipojo.handler.temporal;

 

 import java.lang.reflect.Array;

+import java.lang.reflect.Proxy;

 

 import org.apache.felix.ipojo.FieldInterceptor;

+import org.apache.felix.ipojo.Nullable;

+import org.apache.felix.ipojo.PrimitiveHandler;

+import org.apache.felix.ipojo.handlers.dependency.NullableObject;

 import org.apache.felix.ipojo.util.DependencyModel;

 import org.osgi.framework.BundleContext;

 import org.osgi.framework.Filter;

@@ -38,6 +42,32 @@
      * Timeout.

      */

     private long m_timeout;

+    

+    /**

+     * Default-Implementation

+     */

+    private String m_di;

+    

+    /**

+     * Nullable object / Default-Implementation instance if used

+     */

+    private Object m_nullableObject;

+    

+    /**

+     * Handler managing this dependency.

+     */

+    private PrimitiveHandler m_handler;

+    

+    /**

+     * Timetout policy.

+     * Null inject null

+     * Nullable injects a nullable object or an array with a nullable object

+     * Default-Implementation inject an object created from the specified injected

+     * implementation or an array with it

+     * Empty array inject an empty array (must be an aggregate dependency)

+     * No policy (0) throw a runtime exception when the timeout occurs     * 

+     */

+    private int m_policy;

 

     /**

      * Constructor.

@@ -48,9 +78,12 @@
      * @param timeout : timeout

      * @param handler : Handler managing this dependency

      */

-    public TemporalDependency(Class spec, boolean agg, Filter filter, BundleContext context, long timeout, TemporalHandler handler) {

+    public TemporalDependency(Class spec, boolean agg, Filter filter, BundleContext context, long timeout, int policy, String defaultImpl, TemporalHandler handler) {

         super(spec, agg, true, filter, null, DependencyModel.DYNAMIC_BINDING_POLICY, context, handler);

-        this.m_timeout = timeout;

+        m_di = defaultImpl;

+        m_policy = policy;

+        m_timeout = timeout;

+        m_handler = handler;

     }

 

     /**

@@ -126,20 +159,85 @@
             }

             // Check 

             if (exhausted) {

-                // Timeout, throw an exception

-                throw new RuntimeException("Service " + getSpecification().getName() + " unavailable : timeout");

+            	return onTimeout();

             } else {

                 ref = getServiceReference();

                 if (isAggregate()) {

                     Object[] svc = (Object[]) Array.newInstance(getSpecification(), 1);

-                    svc[0] = ref;

-                    return svc[0];

+                    svc[0] = getService(ref);

+                    return svc;

                 } else {

                     return getService(ref);

                 }

             }

         }

     }

+    

+    public void start() {

+    	super.start();

+        switch(m_policy) {

+            case TemporalHandler.NULL:

+                m_nullableObject = null;

+                break;

+            case TemporalHandler.NULLABLE:

+                // To load the proxy we use the POJO class loader. Indeed, this

+                // classloader imports iPOJO (so can access to Nullable) and has

+                // access to the service specification.

+                try {

+                    m_nullableObject = Proxy.newProxyInstance(m_handler

+                            .getInstanceManager().getClazz().getClassLoader(),

+                            new Class[] { getSpecification(), Nullable.class },

+                            new NullableObject()); // NOPMD

+                    if (isAggregate()) {

+                        Object[] array = (Object[]) Array.newInstance(getSpecification(), 1);

+                        array[0] = m_nullableObject;

+                        m_nullableObject = array;                        

+                    }

+                } catch (NoClassDefFoundError e) {

+                    // A NoClassDefFoundError is thrown if the specification uses a

+                    // class not accessible by the actual instance.

+                    // It generally comes from a missing import.

+                    throw new IllegalStateException(

+                            "Cannot create the Nullable object, a referenced class cannot be loaded: "

+                                    + e.getMessage());

+                }

+

+                break;

+            case TemporalHandler.DEFAULT_IMPLEMENTATION:

+             // Create the default-implementation object.

+                try {

+                    Class clazz = m_handler.getInstanceManager().getContext()

+                            .getBundle().loadClass(m_di);

+                    m_nullableObject = clazz.newInstance();

+                } catch (IllegalAccessException e) {

+                    throw new IllegalStateException(

+                            "Cannot load the default-implementation " + m_di

+                                    + " : " + e.getMessage());

+                } catch (InstantiationException e) {

+                    throw new IllegalStateException(

+                            "Cannot load the default-implementation " + m_di

+                                    + " : " + e.getMessage());

+                } catch (ClassNotFoundException e) {

+                    throw new IllegalStateException(

+                            "Cannot load the default-implementation " + m_di

+                                    + " : " + e.getMessage());

+                }

+                if (isAggregate()) {

+                    Object[] array = (Object[]) Array.newInstance(getSpecification(), 1);

+                    array[0] = m_nullableObject;

+                    m_nullableObject = array;

+                }

+                break;

+            case TemporalHandler.EMPTY_ARRAY:

+                m_nullableObject = Array.newInstance(getSpecification(), 0);

+                break;

+        }

+	}

+    

+    public void stop() {

+    	super.stop();

+    	m_nullableObject = null;

+    }

 

     /**

      * The monitored field receives a value.

@@ -150,5 +248,21 @@
      * @see org.apache.felix.ipojo.FieldInterceptor#onSet(java.lang.Object, java.lang.String, java.lang.Object)

      */

     public void onSet(Object arg0, String arg1, Object arg2) { }

+    

+    /**

+     * Implements the timeout policy according to the specified configuration

+     */

+    private Object onTimeout() {

+        switch(m_policy) {

+            case TemporalHandler.NULL:

+            case TemporalHandler.NULLABLE:

+            case TemporalHandler.DEFAULT_IMPLEMENTATION:

+            case TemporalHandler.EMPTY_ARRAY:

+                return m_nullableObject;

+            default: 

+                // Throws a runtime exception

+                throw new RuntimeException("Service " + getSpecification().getName() + " unavailable : timeout");

+        }

+    }

 

 }

diff --git a/ipojo/temporal.dependency.handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java b/ipojo/temporal.dependency.handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java
index ebe4818..4783c77 100644
--- a/ipojo/temporal.dependency.handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java
+++ b/ipojo/temporal.dependency.handler/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java
@@ -45,6 +45,12 @@
      */

     public static final int DEFAULT_TIMEOUT = 3000;

     

+    public static final int NO_POLICY = 0;

+    public static final int NULLABLE = 1;

+    public static final int DEFAULT_IMPLEMENTATION = 2;

+    public static final int EMPTY_ARRAY = 3;

+    public static final int NULL = 4;

+    

     /**

      * Handler namespace.

      */

@@ -125,11 +131,36 @@
             

             long timeout = DEFAULT_TIMEOUT;

             if (deps[i].containsAttribute("timeout")) {

-                timeout = new Long(deps[i].getAttribute("timeout")).longValue();

+                String to = deps[i].getAttribute("timeout");

+                if (to.equalsIgnoreCase("infinite")  || to.equalsIgnoreCase("-1")) {

+                    timeout = Long.MAX_VALUE; // Infinite wait time ...

+                } else {

+                    timeout = new Long(deps[i].getAttribute("timeout")).longValue();

+                }

             }

             

+            int policy = NO_POLICY;

+            String di = null;

+            String onTimeout = deps[i].getAttribute("onTimeout");

+            if (onTimeout != null) {

+                if (onTimeout.equalsIgnoreCase("nullable")) {

+                    policy = NULLABLE;

+                } else if (onTimeout.equalsIgnoreCase("empty-array")) {

+                    policy = EMPTY_ARRAY;

+                    if (! agg) {

+                        // The empty array policy can only be used on aggregate dependencies

+                        error("Cannot use the empty array policy for " + field + " : non aggregate dependency.");

+                    }

+                } else if (onTimeout.equalsIgnoreCase("null")) {

+                    policy = NULL;

+                } else if (onTimeout.length() > 0) {

+                    di = onTimeout;

+                    policy = DEFAULT_IMPLEMENTATION;

+                }

+            }

+         

             Class specification = DependencyModel.loadSpecification(spec, getInstanceManager().getContext());

-            TemporalDependency dep = new TemporalDependency(specification, agg, filter, getInstanceManager().getContext(), timeout, this);

+            TemporalDependency dep = new TemporalDependency(specification, agg, filter, getInstanceManager().getContext(), timeout, policy, di, this);

             m_dependencies.add(dep);

             

             getInstanceManager().register(fieldmeta, dep);