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);