Fixed FELIX-3204
* Added the support of temporal.from, temporal.filters (with fallback to requires.from and requires.filters) to the temporal dependency handler
* Added an id attribute
* Updated the XSD file
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1204903 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/handler/temporal/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java b/ipojo/handler/temporal/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java
index 6e05562..4dd9a33 100644
--- a/ipojo/handler/temporal/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java
+++ b/ipojo/handler/temporal/src/main/java/org/apache/felix/ipojo/handler/temporal/TemporalHandler.java
@@ -21,6 +21,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
+import java.util.Hashtable;
import java.util.List;
import org.apache.felix.ipojo.ConfigurationException;
@@ -114,6 +115,20 @@
if (deps == null || deps.length == 0) {
deps = meta.getElements("temporal", NAMESPACE);
}
+
+ // Get instance filters.
+ Dictionary filtersConfiguration = getRequiresFilters(dictionary.get("temporal.filters"));
+ if(filtersConfiguration == null || filtersConfiguration.isEmpty()) {
+ // Fall back on the Requires handler configuration, if any
+ filtersConfiguration = getRequiresFilters(dictionary.get("requires.filters"));
+ }
+ // Get from filters if any.
+ Dictionary fromConfiguration = getRequiresFilters(dictionary.get("temporal.from"));
+ if(fromConfiguration == null || fromConfiguration.isEmpty()) {
+ // Fall back on the Requires handler configuration, if any
+ fromConfiguration = getRequiresFilters(dictionary.get("requires.from"));
+ }
+
for (int i = 0; i < deps.length; i++) {
if (!deps[i].containsAttribute("field") || m_dependencies.contains(deps[i].getAttribute("field"))) {
@@ -122,23 +137,17 @@
}
String field = deps[i].getAttribute("field");
+ String id = field;
+ if (deps[i].containsAttribute("id")) {
+ id = deps[i].getAttribute("id");
+ }
+
FieldMetadata fieldmeta = manipulation.getField(field);
if (fieldmeta == null) {
error("The field " + field + " does not exist in the class " + getInstanceManager().getClassName());
return;
}
- String fil = deps[i].getAttribute("filter");
- Filter filter = null;
- if (fil != null) {
- try {
- filter = getInstanceManager().getContext().createFilter(fil);
- } catch (InvalidSyntaxException e) {
- error("Cannot create the field from " + fil + ": " + e.getMessage());
- return;
- }
- }
-
boolean agg = false;
boolean collection = false;
String spec = fieldmeta.getFieldType();
@@ -155,8 +164,42 @@
}
}
+ // Determine the filter
+ String fil = deps[i].getAttribute("filter");
+ // Override the filter if filter configuration if available in the instance configuration
+ if (filtersConfiguration != null && id != null && filtersConfiguration.get(id) != null) {
+ fil = (String) filtersConfiguration.get(id);
+ }
+
+ // Check the from attribute
+ String from = deps[i].getAttribute("from");
+ if (fromConfiguration != null && id != null && fromConfiguration.get(id) != null) {
+ from = (String) fromConfiguration.get(id);
+ }
+
+ if (from != null) {
+ String fromFilter = "(|(instance.name=" + from + ")(service.pid=" + from + "))";
+ if (agg) {
+ warn("The 'from' attribute is incompatible with aggregate requirements: only one provider will " +
+ "match : " + fromFilter);
+ }
+ if (fil != null) {
+ fil = "(&" + fromFilter + fil + ")"; // Append the two filters
+ } else {
+ fil = fromFilter;
+ }
+ }
+
+ Filter filter = null;
+ if (fil != null) {
+ try {
+ filter = getInstanceManager().getContext().createFilter(fil);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("A requirement filter is invalid : " + filter + " - " + e.getMessage());
+ }
+ }
+
String prox = deps[i].getAttribute("proxy");
- //boolean proxy = prox != null && prox.equals("true");
// Use proxy by default except for array:
boolean proxy = prox == null || prox.equals("true");
@@ -216,6 +259,41 @@
getInstanceManager().register(fieldmeta, dep);
}
}
+
+ /**
+ * Gets the requires filter configuration from the given object.
+ * The given object must come from the instance configuration.
+ * This method was made to fix FELIX-2688. It supports filter configuration using
+ * an array:
+ * <code>{"myFirstDep", "(property1=value1)", "mySecondDep", "(property2=value2)"});</code>
+ *
+ * Copied from DependencyHandler#getRequiresFilters(Object)
+ *
+ * @param requiresFiltersValue the value contained in the instance
+ * configuration.
+ * @return the dictionary. If the object in already a dictionary, just returns it,
+ * if it's an array, builds the dictionary.
+ * @throws ConfigurationException the dictionary cannot be built
+ */
+ private Dictionary getRequiresFilters(Object requiresFiltersValue)
+ throws ConfigurationException {
+ if (requiresFiltersValue != null
+ && requiresFiltersValue.getClass().isArray()) {
+ String[] filtersArray = (String[]) requiresFiltersValue;
+ if (filtersArray.length % 2 != 0) {
+ throw new ConfigurationException(
+ "A requirement filter is invalid : "
+ + requiresFiltersValue);
+ }
+ Dictionary requiresFilters = new Hashtable();
+ for (int i = 0; i < filtersArray.length; i += 2) {
+ requiresFilters.put(filtersArray[i], filtersArray[i + 1]);
+ }
+ return requiresFilters;
+ }
+
+ return (Dictionary) requiresFiltersValue;
+ }
/**
* Nothing to do.
diff --git a/ipojo/handler/temporal/src/main/resources/temporal.xsd b/ipojo/handler/temporal/src/main/resources/temporal.xsd
index 33bd3bd..48dbc7f 100644
--- a/ipojo/handler/temporal/src/main/resources/temporal.xsd
+++ b/ipojo/handler/temporal/src/main/resources/temporal.xsd
@@ -33,6 +33,10 @@
<xs:annotation>
<xs:documentation>The implementation field supporting the dependency.</xs:documentation>
</xs:annotation></xs:attribute>
+ <xs:attribute name="id" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>The dependency id</xs:documentation>
+ </xs:annotation></xs:attribute>
<xs:attribute name="filter" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Filter use to discover matching filter.</xs:documentation></xs:annotation></xs:attribute>
diff --git a/ipojo/manipulator/src/main/resources/xsd/temporal.xsd b/ipojo/manipulator/src/main/resources/xsd/temporal.xsd
index 3f2d47c..4ba6292 100644
--- a/ipojo/manipulator/src/main/resources/xsd/temporal.xsd
+++ b/ipojo/manipulator/src/main/resources/xsd/temporal.xsd
@@ -31,6 +31,10 @@
<xs:annotation>
<xs:documentation>The implementation field supporting the dependency.</xs:documentation>
</xs:annotation></xs:attribute>
+ <xs:attribute name="id" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>The dependency id</xs:documentation>
+ </xs:annotation></xs:attribute>
<xs:attribute name="filter" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Filter use to discover matching filter.</xs:documentation></xs:annotation></xs:attribute>
diff --git a/ipojo/tests/handler/temporal/src/main/java/org/apache/felix/ipojo/test/scenarios/temporal/FilterTest.java b/ipojo/tests/handler/temporal/src/main/java/org/apache/felix/ipojo/test/scenarios/temporal/FilterTest.java
new file mode 100644
index 0000000..c8a0cd7
--- /dev/null
+++ b/ipojo/tests/handler/temporal/src/main/java/org/apache/felix/ipojo/test/scenarios/temporal/FilterTest.java
@@ -0,0 +1,256 @@
+/*
+ * 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.test.scenarios.temporal;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.junit4osgi.OSGiTestCase;
+import org.apache.felix.ipojo.test.scenarios.temporal.service.CheckService;
+import org.apache.felix.ipojo.test.scenarios.temporal.service.FooService;
+import org.apache.felix.ipojo.test.scenarios.util.Utils;
+import org.osgi.framework.ServiceReference;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+/**
+ * Checks <tt>requires.filter</tt>, <tt>temporal.filter</tt>, <tt>temporal.from</tt> and
+ * <tt>requires.from</tt> attributes.
+ */
+public class FilterTest extends OSGiTestCase {
+
+ /**
+ * Checks <tt>temporal.filter</tt> with dependency id.
+ * The filter is made to get only one provider.
+ */
+ public void testWithTemporalFilters() {
+ String prov = "provider";
+ ComponentInstance provider1 = Utils.getComponentInstanceByName(context, "TEMPORAL-FooProvider", prov);
+ String prov2 = "provider2";
+ ComponentInstance provider2 = Utils.getComponentInstanceByName(context, "TEMPORAL-FooProvider", prov2);
+
+ Dictionary configuration = new Hashtable();
+ String un = "under-1";
+ configuration.put("instance.name", un);
+ Dictionary filter = new Hashtable();
+ filter.put("foo", "(instance.name=provider2)");
+ configuration.put("temporal.filters", filter);
+ ComponentInstance under = Utils.getComponentInstance(context, "TEMPORAL-MultipleCheckServiceProvider", configuration);
+
+ ServiceReference ref_fs = Utils.getServiceReferenceByName(context, FooService.class.getName(), prov);
+ assertNotNull("Check foo availability", ref_fs);
+
+ ServiceReference ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability", ref_cs);
+
+ CheckService cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation", cs.check());
+
+ // Stop the provider.
+ provider2.stop();
+ ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability - 2", ref_cs);
+ long begin = System.currentTimeMillis();
+ DelayedProvider dp2 = new DelayedProvider(provider2, 100);
+ dp2.start();
+ cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation - 2", cs.check());
+ long end = System.currentTimeMillis();
+ System.out.println("delay = " + (end - begin));
+ assertTrue("Assert min delay", (end - begin) >= 100);
+ assertTrue("Assert max delay", (end - begin) <= 1000);
+ dp2.stop();
+
+ ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability - 3", ref_cs);
+ cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation - 3", cs.check());
+
+ provider1.stop();
+ provider2.stop();
+ provider1.dispose();
+ provider2.dispose();
+ under.stop();
+ under.dispose();
+ }
+
+ /**
+ * Checks <tt>requires.filter</tt> with dependency id.
+ * The filter is made to get only one provider.
+ */
+ public void testWithRequireFilters() {
+ String prov = "provider";
+ ComponentInstance provider1 = Utils.getComponentInstanceByName(context, "TEMPORAL-FooProvider", prov);
+ String prov2 = "provider2";
+ ComponentInstance provider2 = Utils.getComponentInstanceByName(context, "TEMPORAL-FooProvider", prov2);
+
+ Dictionary configuration = new Hashtable();
+ String un = "under-1";
+ configuration.put("instance.name", un);
+ Dictionary filter = new Hashtable();
+ filter.put("foo", "(instance.name=provider2)");
+ configuration.put("requires.filters", filter);
+ ComponentInstance under = Utils.getComponentInstance(context, "TEMPORAL-MultipleCheckServiceProvider", configuration);
+
+ ServiceReference ref_fs = Utils.getServiceReferenceByName(context, FooService.class.getName(), prov);
+ assertNotNull("Check foo availability", ref_fs);
+
+ ServiceReference ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability", ref_cs);
+
+ CheckService cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation", cs.check());
+
+ // Stop the provider.
+ provider2.stop();
+ ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability - 2", ref_cs);
+ long begin = System.currentTimeMillis();
+ DelayedProvider dp2 = new DelayedProvider(provider2, 100);
+ dp2.start();
+ cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation - 2", cs.check());
+ long end = System.currentTimeMillis();
+ System.out.println("delay = " + (end - begin));
+ assertTrue("Assert min delay", (end - begin) >= 100);
+ assertTrue("Assert max delay", (end - begin) <= 1000);
+ dp2.stop();
+
+ ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability - 3", ref_cs);
+ cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation - 3", cs.check());
+
+ provider1.stop();
+ provider2.stop();
+ provider1.dispose();
+ provider2.dispose();
+ under.stop();
+ under.dispose();
+ }
+
+ /**
+ * Checks <tt>temporal.from</tt> with dependency id.
+ * The filter is made to get only one specific provider.
+ */
+ public void testWithTemporalFrom() {
+ String prov = "provider";
+ ComponentInstance provider1 = Utils.getComponentInstanceByName(context, "TEMPORAL-FooProvider", prov);
+ String prov2 = "provider2";
+ ComponentInstance provider2 = Utils.getComponentInstanceByName(context, "TEMPORAL-FooProvider", prov2);
+
+ Dictionary configuration = new Hashtable();
+ String un = "under-1";
+ configuration.put("instance.name", un);
+ Dictionary filter = new Hashtable();
+ filter.put("foo", "provider2");
+ configuration.put("temporal.from", filter);
+ ComponentInstance under = Utils.getComponentInstance(context, "TEMPORAL-MultipleCheckServiceProvider", configuration);
+
+ ServiceReference ref_fs = Utils.getServiceReferenceByName(context, FooService.class.getName(), prov);
+ assertNotNull("Check foo availability", ref_fs);
+
+ ServiceReference ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability", ref_cs);
+
+ CheckService cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation", cs.check());
+
+ // Stop the provider.
+ provider2.stop();
+ ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability - 2", ref_cs);
+ long begin = System.currentTimeMillis();
+ DelayedProvider dp2 = new DelayedProvider(provider2, 100);
+ dp2.start();
+ cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation - 2", cs.check());
+ long end = System.currentTimeMillis();
+ System.out.println("delay = " + (end - begin));
+ assertTrue("Assert min delay", (end - begin) >= 100);
+ assertTrue("Assert max delay", (end - begin) <= 1000);
+ dp2.stop();
+
+ ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability - 3", ref_cs);
+ cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation - 3", cs.check());
+
+ provider1.stop();
+ provider2.stop();
+ provider1.dispose();
+ provider2.dispose();
+ under.stop();
+ under.dispose();
+ }
+
+ /**
+ * Checks <tt>requires.from</tt> with dependency id.
+ * The filter is made to get only one specific provider.
+ */
+ public void testWithRequiresFrom() {
+ String prov = "provider";
+ ComponentInstance provider1 = Utils.getComponentInstanceByName(context, "TEMPORAL-FooProvider", prov);
+ String prov2 = "provider2";
+ ComponentInstance provider2 = Utils.getComponentInstanceByName(context, "TEMPORAL-FooProvider", prov2);
+
+ Dictionary configuration = new Hashtable();
+ String un = "under-1";
+ configuration.put("instance.name", un);
+ Dictionary filter = new Hashtable();
+ filter.put("foo", "provider2");
+ configuration.put("requires.from", filter);
+ ComponentInstance under = Utils.getComponentInstance(context, "TEMPORAL-MultipleCheckServiceProvider", configuration);
+
+ ServiceReference ref_fs = Utils.getServiceReferenceByName(context, FooService.class.getName(), prov);
+ assertNotNull("Check foo availability", ref_fs);
+
+ ServiceReference ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability", ref_cs);
+
+ CheckService cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation", cs.check());
+
+ // Stop the provider.
+ provider2.stop();
+ ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability - 2", ref_cs);
+ long begin = System.currentTimeMillis();
+ DelayedProvider dp2 = new DelayedProvider(provider2, 100);
+ dp2.start();
+ cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation - 2", cs.check());
+ long end = System.currentTimeMillis();
+ System.out.println("delay = " + (end - begin));
+ assertTrue("Assert min delay", (end - begin) >= 100);
+ assertTrue("Assert max delay", (end - begin) <= 1000);
+ dp2.stop();
+
+ ref_cs = Utils.getServiceReferenceByName(context, CheckService.class.getName(), un);
+ assertNotNull("Check cs availability - 3", ref_cs);
+ cs = (CheckService) context.getService(ref_cs);
+ assertTrue("Check invocation - 3", cs.check());
+
+ provider1.stop();
+ provider2.stop();
+ provider1.dispose();
+ provider2.dispose();
+ under.stop();
+ under.dispose();
+ }
+}
diff --git a/ipojo/tests/handler/temporal/src/main/java/org/apache/felix/ipojo/test/scenarios/temporal/TemporalTestSuite.java b/ipojo/tests/handler/temporal/src/main/java/org/apache/felix/ipojo/test/scenarios/temporal/TemporalTestSuite.java
index 180a39c..ebc01b6 100644
--- a/ipojo/tests/handler/temporal/src/main/java/org/apache/felix/ipojo/test/scenarios/temporal/TemporalTestSuite.java
+++ b/ipojo/tests/handler/temporal/src/main/java/org/apache/felix/ipojo/test/scenarios/temporal/TemporalTestSuite.java
@@ -35,6 +35,7 @@
ots.addTestSuite(NullTest.class);
ots.addTestSuite(EmptyTest.class);
ots.addTestSuite(TemporalTest.class);
+ ots.addTestSuite(FilterTest.class);
return ots;
}
diff --git a/ipojo/tests/handler/temporal/src/main/resources/metadata.xml b/ipojo/tests/handler/temporal/src/main/resources/metadata.xml
index 0ce312d..42262d8 100644
--- a/ipojo/tests/handler/temporal/src/main/resources/metadata.xml
+++ b/ipojo/tests/handler/temporal/src/main/resources/metadata.xml
@@ -42,7 +42,7 @@
</component>
<component classname="org.apache.felix.ipojo.test.scenarios.component.MultipleCheckServiceProvider" name="TEMPORAL-MultipleCheckServiceProvider">
- <temp:requires field="fs"/>
+ <temp:requires field="fs" id="foo"/>
<provides/>
</component>