added annotation support for factory configuration adapter service
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@939089 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java
new file mode 100644
index 0000000..991e60d
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java
@@ -0,0 +1,84 @@
+/*
+ * 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.dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a class that acts as a Configuration Factory Adapter Service. For each new Config Admin factory configuration matching
+ * the specified factoryPid, an instance of this service will be created.
+ * The adapter will be registered with the specified interface, and with the specified adapter service properties.
+ * Depending on the <code>propagate</code> parameter, every public factory configuration properties
+ * (which don't start with ".") will be propagated along with the adapter service properties.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface FactoryConfigurationAdapterService
+{
+ /**
+ * Returns the factory pid whose configurations will instantiate the annotated service class. (By default, the pid is the
+ * service class name).
+ */
+ String factoryPid() default "";
+
+ /**
+ * The Update method to invoke (defaulting to "updated"), when a factory configuration is created or updated
+ */
+ String updated() default "updated";
+
+ /**
+ * Returns true if the configuration properties must be published along with the service.
+ * Any additional service properties specified directly are merged with these.
+ * @return true if configuration must be published along with the service, false if not.
+ */
+ boolean propagate() default false;
+
+ /**
+ * The interface(s) to use when registering adapters. By default, directly implemented
+ * interfaces will be registered in the OSGi registry.
+ */
+ Class<?>[] service() default {};
+
+ /**
+ * Adapter Service properties. Notice that public factory configuration is also registered in service properties,
+ * (only if propagate is true). Public factory configuration properties are those which don't starts with a dot (".").
+ */
+ Property[] properties() default {};
+
+ /**
+ * The label used to display the tab name (or section) where the properties are displayed. Example: "Printer Service".
+ * @return The label used to display the tab name where the properties are displayed.
+ */
+ String heading() default "";
+
+ /**
+ * A human readable description of the PID this annotation is associated with. Example: "Configuration for the PrinterService bundle".
+ * @return A human readable description of the PID this annotation is associated with.
+ */
+ String description() default "";
+
+ /**
+ * The list of properties types used to expose properties in web console.
+ * @return The list of properties types used to expose properties in web console.
+ */
+ PropertyMetaData[] metadata() default {};
+}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
index eaed2bd..065b756 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
@@ -36,6 +36,7 @@
import org.apache.felix.dm.annotation.api.Composition;
import org.apache.felix.dm.annotation.api.ConfigurationDependency;
import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.FactoryConfigurationAdapterService;
import org.apache.felix.dm.annotation.api.Init;
import org.apache.felix.dm.annotation.api.ResourceAdapterService;
import org.apache.felix.dm.annotation.api.ResourceDependency;
@@ -83,6 +84,8 @@
+ BundleAdapterService.class.getName().replace('.', '/') + ";";
private final static String A_RESOURCE_ADAPTER_SERVICE = "L"
+ ResourceAdapterService.class.getName().replace('.', '/') + ";";
+ private final static String A_FACTORYCONFIG_ADAPTER_SERVICE = "L"
+ + FactoryConfigurationAdapterService.class.getName().replace('.', '/') + ";";
private Reporter m_reporter;
private String m_className;
@@ -124,7 +127,8 @@
TemporalServiceDependency,
ConfigurationDependency,
BundleDependency,
- ResourceDependency
+ ResourceDependency,
+ FactoryConfigurationAdapterService
};
// List of component descriptor parameters
@@ -149,6 +153,7 @@
removed,
autoConfig,
pid,
+ factoryPid,
propagate,
updated,
timeout,
@@ -384,6 +389,10 @@
{
parseResourceAdapterService(annotation);
}
+ else if (annotation.getName().equals(A_FACTORYCONFIG_ADAPTER_SERVICE))
+ {
+ parseFactoryConfigurationAdapterService(annotation);
+ }
else if (annotation.getName().equals(A_INIT))
{
checkMethod(m_voidMethodPattern);
@@ -566,45 +575,8 @@
info.addParam(annotation, Params.propagate, null);
// Property Meta Types
- if (annotation.get("metadata") != null)
- {
- String propertiesPid = get(annotation, "pid", m_className);
- String propertiesHeading = annotation.get("heading");
- String propertiesDesc = annotation.get("description");
-
- MetaType.OCD ocd = new MetaType.OCD(propertiesPid, propertiesHeading, propertiesDesc);
- for (Object p : (Object[]) annotation.get("metadata"))
- {
- Annotation property = (Annotation) p;
- String heading = property.get("heading");
- String id = property.get("id");
- String type = (String) property.get("type");
- type = (type != null) ? parseClass(type, m_classPattern, 1) : null;
- Object[] defaults = (Object[]) property.get("defaults");
- String description = property.get("description");
- Integer cardinality = property.get("cardinality");
- Boolean required = property.get("required");
-
- MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description,
- cardinality, required);
- Object[] options = property.get("options");
- if (options != null)
- {
- for (Object o : (Object[]) property.get("options"))
- {
- Annotation option = (Annotation) o;
- ad.add(new MetaType.Option((String) option.get("name"),
- (String) option.get("value")));
- }
- }
- ocd.add(ad);
- }
-
- m_metaType.add(ocd);
- MetaType.Designate designate = new MetaType.Designate(propertiesPid);
- m_metaType.add(designate);
- m_reporter.warning("Parsed MetaType Properties from class " + m_className);
- }
+ String pid = get(annotation, Params.pid.toString(), m_className);
+ parseMetaTypes(annotation, pid, false);
}
/**
@@ -765,6 +737,42 @@
info.addParam(annotation, Params.propagate, Boolean.FALSE);
}
+ /**
+ * Parses a Factory Configuration Adapter annotation.
+ * @param annotation
+ */
+ private void parseFactoryConfigurationAdapterService(Annotation annotation)
+ {
+ Info info = new Info(EntryTypes.FactoryConfigurationAdapterService);
+ m_infos.add(info);
+
+ // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+ addCommonServiceParams(info);
+
+ // Generate Adapter Implementation
+ info.addParam(Params.impl, m_className);
+
+ // Parse factory Pid
+ info.addParam(annotation, Params.factoryPid, m_className);
+
+ // Parse updated callback
+ info.addParam(annotation, Params.updated, "updated");
+
+ // propagate attribute
+ info.addParam(annotation, Params.propagate, Boolean.FALSE);
+
+ // Parse the optional adapter service (use directly implemented interface by default).
+ info.addClassParam(annotation, Params.service, m_interfaces);
+
+ // Parse Adapter properties.
+ parseParameters(annotation, Params.properties, info);
+
+ // Parse optional meta types for configuration description.
+ String factoryPid = get(annotation, Params.factoryPid.toString(), m_className);
+ parseMetaTypes(annotation, factoryPid, true);
+ }
+
+
private void parseBundleDependencyAnnotation(Annotation annotation)
{
Info info = new Info(EntryTypes.BundleDependency);
@@ -805,6 +813,52 @@
}
/**
+ * Parse optional meta types annotation attributes
+ * @param annotation
+ */
+ private void parseMetaTypes(Annotation annotation, String pid, boolean factory)
+ {
+ if (annotation.get("metadata") != null)
+ {
+ String propertiesHeading = annotation.get("heading");
+ String propertiesDesc = annotation.get("description");
+
+ MetaType.OCD ocd = new MetaType.OCD(pid, propertiesHeading, propertiesDesc);
+ for (Object p : (Object[]) annotation.get("metadata"))
+ {
+ Annotation property = (Annotation) p;
+ String heading = property.get("heading");
+ String id = property.get("id");
+ String type = (String) property.get("type");
+ type = (type != null) ? parseClass(type, m_classPattern, 1) : null;
+ Object[] defaults = (Object[]) property.get("defaults");
+ String description = property.get("description");
+ Integer cardinality = property.get("cardinality");
+ Boolean required = property.get("required");
+
+ MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description,
+ cardinality, required);
+ Object[] options = property.get("options");
+ if (options != null)
+ {
+ for (Object o : (Object[]) property.get("options"))
+ {
+ Annotation option = (Annotation) o;
+ ad.add(new MetaType.Option((String) option.get("name"),
+ (String) option.get("value")));
+ }
+ }
+ ocd.add(ad);
+ }
+
+ m_metaType.add(ocd);
+ MetaType.Designate designate = new MetaType.Designate(pid, factory);
+ m_metaType.add(designate);
+ m_reporter.warning("Parsed MetaType Properties from class " + m_className);
+ }
+ }
+
+ /**
* Parses a Param annotation (which represents a list of key-value pari).
* @param annotation the annotation where the Param annotation is defined
* @param attribute the attribute name which is of Param type
@@ -915,7 +969,7 @@
// We must have at least a Service or an AspectService annotation.
checkServiceDeclared(EntryTypes.Service, EntryTypes.AspectService, EntryTypes.AdapterService, EntryTypes.BundleAdapterService,
- EntryTypes.ResourceAdapterService);
+ EntryTypes.ResourceAdapterService, EntryTypes.FactoryConfigurationAdapterService);
StringBuilder sb = new StringBuilder();
sb.append("Parsed annotation for class ");
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java
index b9b2aad..142a3e1 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java
@@ -278,11 +278,13 @@
public static class Designate
{
String m_pid;
+ boolean m_factory;
OBject m_object;
- public Designate(String pid)
+ public Designate(String pid, boolean factory)
{
this.m_pid = pid;
+ this.m_factory = factory;
this.m_object = new OBject(pid);
}
@@ -290,6 +292,10 @@
{
pw.print(" <Designate");
writeAttribute("pid", m_pid, pw);
+ if (m_factory)
+ {
+ writeAttribute("factoryPid", m_pid, pw);
+ }
pw.println(">");
m_object.writeTo(pw);
pw.println(" </Designate>");
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ComponentManager.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ComponentManager.java
index ea95cc6..3be1217 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ComponentManager.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ComponentManager.java
@@ -168,6 +168,10 @@
case ResourceAdapterService:
service = createResourceAdapterService(b, dm, parser);
break;
+
+ case FactoryConfigurationAdapterService:
+ service = createFactoryConfigurationAdapterService(b, dm, parser);
+ break;
case ServiceDependency:
checkServiceParsed(service);
@@ -416,6 +420,28 @@
}
/**
+ * Creates a Factory Configuration Adapter Service
+ * @param b
+ * @param dm
+ * @param parser
+ * @return
+ */
+ private Service createFactoryConfigurationAdapterService(Bundle b, DependencyManager dm, DescriptorParser parser)
+ throws ClassNotFoundException
+ {
+ Class<?> impl = b.loadClass(parser.getString(DescriptorParam.impl));
+ String factoryPid = parser.getString(DescriptorParam.factoryPid);
+ String updated = parser.getString(DescriptorParam.updated);
+ String[] services = parser.getStrings(DescriptorParam.service, null);
+ Dictionary<String, String> properties = parser.getDictionary(DescriptorParam.properties, null);
+ boolean propagate = "true".equals(parser.getString(DescriptorParam.propagate, "false"));
+ Service srv = dm.createFactoryConfigurationAdapterService(factoryPid, updated, impl, services, properties, propagate);
+ setCommonServiceParams(srv, parser);
+ return srv;
+ }
+
+
+ /**
* Creates a ServiceDependency that we parsed from a component descriptor "ServiceDependency" entry.
* @param b
* @param dm
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorEntry.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorEntry.java
index da8d2c0..3a5e769 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorEntry.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorEntry.java
@@ -29,6 +29,7 @@
AdapterService,
BundleAdapterService,
ResourceAdapterService,
+ FactoryConfigurationAdapterService,
ServiceDependency,
TemporalServiceDependency,
ConfigurationDependency,
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParam.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParam.java
index e51ad8d..f1e7e66 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParam.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParam.java
@@ -108,7 +108,10 @@
stateMask,
/* The ranking of an aspect (the parsed value is an int) */
- ranking;
+ ranking,
+
+ /* The factory pid of an FactoryConfigurationAdapterService annotation (the parsed value is a string) */
+ factoryPid;
/**
* Indicates if a given attribute is a Service attribute.
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factoryconfadapter/ServiceClient.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factoryconfadapter/ServiceClient.java
new file mode 100644
index 0000000..dc6aeba
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factoryconfadapter/ServiceClient.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.dm.test.bundle.annotation.factoryconfadapter;
+
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Service;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+
+@Service
+public class ServiceClient
+{
+ @ServiceDependency(changed="changeServiceProvider")
+ void addServiceProvider(ServiceInterface si) {
+ si.doService();
+ }
+
+ void changeServiceProvider(Map serviceProperties, ServiceInterface si)
+ {
+ si.doService();
+ }
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factoryconfadapter/ServiceInterface.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factoryconfadapter/ServiceInterface.java
new file mode 100644
index 0000000..68d05fe
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factoryconfadapter/ServiceInterface.java
@@ -0,0 +1,24 @@
+/*
+ * 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.dm.test.bundle.annotation.factoryconfadapter;
+
+public interface ServiceInterface
+{
+ public void doService();
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factoryconfadapter/ServiceProvider.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factoryconfadapter/ServiceProvider.java
new file mode 100644
index 0000000..88e3bc8
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/factoryconfadapter/ServiceProvider.java
@@ -0,0 +1,68 @@
+/*
+ * 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.dm.test.bundle.annotation.factoryconfadapter;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dm.annotation.api.FactoryConfigurationAdapterService;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+
+/**
+ * This service is instantiated when a factory configuration is created from ConfigAdmin
+ */
+@FactoryConfigurationAdapterService(factoryPid = "FactoryPidTest", properties = { @Property(name = "foo", value = "bar") }, propagate = true)
+public class ServiceProvider implements ServiceInterface
+{
+ @ServiceDependency
+ private Sequencer m_sequencer;
+
+ private volatile boolean m_started;
+
+ // Either initial config, or an updated config
+ protected void updated(Dictionary conf)
+ {
+ if (m_started)
+ {
+ m_sequencer.step(3);
+ }
+ }
+
+ @Start
+ void start()
+ {
+ m_started = true;
+ m_sequencer.step(1);
+ }
+
+ // The ServiceClient is invoking our service
+ public void doService()
+ {
+ m_sequencer.step();
+ }
+
+ @Stop
+ void stop()
+ {
+ m_sequencer.step(5);
+ }
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/sequencer/Sequencer.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/sequencer/Sequencer.java
index 61a5841..3eff238 100644
--- a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/sequencer/Sequencer.java
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/sequencer/Sequencer.java
@@ -24,6 +24,11 @@
public interface Sequencer
{
/**
+ * Proceed with the next step.
+ */
+ void step();
+
+ /**
* Crosses a given step.
* @param step the step we are crossing
*/
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/AnnotationBase.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/AnnotationBase.java
index b161a47..7b1b352 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/AnnotationBase.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/AnnotationBase.java
@@ -85,6 +85,14 @@
// ----------------------- Sequencer interface ------------------------------------------
/**
+ * Goes to the next step.
+ */
+ public void step()
+ {
+ m_ensure.step();
+ }
+
+ /**
* Crosses a given step number.
*/
public void step(int step)
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/FactoryConfigurationAdapterAnnotationTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/FactoryConfigurationAdapterAnnotationTest.java
new file mode 100644
index 0000000..28c3426
--- /dev/null
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/FactoryConfigurationAdapterAnnotationTest.java
@@ -0,0 +1,103 @@
+/*
+* 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.dm.test.annotation;
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.test.BundleGenerator;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Use case: Verify that an annotated Configuration Factory Adapter Service is properly created when a factory configuration
+ * is created from Config Admin.
+ */
+@RunWith(JUnit4TestRunner.class)
+public class FactoryConfigurationAdapterAnnotationTest extends AnnotationBase
+{
+ @Configuration
+ public static Option[] configuration()
+ {
+ return options(
+ systemProperty("dm.log").value( "true" ),
+ provision(
+ mavenBundle().groupId("org.osgi").artifactId("org.osgi.compendium").version("4.1.0"),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.configadmin").version("1.2.4"),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject(),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager.runtime").versionAsInProject()),
+ provision(
+ new BundleGenerator()
+ .set(Constants.BUNDLE_SYMBOLICNAME, "FactoryConfigurationAdapterTest")
+ .set("Export-Package", "org.apache.felix.dm.test.bundle.annotation.sequencer")
+ .set("Private-Package", "org.apache.felix.dm.test.bundle.annotation.factoryconfadapter")
+ .set("Import-Package", "*")
+ .set("-plugin", "org.apache.felix.dm.annotation.plugin.bnd.AnnotationPlugin")
+ .build()));
+ }
+
+ @Test
+ public void testFactoryConfigurationAdapterAnnotation(BundleContext context)
+ {
+ DependencyManager m = new DependencyManager(context);
+ // Provide the Sequencer to the adapter bundle service (see main/src/.../factoryconfadapter/*.java).
+ m.add(m.createService().setImplementation(this).setInterface(Sequencer.class.getName(), null));
+ ConfigurationAdmin cm = (ConfigurationAdmin) context.getService(context.getServiceReference(ConfigurationAdmin.class.getName()));
+ try
+ {
+ // Create a factory configuration in order to instantiate the ServiceProvider
+ org.osgi.service.cm.Configuration cf = cm.createFactoryConfiguration("FactoryPidTest", null);
+ cf.update(new Hashtable() {{ put("foo", "bar"); }});
+ // Wait for the ServiceProvider activation.
+ m_ensure.waitForStep(2, 10000);
+ // Avoid bug in CM, which may miss some updates
+ sleep(1);
+ // Update conf
+ cf.update(new Hashtable() {{ put("foo", "bar2"); }});
+ // Wait for effective update
+ m_ensure.waitForStep(4, 10000);
+ // Avoid bug in CM, which may miss some updates
+ sleep(1);
+ // Remove configuration.
+ cf.delete();
+ // Check if ServiceProvider has been stopped.
+ m_ensure.waitForStep(5, 1000);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ Assert.fail("can't create factory configuration");
+ }
+ }
+}