Fix FELIX-3932 - Allow dependency filters to get context-source variables
Dependency filters can now have variables written like ${var}. These placeholders are replaced by values found in context-sources. System properties and instance configurations are used as context sources. Other sources can be exposed as OSGi services.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1481356 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/context/ContextualFilterConsumer.java b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/context/ContextualFilterConsumer.java
new file mode 100644
index 0000000..4415210
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/context/ContextualFilterConsumer.java
@@ -0,0 +1,53 @@
+/*
+ * 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.runtime.core.test.components.context;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Provides;
+import org.apache.felix.ipojo.annotations.Requires;
+import org.apache.felix.ipojo.runtime.core.test.services.CheckService;
+import org.apache.felix.ipojo.runtime.core.test.services.FooService;
+
+import java.util.Properties;
+
+/**
+ * A component using a Foo service.
+ * The foo service is selected using a contextual filter.
+ * The filter is set within the instance configuration.
+ */
+@Component
+@Provides
+public class ContextualFilterConsumer implements CheckService {
+
+ @Requires(nullable = false, id = "foo")
+ private FooService foo;
+
+ @Override
+ public boolean check() {
+ return foo != null;
+ }
+
+ @Override
+ public Properties getProps() {
+ Properties properties = new Properties();
+ properties.put("id", foo.getInt());
+ return properties;
+ }
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/context/Provider1.java b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/context/Provider1.java
new file mode 100644
index 0000000..42e0a1c
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/context/Provider1.java
@@ -0,0 +1,75 @@
+/*
+ * 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.runtime.core.test.components.context;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Provides;
+import org.apache.felix.ipojo.annotations.StaticServiceProperty;
+import org.apache.felix.ipojo.runtime.core.test.services.FooService;
+
+import java.util.Properties;
+
+/**
+ * A simple foo provider with id 1
+ */
+@Component
+@Provides(properties =
+ @StaticServiceProperty(name="id", value = "1", type = "string"))
+// We don't instantiate it here as it may break other tests.
+public class Provider1 implements FooService {
+
+ @Override
+ public boolean foo() {
+ return false;
+ }
+
+ @Override
+ public Properties fooProps() {
+ return null;
+ }
+
+ @Override
+ public Boolean getObject() {
+ return null;
+ }
+
+ @Override
+ public boolean getBoolean() {
+ return false;
+ }
+
+ @Override
+ public int getInt() {
+ // The id.
+ return 1;
+ }
+
+ @Override
+ public long getLong() {
+ // The id.
+ return 1;
+ }
+
+ @Override
+ public double getDouble() {
+ // The id
+ return 1;
+ }
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/context/Provider2.java b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/context/Provider2.java
new file mode 100644
index 0000000..8c2f95b
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/context/Provider2.java
@@ -0,0 +1,75 @@
+/*
+ * 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.runtime.core.test.components.context;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Provides;
+import org.apache.felix.ipojo.annotations.StaticServiceProperty;
+import org.apache.felix.ipojo.runtime.core.test.services.FooService;
+
+import java.util.Properties;
+
+/**
+ * A simple foo provider with id 2
+ */
+@Component
+@Provides(properties =
+ @StaticServiceProperty(name="id", value = "2", type = "string"))
+// We don't instantiate it here as it may break other tests.
+public class Provider2 implements FooService {
+
+ @Override
+ public boolean foo() {
+ return false;
+ }
+
+ @Override
+ public Properties fooProps() {
+ return null;
+ }
+
+ @Override
+ public Boolean getObject() {
+ return null;
+ }
+
+ @Override
+ public boolean getBoolean() {
+ return false;
+ }
+
+ @Override
+ public int getInt() {
+ // The id.
+ return 2;
+ }
+
+ @Override
+ public long getLong() {
+ // The id.
+ return 2;
+ }
+
+ @Override
+ public double getDouble() {
+ // The id
+ return 2;
+ }
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/dependencies/context/TestContextualFilters.java b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/dependencies/context/TestContextualFilters.java
new file mode 100644
index 0000000..50b504f
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/dependencies/context/TestContextualFilters.java
@@ -0,0 +1,200 @@
+/*
+ * 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.runtime.core.test.dependencies.context;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.handlers.dependency.DependencyDescription;
+import org.apache.felix.ipojo.handlers.dependency.DependencyHandlerDescription;
+import org.apache.felix.ipojo.runtime.core.test.dependencies.Common;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Check the contextual filters using:
+ * - a system properties.
+ * - instance configuration (and reconfiguration)
+ */
+public class TestContextualFilters extends Common {
+
+ @Before
+ public void setup() {
+ ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.Provider1");
+
+ ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.Provider2");
+ }
+
+ @After
+ public void tearDown() {
+ System.clearProperty("env.id");
+ }
+
+ @Test
+ public void testContextualFilterWithSystemProperty() {
+ // Set the system property.
+ System.setProperty("env.id", "2");
+
+ Properties configuration = new Properties();
+ Properties filters = new Properties();
+ filters.put("foo", "(id=${env.id})");
+ configuration.put("requires.filters", filters);
+ ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.ContextualFilterConsumer", configuration);
+
+ assertTrue(instance.getState() == ComponentInstance.VALID);
+ DependencyHandlerDescription desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ DependencyDescription dependency = desc.getDependencies()[0];
+ assertEquals("(id=2)", dependency.getFilter());
+ }
+
+ @Test
+ public void testContextualFilterWithUnsetSystemProperty() {
+ // Just to be sure.
+ System.clearProperty("env.id");
+
+ Properties configuration = new Properties();
+ Properties filters = new Properties();
+ filters.put("foo", "(id=${env.id})");
+ configuration.put("requires.filters", filters);
+ ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.ContextualFilterConsumer", configuration);
+
+ assertTrue(instance.getState() == ComponentInstance.INVALID);
+ DependencyHandlerDescription desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ DependencyDescription dependency = desc.getDependencies()[0];
+ assertEquals("(id=${env.id})", dependency.getFilter());
+ }
+
+ @Test
+ public void testContextualFilterWithInstanceProperty() {
+ Properties configuration = new Properties();
+ Properties filters = new Properties();
+ filters.put("foo", "(id=${instance.id})");
+ configuration.put("requires.filters", filters);
+ configuration.put("instance.id", 2);
+
+ ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.ContextualFilterConsumer", configuration);
+
+ assertTrue(instance.getState() == ComponentInstance.VALID);
+ DependencyHandlerDescription desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ DependencyDescription dependency = desc.getDependencies()[0];
+ assertEquals("(id=2)", dependency.getFilter());
+ }
+
+ /**
+ * This test check filter set using instance properties.
+ * However the instance is reconfigured a couple of times to illustrate the different case:
+ * - unset property
+ * - property updated
+ * - reconfiguration that does not impact the filter
+ */
+ @Test
+ public void testContextualFilterAndInstanceReconfiguration() {
+ Properties configuration = new Properties();
+ Properties filters = new Properties();
+ filters.put("foo", "(id=${instance.id})");
+ configuration.put("requires.filters", filters);
+ configuration.put("instance.id", 2);
+
+ ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.ContextualFilterConsumer", configuration);
+
+ assertTrue(instance.getState() == ComponentInstance.VALID);
+ DependencyHandlerDescription desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ DependencyDescription dependency = desc.getDependencies()[0];
+ assertEquals("(id=2)", dependency.getFilter());
+
+ // Reconfigure the instance.
+ Properties newProps = new Properties();
+ newProps.put("instance.id", "3");
+ instance.reconfigure(newProps);
+
+ assertTrue(instance.getState() == ComponentInstance.INVALID);
+ desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ dependency = desc.getDependencies()[0];
+ assertEquals("(id=3)", dependency.getFilter());
+
+ // Another reconfiguration (that does not affect the filters)
+ newProps = new Properties();
+ newProps.put("instance.id", "3");
+ newProps.put("stuff", "stuff");
+ instance.reconfigure(newProps);
+
+ assertTrue(instance.getState() == ComponentInstance.INVALID);
+ desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ dependency = desc.getDependencies()[0];
+ assertEquals("(id=3)", dependency.getFilter());
+
+ // Yet another reconfiguration, un-setting instance.id
+ newProps = new Properties();
+ newProps.put("stuff", "stuff");
+ instance.reconfigure(newProps);
+
+ assertTrue(instance.getState() == ComponentInstance.INVALID);
+ desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ dependency = desc.getDependencies()[0];
+ assertEquals("(id=${instance.id})", dependency.getFilter());
+
+ // Finally another reconfiguration to build a fulfilled filter
+ newProps = new Properties();
+ newProps.put("instance.id", "1");
+ newProps.put("stuff", "stuff");
+ instance.reconfigure(newProps);
+
+ assertTrue(instance.getState() == ComponentInstance.VALID);
+ desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ dependency = desc.getDependencies()[0];
+ assertEquals("(id=1)", dependency.getFilter());
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/dependencies/context/TestContextualFiltersAndExternalSources.java b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/dependencies/context/TestContextualFiltersAndExternalSources.java
new file mode 100644
index 0000000..bcc3fbc
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-service-dependency-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/dependencies/context/TestContextualFiltersAndExternalSources.java
@@ -0,0 +1,208 @@
+/*
+ * 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.runtime.core.test.dependencies.context;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ContextListener;
+import org.apache.felix.ipojo.ContextSource;
+import org.apache.felix.ipojo.handlers.dependency.DependencyDescription;
+import org.apache.felix.ipojo.handlers.dependency.DependencyHandlerDescription;
+import org.apache.felix.ipojo.runtime.core.test.dependencies.Common;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.ServiceRegistration;
+
+import java.util.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Check the contextual filters using external sources
+ */
+public class TestContextualFiltersAndExternalSources extends Common {
+
+ private ServiceRegistration registration;
+ private ServiceRegistration registration2;
+
+ @Before
+ public void setup() {
+ ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.Provider1");
+
+ ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.Provider2");
+ }
+
+ @After
+ public void tearDown() {
+ if (registration != null) {
+ registration.unregister();
+ registration = null;
+ }
+
+ if (registration2 != null) {
+ registration2.unregister();
+ registration2 = null;
+ }
+
+ }
+
+ @Test
+ public void testContextualFilterUsingOneSource() {
+ MyContextSource source = new MyContextSource();
+ source.set("source.id", 2);
+ registration = context.registerService(ContextSource.class.getName(),
+ source, null);
+
+ Properties configuration = new Properties();
+ Properties filters = new Properties();
+ filters.put("foo", "(id=${source.id})");
+ configuration.put("requires.filters", filters);
+ ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.ContextualFilterConsumer", configuration);
+
+ assertTrue(instance.getState() == ComponentInstance.VALID);
+ DependencyHandlerDescription desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ DependencyDescription dependency = desc.getDependencies()[0];
+ assertEquals("(id=2)", dependency.getFilter());
+ }
+
+ @Test
+ public void testContextualFilterUsingOneSourceAppearingLater() {
+ MyContextSource source = new MyContextSource();
+ source.set("source.id", 2);
+
+ Properties configuration = new Properties();
+ Properties filters = new Properties();
+ filters.put("foo", "(id=${source.id})");
+ configuration.put("requires.filters", filters);
+ ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.ContextualFilterConsumer", configuration);
+
+ assertTrue(instance.getState() == ComponentInstance.INVALID);
+ DependencyHandlerDescription desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ DependencyDescription dependency = desc.getDependencies()[0];
+ assertEquals("(id=${source.id})", dependency.getFilter());
+
+ registration = context.registerService(ContextSource.class.getName(),
+ source, null);
+
+ assertTrue(instance.getState() == ComponentInstance.VALID);
+ desc = (DependencyHandlerDescription) instance.getInstanceDescription().getHandlerDescription("org.apache.felix" +
+ ".ipojo:requires");
+
+ // Only one dependency.
+ dependency = desc.getDependencies()[0];
+ assertEquals("(id=2)", dependency.getFilter());
+ }
+
+ @Test
+ public void testContextualFilterUsingOneSourceWithReconfiguration() {
+ MyContextSource source = new MyContextSource();
+ source.set("source.id", 2);
+ registration = context.registerService(ContextSource.class.getName(),
+ source, null);
+
+ Properties configuration = new Properties();
+ Properties filters = new Properties();
+ filters.put("foo", "(id=${source.id})");
+ configuration.put("requires.filters", filters);
+ ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+ ".components.context.ContextualFilterConsumer", configuration);
+
+ assertTrue(instance.getState() == ComponentInstance.VALID);
+
+ // Set id to 3 => INVALID
+ source.set("source.id", 3);
+ assertTrue(instance.getState() == ComponentInstance.INVALID);
+
+ // Set id to null
+ source.set("source.id", null);
+ assertTrue(instance.getState() == ComponentInstance.INVALID);
+
+ DependencyHandlerDescription desc = (DependencyHandlerDescription) instance.getInstanceDescription()
+ .getHandlerDescription("org.apache.felix.ipojo:requires");
+ DependencyDescription dependency = desc.getDependencies()[0];
+ assertEquals("(id=${source.id})", dependency.getFilter());
+
+ // Register a new source.
+ MyContextSource source2 = new MyContextSource();
+ source2.set("source.id", 2);
+ registration2 = context.registerService(ContextSource.class.getName(),
+ source2, null);
+ assertTrue(instance.getState() == ComponentInstance.VALID);
+
+ // This new source disappear
+ registration2.unregister();
+ registration2 = null;
+
+ assertTrue(instance.getState() == ComponentInstance.INVALID);
+
+ source.set("source.id", 1);
+ assertTrue(instance.getState() == ComponentInstance.VALID);
+ }
+
+ private class MyContextSource implements ContextSource {
+
+ List<ContextListener> listeners = new ArrayList<ContextListener>();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+
+ void set(String key, Object value) {
+ if (value == null) {
+ properties.remove(key);
+ } else {
+ properties.put(key, value);
+ }
+ for (ContextListener cl : listeners) {
+ cl.update(this, key, value);
+ }
+ }
+
+ @Override
+ public Object getProperty(String property) {
+ return properties.get(property);
+ }
+
+ @Override
+ public Dictionary getContext() {
+ return properties;
+ }
+
+ @Override
+ public void registerContextListener(ContextListener listener, String[] properties) {
+ listeners.add(listener);
+ }
+
+ @Override
+ public void unregisterContextListener(ContextListener listener) {
+ listeners.remove(listener);
+ }
+ }
+
+
+}
diff --git a/ipojo/runtime/core/pom.xml b/ipojo/runtime/core/pom.xml
index cd65431..b744c43 100644
--- a/ipojo/runtime/core/pom.xml
+++ b/ipojo/runtime/core/pom.xml
@@ -81,6 +81,12 @@
<version>1.9.5</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.easytesting</groupId>
+ <artifactId>fest-assert</artifactId>
+ <version>1.4</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
index cdce659..a8c650d 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
@@ -295,7 +295,7 @@
* This method does not need a synchronized block as the handler set is constant.
* @return the handler array of plugged handlers.
*/
- public Handler[] getRegistredHandlers() {
+ public Handler[] getRegisteredHandlers() {
Handler[] handler = new Handler[m_handlers.length];
for (int i = 0; i < m_handlers.length; i++) {
handler[i] = m_handlers[i].getHandler();
@@ -388,7 +388,7 @@
}
// Plug handler descriptions
- Handler[] handlers = getRegistredHandlers();
+ Handler[] handlers = getRegisteredHandlers();
for (int i = 0; i < handlers.length; i++) {
m_description.addHandler(handlers[i].getDescription());
}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
index a48809b..e7f4a66 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
@@ -20,10 +20,7 @@
import java.util.*;
-import org.apache.felix.ipojo.ConfigurationException;
-import org.apache.felix.ipojo.IPojoContext;
-import org.apache.felix.ipojo.PolicyServiceContext;
-import org.apache.felix.ipojo.PrimitiveHandler;
+import org.apache.felix.ipojo.*;
import org.apache.felix.ipojo.architecture.HandlerDescription;
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.parser.FieldMetadata;
@@ -31,6 +28,8 @@
import org.apache.felix.ipojo.parser.PojoMetadata;
import org.apache.felix.ipojo.util.DependencyModel;
import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.apache.felix.ipojo.util.InstanceConfigurationSource;
+import org.apache.felix.ipojo.util.SystemPropertiesSource;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
@@ -107,6 +106,11 @@
private DependencyHandlerDescription m_description;
/**
+ * The instance configuration context source, updated once reconfiguration.
+ */
+ private InstanceConfigurationSource m_instanceConfigurationSource;
+
+ /**
* Get the list of managed dependency.
* @return the dependency list
*/
@@ -468,6 +472,30 @@
}
m_description = new DependencyHandlerDescription(this, getDependencies()); // Initialize the description.
+
+ manageContextSources(configuration);
+ }
+
+ /**
+ * Add internal context source to all dependencies.
+ * @param configuration the instance configuration to creates the instance configuration source
+ */
+ private void manageContextSources(Dictionary<String, Object> configuration) {
+ m_instanceConfigurationSource = new InstanceConfigurationSource(configuration);
+ SystemPropertiesSource systemPropertiesSource = new SystemPropertiesSource();
+
+ for (Dependency dependency : m_dependencies) {
+ if (dependency.getFilter() != null) {
+ dependency.getContextSourceManager().addContextSource(m_instanceConfigurationSource);
+ dependency.getContextSourceManager().addContextSource(systemPropertiesSource);
+
+ for (Handler handler : getInstanceManager().getRegisteredHandlers()) {
+ if (handler instanceof ContextSource) {
+ dependency.getContextSourceManager().addContextSource((ContextSource) handler);
+ }
+ }
+ }
+ }
}
private String computeFilter(Element dependencyElement, Dictionary filtersConfiguration, Dictionary fromConfiguration, boolean aggregate, String identity) {
@@ -684,4 +712,12 @@
return m_description;
}
+ /**
+ * The instance is reconfigured.
+ * @param configuration the new instance configuration.
+ */
+ @Override
+ public void reconfigure(Dictionary configuration) {
+ m_instanceConfigurationSource.reconfigure(configuration);
+ }
}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ContextSourceManager.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ContextSourceManager.java
new file mode 100644
index 0000000..3b61c96
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ContextSourceManager.java
@@ -0,0 +1,333 @@
+/*
+ * 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.util;
+
+import org.apache.felix.ipojo.ContextListener;
+import org.apache.felix.ipojo.ContextSource;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.List;
+
+/**
+ * This class manages context-source management.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ContextSourceManager implements ContextListener {
+
+ /**
+ * The variable prefix
+ */
+ public static final String VARIABLE_START = "${";
+
+ /**
+ * The variable end
+ */
+ public static final char VARIABLE_END = '}';
+
+ /**
+ * List of characters forbidden in variable names
+ */
+ public static final List<Character> FORBIDDEN_CHARACTERS = Arrays.asList(
+ '(', ')', '&', '|', '!', '=', '*', ' ');
+
+ /**
+ * Managed dependency.
+ */
+ private DependencyModel m_dependency;
+ /**
+ * List of monitored context sources.
+ */
+ private List<ContextSource> m_sources = new ArrayList<ContextSource>(1);
+ /**
+ * Variables contained in the original filter.
+ */
+ private List<String> m_variables;
+ /**
+ * Original filter (containing variables).
+ */
+ private String m_filter;
+ /**
+ * Bundle context.
+ */
+ private BundleContext m_context;
+ /**
+ * The Context-Source service tracker.
+ */
+ private Tracker m_tracker;
+
+ /**
+ * Creates the context source manager.
+ *
+ * @param dependency the dependency model on which this manager is plugged.
+ */
+ public ContextSourceManager(DependencyModel dependency) throws InvalidSyntaxException {
+ m_filter = dependency.getFilter();
+ m_variables = extractVariablesFromFilter(m_filter);
+ m_dependency = dependency;
+ m_context = dependency.getComponentInstance().getContext();
+ }
+
+ /**
+ * This method substitutes ${var} substring by values stored in a map.
+ *
+ * @param str : string with variables
+ * @param values : dictionary containing the variable name and the value.
+ * @return resulted string
+ */
+ public static String substitute(String str, Dictionary values) {
+ int len = str.length();
+ StringBuilder builder = new StringBuilder(len);
+
+ int prev = 0;
+ int start = str.indexOf("${");
+ int end = str.indexOf('}', start);
+ while (start != -1 && end != -1) {
+ String key = str.substring(start + 2, end);
+ Object value = values.get(key);
+ if (value == null) {
+ builder.append(str.substring(prev, end + 1));
+ } else {
+ builder.append(str.substring(prev, start));
+ builder.append(value);
+ }
+ prev = end + 1;
+ if (prev >= str.length()) {
+ break;
+ }
+
+ start = str.indexOf("${", prev);
+ if (start != -1) {
+ end = str.indexOf('}', start);
+ }
+ }
+
+ builder.append(str.substring(prev));
+
+ return builder.toString();
+ }
+
+ /**
+ * Extracts the variables (${name}) from the given filter.
+ *
+ * @param filter : string form of the filter.
+ * @return the list of found properties.
+ * @throws InvalidSyntaxException thrown when the variables are not consistent.
+ */
+ public static List<String> extractVariablesFromFilter(String filter) throws InvalidSyntaxException {
+ List<String> variables = new ArrayList<String>();
+ int prev;
+ int start = filter.indexOf(VARIABLE_START);
+ int end = filter.indexOf(VARIABLE_END, start);
+ while (start != -1) {
+ // Unfinished variable
+ if (end == -1) {
+ throw new InvalidSyntaxException("The filter contains an unfinished variable", filter);
+ }
+
+ String key = filter.substring(start + VARIABLE_START.length(), end);
+
+ // Error detection :
+ // Empty variable.
+ if (key.length() == 0) {
+ throw new InvalidSyntaxException("The filter variable '${}' is not a valid " +
+ "variable", filter);
+ }
+ // Variable with spaces.
+ Character forbidden = containsForbiddenCharacter(key);
+ if (forbidden != null) {
+ throw new InvalidSyntaxException("The filter variable '${" + key + "}' contains a forbidden " +
+ "character : '" + forbidden + "'", filter);
+ }
+
+
+ variables.add(key);
+ prev = end + 1;
+ if (prev >= filter.length()) {
+ break;
+ }
+
+ start = filter.indexOf(VARIABLE_START, prev);
+ if (start != -1) {
+ end = filter.indexOf(VARIABLE_END, start);
+ }
+ }
+
+ return variables;
+ }
+
+ private static Character containsForbiddenCharacter(String key) {
+ for (Character character : FORBIDDEN_CHARACTERS) {
+ if (key.indexOf(character) != -1) {
+ return character;
+ }
+ }
+ // Safe key.
+ return null;
+ }
+
+ /**
+ * Start the context management.
+ */
+ public void start() {
+ if (m_tracker == null) {
+ m_tracker = new Tracker(m_context, ContextSource.class.getName(), new SourceTracker());
+ }
+ m_tracker.open();
+ computeFilter();
+ }
+
+ /**
+ * Stop the context management.
+ */
+ public void stop() {
+ if (m_tracker != null) {
+ m_tracker.close();
+ m_tracker = null;
+ }
+ // Reinitialize the filter.
+ setFilter(m_filter);
+ for (ContextSource source : m_sources) {
+ source.unregisterContextListener(this);
+ }
+ m_sources.clear();
+ }
+
+ /**
+ * Set the filter of the managed dependency.
+ *
+ * @param filter : the new filter to apply
+ */
+ private void setFilter(String filter) {
+ if (!filter.equals(m_dependency.getFilter())) {
+ // Reconfigure
+ try {
+ m_dependency.setFilter(m_context.createFilter(filter));
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalStateException("A context filter is invalid : " + filter, e);
+ }
+ }
+ }
+
+ /**
+ * Compute the new filter.
+ */
+ private void computeFilter() {
+ String fil = m_filter; // Gets a copy.
+ synchronized (this) {
+ for (ContextSource m_source : m_sources) {
+ Dictionary props = m_source.getContext();
+ fil = substitute(fil, props); //NOPMD
+ }
+ }
+ if (!fil.equals(m_dependency.getFilter())) {
+ setFilter(fil);
+ }
+ }
+
+ /**
+ * A context source has modified a monitored property.
+ *
+ * @param source : source
+ * @param property : modified property
+ * @param value : new value.
+ * @see org.apache.felix.ipojo.ContextListener#update(org.apache.felix.ipojo.ContextSource, String, Object)
+ */
+ public synchronized void update(ContextSource source, String property, Object value) {
+ computeFilter();
+ }
+
+ /**
+ * A context source appears.
+ *
+ * @param source : new context source.
+ */
+ public void addContextSource(ContextSource source) {
+ m_sources.add(source);
+ computeFilter();
+ source.registerContextListener(this, m_variables.toArray(new String[m_variables.size()]));
+ }
+
+ /**
+ * A context source disappears.
+ *
+ * @param source : leaving context source.
+ */
+ public void removeContextSource(ContextSource source) {
+ source.unregisterContextListener(this);
+ m_sources.remove(source);
+ computeFilter();
+ }
+
+ private class SourceTracker implements TrackerCustomizer {
+ /**
+ * A new context-source was added.
+ * This method inject the context-source object in the source manager.
+ *
+ * @param reference : service reference.
+ * @see TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public void addedService(ServiceReference reference) {
+ System.out.println("Source added");
+ addContextSource((ContextSource) m_tracker.getService(reference));
+ }
+
+ /**
+ * A new context-source is adding in the tracker..
+ *
+ * @param reference : service reference
+ * @return true.
+ * @see TrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
+ */
+ public boolean addingService(ServiceReference reference) {
+ return true;
+ }
+
+ /**
+ * A used context-source is modified.
+ *
+ * @param reference : service reference.
+ * @param service : service object.
+ * @see TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, Object)
+ */
+ public void modifiedService(ServiceReference reference, Object service) {
+ // Nothing to do.
+ }
+
+ /**
+ * A used context-source disappears.
+ * This method notify the Source Manager in order to manage this departure.
+ *
+ * @param reference : service reference.
+ * @param service : service object.
+ * @see TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, Object)
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ removeContextSource((ContextSource) service);
+ }
+
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java
index e1c3d93..777bca5 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java
@@ -34,6 +34,7 @@
import org.apache.felix.ipojo.metadata.Element;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
/**
@@ -88,6 +89,11 @@
public static final int DYNAMIC_PRIORITY_BINDING_POLICY = 2;
/**
+ * The manager handling context sources.
+ */
+ private final ContextSourceManager m_contextSourceManager;
+
+ /**
* Does the dependency bind several providers ?
*/
private boolean m_aggregate;
@@ -106,7 +112,7 @@
/**
* The comparator to sort service references.
*/
- private Comparator m_comparator;
+ private Comparator<ServiceReference> m_comparator;
/**
* The LDAP filter object selecting service references
@@ -147,7 +153,7 @@
* subset of tracked references. This set is computed according
* to the filter and the {@link DependencyModel#match(ServiceReference)} method.
*/
- private final List m_matchingRefs = new ArrayList();
+ private final List<ServiceReference> m_matchingRefs = new ArrayList<ServiceReference>();
/**
* The instance requiring the service.
@@ -159,7 +165,7 @@
* This map stores service object, and so is able to handle
* iPOJO custom policies.
*/
- private Map/*<ServiceReference, Object>*/ m_serviceObjects = new HashMap();
+ private Map<ServiceReference, Object> m_serviceObjects = new HashMap<ServiceReference, Object>();
/**
* Creates a DependencyModel.
@@ -177,7 +183,8 @@
* @param ci instance managing the dependency
* state changes.
*/
- public DependencyModel(Class specification, boolean aggregate, boolean optional, Filter filter, Comparator comparator, int policy,
+ public DependencyModel(Class specification, boolean aggregate, boolean optional, Filter filter,
+ Comparator<ServiceReference> comparator, int policy,
BundleContext context, DependencyStateListener listener, ComponentInstance ci) {
m_specification = specification;
m_aggregate = aggregate;
@@ -193,6 +200,16 @@
m_state = UNRESOLVED;
m_listener = listener;
m_instance = ci;
+
+ if (m_filter != null) {
+ try {
+ m_contextSourceManager = new ContextSourceManager(this);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ } else {
+ m_contextSourceManager = null;
+ }
}
/**
@@ -204,6 +221,11 @@
m_state = UNRESOLVED;
m_tracker = new Tracker(m_context, m_specification.getName(), this);
m_tracker.open();
+
+ if (m_contextSourceManager != null) {
+ m_contextSourceManager.start();
+ }
+
computeDependencyState();
}
@@ -220,6 +242,9 @@
m_matchingRefs.clear();
ungetAllServices();
m_state = UNRESOLVED;
+ if (m_contextSourceManager != null) {
+ m_contextSourceManager.stop();
+ }
}
/**
@@ -227,11 +252,8 @@
* This also clears the service object map.
*/
private void ungetAllServices() {
- Set entries = m_serviceObjects.entrySet();
- Iterator it = entries.iterator();
- while (it.hasNext()) {
- Map.Entry entry = (Map.Entry) it.next();
- ServiceReference ref = (ServiceReference) entry.getKey();
+ for (Map.Entry<ServiceReference, Object> entry : m_serviceObjects.entrySet()) {
+ ServiceReference ref = entry.getKey();
Object svc = entry.getValue();
if (m_tracker != null) {
m_tracker.ungetService(ref);
@@ -403,7 +425,7 @@
// We are sure that we have at least two references, so if the highest ranked references (first one) is the new received
// references,
// we have to unbind the used one and to bind the the new one.
- onServiceDeparture((ServiceReference) m_matchingRefs.get(1));
+ onServiceDeparture(m_matchingRefs.get(1));
onServiceArrival(ref);
}
}
@@ -440,7 +462,7 @@
m_state = BROKEN;
invalidate(); // This will invalidate the instance.
// Reinitialize the dependency tracking
- ComponentInstance instance = null;
+ ComponentInstance instance;
synchronized (this) {
instance = m_instance;
}
@@ -515,7 +537,7 @@
if (m_matchingRefs.isEmpty()) {
return null;
} else {
- return (ServiceReference) m_matchingRefs.get(0);
+ return m_matchingRefs.get(0);
}
}
}
@@ -538,21 +560,21 @@
* If no service references, returns <code>null</code>
* @return the list of used reference (according to the service tracker).
*/
- public List getUsedServiceReferences() {
+ public List<ServiceReference> getUsedServiceReferences() {
synchronized (this) {
// The list must confront actual matching services with already get services from the tracker.
int size = m_matchingRefs.size();
- List usedByTracker = null;
+ List<ServiceReference> usedByTracker = null;
if (m_tracker != null) {
usedByTracker = m_tracker.getUsedServiceReferences();
}
if (size == 0 || usedByTracker == null) { return null; }
- List list = new ArrayList(1);
- for (int i = 0; i < size; i++) {
- if (usedByTracker.contains(m_matchingRefs.get(i))) {
- list.add(m_matchingRefs.get(i)); // Add the service in the list.
+ List<ServiceReference> list = new ArrayList<ServiceReference>(1);
+ for (ServiceReference ref : m_matchingRefs) {
+ if (usedByTracker.contains(ref)) {
+ list.add(ref); // Add the service in the list.
if (!isAggregate()) { // IF we are not multiple, return the list when the first element is found.
return list;
}
@@ -564,6 +586,13 @@
}
/**
+ * @return the component instance on which this dependency is plugged.
+ */
+ public ComponentInstance getComponentInstance() {
+ return m_instance;
+ }
+
+ /**
* Gets the number of actual matching references.
* @return the number of matching references
*/
@@ -606,7 +635,7 @@
Collections.sort(m_matchingRefs, m_comparator);
if (indexBefore != m_matchingRefs.indexOf(ref) && ! m_aggregate) {
// The order has changed during the sort.
- onServiceDeparture((ServiceReference) m_matchingRefs.get(1));
+ onServiceDeparture(m_matchingRefs.get(1));
onServiceArrival(ref);
}
@@ -679,37 +708,34 @@
public void setFilter(Filter filter) { //NOPMD
m_filter = filter;
if (m_tracker != null) { // Tracking started ...
- List toRemove = new ArrayList();
- List toAdd = new ArrayList();
+ List<ServiceReference> toRemove = new ArrayList<ServiceReference>();
+ List<ServiceReference> toAdd = new ArrayList<ServiceReference>();
ServiceReference usedRef = null;
synchronized (this) {
// Store the used service references.
if (!m_aggregate && !m_matchingRefs.isEmpty()) {
- usedRef = (ServiceReference) m_matchingRefs.get(0);
+ usedRef = m_matchingRefs.get(0);
}
// Get actually all tracked references.
ServiceReference[] refs = m_tracker.getServiceReferences();
if (refs == null) {
- for (int j = 0; j < m_matchingRefs.size(); j++) {
- // All references need to be removed.
- toRemove.add(m_matchingRefs.get(j));
- }
+ // All references need to be removed.
+ toRemove.addAll(m_matchingRefs);
// No more matching dependency. Clear the matching reference set.
m_matchingRefs.clear();
} else {
// Compute matching services.
- List matching = new ArrayList();
- for (int i = 0; i < refs.length; i++) {
- if (matchAgainstFilter(refs[i]) && match(refs[i])) {
- matching.add(refs[i]);
+ List<ServiceReference> matching = new ArrayList<ServiceReference>();
+ for (ServiceReference ref : refs) {
+ if (matchAgainstFilter(ref) && match(ref)) {
+ matching.add(ref);
}
}
// Now compare with used services.
- for (int j = 0; j < m_matchingRefs.size(); j++) {
- ServiceReference ref = (ServiceReference) m_matchingRefs.get(j);
+ for (ServiceReference ref : m_matchingRefs) {
// Check if the reference is inside the matching list:
if (!matching.contains(ref)) {
// The reference should be removed
@@ -722,10 +748,10 @@
// Then, add new matching services.
- for (int k = 0; k < matching.size(); k++) {
- if (!m_matchingRefs.contains(matching.get(k))) {
- m_matchingRefs.add(matching.get(k));
- toAdd.add(matching.get(k));
+ for (ServiceReference ref : matching) {
+ if (!m_matchingRefs.contains(ref)) {
+ m_matchingRefs.add(ref);
+ toAdd.add(ref);
}
}
@@ -744,10 +770,10 @@
ServiceReference[] rem = null;
ServiceReference[] add = null;
if (!toAdd.isEmpty()) {
- add = (ServiceReference[]) toAdd.toArray(new ServiceReference[toAdd.size()]);
+ add = toAdd.toArray(new ServiceReference[toAdd.size()]);
}
if (!toRemove.isEmpty()) {
- rem = (ServiceReference[]) toRemove.toArray(new ServiceReference[toRemove.size()]);
+ rem = toRemove.toArray(new ServiceReference[toRemove.size()]);
}
if (rem != null || add != null) { // Notify the change only when a change is made on the matching reference list.
onDependencyReconfiguration(rem, add);
@@ -759,7 +785,7 @@
synchronized (m_matchingRefs) {
size = m_matchingRefs.size();
if (size > 0) {
- newRef = (ServiceReference) m_matchingRefs.get(0);
+ newRef = m_matchingRefs.get(0);
}
}
// Non aggregate case.
@@ -818,7 +844,7 @@
if (m_state == RESOLVED) {
for (int i = 1; i < m_matchingRefs.size(); i++) { // The loop begin at 1, as the 0 is already injected.
- onServiceArrival((ServiceReference) m_matchingRefs.get(i));
+ onServiceArrival(m_matchingRefs.get(i));
}
}
} else if (m_aggregate && !isAggregate) {
@@ -826,7 +852,7 @@
// We become non-aggregate.
if (m_state == RESOLVED) {
for (int i = 1; i < m_matchingRefs.size(); i++) { // The loop begin at 1, as the 0 stills injected.
- onServiceDeparture((ServiceReference) m_matchingRefs.get(i));
+ onServiceDeparture(m_matchingRefs.get(i));
}
}
}
@@ -871,9 +897,9 @@
// TODO supporting dynamic policy change.
}
- public void setComparator(Comparator cmp) {
+ public void setComparator(Comparator<ServiceReference> cmp) {
m_comparator = cmp;
- // NOTE: the array will be sorted at the next get.
+ // NOTE: the array will be sorted on the next 'get'.
}
/**
@@ -988,7 +1014,7 @@
* @throws ConfigurationException if the class cannot be loaded correctly.
*/
public static Class loadSpecification(String specification, BundleContext context) throws ConfigurationException {
- Class spec = null;
+ Class spec;
try {
spec = context.getBundle().loadClass(specification);
} catch (ClassNotFoundException e) {
@@ -1019,4 +1045,7 @@
}
}
+ public ContextSourceManager getContextSourceManager() {
+ return m_contextSourceManager;
+ }
}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/InstanceConfigurationSource.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/InstanceConfigurationSource.java
new file mode 100644
index 0000000..4e62572
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/InstanceConfigurationSource.java
@@ -0,0 +1,115 @@
+/*
+ * 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.util;
+
+import org.apache.felix.ipojo.ContextListener;
+import org.apache.felix.ipojo.ContextSource;
+
+import java.util.*;
+
+/**
+ * A context source giving access to system properties.
+ */
+public class InstanceConfigurationSource implements ContextSource {
+
+ /**
+ * The instance configuration.
+ */
+ private Dictionary<String,Object> m_configuration;
+
+ /**
+ * Listener list.
+ */
+ private List<ContextListener> m_listeners = new ArrayList<ContextListener>();
+
+ public InstanceConfigurationSource(Dictionary<String, Object> configuration) {
+ m_configuration = configuration;
+ }
+
+ /**
+ * The instance is reconfigured.
+ * Changes are computed by comparing the old and new configuration.
+ * @param newConfiguration the new instance configuration
+ */
+ public void reconfigure(Dictionary<String, Object> newConfiguration) {
+ // Copy the current configuration and update the field.
+ Hashtable<String, Object> configuration = new Hashtable<String, Object>();
+ Enumeration<String> enumeration = m_configuration.keys();
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement();
+ configuration.put(key, m_configuration.get(key));
+ }
+ // We now have a copy of the current configuration.
+ // We must set the field to the new configuration, as updates are going to be fired.
+ // so we must have reflected the updates already.
+ m_configuration = newConfiguration;
+
+ // We use the same loop to find lost and updated properties.
+ enumeration = configuration.keys();
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement();
+ final Object newValue = newConfiguration.get(key);
+ final Object oldValue = configuration.get(key);
+ if (newValue == null) {
+ // If we don't have the property anymore, notify the departure.
+ fireUpdate(key, null);
+ } else {
+ // The new configuration still has a value, is it the same ?
+ if (! newValue.equals(oldValue)) {
+ fireUpdate(key, newValue);
+ }
+ }
+ }
+
+ // Do we have new properties ?
+ enumeration = newConfiguration.keys();
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement();
+ if (configuration.get(key) == null) {
+ Object newValue = newConfiguration.get(key);
+ fireUpdate(key, newValue);
+ }
+ }
+ }
+
+ private void fireUpdate(String key, Object newValue) {
+ for (ContextListener listener : m_listeners) {
+ listener.update(this, key, newValue);
+ }
+ }
+
+ public Object getProperty(String property) {
+ return m_configuration.get(property);
+ }
+
+ public Dictionary getContext() {
+ return m_configuration;
+ }
+
+ public void registerContextListener(ContextListener listener, String[] properties) {
+ if (! m_listeners.contains(listener)) {
+ m_listeners.add(listener);
+ }
+ }
+
+ public void unregisterContextListener(ContextListener listener) {
+ m_listeners.remove(listener);
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ServiceReferenceRankingComparator.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ServiceReferenceRankingComparator.java
index 649116c..112199c 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ServiceReferenceRankingComparator.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/ServiceReferenceRankingComparator.java
@@ -29,7 +29,7 @@
* This comparator follows OSGi Ranking policy.
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-public class ServiceReferenceRankingComparator implements Comparator, Serializable {
+public class ServiceReferenceRankingComparator implements Comparator<ServiceReference>, Serializable {
/**
* Id.
@@ -45,29 +45,29 @@
* (higher is term of ranking means a lower index)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
- public int compare(Object ref1, Object ref2) {
+ public int compare(ServiceReference ref1, ServiceReference ref2) {
if (ref1.equals(ref2)) { return 0; }
- if (ref1 instanceof ServiceReference && ref2 instanceof ServiceReference) {
- Object property1 = ((ServiceReference) ref1).getProperty(Constants.SERVICE_RANKING);
- Object property2 = ((ServiceReference) ref2).getProperty(Constants.SERVICE_RANKING);
+ if (ref2 != null) {
+ Object property1 = ref1.getProperty(Constants.SERVICE_RANKING);
+ Object property2 = ref2.getProperty(Constants.SERVICE_RANKING);
int rank1 = 0;
int rank2 = 0;
if (property1 instanceof Integer) {
- rank1 = ((Integer) property1).intValue();
+ rank1 = (Integer) property1;
}
if (property2 instanceof Integer) {
- rank2 = ((Integer) property2).intValue();
+ rank2 = (Integer) property2;
}
if (rank1 == rank2) {
// Check service.id
- Object sid1 = ((ServiceReference) ref1).getProperty(Constants.SERVICE_ID);
- Object sid2 = ((ServiceReference) ref2).getProperty(Constants.SERVICE_ID);
+ Object sid1 = ref1.getProperty(Constants.SERVICE_ID);
+ Object sid2 = ref2.getProperty(Constants.SERVICE_ID);
- long rankId1 = ((Long) sid1).longValue();
- long rankId2 = ((Long) sid2).longValue();
+ long rankId1 = (Long) sid1;
+ long rankId2 = (Long) sid2;
if (rankId1 == rankId2) {
return 0;
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/SystemPropertiesSource.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/SystemPropertiesSource.java
new file mode 100644
index 0000000..69f93c8
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/SystemPropertiesSource.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util;
+
+import org.apache.felix.ipojo.ContextListener;
+import org.apache.felix.ipojo.ContextSource;
+
+import java.util.Dictionary;
+
+/**
+ * A context source giving access to system properties.
+ */
+public class SystemPropertiesSource implements ContextSource {
+
+ public Object getProperty(String property) {
+ return System.getProperty(property);
+ }
+
+ public Dictionary getContext() {
+ return System.getProperties();
+ }
+
+ public void registerContextListener(ContextListener listener, String[] properties) {
+ // Ignored, as this source is not dynamic, so won't send notifications
+ }
+
+ public void unregisterContextListener(ContextListener listener) {
+ // Ignored, as this source is not dynamic, so won't send notifications
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java
index 4080840..cc2437f 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java
@@ -239,8 +239,8 @@
/* In case the context was stopped. */
}
if (references != null) {
- for (int i = 0; i < references.length; i++) {
- outgoing.untrack(references[i]);
+ for (ServiceReference reference : references) {
+ outgoing.untrack(reference);
}
}
m_tracked = null;
@@ -350,7 +350,7 @@
* Gets the list of stored service reference.
* @return the list containing used service reference
*/
- public List/*<ServiceReference>*/getServiceReferencesList() {
+ public List<ServiceReference> getServiceReferencesList() {
Tracked tracked = this.m_tracked; // use local var since we are not synchronized
if (tracked == null) { // if Tracker is not open
return null;
@@ -358,11 +358,8 @@
synchronized (tracked) {
int length = tracked.size();
if (length == 0) { return null; }
- List references = new ArrayList(length);
- Iterator keys = tracked.keySet().iterator();
- for (int i = 0; i < length; i++) {
- references.add(keys.next());
- }
+ List<ServiceReference> references = new ArrayList<ServiceReference>(length);
+ references.addAll(tracked.keySet());
// The resulting array is sorted by ranking.
return references;
}
@@ -374,20 +371,16 @@
* called getService on this reference.
* @return the list of used references.
*/
- public List/*<ServiceReference>*/getUsedServiceReferences() {
+ public List<ServiceReference> getUsedServiceReferences() {
Tracked tracked = this.m_tracked; // use local var since we are not synchronized
if (tracked == null || tracked.size() == 0) { // if Tracker is not open or empty
return null;
}
synchronized (tracked) {
- int length = tracked.size();
- List references = new ArrayList();
- Iterator keys = tracked.entrySet().iterator();
- for (int i = 0; i < length; i++) {
- Map.Entry entry = (Map.Entry) keys.next();
- Object key = entry.getKey();
+ List<ServiceReference> references = new ArrayList<ServiceReference>();
+ for (Map.Entry<ServiceReference, Object> entry : tracked.entrySet()) {
if (entry.getValue() != null) {
- references.add(key);
+ references.add(entry.getKey());
}
}
return references;
@@ -433,9 +426,8 @@
if (tracked == null) { /* if Tracker is not open */
return null;
}
- Object object = null;
synchronized (tracked) {
- object = tracked.get(reference);
+ Object object = tracked.get(reference);
if (object == null) {
if (tracked.containsKey(reference)) { // Not already get but already tracked.
object = m_context.getService(reference);
@@ -479,7 +471,7 @@
}
synchronized (tracked) {
ServiceReference[] references = getServiceReferences();
- int length = 0;
+ int length;
if (references == null) {
return null;
} else {
@@ -537,18 +529,14 @@
* class is the ServiceListener object for the tracker. This class is used to synchronize access to the tracked services. This is not a public class. It is only for use by the implementation of the Tracker
* class.
*/
- class Tracked extends HashMap implements ServiceListener {
- /**
- * UID.
- */
- static final long serialVersionUID = -7420065199791006079L;
+ class Tracked extends HashMap<ServiceReference, Object> implements ServiceListener {
/**
* The list of ServiceReferences in the process of being added. This is used to deal with nesting of ServiceEvents. Since ServiceEvents are synchronously delivered, ServiceEvents can be nested. For example, when processing the adding of a service
* and the customizer causes the service to be unregistered, notification to the nested call to untrack that the service was unregistered can be made to the track method. Since the ArrayList implementation is not synchronized, all access to
* this list must be protected by the same synchronized object for thread safety.
*/
- private List m_adding;
+ private List<ServiceReference> m_adding;
/**
* <code>true</code> if the tracked object is closed. This field is volatile because it is set by one thread and read by another.
@@ -560,7 +548,7 @@
* "announced" by ServiceEvents and therefore the ServiceEvent for unregistration could be delivered before we track the service. A service must not be in both the initial and adding lists at the same time. A service must be moved from the
* initial list to the adding list "atomically" before we begin tracking it. Since the LinkedList implementation is not synchronized, all access to this list must be protected by the same synchronized object for thread safety.
*/
- private List m_initial;
+ private List<ServiceReference> m_initial;
/**
* Tracked constructor.
@@ -568,8 +556,8 @@
protected Tracked() {
super();
m_closed = false;
- m_adding = new ArrayList(6);
- m_initial = new LinkedList();
+ m_adding = new ArrayList<ServiceReference>(6);
+ m_initial = new LinkedList<ServiceReference>();
}
/**
@@ -579,9 +567,7 @@
protected void setInitialServices(ServiceReference[] references) {
if (references == null) { return; }
int size = references.length;
- for (int i = 0; i < size; i++) {
- m_initial.add(references[i]);
- }
+ m_initial.addAll(Arrays.asList(references).subList(0, size));
}
/**
@@ -591,7 +577,7 @@
while (true) {
ServiceReference reference;
synchronized (this) {
- if (m_initial.isEmpty()) { // if there are no more inital services
+ if (m_initial.isEmpty()) { // if there are no more initial services
return; // we are done
}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/ContextSourceManagerTest.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/ContextSourceManagerTest.java
new file mode 100644
index 0000000..3f6a69d
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/ContextSourceManagerTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.util;
+
+import org.junit.Test;
+import org.osgi.framework.InvalidSyntaxException;
+
+import java.util.Hashtable;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * Checks the behavior of the context source manager.
+ */
+public class ContextSourceManagerTest {
+
+ @Test
+ public void testSubstitute() throws Exception {
+ String filter_with_one_var = "(id=${id})";
+ String filter_with_two_vars = "(&(id=${id})(count=${system.count}))";
+
+ Hashtable<String, Object> context = new Hashtable<String, Object>();
+ context.put("id", "my.id");
+ context.put("system.count", 1);
+
+ String filter = ContextSourceManager.substitute(filter_with_one_var, context);
+ assertThat(filter).isEqualTo("(id=my.id)");
+
+ filter = ContextSourceManager.substitute(filter_with_two_vars, context);
+ assertThat(filter).isEqualTo("(&(id=my.id)(count=1))");
+ }
+
+ @Test
+ public void testEmptySubstitution() {
+ String filter_with_two_vars = "(&(id=${id})(count=${system.count}))";
+ Hashtable<String, Object> context = new Hashtable<String, Object>();
+ String filter = ContextSourceManager.substitute(filter_with_two_vars, context);
+ assertThat(filter).isEqualTo(filter_with_two_vars);
+ }
+
+ @Test
+ public void testTwoStepsSubstitution() {
+ String filter_with_vars_equality = "(${attr}=${var})";
+
+ Hashtable<String, Object> context1 = new Hashtable<String, Object>();
+ context1.put("var", "value");
+
+ Hashtable<String, Object> context2 = new Hashtable<String, Object>();
+ context2.put("attr", "prop");
+
+ String filter = ContextSourceManager.substitute(filter_with_vars_equality, context1);
+ assertThat(filter).isEqualTo("(${attr}=value)");
+
+ filter = ContextSourceManager.substitute(filter, context2);
+ assertThat(filter).isEqualTo("(prop=value)");
+ }
+
+ @Test
+ public void testExtractVariablesFromFilter() throws Exception {
+ String filter_with_one_var = "(id=${id})";
+ String filter_with_two_vars = "(&(id=${id})(count=${system.count}))";
+ String filter_with_vars_equality = "(${attr}=${var})";
+
+ assertThat(ContextSourceManager.extractVariablesFromFilter(filter_with_one_var)).containsExactly("id");
+ assertThat(ContextSourceManager.extractVariablesFromFilter(filter_with_two_vars)).contains("id",
+ "system.count");
+ assertThat(ContextSourceManager.extractVariablesFromFilter(filter_with_vars_equality)).contains("attr",
+ "var");
+ }
+
+ @Test
+ public void testBrokenFilters() throws Exception {
+ String broken = "(id=${i)";
+ try {
+ ContextSourceManager.extractVariablesFromFilter(broken);
+ fail("Unfinished variable undetected");
+ } catch (InvalidSyntaxException e) {
+ // OK
+ }
+
+ String broken2 = "(id=${})";
+ try {
+ ContextSourceManager.extractVariablesFromFilter(broken2);
+ fail("Empty variable undetected");
+ } catch (InvalidSyntaxException e) {
+ // OK
+ }
+
+ String broken3 = "(id=${I contain a space})";
+ try {
+ ContextSourceManager.extractVariablesFromFilter(broken3);
+ fail("Spaced variable undetected");
+ } catch (InvalidSyntaxException e) {
+ // OK
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/InstanceConfigurationSourceTest.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/InstanceConfigurationSourceTest.java
new file mode 100644
index 0000000..ab5f436
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/InstanceConfigurationSourceTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.util;
+
+import org.apache.felix.ipojo.ContextListener;
+import org.apache.felix.ipojo.ContextSource;
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test the instance configuration context source.
+ */
+public class InstanceConfigurationSourceTest {
+
+ @Test
+ public void getProperties() {
+ Dictionary<String, Object> configuration = new Hashtable<String, Object>();
+ configuration.put("prop1", "value");
+ configuration.put("prop2", 1);
+ InstanceConfigurationSource cs = new InstanceConfigurationSource(configuration);
+ assertEquals(cs.getContext().get("prop1"), "value");
+ assertEquals(cs.getContext().get("prop2"), 1);
+ }
+
+ @Test
+ public void getProperty() {
+ Dictionary<String, Object> configuration = new Hashtable<String, Object>();
+ configuration.put("prop1", "value");
+ configuration.put("prop2", 1);
+ InstanceConfigurationSource cs = new InstanceConfigurationSource(configuration);
+ assertEquals(cs.getProperty("prop1"), "value");
+ assertEquals(cs.getProperty("prop2"), 1);
+ }
+
+ @Test
+ public void getMissingProperty() {
+ Dictionary<String, Object> configuration = new Hashtable<String, Object>();
+ configuration.put("prop1", "value");
+ configuration.put("prop2", 1);
+ InstanceConfigurationSource cs = new InstanceConfigurationSource(configuration);
+ assertNull(cs.getProperty("__property"));
+ }
+
+ @Test
+ public void emptyConfiguration() {
+ Dictionary<String, Object> configuration = new Hashtable<String, Object>();
+ InstanceConfigurationSource cs = new InstanceConfigurationSource(configuration);
+ assertNull(cs.getProperty("__property"));
+ }
+
+ @Test
+ public void addPropertyOnConfiguration() {
+ Dictionary<String, Object> configuration = new Hashtable<String, Object>();
+ configuration.put("prop1", "value");
+ InstanceConfigurationSource cs = new InstanceConfigurationSource(configuration);
+
+ SpyContextListener spy = new SpyContextListener();
+ cs.registerContextListener(spy, null);
+
+ Dictionary<String, Object> newConfiguration = new Hashtable<String, Object>();
+ newConfiguration.put("prop1", "value");
+ newConfiguration.put("prop2", "value2");
+
+ cs.reconfigure(newConfiguration);
+
+ assertEquals(spy.variables.size(), 1);
+ assertEquals(spy.variables.get("prop2"), "value2");
+ }
+
+ @Test
+ public void removePropertyOnConfiguration() {
+ Dictionary<String, Object> configuration = new Hashtable<String, Object>();
+ configuration.put("prop1", "value");
+ configuration.put("prop2", "value2");
+ InstanceConfigurationSource cs = new InstanceConfigurationSource(configuration);
+
+ SpyContextListener spy = new SpyContextListener();
+ cs.registerContextListener(spy, null);
+
+ Dictionary<String, Object> newConfiguration = new Hashtable<String, Object>();
+ newConfiguration.put("prop1", "value");
+
+ cs.reconfigure(newConfiguration);
+
+ assertEquals(spy.variables.size(), 1);
+ assertTrue(spy.variables.containsKey("prop2"));
+ assertEquals(spy.variables.get("prop2"), null);
+ }
+
+ @Test
+ public void updatePropertyOnConfiguration() {
+ Dictionary<String, Object> configuration = new Hashtable<String, Object>();
+ configuration.put("prop1", "value");
+ configuration.put("prop2", "value2");
+ InstanceConfigurationSource cs = new InstanceConfigurationSource(configuration);
+
+ SpyContextListener spy = new SpyContextListener();
+ cs.registerContextListener(spy, null);
+
+ Dictionary<String, Object> newConfiguration = new Hashtable<String, Object>();
+ newConfiguration.put("prop1", "new value");
+ newConfiguration.put("prop2", "value2");
+
+ cs.reconfigure(newConfiguration);
+
+ assertEquals(spy.variables.size(), 1);
+ assertEquals(spy.variables.get("prop1"), "new value");
+ }
+
+ @Test
+ public void add_remove_update() {
+ Dictionary<String, Object> configuration = new Hashtable<String, Object>();
+ configuration.put("prop1", "value");
+ configuration.put("prop2", "value2");
+ InstanceConfigurationSource cs = new InstanceConfigurationSource(configuration);
+
+ SpyContextListener spy = new SpyContextListener();
+ cs.registerContextListener(spy, null);
+
+ Dictionary<String, Object> newConfiguration = new Hashtable<String, Object>();
+ newConfiguration.put("prop1", "new value");
+ newConfiguration.put("prop3", "value3");
+
+ cs.reconfigure(newConfiguration);
+
+ assertEquals(spy.variables.size(), 3);
+ assertEquals(spy.variables.get("prop1"), "new value");
+ assertEquals(spy.variables.get("prop3"), "value3");
+ assertEquals(spy.variables.get("prop2"), null);
+
+ }
+
+ private class SpyContextListener implements ContextListener {
+
+ Map<String, Object> variables = new HashMap<String, Object>();
+
+ public void update(ContextSource source, String property, Object value) {
+ variables.put(property, value);
+ }
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/SystemPropertiesSourceTest.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/SystemPropertiesSourceTest.java
new file mode 100644
index 0000000..1141b03
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/util/SystemPropertiesSourceTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.util;
+
+import org.junit.After;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Test the system property context source.
+ */
+public class SystemPropertiesSourceTest {
+
+ @After
+ public void tearDown() {
+ System.clearProperty("__property");
+ }
+
+ @Test
+ public void getProperties() {
+ System.setProperty("__property", "__value");
+ SystemPropertiesSource cs = new SystemPropertiesSource();
+ assertEquals(cs.getContext().get("__property"), "__value");
+ }
+
+ @Test
+ public void getProperty() {
+ System.setProperty("__property", "__value");
+ SystemPropertiesSource cs = new SystemPropertiesSource();
+ assertEquals(cs.getProperty("__property"), "__value");
+ }
+
+ @Test
+ public void getMissingProperty() {
+ SystemPropertiesSource cs = new SystemPropertiesSource();
+ assertNull(cs.getProperty("__property"));
+ }
+}