FELIX-4419 Open access to InstanceDeclaration and TypeDeclaration
* Added DeclarationBuilderService interface
* InstanceBuilder produces DeclarationHandle to XYZDeclaration
* Declarations now also implements DeclarationHandle
* Added some core-it tests to show typical service usage
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1571275 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/runtime/core-it/ipojo-core-declaration-test/pom.xml b/ipojo/runtime/core-it/ipojo-core-declaration-test/pom.xml
new file mode 100644
index 0000000..e1df065
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-declaration-test/pom.xml
@@ -0,0 +1,60 @@
+<?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.11.2-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ipojo-core-declaration-test</artifactId>
+
+ <name>${project.artifactId}</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.api</artifactId>
+ <version>1.11.2-SNAPSHOT</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>asm</groupId>
+ <artifactId>asm-all</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/EnglishHelloService.java b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/EnglishHelloService.java
new file mode 100644
index 0000000..9668318
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/EnglishHelloService.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.test.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.test.services.HelloService;
+
+/**
+ * User: guillaume
+ * Date: 20/02/2014
+ * Time: 13:29
+ */
+@Component(name = "hello-service", version = "2.0")
+@Provides
+public class EnglishHelloService implements HelloService {
+
+ @Property("Hello2")
+ private String message;
+
+ @Override
+ public String hello(final String name) {
+ return message + " " + name;
+ }
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/EnglishHelloService2.java b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/EnglishHelloService2.java
new file mode 100644
index 0000000..6d986d2
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/EnglishHelloService2.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.test.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.test.services.HelloService;
+
+/**
+ * User: guillaume
+ * Date: 20/02/2014
+ * Time: 13:29
+ */
+@Component(name = "hello-service")
+@Provides
+public class EnglishHelloService2 implements HelloService {
+
+ @Property("Hello")
+ private String message;
+
+ @Override
+ public String hello(final String name) {
+ return message + " " + name;
+ }
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/FrenchHelloService.java b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/FrenchHelloService.java
new file mode 100644
index 0000000..57ca670
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/FrenchHelloService.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.test.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.test.services.HelloService;
+
+/**
+ * User: guillaume
+ * Date: 20/02/2014
+ * Time: 13:29
+ */
+@Component
+@Provides
+public class FrenchHelloService implements HelloService {
+
+ @Property("Bonjour")
+ private String message;
+
+ @Override
+ public String hello(final String name) {
+ return message + " " + name;
+ }
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/GermanHelloService.java b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/GermanHelloService.java
new file mode 100644
index 0000000..fe9db64
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/GermanHelloService.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.ipojo.runtime.core.test.components;
+
+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.test.services.HelloService;
+
+/**
+ * I intentionally left empty the component name plus the @Provides annotation.
+ * This is just to trigger the component manipulation
+ */
+@Component
+public class GermanHelloService implements HelloService {
+
+ @Override
+ public String hello(final String name) {
+ return "Hallo " + name;
+ }
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/HelloService.java b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/HelloService.java
new file mode 100644
index 0000000..abfff19
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/HelloService.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.ipojo.runtime.core.test.services;
+
+/**
+ * User: guillaume
+ * Date: 20/02/2014
+ * Time: 13:29
+ */
+public interface HelloService {
+ String hello(String name);
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-declaration-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/declaration/Common.java b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/declaration/Common.java
new file mode 100644
index 0000000..198ab5c
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/declaration/Common.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.ipojo.runtime.core.test.declaration;
+
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.options.CompositeOption;
+import org.ops4j.pax.exam.options.DefaultCompositeOption;
+import org.ow2.chameleon.testing.helpers.BaseTest;
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * Bootstrap the test from this project
+ */
+public class Common extends BaseTest {
+ @Override
+ public boolean quiet() {
+ return false;
+ }
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-declaration-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/declaration/TestDeclarationBuilderService.java b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/declaration/TestDeclarationBuilderService.java
new file mode 100644
index 0000000..ec97f01
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-declaration-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/declaration/TestDeclarationBuilderService.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.ipojo.runtime.core.test.declaration;
+
+import org.apache.felix.ipojo.IPojoFactory;
+//import org.apache.felix.ipojo.api.PrimitiveComponentType;
+//import org.apache.felix.ipojo.api.Service;
+import org.apache.felix.ipojo.extender.DeclarationBuilderService;
+import org.apache.felix.ipojo.extender.DeclarationHandle;
+import org.apache.felix.ipojo.extender.ExtensionDeclaration;
+import org.apache.felix.ipojo.extender.builder.FactoryBuilder;
+import org.apache.felix.ipojo.extender.builder.FactoryBuilderException;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.runtime.core.test.services.HelloService;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+import static java.lang.String.format;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class TestDeclarationBuilderService extends Common {
+
+ private DeclarationBuilderService builder;
+ private DeclarationHandle handle;
+
+ @Override
+ protected List<String> getExtraExports() {
+ // The important thing here is to make sure that this package is in the bundle
+ return Arrays.asList("org.apache.felix.ipojo.runtime.core.test.components");
+ }
+
+ @Before
+ public void setUp() {
+ builder = osgiHelper.getServiceObject(DeclarationBuilderService.class);
+ }
+
+ @After
+ public void tearDown() {
+ if (handle != null) {
+ handle.retract();
+ }
+ }
+
+ @Test
+ public void testAnonymousInstanceCreation() {
+ handle = builder.newInstance("org.apache.felix.ipojo.runtime.core.test.components.FrenchHelloService")
+ .build();
+
+ // When a service is registered, all events are fired synchronously, we
+ // can safely test the declaration binding
+ assertFalse(handle.getStatus().isBound());
+ handle.publish();
+
+ osgiHelper.waitForService(HelloService.class, null, 1000);
+
+ assertTrue(handle.getStatus().isBound());
+ handle.retract();
+ assertFalse(handle.getStatus().isBound());
+ }
+
+ @Test
+ public void testNamedInstanceCreation() {
+ handle = builder.newInstance("org.apache.felix.ipojo.runtime.core.test.components.FrenchHelloService")
+ .name("bonjour-service")
+ .build();
+
+ handle.publish();
+ assertTrue(ipojoHelper.isServiceAvailableByName(HelloService.class.getName(), "bonjour-service"));
+ }
+
+ @Test
+ public void testConfiguredInstanceCreation() {
+ handle = builder.newInstance("org.apache.felix.ipojo.runtime.core.test.components.FrenchHelloService")
+ .name("bonjour-service")
+ .configure()
+ .property("message", "Salut")
+ .build();
+
+ handle.publish();
+ assertTrue(ipojoHelper.isServiceAvailableByName(HelloService.class.getName(), "bonjour-service"));
+
+ HelloService service = osgiHelper.getServiceObject(HelloService.class, format("(instance.name=%s)", "bonjour-service"));
+ assertEquals(service.hello("Guillaume"), "Salut Guillaume");
+ }
+
+ @Test
+ public void testVersionedTypeInstanceCreation() {
+ handle = builder.newInstance("hello-service")
+ .version("2.0")
+ .name("hello2")
+ .build();
+
+ handle.publish();
+
+ String filter = format("(instance.name=%s)", "hello2");
+ osgiHelper.waitForService(HelloService.class, filter, 1000);
+ HelloService service = osgiHelper.getServiceObject(HelloService.class, filter);
+ assertEquals(service.hello("Guillaume"), "Hello2 Guillaume");
+ }
+
+ @Test
+ public void testExtensionCreation() {
+ handle = builder.newExtension("test", new EmptyFactoryBuilder());
+
+ handle.publish();
+
+ osgiHelper.waitForService(ExtensionDeclaration.class, null, 1000);
+ }
+
+ @Test
+ public void testTypeCreation() throws Exception {
+
+ handle = builder.newType(germanComponent());
+ handle.publish();
+
+ DeclarationHandle instance = builder.newInstance("german-service")
+ .name("german-hello")
+ .build();
+ instance.publish();
+
+ String filter = format("(instance.name=%s)", "german-hello");
+ osgiHelper.waitForService(HelloService.class, filter, 1000);
+ HelloService service = osgiHelper.getServiceObject(HelloService.class, filter);
+ assertEquals(service.hello("Guillaume"), "Hallo Guillaume");
+
+ instance.retract();
+
+ }
+
+ /*
+ @Test
+ public void testTypeCreationFromAPI() throws Exception {
+ PrimitiveComponentType type = new PrimitiveComponentType()
+ .setClassName("org.apache.felix.ipojo.runtime.core.test.components.GermanHelloService")
+ .setComponentTypeName("german-service")
+ .addService(new Service());
+
+ Element description = type.getFactory().getComponentMetadata();
+ handle = builder.newType(description);
+ handle.publish();
+
+ DeclarationHandle instance = builder.newInstance("german-service")
+ .name("german-hello")
+ .build();
+ instance.publish();
+
+ String filter = format("(instance.name=%s)", "german-hello");
+ osgiHelper.waitForService(HelloService.class, filter, 1000);
+ HelloService service = osgiHelper.getServiceObject(HelloService.class, filter);
+ assertEquals(service.hello("Guillaume"), "Hallo Guillaume");
+
+ instance.retract();
+
+ }
+ */
+
+ private Element germanComponent() {
+ Element component = new Element("component", null);
+ component.addAttribute(new Attribute("name", "german-service"));
+ component.addAttribute(new Attribute("classname", "org.apache.felix.ipojo.runtime.core.test.components.GermanHelloService"));
+ component.addElement(new Element("provides", null));
+ component.addElement(manipulation());
+ return component;
+ }
+
+ private Element manipulation() {
+ Element manipulation = new Element("manipulation", null);
+ manipulation.addAttribute(new Attribute("classname", "org.apache.felix.ipojo.runtime.core.test.components.GermanHelloService"));
+ manipulation.addAttribute(new Attribute("super", "java.lang.Object"));
+
+ Element itf = new Element("interface", null);
+ itf.addAttribute(new Attribute("name", "org.apache.felix.ipojo.runtime.core.test.services.HelloService"));
+ manipulation.addElement(itf);
+
+ Element method = new Element("method", null);
+ method.addAttribute(new Attribute("name", "hello"));
+ method.addAttribute(new Attribute("return", "java.lang.String"));
+ method.addAttribute(new Attribute("arguments", "{java.lang.String}"));
+ method.addAttribute(new Attribute("names", "{name}"));
+ manipulation.addElement(method);
+
+ return manipulation;
+ }
+
+ private static class EmptyFactoryBuilder implements FactoryBuilder {
+ @Override
+ public IPojoFactory build(final BundleContext bundleContext, final Element metadata) throws FactoryBuilderException {
+ return null;
+ }
+ }
+}
diff --git a/ipojo/runtime/core-it/pom.xml b/ipojo/runtime/core-it/pom.xml
index 70758cc..35dfbf5 100644
--- a/ipojo/runtime/core-it/pom.xml
+++ b/ipojo/runtime/core-it/pom.xml
@@ -46,11 +46,15 @@
</properties>
<modules>
+<!--
<module>ipojo-core-annotations-test</module>
<module>ipojo-core-bad-configuration-test</module>
<module>ipojo-core-configuration-admin-test</module>
<module>ipojo-core-configuration-processor-test</module>
<module>ipojo-core-configuration-test</module>
+-->
+ <module>ipojo-core-declaration-test</module>
+<!--
<module>ipojo-core-external-handlers-test</module>
<module>ipojo-core-factory-test</module>
<module>ipojo-core-factory-version-test</module>
@@ -67,6 +71,7 @@
<module>ipojo-core-service-providing-test</module>
<module>ipojo-api-test</module>
<module>ipojo-compatibility-test</module>
+-->
</modules>
<build>
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/ConfigurationBuilder.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/ConfigurationBuilder.java
new file mode 100644
index 0000000..b3c2537
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/ConfigurationBuilder.java
@@ -0,0 +1,61 @@
+/*
+ * 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;
+
+/**
+ * Support class for fluent instance declaration building.
+ * This class can be used to provide values for instance configuration.
+ * Any object can be used as configuration values (List and Maps are accepted for example).
+ *
+ * @since 1.12
+ */
+public interface ConfigurationBuilder {
+
+ /**
+ * Provide a property value.
+ * @param name property name
+ * @param value property value
+ * @return this builder
+ */
+ ConfigurationBuilder property(String name, Object value);
+
+ /**
+ * Remove a property from the configuration.
+ * This does not affect already created declarations (from this builder).
+ * @param name property name
+ * @return this builder
+ */
+ ConfigurationBuilder remove(String name);
+
+ /**
+ * Remove all properties from the configuration.
+ * This does not affect already created declarations (from this builder).
+ * @return this builder
+ */
+ ConfigurationBuilder clear();
+
+ /**
+ * Build the declaration handle (contains the instance configuration).
+ * Notice that the declaration is not yet published (no automatic activation).
+ * The client has to do it through {@link DeclarationHandle#publish()}
+ * @return the handle to the configured declaration
+ */
+ DeclarationHandle build();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/DeclarationBuilderService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/DeclarationBuilderService.java
new file mode 100644
index 0000000..731acff
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/DeclarationBuilderService.java
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+import org.apache.felix.ipojo.extender.builder.FactoryBuilder;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * This service provides a way for users to manage declarations through code.
+ *
+ * Notice that produced declarations are immutable once build. But it is possible to re-use a
+ * builder to share some configuration through instances.
+ *
+ * <pre>
+ * // Obtain the service through the service registry
+ * DeclarationBuilderService service = ...
+ *
+ * // Get a fresh instance builder
+ * DeclarationBuilder builder = service.newInstance("the.full.name.of.the.component.to.instantiate");
+ *
+ * DeclarationHandle handle = builder.name("a-unique-name") // Make sure name is unique for the expected type
+ * .configure()
+ * .property("a-property", "a-value")
+ * .property("another-property", "another-value")
+ * .build();
+ *
+ * // Push the InstanceDeclaration service in the registry
+ * handle.publish();
+ * </pre>
+ *
+ * Except the {@link InstanceBuilder#build()} call, all methods are optional:
+ * <ul>
+ * <li>{@link InstanceBuilder#name(String)}: if no name is provided,
+ * a default one will be generated by iPOJO.</li>
+ * <li>{@link InstanceBuilder#version(String)}: if no version is provided,
+ * the first un-versioned type will be used.</li>
+ * <li>{@link InstanceBuilder#configure()}: if no configuration is required, can be omitted.</li>
+ * <li>{@link InstanceBuilder#type(String)}: can be used to change the <bold>required</bold> component type.</li>
+ * <li>{@link InstanceBuilder#context(org.osgi.framework.BundleContext)}: by default, the bundle context
+ * used to register the created {@link org.apache.felix.ipojo.extender.Declaration} is the one of the
+ * caller. It can be changed though this method.</li>
+ * </ul>
+ *
+ * Once an instance handle has been created, its configuration (name, type, version and properties) is immutable. It can
+ * only be {@linkplain DeclarationHandle#publish() published} (so that the framework will try to instantiate the
+ * instance) or {@linkplain DeclarationHandle#retract() retracted} (framework will remove the instance).
+ *
+ * Notice that all created instances will appear as "coming from" the bundle that requires the
+ * {@link DeclarationBuilderService} service. Just like having a {@literal metadata.xml} file in your
+ * bundle that declares your instances. It is possible to override this default behavior using
+ * {@link InstanceBuilder#context(org.osgi.framework.BundleContext)}.
+ *
+ * @see org.apache.felix.ipojo.extender.InstanceDeclaration
+ * @see org.apache.felix.ipojo.extender.TypeDeclaration
+ * @see org.apache.felix.ipojo.extender.ExtensionDeclaration
+ * @see org.apache.felix.ipojo.extender.InstanceBuilder
+ * @see org.apache.felix.ipojo.extender.ConfigurationBuilder
+ * @see org.apache.felix.ipojo.extender.DeclarationHandle
+ *
+ * @since 1.12
+ */
+public interface DeclarationBuilderService {
+
+ /**
+ * Declares a new anonymous instance of a given type.
+ * Invoking this method is equivalent to invoking <code>{@linkplain #newInstance(String, String) newInstance(type, null)}</code>.
+ * @param type name of the component to be instantiated (cannot be null).
+ * @return a handle usable to publish / retract the declaration.
+ * @see org.apache.felix.ipojo.extender.InstanceDeclaration
+ */
+ InstanceBuilder newInstance(String type);
+
+ /**
+ * Declares a new instance of a given type.
+ * Invoking this method is equivalent to invoking <code>{@linkplain #newInstance(String, String, String) newInstance(type, name, null)}</code>.
+ * @param type name of the component to be instantiated.
+ * @param name name of the new instance (can be null)
+ * @return a handle usable to publish / retract the declaration.
+ * @see org.apache.felix.ipojo.extender.InstanceDeclaration
+ */
+ InstanceBuilder newInstance(String type, String name);
+
+ /**
+ * Declares a new instance of a given type.
+ * @param type name of the component to be instantiated.
+ * @param name name of the new instance (can be null)
+ * @param version version of the expected type (can be null)
+ * @return a handle usable to publish / retract the declaration.
+ * @see org.apache.felix.ipojo.extender.InstanceDeclaration
+ */
+ InstanceBuilder newInstance(String type, String name, String version);
+
+ /**
+ * Declares a new extension (supports new types like {@literal component}, {@literal composite}, {@literal handler}).
+ * @param name name of the type to support (no namespace to be provided)
+ * @param builder associated factory builder
+ * @return a handle usable to publish / retract the declaration.
+ * @see org.apache.felix.ipojo.IPojoFactory
+ * @see org.apache.felix.ipojo.extender.ExtensionDeclaration
+ */
+ DeclarationHandle newExtension(String name, FactoryBuilder builder);
+
+ /**
+ * Declares a new type using the given element description.
+ * @param description description of the component type
+ * @return a handle usable to publish / retract the declaration.
+ * @see org.apache.felix.ipojo.extender.TypeDeclaration
+ */
+ DeclarationHandle newType(Element description);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/DeclarationHandle.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/DeclarationHandle.java
new file mode 100644
index 0000000..1c0d044
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/DeclarationHandle.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+/**
+ * Handle on the associated {@link org.apache.felix.ipojo.extender.Declaration} service.
+ * It can be used to start and/or stop the underlying declaration as well as retrieving its
+ * {@linkplain org.apache.felix.ipojo.extender.Status status} (bound or not).
+ *
+ * @since 1.12
+ */
+public interface DeclarationHandle {
+
+ /**
+ * Publish the {@link org.apache.felix.ipojo.extender.Declaration}. If the declaration
+ * is already registered, it's a no-op operation.
+ */
+ void publish();
+
+ /**
+ * Retract the {@link org.apache.felix.ipojo.extender.Declaration} service. If the
+ * declaration is not registered, it's a no-op operation.
+ */
+ void retract();
+
+ /**
+ * Return the current (instant) status of the declaration. Remember that
+ * {@link org.apache.felix.ipojo.extender.Status} is immutable (status does not change over time).
+ * If you want an updated status, call again the {@link #getStatus()} method.
+ * @return the instant status of the {@link org.apache.felix.ipojo.extender.Declaration}
+ */
+ Status getStatus();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/InstanceBuilder.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/InstanceBuilder.java
new file mode 100644
index 0000000..69d7021
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/InstanceBuilder.java
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * Support class for fluent instance declaration building.
+ * This class can be used to specify instance details like naming, component's type, component's version.
+ * The {@link #context(org.osgi.framework.BundleContext)} method can be used to override the default
+ * {@link org.osgi.framework.BundleContext} used for declaration service registration.
+ *
+ * @since 1.12
+ */
+public interface InstanceBuilder {
+
+ /**
+ * Specify the instance name. Using {@literal null} will result in an anonymous instance.
+ * @param name instance name or {@literal null}
+ * @return this builder
+ */
+ InstanceBuilder name(String name);
+
+ /**
+ * Specify the component's type of this instance.
+ * @param type type of the instance (cannot be {@literal null}).
+ * @return this builder
+ */
+ InstanceBuilder type(String type);
+
+ /**
+ * Specify the component's type of this instance.
+ * @param type type of the instance (cannot be {@literal null}).
+ * @return this builder
+ */
+ InstanceBuilder type(Class<?> type);
+
+ /**
+ * Specify the component's version of this instance. If {@literal null} is used,
+ * the factory without any version attribute will be used.
+ * @param version component's version (can be {@literal null}).
+ * @return this builder
+ */
+ InstanceBuilder version(String version);
+
+ /**
+ * Override the default BundleContext used for declaration service registration.
+ * Use this with <bold>caution</bold>, for example, if the bundle does not import the
+ * <code>{@link org.apache.felix.ipojo.extender}</code> package, declarations may not be
+ * linked and activated properly.
+ * @param context new context to be used.
+ * @return this builder
+ */
+ InstanceBuilder context(BundleContext context);
+
+ /**
+ * Access the dedicated builder for configuration (properties).
+ * Notice that when this method is used, the called must use {@link ConfigurationBuilder#build()}
+ * to build the instance declaration configured with the right set of properties. Any attempt
+ * to use {@link #build()} will result in a new declaration with no properties associated.
+ *
+ * Good usage (produced declaration has the associated properties):
+ * <pre>
+ * DeclarationHandle handle = builder.configure()
+ * .property("hello", "world")
+ * .build();
+ * </pre>
+ *
+ * Bad usage (produced declaration does not have the associated properties):
+ * <pre>
+ * builder.configure()
+ * .property("hello", "world");
+ *
+ * DeclarationHandle handle = builder.build();
+ * </pre>
+ *
+ * @return the instance configuration builder
+ */
+ ConfigurationBuilder configure();
+
+ /**
+ * Build the declaration handle (never contains any configuration).
+ * Notice that the declaration is not yet published (no automatic activation).
+ * The client has to do it through {@link DeclarationHandle#publish()}
+ * @return the handle to the declaration
+ */
+ DeclarationHandle build();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java
index 7fa1084..9f0a9b2 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java
@@ -94,4 +94,11 @@
protected ServiceRegistration<?> getRegistration() {
return m_registration;
}
+
+ /**
+ * Is this service registered or not ?
+ */
+ public boolean isRegistered() {
+ return m_registration != null;
+ }
}
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 ddac180..492a31d 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
@@ -23,6 +23,7 @@
import org.apache.felix.ipojo.ConfigurationTracker;
import org.apache.felix.ipojo.EventDispatcher;
+import org.apache.felix.ipojo.extender.internal.declaration.service.DeclarationServiceFactory;
import org.apache.felix.ipojo.extender.internal.linker.DeclarationLinker;
import org.apache.felix.ipojo.extender.internal.processor.*;
import org.apache.felix.ipojo.extender.internal.queue.ExecutorQueueService;
@@ -107,6 +108,11 @@
private BundleTracker m_tracker;
/**
+ * Service provided to build declarations through code.
+ */
+ private DeclarationServiceFactory m_declarationService;
+
+ /**
* The iPOJO bundle is starting.
* This method configures the iPOJO system (internal dispatcher and bundle processing). Then it initiates the
* bundle processing.
@@ -211,6 +217,9 @@
m_tracker.open();
+ m_declarationService = new DeclarationServiceFactory(context);
+ m_declarationService.start();
+
m_logger.log(Logger.INFO, "iPOJO Main Extender started");
}
@@ -221,6 +230,9 @@
* @throws Exception something terrible happen
*/
public void stop(BundleContext context) throws Exception {
+
+ m_declarationService.stop();
+
m_tracker.close();
m_processor.deactivate(m_bundle);
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/AbstractDeclaration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/AbstractDeclaration.java
index acca635..69197e8 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/AbstractDeclaration.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/AbstractDeclaration.java
@@ -20,6 +20,7 @@
package org.apache.felix.ipojo.extender.internal.declaration;
import org.apache.felix.ipojo.extender.Declaration;
+import org.apache.felix.ipojo.extender.DeclarationHandle;
import org.apache.felix.ipojo.extender.Status;
import org.apache.felix.ipojo.extender.internal.AbstractService;
import org.osgi.framework.BundleContext;
@@ -27,7 +28,7 @@
/**
* Common code to all Declaration objects.
*/
-public abstract class AbstractDeclaration extends AbstractService implements Declaration, Status {
+public abstract class AbstractDeclaration extends AbstractService implements Declaration, DeclarationHandle, Status {
/**
* The message used when a declaration is bound.
@@ -132,4 +133,16 @@
m_message = message;
m_throwable = throwable;
}
+
+ public void publish() {
+ if (!isRegistered()) {
+ start();
+ }
+ }
+
+ public void retract() {
+ if (isRegistered()) {
+ stop();
+ }
+ }
}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DeclarationServiceFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DeclarationServiceFactory.java
new file mode 100644
index 0000000..5682f65
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DeclarationServiceFactory.java
@@ -0,0 +1,65 @@
+/*
+ * 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.declaration.service;
+
+import org.apache.felix.ipojo.extender.DeclarationBuilderService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * {@link org.osgi.framework.ServiceFactory} for {@link org.apache.felix.ipojo.extender.DeclarationBuilderService}.
+ * It avoids to explicitly gives a {@link org.osgi.framework.BundleContext} for each service method.
+ * The {@link org.osgi.framework.BundleContext} of the client is used.
+ */
+public class DeclarationServiceFactory implements ServiceFactory<DeclarationBuilderService> {
+
+ private final BundleContext bundleContext;
+ private ServiceRegistration<?> registration;
+
+ public DeclarationServiceFactory(final BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void start() {
+ if (registration == null) {
+ registration = bundleContext.registerService(DeclarationBuilderService.class.getName(), this, null);
+ }
+ }
+
+ public void stop() {
+ if (registration != null) {
+ registration.unregister();
+ registration = null;
+ }
+ }
+
+ public DeclarationBuilderService getService(final Bundle bundle, final ServiceRegistration<DeclarationBuilderService> registration) {
+ return new DefaultDeclarationBuilderService(bundle.getBundleContext());
+ }
+
+ public void ungetService(final Bundle bundle, final ServiceRegistration<DeclarationBuilderService> registration, final DeclarationBuilderService service) {
+ // Nothing to do, built declarations will be kept
+ // It's the client responsibility to dispose its declarations
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultConfigurationBuilder.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultConfigurationBuilder.java
new file mode 100644
index 0000000..ee3f823
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultConfigurationBuilder.java
@@ -0,0 +1,60 @@
+/*
+ * 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.declaration.service;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.ipojo.extender.ConfigurationBuilder;
+import org.apache.felix.ipojo.extender.DeclarationHandle;
+
+/**
+ * Declares a configuration and build the immutable {@link org.apache.felix.ipojo.extender.DeclarationHandle}
+ * containing that configuration.
+ */
+public class DefaultConfigurationBuilder implements ConfigurationBuilder {
+
+ private final DefaultInstanceBuilder builder;
+ private Map<String, Object> configuration = new Hashtable<String, Object>();
+
+ public DefaultConfigurationBuilder(final DefaultInstanceBuilder builder) {
+ this.builder = builder;
+ }
+
+ public ConfigurationBuilder property(final String name, final Object value) {
+ configuration.put(name, value);
+ return this;
+ }
+
+ public ConfigurationBuilder remove(final String name) {
+ configuration.remove(name);
+ return this;
+ }
+
+ public ConfigurationBuilder clear() {
+ configuration.clear();
+ return this;
+ }
+
+ public DeclarationHandle build() {
+ // Make a fresh dictionary instance, needed for immutable instance declarations
+ return builder.build(new Hashtable<String, Object>(configuration));
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultDeclarationBuilderService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultDeclarationBuilderService.java
new file mode 100644
index 0000000..a27ddbe
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultDeclarationBuilderService.java
@@ -0,0 +1,60 @@
+/*
+ * 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.declaration.service;
+
+import org.apache.felix.ipojo.extender.InstanceBuilder;
+import org.apache.felix.ipojo.extender.DeclarationBuilderService;
+import org.apache.felix.ipojo.extender.DeclarationHandle;
+import org.apache.felix.ipojo.extender.builder.FactoryBuilder;
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultExtensionDeclaration;
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultTypeDeclaration;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+
+public class DefaultDeclarationBuilderService implements DeclarationBuilderService {
+
+ private final BundleContext context;
+
+ public DefaultDeclarationBuilderService(final BundleContext context) {
+ this.context = context;
+ }
+
+ public InstanceBuilder newInstance(final String type) {
+ return newInstance(type, null);
+ }
+
+ public InstanceBuilder newInstance(final String type, final String name) {
+ return newInstance(type, name, null);
+ }
+
+ public InstanceBuilder newInstance(final String type, final String name, final String version) {
+ return new DefaultInstanceBuilder(context, type)
+ .version(version)
+ .name(name);
+ }
+
+ public DeclarationHandle newExtension(final String name, final FactoryBuilder builder) {
+ return new DefaultExtensionDeclaration(context, builder, name);
+ }
+
+ public DeclarationHandle newType(final Element description) {
+ return new DefaultTypeDeclaration(context, description);
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultInstanceBuilder.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultInstanceBuilder.java
new file mode 100644
index 0000000..d9d6655
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultInstanceBuilder.java
@@ -0,0 +1,114 @@
+/*
+ * 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.declaration.service;
+
+import static java.lang.String.format;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.extender.ConfigurationBuilder;
+import org.apache.felix.ipojo.extender.InstanceBuilder;
+import org.apache.felix.ipojo.extender.DeclarationHandle;
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultInstanceDeclaration;
+import org.osgi.framework.BundleContext;
+
+/**
+ * User: guillaume
+ * Date: 13/02/2014
+ * Time: 09:36
+ */
+public class DefaultInstanceBuilder implements InstanceBuilder {
+
+ private BundleContext context;
+ private String name;
+ private String type;
+ private String version;
+
+ public DefaultInstanceBuilder(final BundleContext context, final String type) {
+ this.context(context);
+ this.type(type);
+ }
+
+ public InstanceBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ public InstanceBuilder type(final String type) {
+ if (type == null) {
+ throw new IllegalArgumentException(format("'type' parameter cannot be null (instance must be of a given type)"));
+ }
+ this.type = type;
+ return this;
+ }
+
+ public InstanceBuilder type(final Class<?> type) {
+ if (type == null) {
+ throw new IllegalArgumentException(format("'type' parameter cannot be null (instance must be of a given type)"));
+ }
+ return type(type.getName());
+ }
+
+ public InstanceBuilder version(final String version) {
+ this.version = version;
+ return this;
+ }
+
+ public InstanceBuilder context(final BundleContext context) {
+ if (context == null) {
+ throw new IllegalArgumentException(format("'context' parameter cannot be null"));
+ }
+ this.context = context;
+ return this;
+ }
+
+ public ConfigurationBuilder configure() {
+ return new DefaultConfigurationBuilder(this);
+ }
+
+ /**
+ * Only called through ConfigurationBuilder to apply the created configuration to this instance
+ * @param configuration
+ */
+ public DeclarationHandle build(Dictionary<String, Object> configuration) {
+
+ // Prepare the instance configuration
+ if (configuration == null) {
+ configuration = new Hashtable<String, Object>();
+ }
+
+ if (name != null) {
+ configuration.put(Factory.INSTANCE_NAME_PROPERTY, name);
+ }
+
+ if (version != null) {
+ configuration.put(Factory.FACTORY_VERSION_PROPERTY, version);
+ }
+
+ return new DefaultInstanceDeclaration(context, type, configuration);
+
+ }
+
+ public DeclarationHandle build() {
+ return build(new Hashtable<String, Object>());
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclarationTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclarationTestCase.java
index e372f00..cc81f0d 100644
--- a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclarationTestCase.java
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclarationTestCase.java
@@ -19,20 +19,23 @@
package org.apache.felix.ipojo.extender.internal.declaration;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import java.util.Dictionary;
import java.util.Hashtable;
import org.apache.felix.ipojo.Factory;
import org.apache.felix.ipojo.extender.InstanceDeclaration;
-import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.runners.MockitoJUnitRunner;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
import junit.framework.TestCase;
@@ -44,12 +47,22 @@
@Mock
private BundleContext m_bundleContext;
+ @Mock
+ private ServiceRegistration<?> m_registration;
+
@Captor
private ArgumentCaptor<Hashtable<String, Object>> argument;
@Override
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ doReturn(m_registration)
+ .when(m_bundleContext)
+ .registerService(
+ eq(InstanceDeclaration.class.getName()),
+ anyObject(),
+ any(Dictionary.class));
+
}
public void testRegistrationWithEmptyConfiguration() throws Exception {
@@ -82,4 +95,38 @@
assertEquals(argument.getValue().get(InstanceDeclaration.COMPONENT_VERSION_PROPERTY), "1.0.0");
}
+
+ public void testHandlePublication() throws Exception {
+ DefaultInstanceDeclaration declaration = new DefaultInstanceDeclaration(m_bundleContext, "component.Hello");
+ declaration.publish();
+ assertTrue(declaration.isRegistered());
+ }
+
+ public void testHandleMultiplePublication() throws Exception {
+ DefaultInstanceDeclaration declaration = new DefaultInstanceDeclaration(m_bundleContext, "component.Hello");
+ declaration.publish();
+ assertTrue(declaration.isRegistered());
+ declaration.publish();
+ assertTrue(declaration.isRegistered());
+ }
+
+ public void testHandleRetraction() throws Exception {
+ DefaultInstanceDeclaration declaration = new DefaultInstanceDeclaration(m_bundleContext, "component.Hello");
+ declaration.publish();
+ assertTrue(declaration.isRegistered());
+ declaration.retract();
+ assertFalse(declaration.isRegistered());
+ verify(m_registration).unregister();
+ }
+
+ public void testHandleMultipleRetraction() throws Exception {
+ DefaultInstanceDeclaration declaration = new DefaultInstanceDeclaration(m_bundleContext, "component.Hello");
+ declaration.publish();
+ assertTrue(declaration.isRegistered());
+ declaration.retract();
+ assertFalse(declaration.isRegistered());
+ declaration.retract();
+ assertFalse(declaration.isRegistered());
+ verify(m_registration).unregister();
+ }
}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultConfigurationBuilderTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultConfigurationBuilderTestCase.java
new file mode 100644
index 0000000..3edb304
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultConfigurationBuilderTestCase.java
@@ -0,0 +1,100 @@
+/*
+ * 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.declaration.service;
+
+import org.apache.felix.ipojo.extender.InstanceDeclaration;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+
+import junit.framework.TestCase;
+
+/**
+ * User: guillaume
+ * Date: 13/02/2014
+ * Time: 11:33
+ */
+public class DefaultConfigurationBuilderTestCase extends TestCase {
+
+ @Mock
+ private BundleContext m_bundleContext;
+ private DefaultInstanceBuilder parent;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ parent = new DefaultInstanceBuilder(m_bundleContext, "type");
+ }
+
+ public void testPropertyAddition() throws Exception {
+ DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(parent);
+ builder.property("a", "b");
+ InstanceDeclaration declaration = (InstanceDeclaration) builder.build();
+ assertEquals(declaration.getConfiguration().get("a"), "b");
+ }
+
+ public void testPropertyAdditionReUse() throws Exception {
+ DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(parent);
+ builder.property("a", "b");
+
+ // First built instance
+ InstanceDeclaration declaration = (InstanceDeclaration) builder.build();
+ assertEquals(declaration.getConfiguration().get("a"), "b");
+
+ // Second built instance
+ builder.property("c", "d");
+ InstanceDeclaration declaration2 = (InstanceDeclaration) builder.build();
+ assertEquals(declaration2.getConfiguration().get("a"), "b");
+ assertEquals(declaration2.getConfiguration().get("c"), "d");
+
+ // Verify that first instance is not modified
+ assertNull(declaration.getConfiguration().get("c"));
+
+ }
+
+ public void testPropertyRemoval() throws Exception {
+ DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(parent);
+ builder.property("a", "b");
+
+ InstanceDeclaration declaration = (InstanceDeclaration) builder.build();
+ assertEquals(declaration.getConfiguration().get("a"), "b");
+
+ builder.remove("a");
+
+ InstanceDeclaration declaration2 = (InstanceDeclaration) builder.build();
+ assertNull(declaration2.getConfiguration().get("a"));
+
+ }
+
+ public void testClear() throws Exception {
+ DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(parent);
+ builder.property("a", "b");
+ builder.property("c", "d");
+
+ InstanceDeclaration declaration = (InstanceDeclaration) builder.build();
+ assertEquals(declaration.getConfiguration().get("a"), "b");
+
+ builder.clear();
+
+ InstanceDeclaration declaration2 = (InstanceDeclaration) builder.build();
+ assertTrue(declaration2.getConfiguration().isEmpty());
+
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultDeclarationBuilderServiceTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultDeclarationBuilderServiceTestCase.java
new file mode 100644
index 0000000..87bf9c4
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultDeclarationBuilderServiceTestCase.java
@@ -0,0 +1,96 @@
+/*
+ * 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.declaration.service;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.verify;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.extender.InstanceBuilder;
+import org.apache.felix.ipojo.extender.DeclarationHandle;
+import org.apache.felix.ipojo.extender.ExtensionDeclaration;
+import org.apache.felix.ipojo.extender.InstanceDeclaration;
+import org.apache.felix.ipojo.extender.TypeDeclaration;
+import org.apache.felix.ipojo.extender.builder.FactoryBuilder;
+import org.apache.felix.ipojo.metadata.Element;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+
+import junit.framework.TestCase;
+
+/**
+ * User: guillaume
+ * Date: 10/02/2014
+ * Time: 15:41
+ */
+public class DefaultDeclarationBuilderServiceTestCase extends TestCase {
+
+ @Mock
+ private BundleContext m_bundleContext;
+
+ @Mock
+ private FactoryBuilder m_factoryBuilder;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testNewInstance() throws Exception {
+ DefaultDeclarationBuilderService service = new DefaultDeclarationBuilderService(m_bundleContext);
+ assertNotNull(service.newInstance("type.of.component"));
+ assertNotNull(service.newInstance("type.of.component", "instance.name"));
+ InstanceBuilder builder = service.newInstance("type.of.component", "instance.name", "component.version");
+ assertNotNull(builder);
+ DeclarationHandle instance = builder.build();
+ instance.publish();
+ verify(m_bundleContext).registerService(
+ eq(InstanceDeclaration.class.getName()),
+ anyObject(),
+ any(Dictionary.class));
+ }
+
+ public void testNewExtension() throws Exception {
+ DefaultDeclarationBuilderService service = new DefaultDeclarationBuilderService(m_bundleContext);
+ DeclarationHandle extension = service.newExtension("test", m_factoryBuilder);
+ assertNotNull(extension);
+ extension.publish();
+ verify(m_bundleContext).registerService(
+ eq(ExtensionDeclaration.class.getName()),
+ anyObject(),
+ any(Dictionary.class));
+ }
+
+ public void testNewType() throws Exception {
+ DefaultDeclarationBuilderService service = new DefaultDeclarationBuilderService(m_bundleContext);
+ DeclarationHandle type = service.newType(new Element("component", null));
+ assertNotNull(type);
+ type.publish();
+ verify(m_bundleContext).registerService(
+ eq(TypeDeclaration.class.getName()),
+ anyObject(),
+ isNull(Dictionary.class));
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultInstanceBuilderTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultInstanceBuilderTestCase.java
new file mode 100644
index 0000000..abc5c73
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/service/DefaultInstanceBuilderTestCase.java
@@ -0,0 +1,111 @@
+/*
+ * 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.declaration.service;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.extender.InstanceBuilder;
+import org.apache.felix.ipojo.extender.DeclarationHandle;
+import org.apache.felix.ipojo.extender.InstanceDeclaration;
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultInstanceDeclaration;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+
+import junit.framework.TestCase;
+
+/**
+ * User: guillaume
+ * Date: 13/02/2014
+ * Time: 10:32
+ */
+public class DefaultInstanceBuilderTestCase extends TestCase {
+
+ @Mock
+ private BundleContext m_bundleContext;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testNoConfiguration() throws Exception {
+ InstanceBuilder builder = new DefaultInstanceBuilder(m_bundleContext, "type");
+ DeclarationHandle handle = builder.build();
+ InstanceDeclaration did = (InstanceDeclaration) handle;
+
+ assertEquals("type", did.getComponentName());
+ assertEquals(InstanceDeclaration.UNNAMED_INSTANCE, did.getInstanceName());
+ assertNull(did.getComponentVersion());
+
+ Dictionary<String,Object> configuration = did.getConfiguration();
+ assertTrue(configuration.isEmpty());
+ }
+
+ public void testNameConfiguration() throws Exception {
+ InstanceBuilder builder = new DefaultInstanceBuilder(m_bundleContext, "type").name("John");
+
+ DeclarationHandle handle = builder.build();
+ InstanceDeclaration did = (InstanceDeclaration) handle;
+
+ assertEquals("type", did.getComponentName());
+ assertEquals("John", did.getInstanceName());
+ assertNull(did.getComponentVersion());
+
+ Dictionary<String,Object> configuration = did.getConfiguration();
+ assertEquals("John", configuration.get(Factory.INSTANCE_NAME_PROPERTY));
+ }
+
+ public void testVersionConfiguration() throws Exception {
+ InstanceBuilder builder = new DefaultInstanceBuilder(m_bundleContext, "type").name("John").version("1.0");
+
+ DeclarationHandle handle = builder.build();
+ InstanceDeclaration did = (InstanceDeclaration) handle;
+
+ assertEquals("type", did.getComponentName());
+ assertEquals("John", did.getInstanceName());
+ assertEquals("1.0", did.getComponentVersion());
+
+ Dictionary<String,Object> configuration = did.getConfiguration();
+ assertEquals("John", configuration.get(Factory.INSTANCE_NAME_PROPERTY));
+ assertEquals("1.0", configuration.get(Factory.FACTORY_VERSION_PROPERTY));
+ }
+
+ public void testBuilderReUseProvidesDifferentInstances() throws Exception {
+ InstanceBuilder builder = new DefaultInstanceBuilder(m_bundleContext, "type");
+ assertNotSame(builder.build(), builder.build());
+ }
+
+
+ public void testDeclarationIsNotAutomaticallyStarted() throws Exception {
+ InstanceBuilder builder = new DefaultInstanceBuilder(m_bundleContext, "type");
+ DeclarationHandle handle = builder.build();
+ DefaultInstanceDeclaration did = (DefaultInstanceDeclaration) handle;
+
+ assertFalse(did.isRegistered());
+ }
+
+ public void testDeepConfiguration() throws Exception {
+ InstanceBuilder builder = new DefaultInstanceBuilder(m_bundleContext, "type");
+ assertNotNull(builder.configure());
+ }
+}
+