Added component factory feature in Service annotation - Reorganized annotation packages - Fixed javadoc - Removed static factory methods from annotations
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@942914 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AspectService.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AspectService.java
index 854faf5..ff74749 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AspectService.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AspectService.java
@@ -62,18 +62,4 @@
* @return the ranking of this aspect
*/
int ranking();
-
- /**
- * Returns the Class of the class which acts as a factory for this Service. The default method
- * factory name is "create". If you need to invoke another method, then you can use the
- * <code>factoryMethod</code> attribute.
- * @return the factory Class name.
- */
- Class<?> factory() default Object.class;
-
- /**
- * Returns the method name of the factory class which will create our Service instance.
- * @return the factory method name.
- */
- String factoryMethod() default "";
}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/PropertyMetaData.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/PropertyMetaData.java
index 0ece247..5c41695 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/PropertyMetaData.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/PropertyMetaData.java
@@ -23,8 +23,21 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.apache.felix.dm.annotation.api.adapter.FactoryConfigurationAdapterService;
+import org.apache.felix.dm.annotation.api.dependency.ConfigurationDependency;
+
/**
* This annotation describes the data types of a configuration Property.
+ * It can be used by other annotations which require meta type support.
+ * For now, the following annotations are using <code>PropertyMeteData</code:
+ * <ul>
+ * <li>{@link ConfigurationDependency}: This dependency allows to define a
+ * dependency over a <code>Configuration Admin</code> configuration dictionaries, whose
+ * metadata can be described using <code>PropertyMetaData</code> annotation.
+ * <li>{@link FactoryConfigurationAdapterService}: This service adapter allows
+ * to dynamically create Services on behalf of <code>Factory Configuration Admin</code>
+ * configuration dictionaries, whose metadata can be described using this <code>PropertyMetaData</code> annotation.
+ * </ul>
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.ANNOTATION_TYPE)
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Service.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Service.java
index 8c69541..01190d4 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Service.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Service.java
@@ -24,7 +24,111 @@
import java.lang.annotation.Target;
/**
- * Annotates an OSGi Service class.
+ * Annotates an OSGi Service class with its dependencies.
+ * By default, all directly implemented interfaces are registered into the OSGi registry,
+ * and the Service is instantiated automatically, when the Service' bundle is started and
+ * when the Service dependencies are available. If you need to take control of when and how
+ * much Service instances are getting created, then you can use the <code>factory</code>
+ * Service attribute.<p>
+ * If a <code>factory</code> attribute is set, the Service is not started automatically
+ * during bundle startup, and a <code>java.util.Set<Dictionary></code>
+ * object is registered into the OSGi registry on behalf of the Service. This Set will act
+ * as a Factory API, and another component may use this Set and add some configuration
+ * dictionaries in it, in order to fire some Service activations (there is one Service
+ * instantiated per dictionary, which is passed to Service instances via a configurable
+ * callback method).
+ *
+ * <h3>Usage Examples</h3>
+ *
+ * <p> Here is a sample showing a X service, which depends on a configuration dependency:<p>
+ * <blockquote>
+ *
+ * <pre>
+ * /**
+ * * This Service will be activated once the bundle is started and when all required dependencies
+ * * are available.
+ * */
+ * @Service
+ * class X implements Z {
+ * @ConfigurationDependency(pid="MyPid")
+ * void configure(Dictionary conf) {
+ * // Configure or reconfigure our service. The conf is provided by the Configuration Admin Service,
+ * // and all public properties (which don't start with a dot) are propagated with the
+ * // Service properties specified in the properties's Service attribute.
+ * }
+ *
+ * @Start
+ * void start() {
+ * // Our Service is starting and is about to be registered in the OSGi registry as a Z service.
+ * }
+ *
+ * public void doService() {
+ * // ...
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * Here is a sample showing how a Y service may dynamically instantiate several X Service instances,
+ * using the {@link #factory()} attribute:<p>
+ * <blockquote>
+ *
+ * <pre>
+ * /**
+ * * All Service instances will be created/updated/removed by the "Y" Service
+ * */
+ * @Service(factory="MyServiceFactory", factoryConfigure="configure")
+ * class X implements Z {
+ * void configure(Dictionary conf) {
+ * // Configure or reconfigure our service. The conf is provided by the factory,
+ * // and all public properties (which don't start with a dot) are propagated with the
+ * // Service properties specified in the properties's Service attribute.
+ * }
+ *
+ * @ServiceDependency
+ * void bindOtherService(OtherService other) {
+ * // store this require dependency
+ * }
+ *
+ * @Start
+ * void start() {
+ * // Our Service is starting and is about to be registered in the OSGi registry as a Z service.
+ * }
+ *
+ * public void doService() {
+ * // ...
+ * }
+ * }
+ *
+ * /**
+ * * This class will instantiate some X Service instances
+ * */
+ * @Service
+ * class Y {
+ * @ServiceDependency(filter="(dm.factory.name=MyServiceFactory))
+ * Set<Dictionary> _XFactory; // This Set acts as a Factory API for creating X Service instances.
+ *
+ * @Start
+ * void start() {
+ * // Instantiate a X Service instance
+ * Dictionary x1 = new Hashtable() {{ put("foo", "bar1"); }};
+ * _XFactory.add(x1);
+ *
+ * // Instantiate another X Service instance
+ * Dictionary x2 = new Hashtable() {{ put("foo", "bar2"); }};
+ * _XFactory.add(x2);
+ *
+ * // Update the first X Service instance
+ * x1.put("foo", "bar1_modified");
+ * _XFactory.add(x1);
+ *
+ * // Destroy X Services (Notice that invoking XFactory.clear() will destroy all X Service instances)
+ * _XFactory.remove(x1);
+ * _XFactory.remove(x2);
+ * }
+ * }
+ * </pre>
+ *
+ * </blockquote>
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
@@ -32,27 +136,37 @@
{
/**
* Returns the list of provided interfaces. By default, the directly implemented interfaces are provided.
- * @return The list of provided interfaces.
*/
- Class<?>[] provide() default Object.class;
+ Class<?>[] provide() default {};
/**
* Returns the list of provided service properties.
- * @return The list of provided service properties.
*/
Property[] properties() default {};
-
- /**
- * Returns the Class of the class which acts as a factory for this Service. The default method
- * factory name is "create". If you need to invoke another method, then you can use the
- * <code>factoryMethod</code> attribute.
- * @return the factory Class name.
- */
- Class<?> factory() default Object.class;
/**
- * Returns the method name of the factory class which will create our Service instance.
- * @return the factory method name.
+ * Returns the <code>factory</code> name used to dynamically instantiate the Service annotated by this class.
+ * When you set this attribute, a <code>java.util.Set<java.lang.Dictionary></code> Service will
+ * be provided with a <code>dm.factory.name</code> service property matching your specified <code>factory</code> attribute.
+ * This Set will be provided once the Service's bundle is started, even if required dependencies are not available, and the
+ * Set will be unregistered from the OSGi registry once the Service's bundle is stopped or being updated.<p>
+ * So, basically, another component may then be injected with this set in order to dynamically instantiate some Service instances:
+ * <ul>
+ * <li> Each time a new Dictionary is added into the Set, then a new instance of the annotated service will be instantiated.</li>
+ * <li> Each time an existing Dictionary is updated from the Set, then the corresponding Service instance will be updated.</li>
+ * <li> Each time an existing Dictionary is removed from the Set, then the corresponding Service instance will be destroyed.</li>
+ * </ul>
+ * The dictionary registered in the Set will be provided to the created Service instance using a callback method that you can
+ * optionally specify in the {@link Service#factoryConfigure()} attribute. Each public properties from that dictionary
+ * (which don't start with a dot) will be propagated along with the annotated Service properties.
*/
- String factoryMethod() default "";
+ String factory() default "";
+
+ /**
+ * Returns the "configure" callback method name to be called with the factory configuration. This attribute only makes sense if the
+ * {@link #factory()} attribute is used. If specified, then this attribute references a Service callback method, which is called
+ * for providing the configuration supplied by the factory that instantiated this Service. The current Service properties will be
+ * also updated with all public properties (which don't start with a dot).
+ */
+ String factoryConfigure() default "";
}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/AdapterService.java
similarity index 95%
rename from dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java
rename to dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/AdapterService.java
index 28d94fd..eb05f03 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/AdapterService.java
@@ -16,13 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dm.annotation.api;
+package org.apache.felix.dm.annotation.api.adapter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.apache.felix.dm.annotation.api.Property;
+
/**
* Annotates an Adapater Service. The adapter will be applied to any service that
* matches the implemented interface and filter. For each matching service
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/BundleAdapterService.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/BundleAdapterService.java
similarity index 95%
rename from dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/BundleAdapterService.java
rename to dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/BundleAdapterService.java
index 56ae2da..1464566 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/BundleAdapterService.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/BundleAdapterService.java
@@ -16,13 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dm.annotation.api;
+package org.apache.felix.dm.annotation.api.adapter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.apache.felix.dm.annotation.api.Property;
import org.osgi.framework.Bundle;
/**
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/adapter/FactoryConfigurationAdapterService.java
similarity index 91%
rename from dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/FactoryConfigurationAdapterService.java
rename to dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/FactoryConfigurationAdapterService.java
index 991e60d..2358a36 100644
--- 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/adapter/FactoryConfigurationAdapterService.java
@@ -16,15 +16,18 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dm.annotation.api;
+package org.apache.felix.dm.annotation.api.adapter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.PropertyMetaData;
+
/**
- * Annotates a class that acts as a Configuration Factory Adapter Service. For each new Config Admin factory configuration matching
+ * Annotates a class that acts as a Factory Configuration Adapter Service. For each new <code>Config Admin</code> 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
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ResourceAdapterService.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/ResourceAdapterService.java
similarity index 94%
rename from dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ResourceAdapterService.java
rename to dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/ResourceAdapterService.java
index 28a1700..c3764bf 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ResourceAdapterService.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/adapter/ResourceAdapterService.java
@@ -16,13 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dm.annotation.api;
+package org.apache.felix.dm.annotation.api.adapter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.apache.felix.dm.annotation.api.Property;
+
/**
* Annotates a class as a Resource Adapter Service. The adapter will be applied to any resource
* that matches the specified filter condition. For each matching resource
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/BundleDependency.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/BundleDependency.java
similarity index 96%
rename from dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/BundleDependency.java
rename to dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/BundleDependency.java
index 50eeada..9632f91 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/BundleDependency.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/BundleDependency.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dm.annotation.api;
+package org.apache.felix.dm.annotation.api.dependency;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ConfigurationDependency.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ConfigurationDependency.java
similarity index 95%
rename from dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
rename to dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ConfigurationDependency.java
index 63335d0..43d1f67 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ConfigurationDependency.java
@@ -16,13 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dm.annotation.api;
+package org.apache.felix.dm.annotation.api.dependency;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.apache.felix.dm.annotation.api.PropertyMetaData;
+
/**
* Annotates a method for injecting a Configuration Dependency.
*/
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ResourceDependency.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ResourceDependency.java
similarity index 94%
rename from dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ResourceDependency.java
rename to dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ResourceDependency.java
index c4d2823..1dbac23 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ResourceDependency.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ResourceDependency.java
@@ -16,13 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dm.annotation.api;
+package org.apache.felix.dm.annotation.api.dependency;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+/**
+ * Annotates a method of field as a Resource Dependency.
+ */
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface ResourceDependency
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ServiceDependency.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ServiceDependency.java
similarity index 97%
rename from dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ServiceDependency.java
rename to dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ServiceDependency.java
index c36c18b..adac069 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ServiceDependency.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ServiceDependency.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dm.annotation.api;
+package org.apache.felix.dm.annotation.api.dependency;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/TemporalServiceDependency.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/TemporalServiceDependency.java
similarity index 98%
rename from dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/TemporalServiceDependency.java
rename to dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/TemporalServiceDependency.java
index 0866d31..e445adb 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/TemporalServiceDependency.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/TemporalServiceDependency.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.dm.annotation.api;
+package org.apache.felix.dm.annotation.api.dependency;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
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 065b756..105e7c9 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
@@ -29,22 +29,22 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.apache.felix.dm.annotation.api.AdapterService;
import org.apache.felix.dm.annotation.api.AspectService;
-import org.apache.felix.dm.annotation.api.BundleAdapterService;
-import org.apache.felix.dm.annotation.api.BundleDependency;
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;
import org.apache.felix.dm.annotation.api.Service;
-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.annotation.api.TemporalServiceDependency;
+import org.apache.felix.dm.annotation.api.adapter.AdapterService;
+import org.apache.felix.dm.annotation.api.adapter.BundleAdapterService;
+import org.apache.felix.dm.annotation.api.adapter.FactoryConfigurationAdapterService;
+import org.apache.felix.dm.annotation.api.adapter.ResourceAdapterService;
+import org.apache.felix.dm.annotation.api.dependency.BundleDependency;
+import org.apache.felix.dm.annotation.api.dependency.ConfigurationDependency;
+import org.apache.felix.dm.annotation.api.dependency.ResourceDependency;
+import org.apache.felix.dm.annotation.api.dependency.ServiceDependency;
+import org.apache.felix.dm.annotation.api.dependency.TemporalServiceDependency;
import org.osgi.framework.Bundle;
import aQute.lib.osgi.Annotation;
@@ -123,12 +123,12 @@
AdapterService,
BundleAdapterService,
ResourceAdapterService,
+ FactoryConfigurationAdapterService,
ServiceDependency,
TemporalServiceDependency,
ConfigurationDependency,
BundleDependency,
ResourceDependency,
- FactoryConfigurationAdapterService
};
// List of component descriptor parameters
@@ -141,8 +141,6 @@
impl,
provide,
properties,
- factory,
- factoryMethod,
composition,
service,
filter,
@@ -162,7 +160,9 @@
adapteeService,
adapteeFilter,
stateMask,
- ranking
+ ranking,
+ factory,
+ factoryConfigure,
};
/**
@@ -460,12 +460,12 @@
// provide attribute
info.addClassParam(annotation, Params.provide, m_interfaces);
-
+
// factory attribute
- info.addClassParam(annotation, Params.factory, null);
-
- // factoryMethod attribute
- info.addParam(annotation, Params.factoryMethod, null);
+ info.addParam(annotation, Params.factory, null);
+
+ // factoryPropertiesCallback attribute
+ info.addParam(annotation, Params.factoryConfigure, null);
}
private void addCommonServiceParams(Info info)
@@ -590,13 +590,7 @@
// Register previously parsed Init/Start/Stop/Destroy/Composition annotations
addCommonServiceParams(info);
-
- // factory attribute
- info.addClassParam(annotation, Params.factory, null);
-
- // factoryMethod attribute
- info.addParam(annotation, Params.factoryMethod, null);
-
+
// Parse service filter
String filter = annotation.get(Params.filter.toString());
if (filter != null) {
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 3be1217..1b96e89 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
@@ -25,7 +25,9 @@
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
+import java.util.Hashtable;
import java.util.List;
+import java.util.Set;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.dependencies.BundleDependency;
@@ -51,6 +53,7 @@
private HashMap<Bundle, List<Service>> m_services = new HashMap<Bundle, List<Service>>();
private LogService m_logService; // Injected
private BundleContext m_bctx; // Injected
+ private ServiceFactory m_serviceFactory;
/**
* Starts our Service (at this point, we have been injected with our bundle context, as well
@@ -131,6 +134,7 @@
* @param b
* @param descriptorURL
*/
+ @SuppressWarnings("null")
private void loadDescriptor(Bundle b, URL descriptorURL)
{
m_logService.log(LogService.LOG_DEBUG, "Parsing descriptor " + descriptorURL
@@ -147,6 +151,7 @@
while ((line = in.readLine()) != null)
{
+ Dependency dp = null;
switch (parser.parse(line))
{
case Service:
@@ -174,32 +179,45 @@
break;
case ServiceDependency:
- checkServiceParsed(service);
- service.add(createServiceDependency(b, dm, parser, false));
+ dp = createServiceDependency(b, dm, parser, false);
break;
case TemporalServiceDependency:
- checkServiceParsed(service);
- service.add(createServiceDependency(b, dm, parser, true));
+ dp = createServiceDependency(b, dm, parser, true);
break;
case ConfigurationDependency:
- checkServiceParsed(service);
- service.add(createConfigurationDependency(b, dm, parser));
+ dp = createConfigurationDependency(b, dm, parser);
break;
case BundleDependency:
- checkServiceParsed(service);
- service.add(createBundleDependency(b, dm, parser));
+ dp = createBundleDependency(b, dm, parser);
break;
case ResourceDependency:
- checkServiceParsed(service);
- service.add(createResourceDependency(b, dm, parser));
+ dp = createResourceDependency(b, dm, parser);
break;
}
+
+ // If we parsed a dependency: add it in the current parsed service, unless the service is a factory service.
+ // In this case, the dependency is just buffered in the service factory, which will register it at
+ // each instantiation time.
+
+ if (dp != null)
+ {
+ checkServiceParsed(service);
+ if (m_serviceFactory != null)
+ {
+ // The dependency is buffered and will be used once this factory fires a service instantiation.
+ m_serviceFactory.addDependency(dp);
+ }
+ else
+ {
+ service.add(dp);
+ }
+ }
}
-
+
List<Service> services = m_services.get(b);
if (services == null)
{
@@ -273,33 +291,39 @@
throws ClassNotFoundException
{
Service service = dm.createService();
- // Get factory parameters.
String factory = parser.getString(DescriptorParam.factory, null);
- String factoryMethod = parser.getString(DescriptorParam.factoryMethod, "create");
+ String factoryConfigure = parser.getString(DescriptorParam.factoryConfigure, null);
+ String impl = parser.getString(DescriptorParam.impl);
+ String init = parser.getString(DescriptorParam.init, null);
+ String start = parser.getString(DescriptorParam.start, null);
+ String stop = parser.getString(DescriptorParam.stop, null);
+ String destroy = parser.getString(DescriptorParam.destroy, null);
+ String composition = parser.getString(DescriptorParam.composition, null);
+ Dictionary<String, String> serviceProperties = parser.getDictionary(DescriptorParam.properties, null);
+ String[] provide = parser.getStrings(DescriptorParam.provide, null);
if (factory == null)
{
- // Set service impl
- String impl = parser.getString(DescriptorParam.impl);
service.setImplementation(b.loadClass(impl));
+ service.setCallbacks(init, start, stop, destroy);
+ if (composition != null)
+ {
+ service.setComposition(composition);
+ }
+ if (provide != null)
+ {
+ service.setInterface(provide, serviceProperties);
+ }
}
else
{
- // Set service factory
- Class<?> factoryClass = b.loadClass(factory);
- service.setFactory(factoryClass, factoryMethod);
- }
-
- // Set service callbacks
- setCommonServiceParams(service, parser);
-
- // Set service interface with associated service properties
- Dictionary<String, String> serviceProperties = parser.getDictionary(
- DescriptorParam.properties, null);
- String[] provides = parser.getStrings(DescriptorParam.provide, null);
- if (provides != null)
- {
- service.setInterface(provides, serviceProperties);
+ m_serviceFactory = new ServiceFactory(b.loadClass(impl), init, start, stop, destroy,
+ composition, serviceProperties, provide, factoryConfigure);
+ service.setImplementation(m_serviceFactory);
+ service.setCallbacks(null, "start", "stop", null);
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.put("dm.factory.name", factory);
+ service.setInterface(Set.class.getName(), props);
}
return service;
@@ -340,19 +364,9 @@
String serviceFilter = parser.getString(DescriptorParam.filter, null);
Dictionary<String, String> aspectProperties = parser.getDictionary(DescriptorParam.properties, null);
int ranking = parser.getInt(DescriptorParam.ranking, 1);
- String factory = parser.getString(DescriptorParam.factory, null);
- if (factory == null)
- {
- String implClass = parser.getString(DescriptorParam.impl);
- Object impl = b.loadClass(implClass);
- service = dm.createAspectService(serviceInterface, serviceFilter, ranking, impl, aspectProperties);
- }
- else
- {
- String factoryMethod = parser.getString(DescriptorParam.factoryMethod, "create");
- Class<?> factoryClass = b.loadClass(factory);
- service = dm.createAspectService(serviceInterface, serviceFilter, ranking, factoryClass, factoryMethod, aspectProperties);
- }
+ String implClass = parser.getString(DescriptorParam.impl);
+ Object impl = b.loadClass(implClass);
+ service = dm.createAspectService(serviceInterface, serviceFilter, ranking, impl, aspectProperties);
setCommonServiceParams(service, parser);
return service;
}
@@ -371,8 +385,7 @@
String[] adapterService = parser.getStrings(DescriptorParam.adapterService, null);
Dictionary<String, String> adapterProperties = parser.getDictionary(DescriptorParam.adapterProperties, null);
Class<?> adapteeService = b.loadClass(parser.getString(DescriptorParam.adapteeService));
- String adapteeFilter = parser.getString(DescriptorParam.adapteeFilter, null);
-
+ String adapteeFilter = parser.getString(DescriptorParam.adapteeFilter, null);
Service service = dm.createAdapterService(adapteeService, adapteeFilter, adapterService, adapterImpl, adapterProperties);
setCommonServiceParams(service, parser);
return service;
@@ -443,62 +456,56 @@
/**
* Creates a ServiceDependency that we parsed from a component descriptor "ServiceDependency" entry.
- * @param b
- * @param dm
- * @param parser
- * @param temporal true if this dependency is a temporal one, false if not.
- * @return
- * @throws ClassNotFoundException
*/
- private ServiceDependency createServiceDependency(Bundle b, DependencyManager dm,
- DescriptorParser parser, boolean temporal) throws ClassNotFoundException
+ private Dependency createServiceDependency(Bundle b, DependencyManager dm, DescriptorParser parser, boolean temporal)
+ throws ClassNotFoundException
{
- ServiceDependency sd = temporal ? dm.createTemporalServiceDependency()
- : dm.createServiceDependency();
-
- // Set service with eventual service filter
String service = parser.getString(DescriptorParam.service);
- Class serviceClass = b.loadClass(service);
+ Class<?> serviceClass = b.loadClass(service);
String serviceFilter = parser.getString(DescriptorParam.filter, null);
- sd.setService(serviceClass, serviceFilter);
-
- // Set default service impl
String defaultServiceImpl = parser.getString(DescriptorParam.defaultImpl, null);
- if (defaultServiceImpl != null)
- {
- Class defaultServiceImplClass = b.loadClass(defaultServiceImpl);
- sd.setDefaultImplementation(defaultServiceImplClass);
- }
-
- // Set bind/unbind/rebind
+ Class<?> defaultServiceImplClass = (defaultServiceImpl != null) ? b.loadClass(defaultServiceImpl) : null;
String added = parser.getString(DescriptorParam.added, null);
String changed = temporal ? null : parser.getString(DescriptorParam.changed, null);
String removed = temporal ? null : parser.getString(DescriptorParam.removed, null);
- sd.setCallbacks(added, changed, removed);
-
- // Set AutoConfig
String autoConfigField = parser.getString(DescriptorParam.autoConfig, null);
+ boolean required = "true".equals(parser.getString(DescriptorParam.required, "true"));
+ String timeout = parser.getString(DescriptorParam.timeout, null);
+
+ Dependency dp = createServiceDependency(dm, temporal, serviceClass, serviceFilter,
+ defaultServiceImplClass, added, changed, removed,
+ autoConfigField, timeout, required);
+ return dp;
+ }
+
+ private Dependency createServiceDependency(DependencyManager dm, boolean temporal, Class<?> serviceClass,
+ String serviceFilter, Class<?> defaultServiceImplClass, String added, String changed, String removed,
+ String autoConfigField, String timeout, boolean required)
+ {
+ ServiceDependency sd = temporal ? dm.createTemporalServiceDependency()
+ : dm.createServiceDependency();
+ sd.setService(serviceClass, serviceFilter);
+ if (defaultServiceImplClass != null)
+ {
+ sd.setDefaultImplementation(defaultServiceImplClass);
+ }
+ sd.setCallbacks(added, changed, removed);
if (autoConfigField != null)
{
sd.setAutoConfig(autoConfigField);
}
-
- // Do specific parsing for temporal service dependency
if (temporal)
{
// Set the timeout value for a temporal service dependency
- String timeout = parser.getString(DescriptorParam.timeout, null);
if (timeout != null)
{
((TemporalServiceDependency) sd).setTimeout(Long.parseLong(timeout));
}
-
// Set required flag (always true for a temporal dependency)
sd.setRequired(true);
} else {
// for ServiceDependency, get required flag.
- String required = parser.getString(DescriptorParam.required, "true");
- sd.setRequired("true".equals(required));
+ sd.setRequired(required);
}
return sd;
}
@@ -513,20 +520,23 @@
private Dependency createConfigurationDependency(Bundle b, DependencyManager dm,
DescriptorParser parser)
{
- ConfigurationDependency cd = dm.createConfigurationDependency();
String pid = parser.getString(DescriptorParam.pid);
+ boolean propagate = "true".equals(parser.getString(DescriptorParam.propagate, "false"));
+ String callback = parser.getString(DescriptorParam.updated, "updated");
+ Dependency dp = createConfigurationDependency(dm, pid, callback, propagate);
+ return dp;
+ }
+
+ private Dependency createConfigurationDependency(DependencyManager dm, String pid, String callback, boolean propagate) {
if (pid == null)
{
throw new IllegalArgumentException(
"pid attribute not provided in ConfigurationDependency declaration");
}
+ ConfigurationDependency cd = dm.createConfigurationDependency();
cd.setPid(pid);
-
- String propagate = parser.getString(DescriptorParam.propagate, "false");
- cd.setPropagate("true".equals(propagate));
-
- String callback = parser.getString(DescriptorParam.updated, "updated");
cd.setCallback(callback);
+ cd.setPropagate(propagate);
return cd;
}
@@ -540,59 +550,60 @@
private Dependency createBundleDependency(Bundle b, DependencyManager dm,
DescriptorParser parser)
{
- BundleDependency bd = dm.createBundleDependency();
-
- // Set add/changed/removed
String added = parser.getString(DescriptorParam.added, null);
String changed = parser.getString(DescriptorParam.changed, null);
String removed = parser.getString(DescriptorParam.removed, null);
- bd.setCallbacks(added, changed, removed);
-
- // required
- bd.setRequired("true".equals(parser.getString(DescriptorParam.required, "true")));
-
- // filter
+ boolean required = "true".equals(parser.getString(DescriptorParam.required, "true"));
String filter = parser.getString(DescriptorParam.filter, null);
+ int stateMask = parser.getInt(DescriptorParam.stateMask, -1);
+ boolean propagate = "true".equals(parser.getString(DescriptorParam.propagate, "false"));
+
+ Dependency dp = createBundleDependency(dm, added, changed, removed, required, propagate, filter, stateMask);
+ return dp;
+ }
+
+ private Dependency createBundleDependency(DependencyManager dm, String added, String changed, String removed,
+ boolean required, boolean propagate, String filter, int stateMask)
+ {
+ BundleDependency bd = dm.createBundleDependency();
+ bd.setCallbacks(added, changed, removed);
+ bd.setRequired(required);
+ bd.setPropagate(propagate);
if (filter != null)
{
bd.setFilter(filter);
}
-
- // stateMask
- int stateMask = parser.getInt(DescriptorParam.stateMask, -1);
if (stateMask != -1)
{
bd.setStateMask(stateMask);
}
-
- // propagate
- bd.setPropagate("true".equals(parser.getString(DescriptorParam.propagate, "false")));
- return bd;
+ return bd;
}
- private Dependency createResourceDependency(Bundle b, DependencyManager dm,
+ private Dependency createResourceDependency(@SuppressWarnings("unused") Bundle b, DependencyManager dm,
DescriptorParser parser)
{
- ResourceDependency rd = dm.createResourceDependency();
-
- // Set add/changed/removed
String added = parser.getString(DescriptorParam.added, null);
String changed = parser.getString(DescriptorParam.changed, null);
String removed = parser.getString(DescriptorParam.removed, null);
- rd.setCallbacks(added, changed, removed);
-
- // required
- rd.setRequired("true".equals(parser.getString(DescriptorParam.required, "true")));
-
- // filter
String filter = parser.getString(DescriptorParam.filter, null);
+ boolean required = "true".equals(parser.getString(DescriptorParam.required, "true"));
+ boolean propagate = "true".equals(parser.getString(DescriptorParam.propagate, "false"));
+
+ Dependency dp = createResourceDependency(dm, added, changed, removed, required, filter, propagate);
+ return dp;
+ }
+
+ private Dependency createResourceDependency(DependencyManager dm, String added, String changed, String removed, boolean required, String filter, boolean propagate)
+ {
+ ResourceDependency rd = dm.createResourceDependency();
+ rd.setCallbacks(added, changed, removed);
+ rd.setRequired(required);
if (filter != null)
{
rd.setFilter(filter);
}
-
- // propagate
- rd.setPropagate("true".equals(parser.getString(DescriptorParam.propagate, "false")));
+ rd.setPropagate(propagate);
return rd;
}
}
\ No newline at end of file
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 f1e7e66..c208a9a 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
@@ -47,12 +47,6 @@
/* Service attribute for the provided service properties (the parsed value is a Hashtable) */
properties,
- /* Service attribute for the factory class name (the parsed value is a String) */
- factory,
-
- /* Service attribute for the factory method name (the parsed value is a String) */
- factoryMethod,
-
/* Service attribute for the composition method name (the parsed value is a String) */
composition,
@@ -111,56 +105,11 @@
ranking,
/* The factory pid of an FactoryConfigurationAdapterService annotation (the parsed value is a string) */
- factoryPid;
+ factoryPid,
- /**
- * Indicates if a given attribute is a Service attribute.
- * @param attr a Descriptor attribute
- * @return true if the descriptor is a Service attribute, false if not
- */
- public static boolean isServiceAttribute(DescriptorParam attr) {
- return serviceAttribute.contains(attr);
- }
+ /* the factory attribute for the Service annotation (the parsed value is a string) */
+ factory,
- /**
- * Indicates if a given attribute is a ServiceDependency attribute.
- * @param attr a Descriptor attribute
- * @return true if the descriptor is a Service attribute, false if not
- */
- public static boolean isServiceDepependencyAttribute(DescriptorParam attr) {
- return serviceDependencyAttribute.contains(attr);
- }
-
- /**
- * Indicates if a given attribute is a TemporalServiceDependency attribute.
- * @param attr a Descriptor attribute
- * @return true if the descriptor is a Temporal Service attribute, false if not
- */
- public static boolean isTemporalServiceDepependencyAttribute(DescriptorParam attr) {
- return serviceDependencyAttribute.contains(attr) || attr == timeout;
- }
-
- /**
- * Indicates if a given attribute is a ServiceDependency attribute.
- * @param attr a Descriptor attribute
- * @return true if the descriptor is a Service attribute, false if not
- */
- public static boolean isConfigurationDepependencyAttribute(DescriptorParam attr) {
- return configurationDependencyAttribute.contains(attr);
- }
-
- /**
- * List of Service attributes
- */
- private final static EnumSet serviceAttribute = EnumSet.range(init, factoryMethod);
-
- /**
- * List of ServiceDependency attributes
- */
- private final static EnumSet serviceDependencyAttribute = EnumSet.range(service, autoConfig);
-
- /**
- * List of ConfigurationDependency attributes
- */
- private final static EnumSet configurationDependencyAttribute = EnumSet.range(pid, updated);
+ /* the factoryConfigure attribute for the Service annotation (the parsed value is a string) */
+ factoryConfigure
}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceFactory.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceFactory.java
new file mode 100644
index 0000000..b37c5c7
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceFactory.java
@@ -0,0 +1,552 @@
+/*
+ * 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.runtime;
+
+import java.lang.reflect.Method;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.dependencies.Dependency;
+import org.apache.felix.dm.service.Service;
+
+/**
+ * This class implements a <code>java.util.Set</code> which acts as a service factory.
+ * When a <code>Service</annotation> contains a <code>factory</code> attribute, this class is provided
+ * into the OSGi registry with a <code>dm.factory.name</code> service property. So, another factory component
+ * may be injected with this Set. And each time a Dictionary configuration is registered in the Set,
+ * then a new Service instance will be instantiated, and will be provided with the Dictionary passed to the
+ * Service instance.
+ */
+@SuppressWarnings( { "unchecked" })
+public class ServiceFactory extends AbstractSet<Dictionary>
+{
+ /**
+ * The actual Service instance that is allocated for each dictionaries added in this Set.
+ */
+ private Object m_impl;
+
+ /**
+ * The Service class used to instantiate Service instances
+ */
+ private final Class<?> m_implClass;
+
+ /**
+ * The Service init lifecycle callback.
+ */
+ private final String m_init;
+
+ /**
+ * The Service start lifecycle callback.
+ */
+ private final String m_start;
+
+ /**
+ * The Service stop lifecycle callback.
+ */
+ private final String m_stop;
+
+ /**
+ * The Service destroy lifecycle callback.
+ */
+ private final String m_destroy;
+
+ /**
+ * The getComposition Service callback.
+ */
+ private final String m_composition;
+
+ /**
+ * The Service provided in the OSGi registry.
+ */
+ private final String[] m_provide;
+
+ /**factoryConfigure
+ * The properties to be provided by the Service.
+ */
+ private final Dictionary m_serviceProperties;
+
+ /**
+ * The configure Service callback used to pass configuration added in this Set.
+ */
+ private final String m_configure;
+
+ /**
+ * The map between our Dictionaries and corresponding Service instances.
+ */
+ private final ConcurrentHashMap<ServiceKey, Object> m_services = new ConcurrentHashMap<ServiceKey, Object>();
+
+ /**
+ * The list of Dependencies which are applied in the Service.
+ */
+ private final ArrayList<Dependency> m_dependencies = new ArrayList<Dependency>();
+
+ /**
+ * The DependencyManager (injected by reflection), which is used to create Service instances.
+ */
+ private DependencyManager m_dm; // Injected
+
+ /**
+ * This class is used to serialize concurrent method calls, and allow to leave our methods unsynchronized.
+ * This is required because some of our methods may invoke some Service callbacks, which must be called outside
+ * synchronized block (in order to avoid potential dead locks).
+ */
+ private SerialExecutor m_serialExecutor = new SerialExecutor();
+
+ /**
+ * Flag used to check if our service is Active.
+ */
+ private volatile boolean m_active;
+
+ /**
+ * Flag used to check if a service is being created
+ */
+ private final static Object SERVICE_CREATING = new Object();
+
+ /**
+ * This class wraps <tt>Dictionary</tt>, allowing to store the dictionary into a Map, using
+ * reference-equality in place of object-equality when getting the Dictionary from the Map.
+ */
+ private static class ServiceKey
+ {
+ private Dictionary m_dictionary;
+
+ public ServiceKey(Dictionary dictionary)
+ {
+ m_dictionary = dictionary;
+ }
+
+ Dictionary getDictionary()
+ {
+ return m_dictionary;
+ }
+
+ @Override
+ public boolean equals(Object that)
+ {
+ return that instanceof ServiceKey ? (((ServiceKey) that).getDictionary() == m_dictionary)
+ : false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return System.identityHashCode(m_dictionary);
+ }
+
+ @Override
+ public String toString()
+ {
+ return Dictionary.class.getName() + "@" + System.identityHashCode(m_dictionary);
+ }
+ }
+
+ /**
+ * Sole constructor.
+ * @param impl The Service impl class
+ * @param init The init lifecyle callback
+ * @param start The start lifecyle callback
+ * @param stop The stop lifecyle callback
+ * @param destroy The destroy lifecyle callback
+ * @param composition The getComposition Service method.
+ * @param serviceProperties The Service properties
+ * @param provide The Services provided by this Service
+ * @param factoryConfigure The configure callback invoked in order to pass configurations added in this Set.
+ */
+ public ServiceFactory(Class<?> impl, String init, String start, String stop, String destroy, String composition, Dictionary serviceProperties, String[] provide, String factoryConfigure)
+ {
+ m_implClass = impl;
+ m_init = init;
+ m_start = start;
+ m_stop = stop;
+ m_destroy = destroy;
+ m_composition = composition;
+ m_serviceProperties = serviceProperties;
+ m_provide = Arrays.copyOf(provide, provide.length);
+ m_configure = factoryConfigure;
+ }
+
+ /**
+ * Method called in order to track all Dependencies added in our Service.
+ * The ComponentManager will forward all Service Dependencies in this method and we'll attach them
+ * to all concrete Service instances (when we will create them).
+ */
+ public void addDependency(Dependency dp)
+ {
+ m_dependencies.add(dp);
+ }
+
+ /**
+ * Our Service is starting.
+ */
+ public void start()
+ {
+ m_active = true;
+ }
+
+ /**
+ * Our Service is stopping: we have to remove all Service instances that we have created.
+ */
+ public void stop()
+ {
+ try
+ {
+ clear();
+ }
+ finally
+ {
+ m_active = false;
+ }
+ }
+
+ /**
+ * Create or Update a Service.
+ */
+ @Override
+ public boolean add(final Dictionary configuration)
+ {
+ // Check parameter validity
+ if (configuration == null)
+ {
+ throw new NullPointerException("configuration parameter can't be null");
+ }
+
+ // Check if our service is running.
+ checkServiceAvailable();
+
+ // Check if service is being created
+ ServiceKey serviceKey = new ServiceKey(configuration);
+ boolean creating = false;
+ synchronized (this)
+ {
+ if (!m_services.containsKey(serviceKey))
+ {
+ m_services.put(serviceKey, SERVICE_CREATING);
+ creating = true;
+ }
+ // Create or Update the Service.
+ m_serialExecutor.enqueue(new Runnable()
+ {
+ public void run()
+ {
+ doAdd(configuration);
+ }
+ });
+ }
+
+ m_serialExecutor.execute();
+ return creating;
+ }
+
+ /**
+ * Another Service wants to remove an existing Service.
+ * This method is not synchronized but uses a SerialExecutor for serializing concurrent method call.
+ * (This avoid potential dead locks, especially when Service callback methods are invoked).
+ */
+ @Override
+ public boolean remove(final Object configuration)
+ {
+ // Check parameter validity.
+ if (configuration == null)
+ {
+ throw new NullPointerException("configuration parameter can't be null");
+ }
+ if (!(configuration instanceof Dictionary))
+ {
+ throw new IllegalArgumentException("configuration must be an instance of a Dictionary");
+ }
+
+ // Check if our service is active.
+ checkServiceAvailable();
+
+ // Check if service is created (or creating)
+ boolean found = m_services.containsKey(new ServiceKey((Dictionary) configuration));
+ if (found)
+ {
+ // Create or Update the Service.
+ m_serialExecutor.enqueue(new Runnable()
+ {
+ public void run()
+ {
+ doRemove((Dictionary) configuration);
+ }
+ });
+ m_serialExecutor.execute();
+ }
+ return found;
+ }
+
+ /**
+ * Another Service wants to remove all existing Services.
+ * This method is not synchronized but uses a SerialExecutor for serializing concurrent method call.
+ * (This avoid potential dead locks, especially when Service callback methods are invoked).
+ */
+ @Override
+ public void clear()
+ {
+ if (!m_active)
+ {
+ return;
+ }
+
+ // Create or Update the Service.
+ m_serialExecutor.enqueue(new Runnable()
+ {
+ public void run()
+ {
+ doClear();
+ }
+ });
+ m_serialExecutor.execute();
+ }
+
+ /**
+ * returns the list of active Service instances configurations.
+ */
+ @Override
+ public Iterator<Dictionary> iterator()
+ {
+ throw new UnsupportedOperationException(
+ "iterator method is not supported by DependencyManager Set's service factories");
+ }
+
+ @Override
+ public String toString()
+ {
+ return ServiceFactory.class.getName() + "(" + m_services.size() + " active instances)";
+ }
+
+ /**
+ * Returns the number of active Service instances.
+ */
+ @Override
+ public int size()
+ {
+ if (!m_active)
+ {
+ return 0;
+ }
+ return m_services.size();
+ }
+
+ /**
+ * Checks if our Service is available (we are not stopped").
+ */
+ private void checkServiceAvailable()
+ {
+ if (!m_active)
+ {
+ throw new IllegalStateException("Service not available");
+ }
+ }
+
+ /**
+ * Add or create a new Service instance, given its configuration. This method is invoked by the
+ * SerialExecutor, hence it's thread safe and we'll invoke Service's callbacks without being
+ * synchronized (hence this will avoid potential dead locks).
+ */
+ private void doAdd(Dictionary configuration)
+ {
+ // Check if the service exists.
+ ServiceKey serviceKey = new ServiceKey(configuration);
+ Object service = m_services.get(serviceKey);
+ if (service == null || service == SERVICE_CREATING)
+ {
+ try
+ {
+ // Create the Service / impl
+ Service s = m_dm.createService();
+ m_impl = createServiceImpl();
+
+ // Invoke "configure" callback
+ if (m_configure != null)
+ {
+ invokeConfigure(m_impl, m_configure, configuration);
+ }
+
+ // Create Service
+ s.setImplementation(m_impl);
+ s.setCallbacks(m_init, m_start, m_stop, m_destroy);
+ if (m_composition != null)
+ {
+ s.setComposition(m_composition);
+ }
+ if (m_provide != null)
+ {
+ // Merge service properties with the configuration provided by the factory.
+ Dictionary serviceProperties = mergeSettings(m_serviceProperties, configuration);
+ s.setInterface(m_provide, serviceProperties);
+ }
+
+ // Plug original dependencies
+ s.add((List<Dependency>) m_dependencies.clone());
+
+ // Register the Service instance, and keep track of it.
+ m_dm.add(s);
+ m_services.put(serviceKey, s);
+ }
+ catch (RuntimeException e)
+ {
+ // Make sure the SERVICE_CREATING flag is also removed
+ m_services.remove(serviceKey);
+ throw e;
+ }
+ }
+ else
+ {
+ // Reconfigure an already existing Service.
+ if (m_configure != null)
+ {
+ invokeConfigure(m_impl, m_configure, configuration);
+ }
+
+ // Update service properties
+ if (m_provide != null)
+ {
+ Dictionary settings = mergeSettings(m_serviceProperties, configuration);
+ ((Service) service).setServiceProperties(settings);
+ }
+ }
+ }
+
+ private void doRemove(Dictionary configuraton)
+ {
+ ServiceKey serviceKey = new ServiceKey(configuraton);
+ Object service = m_services.remove(serviceKey);
+ if (service != null && service != SERVICE_CREATING)
+ {
+ m_dm.remove((Service) service);
+ }
+ }
+
+ private void doClear()
+ {
+ try
+ {
+ for (Object service : m_services.values())
+ {
+ if (service instanceof Service)
+ {
+ m_dm.remove((Service) service);
+ }
+ }
+ }
+ finally
+ {
+ m_services.clear();
+ }
+ }
+
+ /**
+ * Merge factory configuration settings with the service properties. The private factory configuration
+ * settings are ignored. A factory configuration property is private if its name starts with a dot (".").
+ *
+ * @param serviceProperties
+ * @param factoryConfiguration
+ * @return
+ */
+ private Dictionary mergeSettings(Dictionary serviceProperties, Dictionary factoryConfiguration)
+ {
+ Dictionary props = new Hashtable();
+
+ if (serviceProperties != null)
+ {
+ Enumeration keys = serviceProperties.keys();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ Object val = serviceProperties.get(key);
+ props.put(key, val);
+ }
+ }
+
+ Enumeration keys = factoryConfiguration.keys();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ if (!key.toString().startsWith("."))
+ {
+ // public properties are propagated
+ Object val = factoryConfiguration.get(key);
+ props.put(key, val);
+ }
+ }
+ return props;
+ }
+
+ /**
+ * Create a Service impl instance
+ */
+ private Object createServiceImpl()
+ {
+ try
+ {
+ m_impl = m_implClass.newInstance();
+ return m_impl;
+ }
+ catch (Throwable t)
+ {
+ if (t instanceof RuntimeException)
+ {
+ throw (RuntimeException) t;
+ }
+ else
+ {
+ throw new RuntimeException("Could not create Service instance for class "
+ + m_implClass, t);
+ }
+ }
+ }
+
+ /**
+ * Invokes the configure callback method on the service instance implemenatation.
+ * @param impl
+ * @param factoryConfige
+ * @param config
+ */
+ private void invokeConfigure(Object impl, String factoryConfige, Dictionary config)
+ {
+ try
+ {
+ Method m = impl.getClass().getMethod(factoryConfige, Dictionary.class);
+ m.setAccessible(true);
+ m.invoke(impl, new Object[] { config });
+ }
+
+ catch (Throwable t)
+ {
+ if (t instanceof RuntimeException)
+ {
+ throw (RuntimeException) t;
+ }
+ else
+ {
+ throw new RuntimeException("Could not invoke method " + factoryConfige
+ + " on object " + impl);
+ }
+ }
+ }
+}