Fix FELIX-4034
Define the DSL, the @Configuration annotation
Implement the processor looking at the @Configuration annotation and creating the instance declaration
Add some tests
I also introduce a couple of utility class to simplify reflection.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1471456 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/pom.xml b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/pom.xml
new file mode 100644
index 0000000..5b82d29
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.runtime.core-it</artifactId>
+ <version>1.9.0-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ipojo-core-configuration-processor-test</artifactId>
+
+ <name>${project.artifactId}</name>
+
+</project>
\ No newline at end of file
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/Bean.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/Bean.java
new file mode 100644
index 0000000..7b0d406
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/Bean.java
@@ -0,0 +1,46 @@
+/*
+ * 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.components;
+
+/**
+ * A bean
+ */
+public class Bean {
+
+ private String message;
+
+ private int count;
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureAnotherInstance.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureAnotherInstance.java
new file mode 100644
index 0000000..f5b5c3c
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureAnotherInstance.java
@@ -0,0 +1,43 @@
+/*
+ * 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.components;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+import org.apache.felix.ipojo.configuration.Instance;
+
+import java.util.Properties;
+
+import static org.apache.felix.ipojo.configuration.Instance.instance;
+
+/**
+ * Simple configuration
+ */
+@Configuration
+public class ConfigureAnotherInstance {
+
+ // Declare an instance of MyComponent named myInstance
+ Instance anotherInstance = instance().of(MyComponent.class)
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureNothing.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureNothing.java
new file mode 100644
index 0000000..c3c6588
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureNothing.java
@@ -0,0 +1,32 @@
+/*
+ * 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.components;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+
+/**
+ * Nothing happen in this class..
+ */
+@Configuration
+public class ConfigureNothing {
+
+ // Nothing on purpose
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureOneInstance.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureOneInstance.java
new file mode 100644
index 0000000..d3f2511
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureOneInstance.java
@@ -0,0 +1,43 @@
+/*
+ * 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.components;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+import org.apache.felix.ipojo.configuration.Instance;
+
+import java.util.Properties;
+
+import static org.apache.felix.ipojo.configuration.Instance.instance;
+
+/**
+ * Simple configuration
+ */
+@Configuration
+public class ConfigureOneInstance {
+
+ // Declare an instance of MyComponent named myInstance
+ Instance myInstance = instance().of(MyComponent.class)
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureThreeInstancesUsingMethods.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureThreeInstancesUsingMethods.java
new file mode 100644
index 0000000..58c05a8
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureThreeInstancesUsingMethods.java
@@ -0,0 +1,66 @@
+/*
+ * 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.components;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+import org.apache.felix.ipojo.configuration.Instance;
+import org.osgi.framework.BundleContext;
+
+import java.util.Properties;
+
+import static org.apache.felix.ipojo.configuration.Instance.instance;
+
+/**
+ * Simple configuration using methods and fields.
+ */
+@Configuration
+public class ConfigureThreeInstancesUsingMethods {
+
+ // Declare an instance of MyComponent named myInstance
+ Instance myInstance = instance().of(MyComponent.class)
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+
+ Instance instance1() {
+ return instance().of(MyComponent.class)
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+ }
+
+ Instance instance2(BundleContext bc) {
+ return instance().of(MyComponent.class)
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(bc.getBundles().length)
+ .with("props").setto(new Properties());
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureTwoInstances.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureTwoInstances.java
new file mode 100644
index 0000000..0dfb520
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureTwoInstances.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.components;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+import org.apache.felix.ipojo.configuration.Instance;
+
+import java.util.Properties;
+
+import static org.apache.felix.ipojo.configuration.Instance.instance;
+
+/**
+ * Simple configuration
+ */
+@Configuration
+public class ConfigureTwoInstances {
+
+ // Declare an instance of MyComponent named myInstance
+ Instance myInstance1 = instance().of(MyComponent.class)
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+
+ // Declare an instance of MyComponent named hello
+ Instance myInstance2 = instance().of(MyComponent.class)
+ .named("hello")
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureTwoInstancesWithInheritance.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureTwoInstancesWithInheritance.java
new file mode 100644
index 0000000..c290747
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureTwoInstancesWithInheritance.java
@@ -0,0 +1,43 @@
+/*
+ * 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.components;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+import org.apache.felix.ipojo.configuration.Instance;
+
+import java.util.Properties;
+
+import static org.apache.felix.ipojo.configuration.Instance.instance;
+
+/**
+ * Simple configuration
+ */
+@Configuration
+public class ConfigureTwoInstancesWithInheritance extends ParentConfiguration {
+
+ // Declare an instance of MyComponent named myInstance
+ Instance myInstance1 = instance().of(MyComponent.class)
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureTwoInstancesWithOverridding.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureTwoInstancesWithOverridding.java
new file mode 100644
index 0000000..564bc67
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ConfigureTwoInstancesWithOverridding.java
@@ -0,0 +1,52 @@
+/*
+ * 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.components;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+import org.apache.felix.ipojo.configuration.Instance;
+
+import java.util.Properties;
+
+import static org.apache.felix.ipojo.configuration.Instance.instance;
+
+/**
+ * Simple configuration
+ */
+@Configuration
+public class ConfigureTwoInstancesWithOverridding extends ParentConfiguration {
+
+ // Declare an instance of MyComponent named myInstance
+ Instance myInstance1 = instance().of(MyComponent.class)
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+
+ protected Instance myInstance2 = instance().of(MyComponent.class)
+ .named("hello-over")
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/MyComplexComponent.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/MyComplexComponent.java
new file mode 100644
index 0000000..b61512d
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/MyComplexComponent.java
@@ -0,0 +1,106 @@
+/*
+ * 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.components;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Property;
+import org.apache.felix.ipojo.annotations.Provides;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * A complex component
+ */
+@Component
+@Provides
+public class MyComplexComponent implements FooService {
+
+ @Property
+ private File file;
+ @Property
+ private Bean bean;
+ @Property
+ private Map<String, String> map;
+
+ @Override
+ public boolean foo() {
+ return file != null;
+ }
+
+ @Override
+ public Properties fooProps() {
+ String content;
+ try {
+ content = read();
+ } catch (IOException e) {
+ throw new IllegalStateException("unexpected error", e);
+ }
+ Properties props = new Properties();
+ props.put("map", map);
+ props.put("content", content);
+ props.put("bean", bean);
+ return props;
+ }
+
+ private String read() throws IOException {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ String line;
+ StringBuilder stringBuilder = new StringBuilder();
+ String ls = System.getProperty("line.separator");
+
+ while ((line = reader.readLine()) != null) {
+ stringBuilder.append(line);
+ stringBuilder.append(ls);
+ }
+
+ return stringBuilder.toString();
+ }
+
+ @Override
+ public Boolean getObject() {
+ return false;
+ }
+
+ @Override
+ public boolean getBoolean() {
+ return false;
+ }
+
+ @Override
+ public int getInt() {
+ return 0;
+ }
+
+ @Override
+ public long getLong() {
+ return 1;
+ }
+
+ @Override
+ public double getDouble() {
+ return 1;
+ }
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/MyComponent.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/MyComponent.java
new file mode 100644
index 0000000..bbd5a44
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/MyComponent.java
@@ -0,0 +1,89 @@
+/*
+ * 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.components;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Property;
+import org.apache.felix.ipojo.annotations.Provides;
+import org.apache.felix.ipojo.annotations.ServiceProperty;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+
+import java.util.Properties;
+
+/**
+ * A simple component
+ */
+@Component
+@Provides
+public class MyComponent implements FooService {
+
+ @Property
+ private Boolean bool;
+
+ @Property
+ private Properties props;
+
+ @ServiceProperty
+ private String message;
+
+ @Property
+ private long number;
+
+ @Property
+ private int integer;
+
+ @Property
+ private double floating;
+
+ @Override
+ public boolean foo() {
+ return message != null;
+ }
+
+ @Override
+ public Properties fooProps() {
+ return props;
+ }
+
+ @Override
+ public Boolean getObject() {
+ return bool;
+ }
+
+ @Override
+ public boolean getBoolean() {
+ return bool;
+ }
+
+ @Override
+ public int getInt() {
+ return integer;
+ }
+
+ @Override
+ public long getLong() {
+ return number;
+ }
+
+ @Override
+ public double getDouble() {
+ return floating;
+ }
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ParentConfiguration.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ParentConfiguration.java
new file mode 100644
index 0000000..f8c9481
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ParentConfiguration.java
@@ -0,0 +1,54 @@
+/*
+ * 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.components;
+
+import org.apache.felix.ipojo.configuration.Instance;
+
+import java.util.Properties;
+
+import static org.apache.felix.ipojo.configuration.Instance.instance;
+
+/**
+ * This configuration is a parent configuration.
+ */
+public class ParentConfiguration {
+
+ // Inherited fields must be public.
+ protected Instance myInstance2 = instance().of(MyComponent.class)
+ .named("hello")
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+
+ private Instance hidden = instance().of(MyComponent.class)
+ .named("hello")
+ .with("floating").setto("1.0")
+ .with("message").setto("hidden")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/configuration/MyComplexConfiguration.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/configuration/MyComplexConfiguration.java
new file mode 100644
index 0000000..3aed514
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/configuration/MyComplexConfiguration.java
@@ -0,0 +1,77 @@
+/*
+ * 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.components.configuration;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+import org.apache.felix.ipojo.configuration.Instance;
+import org.apache.felix.ipojo.runtime.core.components.Bean;
+import org.apache.felix.ipojo.runtime.core.components.MyComplexComponent;
+import org.osgi.framework.BundleContext;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+
+import static org.apache.felix.ipojo.configuration.Instance.*;
+
+/**
+ * A complex configuration creating two instances of MyComplex component
+ */
+@Configuration
+public class MyComplexConfiguration {
+
+ Instance complex1(BundleContext bc) throws FileNotFoundException {
+
+ File file = bc.getBundle().getDataFile("file1.txt");
+ write(file, "I'm file 1");
+
+ Bean bean = new Bean();
+ bean.setMessage("I'm 1");
+ bean.setCount(1);
+
+ return instance().of(MyComplexComponent.class)
+ .with("file").setto(file)
+ .with("bean").setto(bean)
+ .with("map").setto(map(pair("a", "b"), pair("c", "d")));
+
+ }
+
+ Instance complex2(BundleContext bc) throws FileNotFoundException {
+
+ File file = bc.getBundle().getDataFile("file2.txt");
+ write(file, "I'm file 2");
+
+ Bean bean = new Bean();
+ bean.setMessage("I'm 2");
+ bean.setCount(2);
+
+ return instance().of(MyComplexComponent.class)
+ .with("file").setto(file)
+ .with("bean").setto(bean)
+ .with("map").setto(map(pair("a", "b2"), pair("c", "d2")));
+
+ }
+
+ private void write(File file, String message) throws FileNotFoundException {
+ PrintWriter out = new PrintWriter(file);
+ out.println(message);
+ out.close();
+ }
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/configuration/MyConfiguration.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/configuration/MyConfiguration.java
new file mode 100644
index 0000000..5d2cf8d
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/configuration/MyConfiguration.java
@@ -0,0 +1,45 @@
+/*
+ * 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.components.configuration;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+import org.apache.felix.ipojo.configuration.Instance;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+
+import java.util.Properties;
+
+import static org.apache.felix.ipojo.configuration.Instance.instance;
+
+/**
+ * Simple configuration in another package.
+ */
+@Configuration
+public class MyConfiguration {
+
+ // Declare an instance of MyComponent named myInstance
+ // We use the factory name to avoid import-export package.
+ Instance myInstance = instance().of("org.apache.felix.ipojo.runtime.core.components.MyComponent")
+ .with("floating").setto("1.0")
+ .with("message").setto("foo")
+ .with("bool").setto(true)
+ .with("number").setto(1l)
+ .with("integer").setto(1)
+ .with("props").setto(new Properties());
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/services/FooService.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/services/FooService.java
new file mode 100644
index 0000000..4ce1646
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/main/java/org/apache/felix/ipojo/runtime/core/services/FooService.java
@@ -0,0 +1,39 @@
+/*
+ * 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.services;
+
+import java.util.Properties;
+
+public interface FooService {
+
+ boolean foo();
+
+ Properties fooProps();
+
+ Boolean getObject();
+
+ boolean getBoolean();
+
+ int getInt();
+
+ long getLong();
+
+ double getDouble();
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
new file mode 100644
index 0000000..1025f5f
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
@@ -0,0 +1,349 @@
+/*
+ * 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;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.options.CompositeOption;
+import org.ops4j.pax.exam.options.DefaultCompositeOption;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerMethod;
+import org.ops4j.pax.tinybundles.core.TinyBundle;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.ow2.chameleon.testing.helpers.IPOJOHelper;
+import org.ow2.chameleon.testing.helpers.OSGiHelper;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static junit.framework.Assert.fail;
+import static org.ops4j.pax.exam.CoreOptions.*;
+
+/**
+ * Bootstrap the test from this project
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerMethod.class)
+public class Common {
+
+ @Inject
+ protected
+ BundleContext bc;
+ protected OSGiHelper osgiHelper;
+ protected IPOJOHelper ipojoHelper;
+ protected boolean deployTestedBundle = true;
+
+ public static Option junitAndMockitoBundles() {
+ return new DefaultCompositeOption(
+ // Repository required to load harmcrest (OSGi-fied version).
+ repository("http://repository.springsource.com/maven/bundles/external").id(
+ "com.springsource.repository.bundles.external"),
+
+ // Mockito without Hamcrest and Objenesis
+ mavenBundle("org.mockito", "mockito-core", "1.9.5"),
+
+ // Hamcrest with a version matching the range expected by Mockito
+ mavenBundle("org.hamcrest", "com.springsource.org.hamcrest.core", "1.1.0"),
+
+ // Objenesis with a version matching the range expected by Mockito
+ wrappedBundle(mavenBundle("org.objenesis", "objenesis", "1.2"))
+ .exports("*;version=1.2"),
+
+ // The default JUnit bundle also exports Hamcrest, but with an (incorrect) version of
+ // 4.9 which does not match the Mockito import.
+ CoreOptions.junitBundles(),
+
+ /*
+ * Felix has implicit boot delegation enabled by default. It conflicts with Mockito:
+ * java.lang.LinkageError: loader constraint violation in interface itable initialization:
+ * when resolving method "org.osgi.service.useradmin.User$$EnhancerByMockitoWithCGLIB$$dd2f81dc
+ * .newInstance(Lorg/mockito/cglib/proxy/Callback;)Ljava/lang/Object;" the class loader
+ * (instance of org/mockito/internal/creation/jmock/SearchingClassLoader) of the current class,
+ * org/osgi/service/useradmin/User$$EnhancerByMockitoWithCGLIB$$dd2f81dc, and the class loader
+ * (instance of org/apache/felix/framework/BundleWiringImpl$BundleClassLoaderJava5) for interface
+ * org/mockito/cglib/proxy/Factory have different Class objects for the type org/mockito/cglib/
+ * proxy/Callback used in the signature
+ *
+ * So we disable the bootdelegation.
+ */
+ frameworkProperty("felix.bootdelegation.implicit").value("false")
+ );
+ }
+
+ public static void dump(BundleContext bc, File output) throws IOException {
+ if (!output.exists()) {
+ output.mkdirs();
+ }
+
+ for (Bundle bundle : bc.getBundles()) {
+ if (bundle.getBundleId() == 0) {
+ continue;
+ }
+ System.out.println("Location : " + bundle.getLocation());
+ if ("local".equals(bundle.getLocation())) {
+ continue; // Pax Exam, when you hug me, I feel so...
+ }
+ URL location = new URL(bundle.getLocation());
+ FileOutputStream outputStream = null;
+ if (bundle.getVersion() != null) {
+ outputStream = new FileOutputStream(new File(output,
+ bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + ".jar"));
+ } else {
+ outputStream = new FileOutputStream(new File(output, bundle.getSymbolicName() + ".jar"));
+ }
+
+ int read = 0;
+ byte[] bytes = new byte[1024];
+
+ InputStream inputStream = location.openStream();
+ while ((read = inputStream.read(bytes)) != -1) {
+ outputStream.write(bytes, 0, read);
+ }
+ inputStream.close();
+ outputStream.close();
+ }
+ }
+
+ @Configuration
+ public Option[] config() throws IOException {
+ Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+ root.setLevel(Level.INFO);
+
+ if (deployTestedBundle) {
+ return options(
+ cleanCaches(),
+ ipojoBundles(),
+ junitBundles(),
+ testedBundle(),
+ systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("WARN")
+ );
+ } else {
+ return options(
+ cleanCaches(),
+ ipojoBundles(),
+ junitBundles(),
+ systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("WARN")
+ );
+ }
+ }
+
+ @Before
+ public void commonSetUp() {
+ osgiHelper = new OSGiHelper(bc);
+ ipojoHelper = new IPOJOHelper(bc);
+
+ // Dump OSGi Framework information
+ String vendor = (String) osgiHelper.getBundle(0).getHeaders().get(Constants.BUNDLE_VENDOR);
+ if (vendor == null) {
+ vendor = (String) osgiHelper.getBundle(0).getHeaders().get(Constants.BUNDLE_SYMBOLICNAME);
+ }
+ String version = (String) osgiHelper.getBundle(0).getHeaders().get(Constants.BUNDLE_VERSION);
+ System.out.println("OSGi Framework : " + vendor + " - " + version);
+
+ waitForStability(bc);
+ }
+
+ @After
+ public void commonTearDown() {
+ ipojoHelper.dispose();
+ osgiHelper.dispose();
+ }
+
+ public CompositeOption ipojoBundles() {
+ return new DefaultCompositeOption(
+ mavenBundle("org.apache.felix", "org.apache.felix.ipojo").versionAsInProject(),
+ mavenBundle("org.ow2.chameleon.testing", "osgi-helpers").versionAsInProject(),
+ // harmcrest-all
+ //mavenBundle("de.twentyeleven.skysail", "org.hamcrest.hamcrest-all-osgi").versionAsInProject(),
+ // configuration admin
+ mavenBundle("org.apache.felix", "org.apache.felix.configadmin").versionAsInProject()
+ );
+ }
+
+ public Option testedBundle() throws MalformedURLException {
+ File out = new File("target/tested/bundle.jar");
+ if (out.exists()) {
+ return bundle(out.toURI().toURL().toExternalForm());
+ }
+
+ TinyBundle tested = TinyBundles.bundle();
+
+ // We look inside target/classes to find the class and resources
+ File classes = new File("target/classes");
+ Collection<File> files = FileUtils.listFilesAndDirs(classes, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
+ List<File> services = new ArrayList<File>();
+ for (File file : files) {
+ if (file.isDirectory()) {
+ // By convention we export of .services and .service package
+ if (file.getName().endsWith("services") || file.getName().endsWith("service")) {
+ services.add(file);
+ }
+ } else {
+ // We need to compute the path
+ String path = file.getAbsolutePath().substring(classes.getAbsolutePath().length() + 1);
+ tested.add(path, file.toURI().toURL());
+ System.out.println(file.getName() + " added to " + path);
+ }
+ }
+
+ // Export the inherited package, components and strategies
+ String export = "org.apache.felix.ipojo.runtime.core.components.inherited";
+ export += ", org.apache.felix.ipojo.runtime.core.components";
+ export += ", org.apache.felix.ipojo.runtime.core.components.strategies";
+ // Inheritance.
+ export += ", org.apache.felix.ipojo.runtime.core.components.inheritance.a";
+ export += ", org.apache.felix.ipojo.runtime.core.components.inheritance.b";
+ for (File file : services) {
+ if (export.length() > 0) {
+ export += ", ";
+ }
+ String path = file.getAbsolutePath().substring(classes.getAbsolutePath().length() + 1);
+ String packageName = path.replace('/', '.');
+ export += packageName;
+ }
+
+ System.out.println("Exported packages : " + export);
+
+ InputStream inputStream = tested
+ .set(Constants.BUNDLE_SYMBOLICNAME, "test.bundle")
+ .set(Constants.IMPORT_PACKAGE, "*")
+ .set(Constants.EXPORT_PACKAGE, export)
+ .build(IPOJOStrategy.withiPOJO(new File("src/main/resources")));
+
+ try {
+ org.apache.commons.io.FileUtils.copyInputStreamToFile(inputStream, out);
+ return bundle(out.toURI().toURL().toExternalForm());
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Cannot compute the url of the manipulated bundle");
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot write of the manipulated bundle");
+ }
+ }
+
+ public void assertContains(String s, String[] arrays, String object) {
+ for (String suspect : arrays) {
+ if (object.equals(suspect)) {
+ return;
+ }
+ }
+ fail("Assertion failed : " + s);
+ }
+
+ /**
+ * Waits for stability:
+ * <ul>
+ * <li>all bundles are activated
+ * <li>service count is stable
+ * </ul>
+ * If the stability can't be reached after a specified time,
+ * the method throws a {@link IllegalStateException}.
+ *
+ * @param context the bundle context
+ * @throws IllegalStateException when the stability can't be reach after a several attempts.
+ */
+ private void waitForStability(BundleContext context) throws IllegalStateException {
+ // Wait for bundle initialization.
+ boolean bundleStability = getBundleStability(context);
+ int count = 0;
+ while (!bundleStability && count < 500) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {
+ // Interrupted
+ }
+ count++;
+ bundleStability = getBundleStability(context);
+ }
+
+ if (count == 500) {
+ System.err.println("Bundle stability isn't reached after 500 tries");
+ throw new IllegalStateException("Cannot reach the bundle stability");
+ }
+
+ boolean serviceStability = false;
+ count = 0;
+ int count1 = 0;
+ int count2 = 0;
+ while (!serviceStability && count < 500) {
+ try {
+ ServiceReference[] refs = context.getServiceReferences((String) null, null);
+ count1 = refs.length;
+ Thread.sleep(500);
+ refs = context.getServiceReferences((String) null, null);
+ count2 = refs.length;
+ serviceStability = count1 == count2;
+ } catch (Exception e) {
+ System.err.println(e);
+ serviceStability = false;
+ // Nothing to do, while recheck the condition
+ }
+ count++;
+ }
+
+ if (count == 500) {
+ System.err.println("Service stability isn't reached after 500 tries (" + count1 + " != " + count2);
+ throw new IllegalStateException("Cannot reach the service stability");
+ }
+ }
+
+ /**
+ * Are bundle stables.
+ *
+ * @param bc the bundle context
+ * @return <code>true</code> if every bundles are activated.
+ */
+ private boolean getBundleStability(BundleContext bc) {
+ boolean stability = true;
+ Bundle[] bundles = bc.getBundles();
+ for (Bundle bundle : bundles) {
+ stability = stability && (bundle.getState() == Bundle.ACTIVE);
+ }
+ return stability;
+ }
+
+ public boolean isKF() {
+ return bc.getClass().toString().contains("knopflerfish");
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestComplexConfigurations.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestComplexConfigurations.java
new file mode 100644
index 0000000..42e454f
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestComplexConfigurations.java
@@ -0,0 +1,147 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+import org.apache.felix.ipojo.runtime.core.components.Bean;
+import org.apache.felix.ipojo.runtime.core.components.MyComplexComponent;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+import org.apache.felix.ipojo.runtime.core.components.configuration.MyComplexConfiguration;
+import org.apache.felix.ipojo.runtime.core.components.configuration.MyConfiguration;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+import org.junit.Test;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Check @Configuration embedded in another bundle using complex configurations.
+ */
+public class TestComplexConfigurations extends Common {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ deployTestedBundle = false;
+ Option[] options = super.config();
+
+ // Build a service bundle
+ return OptionUtils.combine(options,
+ streamBundle(
+ TinyBundles.bundle()
+ .add(FooService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.services")
+ .build(withBnd())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(MyComplexComponent.class)
+ .add(Bean.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "MyComponent")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.components")
+ .build(IPOJOStrategy.withiPOJO())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(MyComplexConfiguration.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "Configuration")
+ .build(IPOJOStrategy.withiPOJO())
+ )
+ );
+ }
+
+ /**
+ * <ol>
+ * <li>Check when all bundles are deployed</li>
+ * <li>Check when the configuration bundle is stopped</li>
+ * <li>Check when the configuration is restarted</li>
+ * <li>Check when the component bundle is stopped</li>
+ * <li>Check when the component bundle is restarted</li>
+ * </ol>
+ */
+ @Test
+ public void testDynamism() throws BundleException {
+ if (isKF()) {
+ return; // Test disabled on KF
+ }
+ //1)
+ osgiHelper.waitForService(FooService.class, null, 10000);
+ Assert.assertNotNull(osgiHelper.getServiceReference(FooService.class));
+
+ //2) Stopping configuration bundle
+ osgiHelper.getBundle("Configuration").stop();
+ Assert.assertNull(osgiHelper.getServiceReference(FooService.class));
+
+ //3) Restart configuration bundle
+ osgiHelper.getBundle("Configuration").start();
+ osgiHelper.waitForService(FooService.class, null, 10000);
+ Assert.assertNotNull(osgiHelper.getServiceReference(FooService.class));
+
+ //4) Stop the component bundle
+ osgiHelper.getBundle("MyComponent").stop();
+ Assert.assertNull(osgiHelper.getServiceReference(FooService.class));
+
+ //5) Restart the component bundle
+ osgiHelper.getBundle("MyComponent").start();
+ osgiHelper.waitForService(FooService.class, null, 10000);
+ Assert.assertNotNull(osgiHelper.getServiceReference(FooService.class));
+ }
+
+ @Test
+ public void testConfiguration() {
+ if (isKF()) {
+ return; // Test disabled on KF
+ }
+ osgiHelper.waitForService(FooService.class, null, 10000);
+
+ ServiceReference ref1 = ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "complex1");
+ Assert.assertNotNull(ref1);
+ FooService fs1 = (FooService) osgiHelper.getServiceObject(ref1);
+ Properties props1 = fs1.fooProps();
+ Assert.assertTrue(((String)props1.get("content")).contains("I'm file 1"));
+ Assert.assertEquals(((Bean)props1.get("bean")).getMessage(), "I'm 1");
+ Assert.assertEquals(((Bean)props1.get("bean")).getCount(), 1);
+ Assert.assertEquals(((Map<String, String>)props1.get("map")).get("a"), "b");
+
+ ServiceReference ref2 = ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "complex2");
+ Assert.assertNotNull(ref2);
+ FooService fs2 = (FooService) osgiHelper.getServiceObject(ref2);
+ Properties props2 = fs2.fooProps();
+ Assert.assertTrue(((String)props2.get("content")).contains("I'm file 2"));
+ Assert.assertEquals(((Bean)props2.get("bean")).getMessage(), "I'm 2");
+ Assert.assertEquals(((Bean)props2.get("bean")).getCount(), 2);
+ Assert.assertEquals(((Map<String, String>)props2.get("map")).get("a"), "b2");
+
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationInAnotherBundle.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationInAnotherBundle.java
new file mode 100644
index 0000000..f59b3cd
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationInAnotherBundle.java
@@ -0,0 +1,112 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+import org.apache.felix.ipojo.runtime.core.components.configuration.MyConfiguration;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+import org.junit.Test;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+
+import java.io.IOException;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Check @Configuration embedded in another bundle.
+ */
+public class TestConfigurationInAnotherBundle extends Common {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ deployTestedBundle = false;
+ Option[] options = super.config();
+
+ // Build a service bundle
+ return OptionUtils.combine(options,
+ streamBundle(
+ TinyBundles.bundle()
+ .add(FooService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.services")
+ .build(withBnd())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(MyComponent.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "MyComponent")
+ .build(IPOJOStrategy.withiPOJO())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(MyConfiguration.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "Configuration")
+ .build(IPOJOStrategy.withiPOJO())
+ )
+ );
+ }
+
+ /**
+ * <ol>
+ * <li>Check when all bundles are deployed</li>
+ * <li>Check when the configuration bundle is stopped</li>
+ * <li>Check when the configuration is restarted</li>
+ * <li>Check when the component bundle is stopped</li>
+ * <li>Check when the component bundle is restarted</li>
+ * </ol>
+ */
+ @Test
+ public void testDynamism() throws BundleException {
+ if (isKF()) {
+ return; // Test disabled on KF
+ }
+ //1)
+ osgiHelper.waitForService(FooService.class, null, 10000);
+ Assert.assertNotNull(osgiHelper.getServiceReference(FooService.class));
+
+ //2) Stopping configuration bundle
+ osgiHelper.getBundle("Configuration").stop();
+ Assert.assertNull(osgiHelper.getServiceReference(FooService.class));
+
+ //3) Restart configuration bundle
+ osgiHelper.getBundle("Configuration").start();
+ osgiHelper.waitForService(FooService.class, null, 10000);
+ Assert.assertNotNull(osgiHelper.getServiceReference(FooService.class));
+
+ //4) Stop the component bundle
+ osgiHelper.getBundle("MyComponent").stop();
+ Assert.assertNull(osgiHelper.getServiceReference(FooService.class));
+
+ //5) Restart the component bundle
+ osgiHelper.getBundle("MyComponent").start();
+ osgiHelper.waitForService(FooService.class, null, 10000);
+ Assert.assertNotNull(osgiHelper.getServiceReference(FooService.class));
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationOfMyComponent.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationOfMyComponent.java
new file mode 100644
index 0000000..5fbed4c
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationOfMyComponent.java
@@ -0,0 +1,85 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+import org.apache.felix.ipojo.runtime.core.components.ConfigureOneInstance;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+import org.junit.Test;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+
+import java.io.IOException;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Check simple @Configuration
+ */
+public class TestConfigurationOfMyComponent extends Common {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ deployTestedBundle = false;
+ Option[] options = super.config();
+
+ // Build a service bundle
+ return OptionUtils.combine(options,
+ streamBundle(
+ TinyBundles.bundle()
+ .add(FooService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.services")
+ .build(withBnd())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(MyComponent.class)
+ .add(ConfigureOneInstance.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "MyComponent")
+ .build(IPOJOStrategy.withiPOJO())
+ )
+ );
+ }
+
+ @Test
+ public void testConfiguration() throws IOException {
+ if (isKF()) {
+ return; // Test disabled on KF
+ }
+
+ osgiHelper.waitForService(FooService.class, null, 10000);
+
+ // Check configuration
+ FooService fs = osgiHelper.getServiceObject(FooService.class);
+ Assert.assertTrue(fs.foo());
+ Assert.assertEquals(fs.getDouble(), 1.0, 0);
+ Assert.assertEquals(fs.getInt(), 1);
+ Assert.assertEquals(fs.getLong(), 1l);
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationOfThreeInstancesUsingMethods.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationOfThreeInstancesUsingMethods.java
new file mode 100644
index 0000000..a0d562d
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationOfThreeInstancesUsingMethods.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.runtime.core.components.ConfigureThreeInstancesUsingMethods;
+import org.apache.felix.ipojo.runtime.core.components.ConfigureTwoInstances;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+import org.junit.Test;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Check a @Configuration with 2 instances.
+ */
+public class TestConfigurationOfThreeInstancesUsingMethods extends Common {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ deployTestedBundle = false;
+ Option[] options = super.config();
+
+ // Build a service bundle
+ return OptionUtils.combine(options,
+ streamBundle(
+ TinyBundles.bundle()
+ .add(FooService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.services")
+ .build(withBnd())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(ConfigureThreeInstancesUsingMethods.class)
+ .add(MyComponent.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "MyComponent")
+ .build(IPOJOStrategy.withiPOJO())
+ )
+ );
+ }
+
+ @Test
+ public void testConfiguration() {
+ if (isKF()) {
+ return; // Test disabled on KF
+ }
+
+ osgiHelper.waitForService(FooService.class, null, 10000);
+
+ // Check we have two instances
+ ServiceReference[] refs = osgiHelper.getServiceReferences(FooService.class, null);
+ Assert.assertEquals(refs.length, 3);
+
+ List<Architecture> arch = osgiHelper.getServiceObjects(Architecture.class, null);
+ for (Architecture a : arch) {
+ System.out.println("Instance " + a.getInstanceDescription().getName());
+ }
+
+ // Check name
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "myInstance"));
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "instance1"));
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "instance2"));
+
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationOfTwoInstances.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationOfTwoInstances.java
new file mode 100644
index 0000000..120e8e9
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationOfTwoInstances.java
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.runtime.core.components.ConfigureTwoInstances;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+import org.junit.Test;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Check a @Configuration with 2 instances.
+ */
+public class TestConfigurationOfTwoInstances extends Common {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ deployTestedBundle = false;
+ Option[] options = super.config();
+
+ // Build a service bundle
+ return OptionUtils.combine(options,
+ streamBundle(
+ TinyBundles.bundle()
+ .add(FooService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.services")
+ .build(withBnd())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(ConfigureTwoInstances.class)
+ .add(MyComponent.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "MyComponent")
+ .build(IPOJOStrategy.withiPOJO())
+ )
+ );
+ }
+
+ @Test
+ public void testConfiguration() {
+ if (isKF()) {
+ return; // Test disabled on KF
+ }
+
+ osgiHelper.waitForService(FooService.class, null, 10000);
+
+ // Check we have two instances
+ ServiceReference[] refs = osgiHelper.getServiceReferences(FooService.class, null);
+ Assert.assertEquals(refs.length, 2);
+
+ List<Architecture> arch = osgiHelper.getServiceObjects(Architecture.class, null);
+ for (Architecture a : arch) {
+ System.out.println("Instance " + a.getInstanceDescription().getName());
+ }
+
+ // Check name
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "hello"));
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "myInstance1"));
+
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationWithInheritedInstance.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationWithInheritedInstance.java
new file mode 100644
index 0000000..ef3e646
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationWithInheritedInstance.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.runtime.core.components.ConfigureTwoInstancesWithInheritance;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+import org.apache.felix.ipojo.runtime.core.components.ParentConfiguration;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+import org.junit.Test;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Check a @Configuration inheriting an instance from a parent class.
+ */
+public class TestConfigurationWithInheritedInstance extends Common {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ deployTestedBundle = false;
+ Option[] options = super.config();
+
+ // Build a service bundle
+ return OptionUtils.combine(options,
+ streamBundle(
+ TinyBundles.bundle()
+ .add(FooService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.services")
+ .build(withBnd())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(ConfigureTwoInstancesWithInheritance.class)
+ .add(ParentConfiguration.class)
+ .add(MyComponent.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "MyComponent")
+ .build(IPOJOStrategy.withiPOJO())
+ )
+ );
+ }
+
+ @Test
+ public void testConfiguration() {
+ if (isKF()) {
+ return; // Test disabled on KF
+ }
+
+ osgiHelper.waitForService(FooService.class, null, 10000);
+
+ // Check we have two instances
+ ServiceReference[] refs = osgiHelper.getServiceReferences(FooService.class, null);
+ Assert.assertEquals(refs.length, 2);
+
+ List<Architecture> arch = osgiHelper.getServiceObjects(Architecture.class, null);
+ for (Architecture a : arch) {
+ System.out.println("Instance " + a.getInstanceDescription().getName());
+ }
+
+ // Check name
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "hello"));
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "myInstance1"));
+
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationWithOverriddenInstance.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationWithOverriddenInstance.java
new file mode 100644
index 0000000..3d86d11
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestConfigurationWithOverriddenInstance.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.runtime.core.components.ConfigureTwoInstancesWithOverridding;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+import org.apache.felix.ipojo.runtime.core.components.ParentConfiguration;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+import org.junit.Test;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Check a @Configuration overriding instance declaration.
+ */
+public class TestConfigurationWithOverriddenInstance extends Common {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ deployTestedBundle = false;
+ Option[] options = super.config();
+
+ // Build a service bundle
+ return OptionUtils.combine(options,
+ streamBundle(
+ TinyBundles.bundle()
+ .add(FooService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.services")
+ .build(withBnd())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(ConfigureTwoInstancesWithOverridding.class)
+ .add(ParentConfiguration.class)
+ .add(MyComponent.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "MyComponent")
+ .build(IPOJOStrategy.withiPOJO())
+ )
+ );
+ }
+
+ @Test
+ public void testConfiguration() {
+ if (isKF()) {
+ return; // Test disabled on KF
+ }
+
+ osgiHelper.waitForService(FooService.class, null, 10000);
+
+ // Check we have two instances
+ ServiceReference[] refs = osgiHelper.getServiceReferences(FooService.class, null);
+ Assert.assertEquals(refs.length, 2);
+
+ List<Architecture> arch = osgiHelper.getServiceObjects(Architecture.class, null);
+ for (Architecture a : arch) {
+ System.out.println("Instance " + a.getInstanceDescription().getName());
+ }
+
+ // Check name
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "hello-over"));
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "myInstance1"));
+
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestEmptyConfiguration.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestEmptyConfiguration.java
new file mode 100644
index 0000000..0eb70c7
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestEmptyConfiguration.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+import org.apache.felix.ipojo.runtime.core.components.ConfigureNothing;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+import org.junit.Test;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+
+import java.io.IOException;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Check an empty @Configuration
+ */
+public class TestEmptyConfiguration extends Common {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ deployTestedBundle = false;
+ Option[] options = super.config();
+
+ // Build a service bundle
+ return OptionUtils.combine(options,
+ streamBundle(
+ TinyBundles.bundle()
+ .add(FooService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.services")
+ .build(withBnd())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(MyComponent.class)
+ .add(ConfigureNothing.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "MyComponent")
+ .build(IPOJOStrategy.withiPOJO())
+ )
+ );
+ }
+
+ @Test
+ public void testConfiguration() throws InterruptedException {
+ Thread.sleep(200);
+ // Check configuration
+ Assert.assertNull(osgiHelper.getServiceReference(FooService.class));
+ }
+
+
+}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestSeveralConfigurations.java b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestSeveralConfigurations.java
new file mode 100644
index 0000000..693034c
--- /dev/null
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-configuration-processor-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestSeveralConfigurations.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+import junit.framework.Assert;
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.runtime.core.components.ConfigureAnotherInstance;
+import org.apache.felix.ipojo.runtime.core.components.ConfigureOneInstance;
+import org.apache.felix.ipojo.runtime.core.components.MyComponent;
+import org.apache.felix.ipojo.runtime.core.services.FooService;
+import org.junit.Test;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Check several configuration @Configuration in the same bundle.
+ */
+public class TestSeveralConfigurations extends Common {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ deployTestedBundle = false;
+ Option[] options = super.config();
+
+ // Build a service bundle
+ return OptionUtils.combine(options,
+ streamBundle(
+ TinyBundles.bundle()
+ .add(FooService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ServiceInterface")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.felix.ipojo.runtime.core.services")
+ .build(withBnd())
+ ),
+ streamBundle(
+ TinyBundles.bundle()
+ .add(ConfigureOneInstance.class)
+ .add(ConfigureAnotherInstance.class)
+ .add(MyComponent.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "MyComponent")
+ .build(IPOJOStrategy.withiPOJO())
+ )
+ );
+ }
+
+ @Test
+ public void testConfiguration() {
+ if (isKF()) {
+ return; // Test disabled on KF
+ }
+
+ osgiHelper.waitForService(FooService.class, null, 10000);
+
+ // Check we have two instances
+ ServiceReference[] refs = osgiHelper.getServiceReferences(FooService.class, null);
+ Assert.assertEquals(refs.length, 2);
+
+ List<Architecture> arch = osgiHelper.getServiceObjects(Architecture.class, null);
+ for (Architecture a : arch) {
+ System.out.println("Instance " + a.getInstanceDescription().getName());
+ }
+
+ // Check name
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "anotherInstance"));
+ Assert.assertNotNull(ipojoHelper.getServiceReferenceByName(FooService.class.getName(), "myInstance"));
+
+ }
+
+
+}
diff --git a/ipojo/runtime/core/pom.xml b/ipojo/runtime/core/pom.xml
index 0751152..cd65431 100644
--- a/ipojo/runtime/core/pom.xml
+++ b/ipojo/runtime/core/pom.xml
@@ -116,11 +116,14 @@
</IPOJO-Extension>
<Import-Package>
org.osgi.framework;version=1.3, <!-- To support KF 2 -->
+ org.osgi.framework.wiring;resolution:=optional,
org.osgi.service.cm,
org.osgi.service.log,
org.osgi.util.tracker;version=1.3,
!sun.io,
- !net.sourceforge.cobertura.* <!-- To support code coverage -->
+ !net.sourceforge.cobertura.*, <!-- To support code coverage -->
+ !org.objectweb.asm.signature,
+ !org.objectweb.asm.tree
</Import-Package>
<Private-Package>
org.apache.felix.ipojo.handlers.architecture,
@@ -128,6 +131,7 @@
org.apache.felix.ipojo.handlers.lifecycle.controller,
org.apache.felix.ipojo.extender.internal*,
org.objectweb.asm;-split-package:=merge-last,
+ org.objectweb.asm.commons;-split-package:=merge-last,
org.apache.felix.ipojo.metadata,
<!-- Compendium packages -->
org.osgi.service.cm,
@@ -135,6 +139,7 @@
</Private-Package>
<Export-Package>
org.apache.felix.ipojo; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.configuration; version="${ipojo.package.version}",
org.apache.felix.ipojo.metadata; version="${ipojo.package.version}",
org.apache.felix.ipojo.architecture; version="${ipojo.package.version}",
org.apache.felix.ipojo.extender;
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/configuration/Configuration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/configuration/Configuration.java
new file mode 100644
index 0000000..76f3720
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/configuration/Configuration.java
@@ -0,0 +1,32 @@
+/*
+ * 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.configuration;
+
+/**
+ * A marker interface to detect configurations
+ */
+public @interface Configuration {
+
+ /**
+ * An optional name
+ * @return the optional configuration name
+ */
+ String value() default "";
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/configuration/Instance.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/configuration/Instance.java
new file mode 100644
index 0000000..e4994e3
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/configuration/Instance.java
@@ -0,0 +1,173 @@
+/*
+ * 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.configuration;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.Factory;
+
+import java.util.*;
+
+/**
+ * Instance Builder
+ */
+public class Instance {
+
+ private String factory;
+ private String name;
+ private List<Property> configuration;
+
+ public static Instance instance() {
+ return new Instance();
+ }
+
+ public static <T> FluentList<T> list(T... items) {
+ return new FluentList<T>(items);
+ }
+
+ public static <K, T> FluentMap<K, T> map(Pair<K, T>... pairs) {
+ return new FluentMap<K, T>(pairs);
+ }
+
+ public static <K, T> Pair<K, T> pair(K k, T v) {
+ return new Pair<K, T>(k, v);
+ }
+
+ public String factory() {
+ return factory;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public Dictionary<String, Object> configuration() {
+ Hashtable<String, Object> configuration = new Hashtable<String, Object>();
+ for (Property property : this.configuration) {
+ configuration.put(property.name, property.value);
+ }
+
+ if (name != null) {
+ configuration.put(Factory.INSTANCE_NAME_PROPERTY, name);
+ }
+
+ return configuration;
+ }
+
+ public Instance of(String factory) {
+ this.factory = factory;
+ return this;
+ }
+
+ public Instance of(Class clazz) {
+ this.factory = clazz.getName();
+ return this;
+ }
+
+ public Instance named(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Instance.Property<Object> with(String property) {
+ if (this.configuration == null) {
+ this.configuration = new ArrayList<Property>();
+ }
+ Property<Object> prop = new Property<Object>(property);
+ this.configuration.add(prop);
+ return prop;
+ }
+
+ public Instance nameIfUnnamed(String name) {
+ if (this.name == null) {
+ this.name = name;
+ }
+ return this;
+ }
+
+ public static class FluentList<T> extends ArrayList<T> {
+
+ public FluentList() {
+ super(new ArrayList<T>());
+ }
+
+ public FluentList(T... items) {
+ this();
+ addAll(Arrays.asList(items));
+ }
+
+ public FluentList<T> with(T o) {
+ add(o);
+ return this;
+ }
+ }
+
+ public static class FluentMap<K,T> extends LinkedHashMap<K, T> {
+
+ public FluentMap() {
+ super(new LinkedHashMap<K, T>());
+ }
+
+ public FluentMap(Pair<? extends K, ? extends T>... pairs) {
+ this();
+ with(pairs);
+ }
+
+ public FluentMap<K, T> with(Pair<? extends K, ? extends T>... pairs) {
+ for (Pair<? extends K, ? extends T> pair : pairs) {
+ this.put(pair.key, pair.value);
+ }
+ return this;
+ }
+
+ public FluentMap<K, T> putAt(K k, T value) {
+ this.put(k, value);
+ return this;
+ }
+ }
+
+ public static class Pair<K, V> {
+ private final K key;
+ private final V value;
+
+ Pair(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+ public class Property<T> {
+
+ private final String name;
+ private T value;
+
+ Property(String name) {
+ this.name = name;
+ }
+
+ public Instance setto(T value) {
+ this.value = value;
+ return Instance.this;
+ }
+
+ public T get() {
+ return value;
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java
index 7fc4427..af074ab 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java
@@ -21,10 +21,7 @@
import org.apache.felix.ipojo.EventDispatcher;
import org.apache.felix.ipojo.extender.internal.linker.DeclarationLinker;
-import org.apache.felix.ipojo.extender.internal.processor.ChainedBundleProcessor;
-import org.apache.felix.ipojo.extender.internal.processor.ComponentsBundleProcessor;
-import org.apache.felix.ipojo.extender.internal.processor.ExtensionBundleProcessor;
-import org.apache.felix.ipojo.extender.internal.processor.QueuingActivationProcessor;
+import org.apache.felix.ipojo.extender.internal.processor.*;
import org.apache.felix.ipojo.extender.internal.queue.ExecutorQueueService;
import org.apache.felix.ipojo.extender.internal.queue.PrefixedThreadFactory;
import org.apache.felix.ipojo.extender.internal.queue.SynchronousQueueService;
@@ -120,6 +117,7 @@
BundleProcessor extensionBundleProcessor = new ExtensionBundleProcessor(m_logger);
BundleProcessor componentsProcessor = new ComponentsBundleProcessor(m_logger);
+ BundleProcessor configurationProcessor = new ConfigurationProcessor(m_logger);
if (SYNCHRONOUS_PROCESSING_ENABLED) {
m_queueService = new EnforcedQueueService(
new HeaderPreferenceSelection(),
@@ -133,6 +131,7 @@
extensionBundleProcessor = new QueuingActivationProcessor(extensionBundleProcessor, m_queueService);
componentsProcessor = new QueuingActivationProcessor(componentsProcessor, m_queueService);
+ configurationProcessor = new QueuingActivationProcessor(configurationProcessor, m_queueService);
}
m_queueService.start();
@@ -140,7 +139,7 @@
m_linker = new DeclarationLinker(context, m_queueService);
m_linker.start();
- m_processor = ChainedBundleProcessor.create(extensionBundleProcessor, componentsProcessor);
+ m_processor = ChainedBundleProcessor.create(extensionBundleProcessor, componentsProcessor, configurationProcessor);
m_processor.start();
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ConfigurationAnnotationScanner.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ConfigurationAnnotationScanner.java
new file mode 100644
index 0000000..66f121d
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ConfigurationAnnotationScanner.java
@@ -0,0 +1,67 @@
+/*
+ * 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.extender.internal.processor;
+
+import org.apache.felix.ipojo.configuration.Configuration;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * Class visitor detecting @Configuration annotation.
+ * <p/>
+ * After the visit, two state variables are set:
+ * {@literal isConfiguration} is set to true if the class contained the @Configuration annotation
+ * {@literal parent} is set to the parent class if and only if it's not a java.* class (which don't contain the
+ * Configuration annotation) and {@literal isConfiguration} is set to false
+ */
+public class ConfigurationAnnotationScanner extends EmptyVisitor implements Opcodes {
+
+ private static final String CONFIGURATION_ANNOTATION_DESCRIPTOR = Type.getType(Configuration.class)
+ .getDescriptor();
+ private boolean m_isConfiguration = false;
+ private String m_super;
+
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ m_super = superName; // This is the internal class name.
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean vis) {
+ if (desc.equals(CONFIGURATION_ANNOTATION_DESCRIPTOR)) {
+ m_isConfiguration = true;
+ }
+ return null;
+ }
+
+ public boolean isConfiguration() {
+ return m_isConfiguration;
+ }
+
+ public String getParent() {
+ if (m_super == null || m_super.startsWith("java/") || m_isConfiguration) {
+ return null;
+ }
+ return m_super.replace("/", ".");
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ConfigurationProcessor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ConfigurationProcessor.java
new file mode 100644
index 0000000..9b1a351
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ConfigurationProcessor.java
@@ -0,0 +1,304 @@
+/*
+ * 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.extender.internal.processor;
+
+import org.apache.felix.ipojo.configuration.Instance;
+import org.apache.felix.ipojo.extender.internal.BundleProcessor;
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultInstanceDeclaration;
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultTypeDeclaration;
+import org.apache.felix.ipojo.util.InvocationResult;
+import org.apache.felix.ipojo.util.Log;
+import org.objectweb.asm.ClassReader;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleWiring;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.*;
+
+import static org.apache.felix.ipojo.util.Reflection.fields;
+import static org.apache.felix.ipojo.util.Reflection.methods;
+
+/**
+ * Processor looking for classes annotated with @Configuration and creating the corresponding instance declaration.
+ */
+public class ConfigurationProcessor implements BundleProcessor {
+
+ /**
+ * The logger.
+ */
+ private final Log m_logger;
+ /**
+ * Registry storing the bundle to components and instances declared within this bundle.
+ * Only instances are expected.
+ */
+ private final Map<Bundle, ComponentsAndInstances> m_registry = new HashMap<Bundle, ComponentsAndInstances>();
+ private final boolean m_enabled;
+
+ /**
+ * Creates the configuration processor.
+ *
+ * @param logger the logger.
+ */
+ public ConfigurationProcessor(Log logger) {
+
+ m_logger = logger;
+
+ // org.osgi.framework.wiring may not be available, in this case, disable us.
+ try {
+ this.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
+ } catch (ClassNotFoundException e) {
+ m_logger.log(Log.ERROR, "The org.osgi.framework.wiring.BundleWiring class is not provided by the " +
+ "framework, configuration processor disabled.");
+ m_enabled = false;
+ return;
+ }
+ // Check ok.
+ m_enabled = true;
+ }
+
+ public static String getClassNameFromResource(String resource) {
+ String res = resource;
+ if (resource.startsWith("/")) {
+ res = resource.substring(1); // Remove the first /
+ }
+ res = res.substring(0, res.length() - ".class".length()); // Remove the .class
+ return res.replace("/", ".");
+ }
+
+ /**
+ * A bundle is starting.
+ *
+ * @param bundle the bundle
+ */
+ public void activate(Bundle bundle) {
+ if (! m_enabled) { return; }
+
+ // TODO - optimization, ignore all bundles that do not import org.apache.felix.ipojo.configuration
+
+ BundleWiring wiring = bundle.adapt(BundleWiring.class);
+ if (wiring == null) {
+ // Invalid state.
+ m_logger.log(Log.ERROR, "The bundle " + bundle.getBundleId() + " (" + bundle.getSymbolicName() + ") " +
+ "cannot be adapted to BundleWiring, state: " + bundle.getState());
+ return;
+ }
+
+ // Only lookup for local classes, parent classes will be analyzed on demand.
+ Collection<String> resources = wiring.listResources("/", "*.class",
+ BundleWiring.FINDENTRIES_RECURSE + BundleWiring.LISTRESOURCES_LOCAL);
+ if (resources == null) {
+ m_logger.log(Log.ERROR, "The bundle " + bundle.getBundleId() + " (" + bundle.getSymbolicName() + ") " +
+ " does not have any classes to be analyzed");
+ return;
+ }
+ m_logger.log(Log.DEBUG, resources.size() + " classes found");
+ handleResources(bundle, resources, wiring.getClassLoader());
+ }
+
+ /**
+ * A bundle is stopping.
+ *
+ * @param bundle the bundle
+ */
+ public void deactivate(Bundle bundle) {
+ if (! m_enabled) { return; }
+
+ ComponentsAndInstances cai = m_registry.remove(bundle);
+ if (cai != null) {
+ cai.stop();
+ }
+ }
+
+ /**
+ * {@inheritDoc BundleProcessor#start}
+ */
+ public void start() {
+ // Nothing to do
+ }
+
+ /**
+ * {@inheritDoc BundleProcessor#stop}
+ * <p/>
+ * This method cleans up all created factories and instances.
+ */
+ public void stop() {
+ // Ignored, for a simple ordered shutdown, use ReverseBundleProcessor
+ }
+
+ private void handleResources(Bundle bundle, Collection<String> resources, ClassLoader classLoader) {
+ for (String resource : resources) {
+ handleResource(bundle, resource, classLoader);
+ }
+ }
+
+ private void handleResource(Bundle bundle, String resource, ClassLoader classLoader) {
+ URL url = classLoader.getResource(resource);
+ if (url == null) {
+ m_logger.log(Log.ERROR, "The resource " + resource + " cannot be loaded by " + bundle.getBundleId() + " " +
+ "(" + bundle.getSymbolicName() + ")");
+ return;
+ }
+
+ try {
+ if (hasConfigurationAnnotation(bundle, url, classLoader)) {
+ instantiateAndDeclareInstances(bundle, resource, classLoader);
+ }
+ } catch (IOException e) {
+ m_logger.log(Log.ERROR, "The resource " + resource + " cannot be loaded by " + bundle.getBundleId() + " " +
+ "(" + bundle.getSymbolicName() + ")", e);
+ }
+
+ }
+
+ private void instantiateAndDeclareInstances(Bundle bundle, String resource, ClassLoader classLoader) {
+ String classname = getClassNameFromResource(resource);
+ List<Instance> instances = new ArrayList<Instance>();
+ try {
+ Class clazz = classLoader.loadClass(classname);
+ Object configuration = clazz.newInstance();
+
+ // Collect fields
+ Map<Field, Instance> fields =
+ fields().ofType(Instance.class).in(configuration).map();
+ for (Map.Entry<Field, Instance> entry : fields.entrySet()) {
+ Instance instance = entry.getValue();
+ instance.nameIfUnnamed(entry.getKey().getName());
+ instances.add(instance);
+ }
+
+ // Collect methods with Bundle Context as argument
+ Map<Method, InvocationResult<Instance>> methods =
+ methods().in(configuration).ofReturnType(Instance.class).withParameter(BundleContext.class)
+ .map(bundle.getBundleContext());
+
+ // Collect methods without arguments
+ methods.putAll(methods().in(configuration).ofReturnType(Instance.class).map());
+
+ for (Map.Entry<Method, InvocationResult<Instance>> entry : methods.entrySet()) {
+ Instance instance = entry.getValue().get();
+ if (instance == null) {
+ m_logger.log(Log.ERROR, "The Instance declaration creation failed because the method " + entry
+ .getKey().getName() + " of class " + entry.getKey().getDeclaringClass() + " threw an " +
+ "exception", entry.getValue().error());
+ } else {
+ instance.nameIfUnnamed(entry.getKey().getName());
+ instances.add(instance);
+ }
+ }
+
+ } catch (ClassNotFoundException e) {
+ m_logger.log(Log.ERROR, "Cannot load class " + classname + " despite it was considered as a " +
+ "configuration", e);
+ return;
+ } catch (InstantiationException e) {
+ m_logger.log(Log.ERROR, "Cannot instantiate class " + classname + " despite it was considered as a " +
+ "configuration", e);
+ return;
+ } catch (IllegalAccessException e) {
+ m_logger.log(Log.ERROR, "Cannot instantiate class " + classname + " despite it was considered as a " +
+ "configuration", e);
+ return;
+ }
+
+ m_logger.log(Log.WARNING, instances.size() + " instances found in class " + classname);
+ //Build and enqueue declaration
+ for (Instance instance : instances) {
+ DefaultInstanceDeclaration declaration = new DefaultInstanceDeclaration(bundle.getBundleContext(),
+ instance.factory(), instance.configuration());
+ declaration.start();
+ getComponentsAndInstances(bundle).m_instances.add(declaration);
+ }
+ }
+
+ private boolean hasConfigurationAnnotation(Bundle bundle, URL url, ClassLoader classLoader) throws IOException {
+ InputStream is = url.openStream();
+ try {
+ ClassReader reader = new ClassReader(is);
+ ConfigurationAnnotationScanner scanner = new ConfigurationAnnotationScanner();
+ reader.accept(scanner, 0);
+
+ // Only class with the @Configuration are considered, parent classes are not analyzed.
+ // Indeed, we have to detect when the parent must be considered independently,
+ // or when only the daughter needs too (to avoid creating the instances twice).
+
+ return scanner.isConfiguration();
+
+ // The following code would traverse the whole class hierarchy.
+// if (scanner.isConfiguration()) {
+// return true;
+// } else if (scanner.getParent() != null) {
+// URL parentUrl = classLoader.getResource("/" + scanner.getParent().replace(".", "/") + ".class");
+// if (parentUrl == null) {
+// m_logger.log(Log.DEBUG, "Cannot load the parent class " + scanner.getParent() + " - stopping " +
+// "scan");
+// return false;
+// }
+// return hasConfigurationAnnotation(bundle, parentUrl, classLoader);
+// }
+ } finally {
+ is.close();
+ }
+ }
+
+ /**
+ * Gets the {@link ComponentsAndInstances} declared by the given bundle.
+ *
+ * @param bundle the bundle
+ * @return the set of component and instances declared by the bundle, <code>null</code> otherwise
+ */
+ private ComponentsAndInstances getComponentsAndInstances(Bundle bundle) {
+ ComponentsAndInstances cai = m_registry.get(bundle);
+ if (cai == null) {
+ cai = new ComponentsAndInstances();
+ m_registry.put(bundle, cai);
+ }
+ return cai;
+ }
+
+ /**
+ * Container storing the components and instances declared by a bundle.
+ * This class is not intended to be used outside from the current processor.
+ */
+ private static class ComponentsAndInstances {
+ List<DefaultTypeDeclaration> m_types = new ArrayList<DefaultTypeDeclaration>();
+ List<DefaultInstanceDeclaration> m_instances = new ArrayList<DefaultInstanceDeclaration>();
+
+ /**
+ * Stops all declarations.
+ */
+ void stop() {
+ for (DefaultInstanceDeclaration instance : m_instances) {
+ instance.stop();
+ }
+ for (DefaultTypeDeclaration declaration : m_types) {
+ declaration.stop();
+ }
+ m_instances.clear();
+ m_types.clear();
+ }
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Fields.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Fields.java
new file mode 100644
index 0000000..3dc5b78
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Fields.java
@@ -0,0 +1,134 @@
+/*
+ * 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 java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+* Fluent API to retrieve fields of a given type.
+*/
+public class Fields<T> {
+
+ private Class<? extends T> type;
+ private Class<?> clazz;
+ private Object object;
+ private Map<String, Field> fields;
+
+ public Collection<T> get() {
+ return map().values();
+ }
+
+ public Map<Field, T> map(Object o) {
+ this.object = o;
+ return map();
+ }
+
+ public Map<Field, T> map() {
+ Collection<Field> set = retrieve();
+ Map<Field, T> results = new LinkedHashMap<Field, T>();
+ for (Field field : set) {
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+
+ try {
+ results.put(field, (T) field.get(object));
+ } catch (IllegalAccessException e) {
+ // Hopefully should not happen.
+ }
+ }
+ return results;
+ }
+
+ public Collection<T> get(Object o) {
+ this.object = o;
+ return get();
+ }
+
+ public Fields ofType(Class<? extends T> clazz) {
+ this.type = clazz;
+ return this;
+ }
+
+ public Fields in(Object o) {
+ this.object = o;
+ this.clazz = o.getClass();
+ return this;
+ }
+
+ public Fields in(Class<?> c) {
+ this.clazz = c;
+ return this;
+ }
+
+ private Collection<Field> retrieve() {
+ if (fields != null) {
+ return fields.values();
+ }
+
+ if (clazz == null) {
+ throw new NullPointerException("Cannot retrieve field, class not set");
+ }
+
+
+ fields = new LinkedHashMap<String, Field>();
+
+ // First the class itself
+ Field[] list = clazz.getDeclaredFields();
+ for (Field field : list) {
+ if (type == null || type.isAssignableFrom(field.getType())) {
+ fields.put(field.getName(), field);
+ }
+ }
+
+ // Traverse class hierarchy
+ if (clazz.getSuperclass() != null) {
+ traverse(fields, clazz.getSuperclass());
+ }
+
+ return fields.values();
+ }
+
+ private void traverse(Map<String, Field> fields, Class<?> clazz) {
+ // First the given class
+ Field[] list = clazz.getDeclaredFields();
+ for (Field field : list) {
+ if (type == null || type.isAssignableFrom(field.getType())) {
+ // Already defined by a daughter class
+ if (!fields.containsKey(field.getName())) {
+ // Check visibility if must be either public or protected.
+ if (Modifier.isPublic(field.getModifiers()) || Modifier.isProtected(field.getModifiers())) {
+ fields.put(field.getName(), field);
+ }
+ }
+ }
+ }
+
+ // If we have a parent class, traverse it
+ if (clazz.getSuperclass() != null) {
+ traverse(fields, clazz.getSuperclass());
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/InvocationResult.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/InvocationResult.java
new file mode 100644
index 0000000..96812a5
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/InvocationResult.java
@@ -0,0 +1,76 @@
+/*
+ * 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 java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Wraps the result of a method invocation
+ */
+public class InvocationResult<T> {
+
+ private final Method method;
+ private final Object target;
+ private final T result;
+ private final Throwable error;
+
+ public InvocationResult(Method method, Object target, T result, Throwable error) {
+ this.method = method;
+ this.target = target;
+ this.result = result;
+ this.error = error;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public Object getTarget() {
+ return target;
+ }
+
+ public Throwable error() {
+ return error;
+ }
+
+ public T get() {
+ return result;
+ }
+
+ public T getOrElse(T def) {
+ if (error == null) {
+ return def;
+ } else {
+ return result;
+ }
+ }
+
+ public static <T> InvocationResult<T> fromInvocation(Method method, Object target, Object[] args) {
+ try {
+ T result = (T) method.invoke(target, args);
+ return new InvocationResult<T>(method, target, result, null);
+ } catch (IllegalAccessException e) {
+ return new InvocationResult<T>(method, target, null, e);
+ } catch (InvocationTargetException e) {
+ return new InvocationResult<T>(method, target, null, e);
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Methods.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Methods.java
new file mode 100644
index 0000000..d25d02c
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Methods.java
@@ -0,0 +1,189 @@
+/*
+ * 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 java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * Fluent API to retrieve methods.
+ */
+public class Methods<T> {
+
+ private Class<? extends T> returnType;
+ private List<Class<?>> argumentTypes = new ArrayList<Class<?>>();
+ private Class<?> clazz;
+ private Object object;
+ private List<Method> methods;
+
+ public Map<Method, InvocationResult<T>> map(Object... args) {
+ Collection<Method> set = retrieve();
+ Map<Method, InvocationResult<T>> results = new LinkedHashMap<Method, InvocationResult<T>>();
+ for (Method method : set) {
+ if (!method.isAccessible()) {
+ method.setAccessible(true);
+ }
+
+ results.put(method, InvocationResult.<T>fromInvocation(method, object, args));
+
+ }
+ return results;
+ }
+
+ public Collection<InvocationResult<T>> invoke(Object... args) {
+ return map(args).values();
+ }
+
+ public Methods ofReturnType(Class<? extends T> clazz) {
+ this.returnType = clazz;
+ return this;
+ }
+
+ public Methods withParameter(Class<?>... type) {
+ argumentTypes.addAll(Arrays.asList(type));
+ return this;
+ }
+
+ public Methods in(Object o) {
+ this.object = o;
+ this.clazz = o.getClass();
+ return this;
+ }
+
+ public Methods in(Class<?> c) {
+ this.clazz = c;
+ this.object = null;
+ return this;
+ }
+
+ private Collection<Method> retrieve() {
+ if (methods != null) {
+ return methods;
+ }
+
+ if (clazz == null) {
+ throw new NullPointerException("Cannot retrieve method, class not set");
+ }
+
+
+ methods = new ArrayList<Method>();
+
+ // First the class itself
+ Method[] list = clazz.getDeclaredMethods();
+ for (Method method : list) {
+ // Two criteria : the return type and the argument type
+ if (matchReturnType(method)
+ && matchArgumentTypes(method)) {
+ // The method matches
+ methods.add(method);
+ }
+ }
+
+ // Traverse class hierarchy
+ if (clazz.getSuperclass() != null) {
+ traverse(methods, clazz.getSuperclass());
+ }
+
+ return methods;
+ }
+
+ private boolean matchReturnType(Method method) {
+ if (returnType == null) { // Void.
+ return method.getReturnType() == null;
+ }
+
+ return !(method.getReturnType() == null || !returnType.isAssignableFrom(method.getReturnType()));
+ }
+
+ private boolean matchArgumentTypes(Method method) {
+ // Fast check, the size must be the same.
+ if (argumentTypes.size() != method.getParameterTypes().length) {
+ return false;
+ }
+
+ // We have the same size.
+ for (int i = 0; i < argumentTypes.size(); i++) {
+ Class<?> argType = method.getParameterTypes()[i];
+ if (!argumentTypes.get(i).isAssignableFrom(argType)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean matchInheritanceVisibility(Method method) {
+ return Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers());
+ }
+
+ private boolean matchNotOverridden(Method method, List<Method> methods) {
+ for (Method meth : methods) {
+ if (methodEquality(meth, method)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compares this <code>Method</code> against the specified object. Returns
+ * true if the objects are the same. Two <code>Methods</code> are the same if
+ * they were declared by the same class and have the same name
+ * and formal parameter types and return type.
+ */
+ private boolean methodEquality(Method method1, Method method2) {
+ if (method1.getName().equals(method2.getName())) {
+ if (!method1.getReturnType().equals(method2.getReturnType())) {
+ return false;
+ }
+
+ Class[] params1 = method1.getParameterTypes();
+ Class[] params2 = method2.getParameterTypes();
+ if (params1.length == params2.length) {
+ for (int i = 0; i < params1.length; i++) {
+ if (params1[i] != params2[i])
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ return false;
+ }
+
+ private void traverse(List<Method> methods, Class<?> clazz) {
+ // First the given class
+ Method[] list = clazz.getDeclaredMethods();
+ for (Method method : list) {
+ if (matchReturnType(method) && matchArgumentTypes(method) && matchInheritanceVisibility(method)
+ && matchNotOverridden(method, methods)) {
+ methods.add(method);
+ }
+ }
+
+ // If we have a parent class, traverse it
+ if (clazz.getSuperclass() != null) {
+ traverse(methods, clazz.getSuperclass());
+ }
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Reflection.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Reflection.java
new file mode 100644
index 0000000..44201f3
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Reflection.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * Class containing utility method helping reflection
+ */
+public class Reflection {
+
+ public static <T> Fields<T> fields() {
+ return new Fields<T>();
+ }
+
+ public static <T> Methods<T> methods() {
+ return new Methods<T>();
+ }
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/configuration/InstanceDSLTest.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/configuration/InstanceDSLTest.java
new file mode 100644
index 0000000..c3fac11
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/configuration/InstanceDSLTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.configuration;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.felix.ipojo.extender.internal.processor.ConfigurationProcessor;
+
+import static org.apache.felix.ipojo.configuration.Instance.*;
+
+/**
+ * Check the instance DSL.
+ */
+public class InstanceDSLTest extends TestCase {
+
+
+ public void testInstance() {
+ instance().of("my.factory")
+ .with("simple").setto("simple");
+
+ instance()
+ .of("my.factory")
+ .with("simple").setto("simple")
+
+ .with("list").setto(list(1, 2, 3))
+ .with("list2").setto(list().with(1).with(2).with(3))
+
+ .with("map").setto(map().with(pair("entry", "value")))
+ .with("map").setto(map()
+ .with(pair("entry2", list("aaa", "bbb"))));
+ }
+
+ public void testClassnameExtraction() {
+ String cn = ConfigurationProcessor.getClassNameFromResource("/org/apache/felix/ipojo/Pojo.class");
+ Assert.assertEquals(cn, "org.apache.felix.ipojo.Pojo");
+ }
+}