Fix FELIX-2746 Composite should support instance configuration
Composite configuration is now appended to component instance (<instance>) and service instance configuration (<service action="instantiate">).
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1053307 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java
index 7f686d9..133758e 100644
--- a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Dictionary;
+import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
@@ -48,17 +49,17 @@
* Internal context.
*/
private ServiceContext m_scope;
-
+
/**
* Available factories.
*/
private Factory[] m_factories;
-
+
/**
* Handler description.
*/
private InstanceHandlerDescription m_description;
-
+
/**
* This structure aims to manage a configuration. It stores all necessary
@@ -79,7 +80,7 @@
* Created instance.
*/
private ComponentInstance m_instance;
-
+
/**
* Desired Factory (can be the classname).
*/
@@ -87,7 +88,7 @@
/**
* Constructor.
- *
+ *
* @param conf : the configuration to create.
*/
ManagedConfiguration(Dictionary conf) {
@@ -110,7 +111,7 @@
protected String getFactory() {
return m_factoryName;
}
-
+
protected String getNeededFactoryName() {
return m_desiredFactory;
}
@@ -125,7 +126,7 @@
/**
* Set the factory name.
- *
+ *
* @param name : the factory name.
*/
protected void setFactory(String name) {
@@ -134,7 +135,7 @@
/**
* Set the instance object.
- *
+ *
* @param instance : the instance
*/
protected void setInstance(ComponentInstance instance) {
@@ -149,7 +150,7 @@
/**
* Create an instance using the given factory and the given configuration.
- *
+ *
* @param fact : the factory name to used.
* @param config : the configuration.
*/
@@ -167,7 +168,7 @@
error("The instance creation has failed, an error during the configuration has occured", e);
}
}
-
+
/**
* A new valid factory appears.
* @param factory : factory.
@@ -177,7 +178,9 @@
String factName = factory.getName();
String className = factory.getComponentDescription().getClassName();
for (int i = 0; i < m_configurations.length; i++) {
- if (m_configurations[i].getInstance() == null && (m_configurations[i].getNeededFactoryName().equals(factName) || m_configurations[i].getNeededFactoryName().equals(className))) {
+ if (m_configurations[i].getInstance() == null
+ && (m_configurations[i].getNeededFactoryName().equals(factName)
+ || m_configurations[i].getNeededFactoryName().equals(className))) {
createInstance(factory, m_configurations[i]);
implicated = true;
}
@@ -186,7 +189,7 @@
checkValidity();
}
}
-
+
/**
* An existing factory disappears or becomes invalid.
* @param factory : factory
@@ -226,24 +229,40 @@
* Configure method.
* @param metadata : component type metadata.
* @param configuration : instance configuration.
- * @throws ConfigurationException : occurs an instance cannot be parsed correctly.
+ * @throws ConfigurationException : occurs an instance cannot be parsed correctly.
* @see org.apache.felix.ipojo.CompositeHandler#configure(org.apache.felix.ipojo.CompositeManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
*/
public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
m_scope = getCompositeManager().getServiceContext();
+
+ // Prepare the configuration to append.
+ Properties toAppend = new Properties();
+ Enumeration keys = configuration.keys();
+ while(keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (! key.equals("instance.name")
+ || key.equals("component")) { // Remove instance.name and component
+ toAppend.put(key, configuration.get(key));
+ }
+ }
+
Element[] instances = metadata.getElements("instance");
m_configurations = new ManagedConfiguration[instances.length];
for (int i = 0; i < instances.length; i++) {
- Dictionary conf = null;
+ Properties conf = null;
try {
conf = parseInstance(instances[i]);
} catch (ParseException e) {
error("An instance cannot be parsed correctly", e);
throw new ConfigurationException("An instance cannot be parsed correctly : " + e.getMessage());
}
- m_configurations[i] = new ManagedConfiguration(conf);
+
+ Properties instanceConfiguration = new Properties();
+ instanceConfiguration.putAll(conf);
+ instanceConfiguration.putAll(toAppend);
+ m_configurations[i] = new ManagedConfiguration(instanceConfiguration);
}
-
+
m_description = new InstanceHandlerDescription(this, m_configurations);
}
@@ -253,16 +272,16 @@
* @return : the resulting dictionary
* @throws ParseException : occurs when a configuration cannot be parse correctly.
*/
- public static Dictionary parseInstance(Element instance) throws ParseException {
- Dictionary dict = new Properties();
+ public static Properties parseInstance(Element instance) throws ParseException {
+ Properties dict = new Properties();
String name = instance.getAttribute("name");
if (name != null) {
dict.put("name", name);
}
-
+
String comp = instance.getAttribute("component");
- if (comp == null) {
- throw new ParseException("An instance does not have the 'component' attribute");
+ if (comp == null) {
+ throw new ParseException("An instance does not have the 'component' attribute");
} else {
dict.put("component", comp);
}
@@ -306,10 +325,10 @@
* Start method.
* @see org.apache.felix.ipojo.CompositeHandler#start()
*/
- public void start() {
+ public void start() {
for (int j = 0; j < m_factories.length; j++) {
String factName = m_factories[j].getName();
- String className = m_factories[j].getClassName();
+ String className = m_factories[j].getClassName();
for (int i = 0; i < m_configurations.length; i++) {
if (m_configurations[i].getInstance() == null && (m_configurations[i].getNeededFactoryName().equals(factName) || m_configurations[i].getNeededFactoryName().equals(className))) {
createInstance(m_factories[j], m_configurations[i]);
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java
index 4cbf2f1..5c1d8e0 100644
--- a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -21,6 +21,7 @@
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Dictionary;
+import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
@@ -42,7 +43,7 @@
/**
* Service Instantiator Class. This handler allows to instantiate service
* instance inside the composition.
- *
+ *
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class ServiceDependencyHandler extends CompositeHandler implements DependencyStateListener {
@@ -51,17 +52,17 @@
* List of instances to manage.
*/
private List/* <SvcInstance> */m_instances = new ArrayList();
-
+
/**
* List of importers.
*/
private List/* <ServiceImporter> */ m_importers = new ArrayList();
-
+
/**
* Flag indicating if the handler has already finished the start method.
*/
private boolean m_isStarted;
-
+
/**
* The handler description.
*/
@@ -71,15 +72,27 @@
* Source Managers.
*/
private List m_sources;
-
-
+
+
/**
* Create a Service instance object form the given Element.
* This method parse the given element and configure the service instance object.
* @param service : the Element describing the service instance
+ * @param conf : the configuration from the composite instance
* @throws ConfigurationException : the service instance cannot be created correctly
*/
- private void createServiceInstance(Element service) throws ConfigurationException {
+ private void createServiceInstance(Element service, Dictionary conf) throws ConfigurationException {
+ // Prepare the configuration to append.
+ Properties toAppend = new Properties();
+ Enumeration keys = conf.keys();
+ while(keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (! key.equals("instance.name")
+ || key.equals("component")) { // Remove instance.name and component
+ toAppend.put(key, conf.get(key));
+ }
+ }
+
String spec = service.getAttribute("specification");
if (spec == null) {
throw new ConfigurationException("Malformed service : the specification attribute is mandatory");
@@ -89,14 +102,14 @@
if (givenFilter != null) {
filter = "(&" + filter + givenFilter + ")"; //NOPMD
}
-
+
Filter fil;
try {
fil = getCompositeManager().getGlobalContext().createFilter(filter);
} catch (InvalidSyntaxException e) {
throw new ConfigurationException("Malformed filter " + filter + " : " + e.getMessage());
}
-
+
Properties prop = new Properties();
Element[] props = service.getElements("property");
for (int k = 0; props != null && k < props.length; k++) {
@@ -106,20 +119,24 @@
throw new ConfigurationException("An instance configuration is invalid : " + e.getMessage());
}
}
-
+
+ Properties instanceConfiguration = new Properties();
+ instanceConfiguration.putAll(prop);
+ instanceConfiguration.putAll(toAppend);
+
String aggregate = service.getAttribute("aggregate");
boolean agg = aggregate != null && aggregate.equalsIgnoreCase("true");
-
+
String optional = service.getAttribute("optional");
boolean opt = optional != null && optional.equalsIgnoreCase("true");
-
+
int policy = DependencyModel.getPolicy(service);
-
+
Comparator cmp = DependencyModel.getComparator(service, getCompositeManager().getGlobalContext());
-
- SvcInstance inst = new SvcInstance(this, spec, prop, agg, opt, fil, cmp, policy);
+
+ SvcInstance inst = new SvcInstance(this, spec, instanceConfiguration, agg, opt, fil, cmp, policy);
m_instances.add(inst);
-
+
String sources = service.getAttribute("context-source");
if (sources != null) {
SourceManager source = new SourceManager(sources, filter, inst, getCompositeManager());
@@ -129,7 +146,7 @@
m_sources.add(source);
}
}
-
+
/**
* Create a Service importer object from the given Element.
* This method parse the given element and configure the service importer object.
@@ -142,7 +159,7 @@
boolean aggregate = false;
String specification = imp.getAttribute("specification");
- if (specification == null) {
+ if (specification == null) {
// Malformed import
error("Malformed import: the specification attribute is mandatory");
throw new ConfigurationException("Malformed import : the specification attribute is mandatory");
@@ -194,7 +211,7 @@
ServiceImporter importer = new ServiceImporter(spec, fil, aggregate, optional, cmp, policy, context, identitity, this);
m_importers.add(importer);
-
+
String sources = imp.getAttribute("context-source");
if (sources != null) {
SourceManager source = new SourceManager(sources, filter, importer, getCompositeManager());
@@ -203,7 +220,7 @@
}
m_sources.add(source);
}
-
+
}
}
@@ -221,20 +238,20 @@
if (conf.get("requires.filters") != null) {
confFilter = (Dictionary) conf.get("requires.filters");
}
-
+
for (int i = 0; i < services.length; i++) {
String action = services[i].getAttribute("action");
if (action == null) {
throw new ConfigurationException("The action attribute must be set to 'instantiate' or 'import'");
} else if ("instantiate".equalsIgnoreCase(action)) {
- createServiceInstance(services[i]);
+ createServiceInstance(services[i], conf);
} else if ("import".equalsIgnoreCase(action)) {
createServiceImport(services[i], confFilter);
} else {
throw new ConfigurationException("Unknown action : " + action);
}
}
-
+
m_description = new ServiceInstantiatorDescription(this, m_instances, m_importers);
}
@@ -248,12 +265,12 @@
SourceManager source = (SourceManager) m_sources.get(i);
source.start();
}
-
+
for (int i = 0; i < m_importers.size(); i++) {
ServiceImporter imp = (ServiceImporter) m_importers.get(i);
imp.start();
}
-
+
for (int i = 0; i < m_instances.size(); i++) {
SvcInstance inst = (SvcInstance) m_instances.get(i);
inst.start();
@@ -275,7 +292,7 @@
return;
}
}
-
+
for (int i = 0; i < m_instances.size(); i++) {
SvcInstance inst = (SvcInstance) m_instances.get(i);
if (inst.getState() != DependencyModel.RESOLVED) {
@@ -283,7 +300,7 @@
return;
}
}
-
+
setValidity(true);
}
@@ -297,12 +314,12 @@
SourceManager source = (SourceManager) m_sources.get(i);
source.stop();
}
-
+
for (int i = 0; i < m_instances.size(); i++) {
SvcInstance inst = (SvcInstance) m_instances.get(i);
inst.stop();
}
-
+
for (int i = 0; i < m_importers.size(); i++) {
ServiceImporter imp = (ServiceImporter) m_importers.get(i);
imp.stop();
@@ -310,7 +327,7 @@
m_isStarted = false;
}
-
+
/**
* State change callback.
* This method is used to freeze the set of used provider if the static binding policy is used.
@@ -319,14 +336,14 @@
*/
public void stateChanged(int newState) {
// If we are becoming valid and started, check if we need to freeze importers.
- if (m_isStarted && newState == ComponentInstance.VALID) {
+ if (m_isStarted && newState == ComponentInstance.VALID) {
for (int i = 0; i < m_importers.size(); i++) {
ServiceImporter imp = (ServiceImporter) m_importers.get(i);
if (imp.getBindingPolicy() == DependencyModel.STATIC_BINDING_POLICY) {
imp.freeze();
}
}
-
+
for (int i = 0; i < m_instances.size(); i++) {
SvcInstance imp = (SvcInstance) m_instances.get(i);
if (imp.getBindingPolicy() == DependencyModel.STATIC_BINDING_POLICY) {
@@ -364,11 +381,11 @@
public HandlerDescription getDescription() {
return m_description;
}
-
+
public List getInstances() {
return m_instances;
}
-
+
public List getRequirements() {
return m_importers;
}
diff --git a/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/ConfigurableInstantiation.java b/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/ConfigurableInstantiation.java
index 99ccb8d..510c3a6 100644
--- a/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/ConfigurableInstantiation.java
+++ b/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/ConfigurableInstantiation.java
@@ -23,7 +23,6 @@
import org.apache.felix.ipojo.ComponentFactory;
import org.apache.felix.ipojo.ComponentInstance;
import org.apache.felix.ipojo.ServiceContext;
-import org.apache.felix.ipojo.architecture.Architecture;
import org.apache.felix.ipojo.junit4osgi.OSGiTestCase;
import org.apache.felix.ipojo.test.composite.service.FooService;
import org.apache.felix.ipojo.test.composite.util.Utils;
diff --git a/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/ConfigurationTest.java b/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/ConfigurationTest.java
new file mode 100644
index 0000000..bf6923c
--- /dev/null
+++ b/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/ConfigurationTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.composite.instantiator;
+
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.junit4osgi.OSGiTestCase;
+import org.apache.felix.ipojo.test.composite.service.FooService;
+import org.apache.felix.ipojo.test.composite.util.Utils;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+public class ConfigurationTest extends OSGiTestCase {
+
+ private ComponentFactory compositeFactory;
+
+ public void setUp() {
+ compositeFactory = (ComponentFactory) Utils.getFactoryByName(getContext(), "CONF-MySuperComposite");
+ }
+
+ public void tearDown() {
+
+ }
+
+ public void testDefaultInstantiation() throws InvalidSyntaxException {
+ Properties props = new Properties();
+ props.put("instance.name","under");
+ ComponentInstance under = null;
+ try {
+ under = compositeFactory.createComponentInstance(props);
+ } catch(Exception e) {
+ e.printStackTrace();
+ fail("Cannot instantiate under : " + e.getMessage());
+ }
+ assertTrue("Check instance validity", under.getState() == ComponentInstance.VALID);
+ ServiceContext sc = Utils.getServiceContext(under);
+ ServiceReference[] refs = sc.getServiceReferences(FooService.class.getName(), null);
+ assertEquals(2, refs.length);
+ for (int i = 0; i < refs.length; i++) {
+ assertEquals(3, ((Integer) refs[i].getProperty("int")).intValue());
+ assertEquals("foo", (String) refs[i].getProperty("string"));
+ }
+ under.dispose();
+ }
+
+ public void testConfiguredInstantiation() throws InvalidSyntaxException {
+ Properties props = new Properties();
+ props.put("instance.name","under");
+ props.put("string", "bar");
+ props.put("int", "25");
+ ComponentInstance under = null;
+ try {
+ under = compositeFactory.createComponentInstance(props);
+ } catch(Exception e) {
+ e.printStackTrace();
+ fail("Cannot instantiate under : " + e.getMessage());
+ }
+ assertTrue("Check instance validity", under.getState() == ComponentInstance.VALID);
+ ServiceContext sc = Utils.getServiceContext(under);
+ ServiceReference[] refs = sc.getServiceReferences(FooService.class.getName(), null);
+ assertEquals(2, refs.length);
+ for (int i = 0; i < refs.length; i++) {
+ assertEquals(25, ((Integer) refs[i].getProperty("int")).intValue());
+ assertEquals("bar", (String) refs[i].getProperty("string"));
+ }
+ under.dispose();
+ }
+
+
+
+}
diff --git a/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/InstantiatorTestSuite.java b/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/InstantiatorTestSuite.java
index 8fa0dce..1ae39b7 100644
--- a/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/InstantiatorTestSuite.java
+++ b/ipojo/tests/composite/service-instance/src/main/java/org/apache/felix/ipojo/test/composite/instantiator/InstantiatorTestSuite.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -36,6 +36,7 @@
ots.addTestSuite(ConfigurableInstantiation.class);
ots.addTestSuite(SimpleInstance.class);
ots.addTestSuite(InstanceScopeTest.class);
+ ots.addTestSuite(ConfigurationTest.class);
return ots;
}
diff --git a/ipojo/tests/composite/service-instance/src/main/resources/metadata.xml b/ipojo/tests/composite/service-instance/src/main/resources/metadata.xml
index 3ec92cc..ede6935 100644
--- a/ipojo/tests/composite/service-instance/src/main/resources/metadata.xml
+++ b/ipojo/tests/composite/service-instance/src/main/resources/metadata.xml
@@ -1,26 +1,26 @@
<ipojo
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="org.apache.felix.ipojo http://felix.apache.org/ipojo/schemas/SNAPSHOT/core.xsd
+ xsi:schemaLocation="org.apache.felix.ipojo http://felix.apache.org/ipojo/schemas/SNAPSHOT/core.xsd
org.apache.felix.composite http://felix.apache.org/ipojo/schemas/SNAPSHOT/composite.xsd"
xmlns="org.apache.felix.ipojo"
xmlns:comp="org.apache.felix.ipojo.composite"
- xmlns:cs="org.apache.felix.ipojo.test.composite.handler.CheckServiceHandler">
+ xmlns:cs="org.apache.felix.ipojo.test.composite.handler.CheckServiceHandler">
<comp:composite name="composite.bar.1" architecture="true">
<subservice action="instantiate" specification="org.apache.felix.ipojo.test.composite.service.BarService"/>
</comp:composite>
-
+
<comp:composite name="composite.bar.2" architecture="true">
<subservice action="instantiate" specification="org.apache.felix.ipojo.test.composite.service.BarService" aggregate="true"/>
</comp:composite>
-
+
<comp:composite name="composite.bar.3" architecture="true">
<subservice action="instantiate" specification="org.apache.felix.ipojo.test.composite.service.BarService" optional="true"/>
</comp:composite>
-
+
<comp:composite name="composite.bar.4" architecture="true">
<subservice action="instantiate" specification="org.apache.felix.ipojo.test.composite.service.FooService" aggregate="true" optional="true"/>
</comp:composite>
-
+
<comp:composite name="composite.bar.5-accept" architecture="true">
<subservice action="instantiate" specification="org.apache.felix.ipojo.test.composite.service.FooService">
<property name="boolean" value="true"/>
@@ -29,7 +29,7 @@
<property name="int" value="5"/>
</subservice>
</comp:composite>
-
+
<comp:composite name="composite.bar.5-refuse1" architecture="true">
<subservice action="instantiate" specification="org.apache.felix.ipojo.test.composite.service.BarService">
<property name="foo" value="bar"/>
@@ -39,7 +39,7 @@
<property name="int" value="5"/>
</subservice>
</comp:composite>
-
+
<comp:composite name="composite.bar.5-refuse2" architecture="true">
<subservice action="instantiate" specification="org.apache.felix.ipojo.test.composite.service.BarService">
<property name="string" value="foo"/>
@@ -56,7 +56,7 @@
<property name="strAProp" value="{a,b,c}"/>
</instance>
</comp:composite>
-
+
<!-- Scope test -->
<component name="SCOPE-provider" classname="org.apache.felix.ipojo.test.instance.ServiceProvider">
<provides/>
@@ -72,4 +72,18 @@
<comp:composite name="SCOPE-badscope">
<instance component="SCOPE-cons"/>
</comp:composite>
+
+ <!-- Instance configuration -->
+ <comp:composite name="CONF-MySuperComposite">
+ <instance component="COMPO-FooProviderType-2">
+ <property name="int" value="3"/>
+ </instance>
+ <subservice action="instantiate"
+ specification="org.apache.felix.ipojo.test.composite.service.FooService">
+ <property name="boolean" value="true"/>
+ <property name="string" value="foo"/>
+ <property name="strAprop" value="{foo, bar, baz}"/>
+ <property name="int" value="3"/>
+ </subservice>
+ </comp:composite>
</ipojo>