added support for temporal service dependency annotations
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@902651 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/TemporalServiceDependency.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/TemporalServiceDependency.java
new file mode 100644
index 0000000..9d75d2b
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/TemporalServiceDependency.java
@@ -0,0 +1,91 @@
+package org.apache.felix.dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method or a field for injecting a Temporal Service Dependency.
+ * A Temporal Service dependency can block the caller thread between service updates. Only
+ * useful for required stateless dependencies that can be replaced transparently.
+ * A Dynamic Proxy is used to wrap the actual service dependency. When the dependency goes
+ * away, an attempt is made to replace it with another one which satisfies the service dependency
+ * criteria. If no service replacement is available, then any method invocation (through the
+ * dynamic proxy) will block during a configurable timeout. On timeout, an unchecked
+ * <code>IllegalStateException</code> exception is raised (but the service is not deactivated).<p>
+ *
+ * <b>Only supports required dependencies</b>
+ *
+ * <p> Sample Code:<p>
+ * <blockquote>
+ *
+ * <pre>
+ * class MyServer implements Runnable {
+ * @TemporalServiceDependency(timeout=15000)
+ * MyDependency _dependency;.
+ *
+ * @Start
+ * void start() {
+ * (new Thread(this)).start();
+ * }
+ *
+ * public void run() {
+ * try {
+ * _dependency.doWork();
+ * } catch (IllegalStateException e) {
+ * t.printStackTrace();
+ * }
+ * }
+ * </pre>
+ *
+ * </blockquote>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target( { ElementType.METHOD, ElementType.FIELD })
+public @interface TemporalServiceDependency
+{
+ /**
+ * Sets the timeout for this temporal dependency. Specifying a timeout value of zero means that there is no timeout period,
+ * and an invocation on a missing service will fail immediately.
+ *
+ * @param timeout the dependency timeout value greater or equals to 0
+ * @throws IllegalArgumentException if the timeout is negative
+ * @return this temporal dependency
+ */
+ long timeout() default 30000L;
+
+ /**
+ * Returns the Service dependency type (by default, the type is method parameter type).
+ * @return the Service dependency type.
+ */
+ Class<?> service() default Object.class;
+
+ /**
+ * Returns the Service dependency OSGi filter.
+ * @return The Service dependency filter.
+ */
+ String filter() default "";
+
+ /**
+ * Returns the class for the default implementation, if the dependency is not available.
+ * @return The default class used when the dependency is not available.
+ */
+ Class<?> defaultImpl() default Object.class;
+
+ /**
+ * Returns the callback method to be invoked when the service is available. This attribute is only meaningful when
+ * the annotation is applied on a class field.
+ */
+ String added() default "";
+
+ /**
+ * Returns the callback method to be invoked when the service properties have changed.
+ */
+ String changed() default "";
+
+ /**
+ * Returns the callback method to invoke when the service is lost.
+ */
+ String removed() default "";
+}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
index 6455c85..02e3f4f 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
@@ -37,6 +37,7 @@
import org.apache.felix.dm.annotation.api.ServiceDependency;
import org.apache.felix.dm.annotation.api.Start;
import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.annotation.api.TemporalServiceDependency;
import aQute.lib.osgi.Annotation;
import aQute.lib.osgi.ClassDataCollector;
@@ -61,6 +62,8 @@
+ ServiceDependency.class.getName().replace('.', '/') + ";";
private final static String A_CONFIGURATION_DEPENDENCY = "L"
+ ConfigurationDependency.class.getName().replace('.', '/') + ";";
+ private final static String A_TEMPORAL_SERVICE_DEPENDENCY = "L"
+ + TemporalServiceDependency.class.getName().replace('.', '/') + ";";
private Reporter m_reporter;
private String m_className;
@@ -88,6 +91,7 @@
enum EntryTypes {
Service,
ServiceDependency,
+ TemporalServiceDependency,
ConfigurationDependency,
};
@@ -113,7 +117,8 @@
autoConfig,
pid,
propagate,
- updated;
+ updated,
+ timeout
};
/**
@@ -122,7 +127,7 @@
*/
private class Info
{
- /** The component descriptor entry type (either Service, ServiceDependency, or ConfigurationDependency) */
+ /** The component descriptor entry type: either Service, (Temporal)ServiceDependency, or ConfigurationDependency */
EntryTypes m_entry;
/** The component descriptor entry parameters (init/start/stop ...) */
@@ -350,11 +355,15 @@
}
else if (annotation.getName().equals(A_SERVICE_DEP))
{
- parseServiceDependencyAnnotation(annotation);
+ parseServiceDependencyAnnotation(annotation, false);
}
else if (annotation.getName().equals(A_CONFIGURATION_DEPENDENCY))
{
parseConfigurationDependencyAnnotation(annotation);
+ }
+ else if (annotation.getName().equals(A_TEMPORAL_SERVICE_DEPENDENCY))
+ {
+ parseServiceDependencyAnnotation(annotation, true);
}
}
@@ -390,12 +399,12 @@
}
/**
- * Parses a ServiceDependency Annotation.
+ * Parses a ServiceDependency or a TemporalServiceDependency Annotation.
* @param annotation the ServiceDependency Annotation.
*/
- private void parseServiceDependencyAnnotation(Annotation annotation)
+ private void parseServiceDependencyAnnotation(Annotation annotation, boolean temporal)
{
- Info info = new Info(EntryTypes.ServiceDependency);
+ Info info = new Info(temporal ? EntryTypes.TemporalServiceDependency : EntryTypes.ServiceDependency);
m_infos.add(info);
// service attribute
@@ -434,8 +443,10 @@
// defaultImpl attribute
info.addClassParam(annotation, Params.defaultImpl, null);
- // required attribute
- info.addParam(annotation, Params.required, null);
+ // required attribute (not valid if parsing a temporal service dependency)
+ if (! temporal) {
+ info.addParam(annotation, Params.required, null);
+ }
// added callback
info.addParam(annotation, Params.added, (!m_isField) ? m_method : null);
@@ -445,6 +456,11 @@
// removed callback
info.addParam(annotation, Params.removed, null);
+
+ // timeout attribute (only valid if parsing a temporal service dependency)
+ if (temporal) {
+ info.addParam(annotation, Params.timeout, null);
+ }
}
/**
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ComponentManager.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ComponentManager.java
index 5806201..cda68ea 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ComponentManager.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ComponentManager.java
@@ -31,6 +31,7 @@
import org.apache.felix.dm.dependencies.ConfigurationDependency;
import org.apache.felix.dm.dependencies.Dependency;
import org.apache.felix.dm.dependencies.ServiceDependency;
+import org.apache.felix.dm.dependencies.TemporalServiceDependency;
import org.apache.felix.dm.service.Service;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@@ -152,7 +153,12 @@
case ServiceDependency:
checkServiceParsed(service);
- service.add(createServiceDependency(b, dm, parser));
+ service.add(createServiceDependency(b, dm, parser, false));
+ break;
+
+ case TemporalServiceDependency:
+ checkServiceParsed(service);
+ service.add(createServiceDependency(b, dm, parser, true));
break;
case ConfigurationDependency:
@@ -283,13 +289,15 @@
* @param b
* @param dm
* @param parser
+ * @param temporal true if this dependency is a temporal one, false if not.
* @return
* @throws ClassNotFoundException
*/
private ServiceDependency createServiceDependency(Bundle b, DependencyManager dm,
- DescriptorParser parser) throws ClassNotFoundException
+ DescriptorParser parser, boolean temporal) throws ClassNotFoundException
{
- ServiceDependency sd = dm.createServiceDependency();
+ ServiceDependency sd = temporal ? dm.createTemporalServiceDependency()
+ : dm.createServiceDependency();
// Set service with eventual service filter
String service = parser.getString(DescriptorParam.service);
@@ -305,9 +313,16 @@
sd.setDefaultImplementation(defaultServiceImplClass);
}
- // Set required flag
- String required = parser.getString(DescriptorParam.required, "true");
- sd.setRequired("true".equals(required));
+ // Set required flag (always true for a temporal dependency)
+ if (temporal)
+ {
+ sd.setRequired(true);
+ }
+ else
+ {
+ String required = parser.getString(DescriptorParam.required, "true");
+ sd.setRequired("true".equals(required));
+ }
// Set bind/unbind/rebind
String added = parser.getString(DescriptorParam.added, null);
@@ -321,6 +336,16 @@
{
sd.setAutoConfig(autoConfigField);
}
+
+ // Set the timeout value for a temporal service dependency
+ if (temporal)
+ {
+ String timeout = parser.getString(DescriptorParam.timeout, null);
+ if (timeout != null)
+ {
+ ((TemporalServiceDependency) sd).setTimeout(Long.parseLong(timeout));
+ }
+ }
return sd;
}
@@ -336,14 +361,16 @@
{
ConfigurationDependency cd = dm.createConfigurationDependency();
String pid = parser.getString(DescriptorParam.pid);
- if (pid == null) {
- throw new IllegalArgumentException("pid attribute not provided in ConfigurationDependency declaration");
+ if (pid == null)
+ {
+ throw new IllegalArgumentException(
+ "pid attribute not provided in ConfigurationDependency declaration");
}
cd.setPid(pid);
-
+
String propagate = parser.getString(DescriptorParam.propagate, "false");
cd.setPropagate("true".equals(propagate));
-
+
String callback = parser.getString(DescriptorParam.updated, "updated");
cd.setCallback(callback);
return cd;
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorEntry.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorEntry.java
index 226799f..56e8f39 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorEntry.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorEntry.java
@@ -26,5 +26,6 @@
{
Service,
ServiceDependency,
+ TemporalServiceDependency,
ConfigurationDependency
}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParam.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParam.java
index 9ba1af2..5fd0dc9 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParam.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParam.java
@@ -87,7 +87,10 @@
propagate,
/* ConfigurationDependency attribute for the updated callback method (the parsed value is a String */
- updated;
+ updated,
+
+ /* TemporalServiceDependency attribute for the timeout (the parsed value is a String) */
+ timeout;
/**
* Indicates if a given attribute is a Service attribute.
@@ -108,6 +111,15 @@
}
/**
+ * Indicates if a given attribute is a TemporalServiceDependency attribute.
+ * @param attr a Descriptor attribute
+ * @return true if the descriptor is a Temporal Service attribute, false if not
+ */
+ public static boolean isTemporalServiceDepependencyAttribute(DescriptorParam attr) {
+ return serviceDependencyAttribute.contains(attr) || attr == timeout;
+ }
+
+ /**
* Indicates if a given attribute is a ServiceDependency attribute.
* @param attr a Descriptor attribute
* @return true if the descriptor is a Service attribute, false if not