- Reworked design of the runtime bundle.
- Reworked design of the annotation scanner.
- Replaced proprietary runtime parser by JSON.
- Fixed BundleDependency.
- Allow to configure annotated service dependency filters from config admin (work in progress).
- Reworked Logging.
- Allow to specify service property arrays.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@947379 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/LICENSE.json b/dependencymanager/annotation/LICENSE.json
new file mode 100644
index 0000000..87d1411
--- /dev/null
+++ b/dependencymanager/annotation/LICENSE.json
@@ -0,0 +1,21 @@
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/dependencymanager/annotation/pom.xml b/dependencymanager/annotation/pom.xml
index c3bab2d..da380f3 100644
--- a/dependencymanager/annotation/pom.xml
+++ b/dependencymanager/annotation/pom.xml
@@ -79,6 +79,13 @@
<version>2.0</version>
<scope>compile</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>20070829</version>
+ <scope>compile</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Property.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Property.java
index 6800747..c87d7e8 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Property.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Property.java
@@ -25,7 +25,7 @@
/**
* Annotation used to describe a property key-value pair. It is used when
- * declaring {@link Service#properties()} attribute, as well as {@link PropertyMetaData#options()} attribute.
+ * declaring {@link Service#properties()} attribute.
*/
@Retention(RetentionPolicy.CLASS)
@Target( { ElementType.ANNOTATION_TYPE })
@@ -41,5 +41,11 @@
* Returns the property value
* @return this property value
*/
- String value();
+ String value() default "";
+
+ /**
+ * Returns the property values as a String array).
+ * @return this property value as a String array
+ */
+ String[] values() 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 5c41695..a955ef3 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
@@ -106,10 +106,16 @@
boolean required() default true;
/**
- * Return a list of valid options for this property.
- * The Options are defined using the {@link Property} annotation, where the {@link Property#name()} attribute is used to
- * reference the option label, and the {@link Property#value()} attribute is used to reference the option value.
- * @return the list of valid options for this property.
+ * Return a list of valid option labels for this property. The purpose of this method is to allow menus with localized labels.
+ * It is associated with the {@link #optionValues()} attribute. The labels returned here are ordered in the same way as the
+ * {@link #optionValues()} attribute values.
+ * @return the list of valid option labels for this property.
*/
- Property[] options() default {};
+ String[] optionLabels() default {};
+
+ /**
+ * Return a list of option values that this property can take. This list must be in the same sequence as the {@link #optionLabels()}
+ * attribute.
+ */
+ String[] optionValues() default {};
}
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 8abbf91..725e588 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
@@ -102,7 +102,7 @@
* */
* @Service
* class Y {
- * @ServiceDependency(filter="(dm.factory.name=MyServiceFactory))
+ * @ServiceDependency(filter="(dm.factory.name=MyServiceFactory)")
* Set<Dictionary> _XFactory; // This Set acts as a Factory API for creating X Service instances.
*
* @Start
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ServiceDependency.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ServiceDependency.java
index adac069..85bb686 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ServiceDependency.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/dependency/ServiceDependency.java
@@ -32,42 +32,38 @@
public @interface ServiceDependency
{
/**
- * Returns the Service dependency type (by default, the type is method parameter type).
- * @return the Service dependency type.
+ * The type if the service this dependency is applying on. By default, the type is method parameter or the class field type.
*/
Class<?> service() default Object.class;
/**
- * Returns the Service dependency OSGi filter.
- * @return The Service dependency filter.
+ * The Service dependency OSGi filter.
*/
String filter() default "";
/**
- * Returns the class for the default implementation, if the dependency is not available.
- * @return The default class used when the dependency is not available.
+ * The class for the default implementation, if the dependency is not available.
*/
Class<?> defaultImpl() default Object.class;
/**
- * Returns whether the Service dependency is required or not.
- * @return true if the dependency is required, false if not.
+ * Whether the Service dependency is required or not.
*/
boolean required() default true;
/**
- * Returns the callback method to be invoked when the service is available. This attribute is only meaningful when
+ * The callback method to be invoked when the service is available. This attribute is only meaningful when
* the annotation is applied on a class field.
*/
String added() default "";
/**
- * Returns the callback method to be invoked when the service properties have changed.
+ * The callback method to be invoked when the service properties have changed.
*/
String changed() default "";
/**
- * Returns the callback method to invoke when the service is lost.
+ * The callback method to invoke when the service is lost.
*/
String removed() 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 105e7c9..a192057 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
@@ -26,8 +26,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.apache.felix.dm.annotation.api.AspectService;
import org.apache.felix.dm.annotation.api.Composition;
@@ -95,7 +93,7 @@
private String m_method;
private String m_descriptor;
private Set<String> m_methods = new HashSet<String>();
- private List<Info> m_infos = new ArrayList<Info>(); // Last elem is either Service or AspectService
+ private List<EntryWriter> m_writers = new ArrayList<EntryWriter>(); // Last elem is either Service or AspectService
private MetaType m_metaType;
private String m_startMethod;
private String m_stopMethod;
@@ -103,193 +101,10 @@
private String m_destroyMethod;
private String m_compositionMethod;
- // Pattern used to parse the class parameter from the bind methods ("bind(Type)" or "bind(Map, Type)" or "bind(BundleContext, Type)"
- private final static Pattern m_bindClassPattern = Pattern.compile("\\((L[^;]+;)?L([^;]+);\\)V");
-
- // Pattern used to parse classes from class descriptors;
- private final static Pattern m_classPattern = Pattern.compile("L([^;]+);");
-
- // Pattern used to check if a method is void and does not take any params
- private final static Pattern m_voidMethodPattern = Pattern.compile("\\(\\)V");
-
- // Pattern used to check if a method returns an array of Objects
- private final static Pattern m_methodCompoPattern = Pattern.compile("\\(\\)\\[Ljava/lang/Object;");
-
- // List of component descriptor entry types
- enum EntryTypes
- {
- Service,
- AspectService,
- AdapterService,
- BundleAdapterService,
- ResourceAdapterService,
- FactoryConfigurationAdapterService,
- ServiceDependency,
- TemporalServiceDependency,
- ConfigurationDependency,
- BundleDependency,
- ResourceDependency,
- };
-
- // List of component descriptor parameters
- enum Params
- {
- init,
- start,
- stop,
- destroy,
- impl,
- provide,
- properties,
- composition,
- service,
- filter,
- defaultImpl,
- required,
- added,
- changed,
- removed,
- autoConfig,
- pid,
- factoryPid,
- propagate,
- updated,
- timeout,
- adapterService,
- adapterProperties,
- adapteeService,
- adapteeFilter,
- stateMask,
- ranking,
- factory,
- factoryConfigure,
- };
-
/**
- * This class represents a parsed DependencyManager component descriptor entry.
- * (either a Service, a ServiceDependency, or a ConfigurationDependency descriptor entry).
+ * This class represents a DependencyManager component descriptor entry.
+ * (Service, a ServiceDependency ... see EntryType enum).
*/
- private class Info
- {
- /** The component descriptor entry type: either Service, (Temporal)ServiceDependency, or ConfigurationDependency */
- EntryTypes m_entry;
-
- /** The component descriptor entry parameters (init/start/stop ...) */
- Map<Params, Object> m_params = new HashMap<Params, Object>();
-
- /**
- * Makes a new component descriptor entry.
- * @param entry the component descriptor entry type (either Service, ServiceDependency, or ConfigurationDependency)
- */
- Info(EntryTypes entry)
- {
- m_entry = entry;
- }
-
- /**
- * Returns a string representation for the given component descriptor line entry.
- */
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append(m_entry);
- sb.append(":").append(" ");
- for (Map.Entry<Params, Object> e : m_params.entrySet())
- {
- sb.append(e.getKey());
- sb.append("=");
- sb.append(e.getValue());
- sb.append("; ");
- }
- return sb.toString();
- }
-
- /**
- * Adds a parameter to this component descriptor entry.
- * @param param the param name
- * @param value the param value
- */
- void addParam(Params param, String value)
- {
- String old = (String) m_params.get(param);
- if (old != null)
- {
- value = old + "," + value;
- }
- m_params.put(param, value);
- }
-
- /**
- * Adds an annotation parameter to this component descriptor entry.
- * @param annotation the annotation where the parameter has been parsed
- * @param param the param name
- * @param def the default value to add, if the param is not present in the parsed annotation.
- */
- void addParam(Annotation annotation, Params param, Object def)
- {
- Object value = annotation.get(param.toString());
- if (value == null && def != null)
- {
- value = def;
- }
- if (value != null)
- {
- if (value instanceof Object[])
- {
- for (Object v : ((Object[]) value))
- {
- addParam(param, v.toString());
- }
- }
- else
- {
- addParam(param, value.toString());
- }
- }
- }
-
- /**
- * Adds a annotation parameter of type 'class' to this component descriptor entry.
- * The parsed class parameter has the format "Lfull.package.ClassName;"
- * @param annotation the annotation where the class parameter has been parsed
- * @param param the annotation class param name
- * @param def the default class name to add if the param is not present in the parsed annotation.
- */
- void addClassParam(Annotation annotation, Params param, Object def)
- {
- Pattern pattern = m_classPattern;
- Object value = annotation.get(param.toString());
- if (value == null && def != null)
- {
- value = def;
- pattern = null;
- }
- if (value != null)
- {
- if (value instanceof Object[])
- {
- for (Object v : ((Object[]) value))
- {
- if (pattern != null)
- {
- v = parseClass(v.toString(), pattern, 1);
- }
- addParam(param, v.toString());
- }
- }
- else
- {
- if (pattern != null)
- {
- value = parseClass(value.toString(), pattern, 1);
- }
- addParam(param, value.toString());
- }
- }
-
- }
- }
/**
* Makes a new Collector for parsing a given class.
@@ -368,11 +183,11 @@
public void annotation(Annotation annotation)
{
m_reporter.trace("Parsed annotation: %s", annotation);
-
+
if (annotation.getName().equals(A_SERVICE))
{
parseServiceAnnotation(annotation);
- }
+ }
else if (annotation.getName().equals(A_ASPECT_SERVICE))
{
parseAspectService(annotation);
@@ -395,27 +210,31 @@
}
else if (annotation.getName().equals(A_INIT))
{
- checkMethod(m_voidMethodPattern);
+ //Patterns.parseMethod(m_method, m_descriptor, Patterns.VOID);
+ // TODO check if method takes optional params like Service, DependencyManager, etc ...
m_initMethod = m_method;
}
else if (annotation.getName().equals(A_START))
{
- checkMethod(m_voidMethodPattern);
+ //Patterns.parseMethod(m_method, m_descriptor, Patterns.VOID);
+ // TODO check if method takes optional params like Service, DependencyManager, etc ...
m_startMethod = m_method;
}
else if (annotation.getName().equals(A_STOP))
{
- checkMethod(m_voidMethodPattern);
+ //Patterns.parseMethod(m_method, m_descriptor, Patterns.VOID);
+ // TODO check if method takes optional params like Service, DependencyManager, etc ...
m_stopMethod = m_method;
}
else if (annotation.getName().equals(A_DESTROY))
{
- checkMethod(m_voidMethodPattern);
+ //Patterns.parseMethod(m_method, m_descriptor, Patterns.VOID);
+ // TODO check if method takes optional params like Service, DependencyManager, etc ...
m_destroyMethod = m_method;
}
else if (annotation.getName().equals(A_COMPOSITION))
{
- checkMethod(m_methodCompoPattern);
+ Patterns.parseMethod(m_method, m_descriptor, Patterns.COMPOSITION);
m_compositionMethod = m_method;
}
else if (annotation.getName().equals(A_SERVICE_DEP))
@@ -429,12 +248,12 @@
else if (annotation.getName().equals(A_TEMPORAL_SERVICE_DEPENDENCY))
{
parseServiceDependencyAnnotation(annotation, true);
- }
- else if (annotation.getName().equals(A_BUNDLE_DEPENDENCY))
+ }
+ else if (annotation.getName().equals(A_BUNDLE_DEPENDENCY))
{
parseBundleDependencyAnnotation(annotation);
}
- else if (annotation.getName().equals(A_RESOURCE_DEPENDENCY))
+ else if (annotation.getName().equals(A_RESOURCE_DEPENDENCY))
{
parseRersourceDependencyAnnotation(annotation);
}
@@ -446,50 +265,55 @@
*/
private void parseServiceAnnotation(Annotation annotation)
{
- Info info = new Info(EntryTypes.Service);
- m_infos.add(info);
+ EntryWriter writer = new EntryWriter(EntryType.Service);
+ m_writers.add(writer);
// Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(info);
-
+ addCommonServiceParams(writer);
+
// impl attribute
- info.addParam(Params.impl, m_className);
+ writer.put(EntryParam.impl, m_className);
// properties attribute
- parseParameters(annotation, Params.properties, info);
+ parseProperties(annotation, EntryParam.properties, writer);
// provide attribute
- info.addClassParam(annotation, Params.provide, m_interfaces);
-
+ writer.putClassArray(annotation, EntryParam.provide, m_interfaces);
+
// factory attribute
- info.addParam(annotation, Params.factory, null);
-
+ writer.putString(annotation, EntryParam.factory, null);
+
// factoryPropertiesCallback attribute
- info.addParam(annotation, Params.factoryConfigure, null);
+ writer.putString(annotation, EntryParam.factoryConfigure, null);
}
- private void addCommonServiceParams(Info info)
+ private void addCommonServiceParams(EntryWriter writer)
{
- if (m_initMethod != null) {
- info.addParam(Params.init, m_initMethod);
+ if (m_initMethod != null)
+ {
+ writer.put(EntryParam.init, m_initMethod);
}
-
- if (m_startMethod != null) {
- info.addParam(Params.start, m_startMethod);
+
+ if (m_startMethod != null)
+ {
+ writer.put(EntryParam.start, m_startMethod);
}
-
- if (m_stopMethod != null) {
- info.addParam(Params.stop, m_stopMethod);
+
+ if (m_stopMethod != null)
+ {
+ writer.put(EntryParam.stop, m_stopMethod);
}
-
- if (m_destroyMethod != null) {
- info.addParam(Params.destroy, m_destroyMethod);
+
+ if (m_destroyMethod != null)
+ {
+ writer.put(EntryParam.destroy, m_destroyMethod);
}
-
+
// Register Composition method
- if (m_compositionMethod != null) {
- info.addParam(Params.composition, m_compositionMethod);
- }
+ if (m_compositionMethod != null)
+ {
+ writer.put(EntryParam.composition, m_compositionMethod);
+ }
}
/**
@@ -498,65 +322,68 @@
*/
private void parseServiceDependencyAnnotation(Annotation annotation, boolean temporal)
{
- Info info = new Info(temporal ? EntryTypes.TemporalServiceDependency
- : EntryTypes.ServiceDependency);
- m_infos.add(info);
+ EntryWriter writer = new EntryWriter(temporal ? EntryType.TemporalServiceDependency
+ : EntryType.ServiceDependency);
+ m_writers.add(writer);
// service attribute
- String service = annotation.get(Params.service.toString());
+ String service = annotation.get(EntryParam.service.toString());
if (service != null)
{
- service = parseClass(service, m_classPattern, 1);
+ service = Patterns.parseClass(service, Patterns.CLASS, 1);
}
else
{
if (m_isField)
{
- service = parseClass(m_descriptor, m_classPattern, 1);
+ service = Patterns.parseClass(m_descriptor, Patterns.CLASS, 1);
}
else
{
- service = parseClass(m_descriptor, m_bindClassPattern, 2);
+ service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS, 2);
}
}
- info.addParam(Params.service, service);
+ writer.put(EntryParam.service, service);
// autoConfig attribute
if (m_isField)
{
- info.addParam(Params.autoConfig, m_field);
+ writer.put(EntryParam.autoConfig, m_field);
}
// filter attribute
- String filter = annotation.get(Params.filter.toString());
+ String filter = annotation.get(EntryParam.filter.toString());
if (filter != null)
{
Verifier.verifyFilter(filter, 0);
- info.addParam(Params.filter, filter);
+ writer.put(EntryParam.filter, filter);
}
// defaultImpl attribute
- info.addClassParam(annotation, Params.defaultImpl, null);
+ writer.putClass(annotation, EntryParam.defaultImpl, null);
// added callback
- info.addParam(annotation, Params.added, (!m_isField) ? m_method : null);
+ writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
if (temporal)
{
// timeout attribute (only valid if parsing a temporal service dependency)
- info.addParam(annotation, Params.timeout, null);
+ writer.putString(annotation, EntryParam.timeout, null);
}
else
{
// required attribute (not valid if parsing a temporal service dependency)
- info.addParam(annotation, Params.required, null);
+ writer.putString(annotation, EntryParam.required, null);
// changed callback
- info.addParam(annotation, Params.changed, null);
+ writer.putString(annotation, EntryParam.changed, null);
// removed callback
- info.addParam(annotation, Params.removed, null);
+ writer.putString(annotation, EntryParam.removed, null);
}
+
+ // id attribute
+ writer.putString(annotation, EntryParam.name, null);
}
/**
@@ -565,17 +392,17 @@
*/
private void parseConfigurationDependencyAnnotation(Annotation annotation)
{
- Info info = new Info(EntryTypes.ConfigurationDependency);
- m_infos.add(info);
+ EntryWriter writer = new EntryWriter(EntryType.ConfigurationDependency);
+ m_writers.add(writer);
// pid attribute
- info.addParam(annotation, Params.pid, m_className);
+ writer.putString(annotation, EntryParam.pid, m_className);
// propagate attribute
- info.addParam(annotation, Params.propagate, null);
+ writer.putString(annotation, EntryParam.propagate, null);
// Property Meta Types
- String pid = get(annotation, Params.pid.toString(), m_className);
+ String pid = get(annotation, EntryParam.pid.toString(), m_className);
parseMetaTypes(annotation, pid, false);
}
@@ -585,47 +412,52 @@
*/
private void parseAspectService(Annotation annotation)
{
- Info info = new Info(EntryTypes.AspectService);
- m_infos.add(info);
+ EntryWriter writer = new EntryWriter(EntryType.AspectService);
+ m_writers.add(writer);
// Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(info);
-
+ addCommonServiceParams(writer);
+
// Parse service filter
- String filter = annotation.get(Params.filter.toString());
- if (filter != null) {
+ String filter = annotation.get(EntryParam.filter.toString());
+ if (filter != null)
+ {
Verifier.verifyFilter(filter, 0);
- info.addParam(Params.filter, filter);
+ writer.put(EntryParam.filter, filter);
}
-
+
// Parse service aspect ranking
- Integer ranking = annotation.get(Params.ranking.toString());
- info.addParam(Params.ranking, ranking.toString());
-
+ Integer ranking = annotation.get(EntryParam.ranking.toString());
+ writer.put(EntryParam.ranking, ranking.toString());
+
// Generate Aspect Implementation
- info.addParam(Params.impl, m_className);
-
+ writer.put(EntryParam.impl, m_className);
+
// Parse Aspect properties.
- parseParameters(annotation, Params.properties, info);
-
+ parseProperties(annotation, EntryParam.properties, writer);
+
// Parse service interface this aspect is applying to
- Object service = annotation.get(Params.service.toString());
- if (service == null) {
+ Object service = annotation.get(EntryParam.service.toString());
+ if (service == null)
+ {
if (m_interfaces == null)
{
throw new IllegalStateException("Invalid AspectService annotation: " +
- "the service attribute has not been set and the class " + m_className + " does not implement any interfaces");
+ "the service attribute has not been set and the class " + m_className
+ + " does not implement any interfaces");
}
- if (m_interfaces.length != 1)
+ if (m_interfaces.length != 1)
{
throw new IllegalStateException("Invalid AspectService annotation: " +
- "the service attribute has not been set and the class " + m_className + " implements more than one interface");
+ "the service attribute has not been set and the class " + m_className
+ + " implements more than one interface");
}
-
- info.addParam(Params.service, m_interfaces[0]);
- } else
+
+ writer.put(EntryParam.service, m_interfaces[0]);
+ }
+ else
{
- info.addClassParam(annotation, Params.service, null);
+ writer.putClassArray(annotation, EntryParam.service, null);
}
}
@@ -633,33 +465,33 @@
* Parses an AspectService annotation.
* @param annotation
*/
- private void parseAdapterService(Annotation annotation)
+ private void parseAdapterService(Annotation annotation)
{
- Info info = new Info(EntryTypes.AdapterService);
- m_infos.add(info);
-
+ EntryWriter writer = new EntryWriter(EntryType.AdapterService);
+ m_writers.add(writer);
+
// Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(info);
-
+ addCommonServiceParams(writer);
+
// Generate Adapter Implementation
- info.addParam(Params.impl, m_className);
-
+ writer.put(EntryParam.impl, m_className);
+
// Parse adaptee filter
- String adapteeFilter = annotation.get(Params.adapteeFilter.toString());
+ String adapteeFilter = annotation.get(EntryParam.adapteeFilter.toString());
if (adapteeFilter != null)
{
Verifier.verifyFilter(adapteeFilter, 0);
- info.addParam(Params.adapteeFilter, adapteeFilter);
+ writer.put(EntryParam.adapteeFilter, adapteeFilter);
}
-
+
// Parse the mandatory adapted service interface.
- info.addClassParam(annotation, Params.adapteeService, null);
-
+ writer.putClass(annotation, EntryParam.adapteeService, null);
+
// Parse Adapter properties.
- parseParameters(annotation, Params.adapterProperties, info);
+ parseProperties(annotation, EntryParam.adapterProperties, writer);
// Parse the optional adapter service (use directly implemented interface by default).
- info.addClassParam(annotation, Params.adapterService, m_interfaces);
+ writer.putClassArray(annotation, EntryParam.adapterService, m_interfaces);
}
/**
@@ -668,34 +500,35 @@
*/
private void parseBundleAdapterService(Annotation annotation)
{
- Info info = new Info(EntryTypes.BundleAdapterService);
- m_infos.add(info);
-
+ EntryWriter writer = new EntryWriter(EntryType.BundleAdapterService);
+ m_writers.add(writer);
+
// Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(info);
-
+ addCommonServiceParams(writer);
+
// Generate Adapter Implementation
- info.addParam(Params.impl, m_className);
-
+ writer.put(EntryParam.impl, m_className);
+
// Parse bundle filter
- String filter = annotation.get(Params.filter.toString());
+ String filter = annotation.get(EntryParam.filter.toString());
if (filter != null)
{
Verifier.verifyFilter(filter, 0);
- info.addParam(Params.filter, filter);
+ writer.put(EntryParam.filter, filter);
}
-
+
// Parse stateMask attribute
- info.addParam(annotation, Params.stateMask, Integer.valueOf(Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE));
-
+ writer.putString(annotation, EntryParam.stateMask, Integer.valueOf(
+ Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE).toString());
+
// Parse Adapter properties.
- parseParameters(annotation, Params.properties, info);
+ parseProperties(annotation, EntryParam.properties, writer);
// Parse the optional adapter service (use directly implemented interface by default).
- info.addClassParam(annotation, Params.service, m_interfaces);
-
+ writer.putClassArray(annotation, EntryParam.service, m_interfaces);
+
// Parse propagate attribute
- info.addParam(annotation, Params.propagate, Boolean.FALSE);
+ writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
}
/**
@@ -704,31 +537,31 @@
*/
private void parseResourceAdapterService(Annotation annotation)
{
- Info info = new Info(EntryTypes.ResourceAdapterService);
- m_infos.add(info);
-
+ EntryWriter writer = new EntryWriter(EntryType.ResourceAdapterService);
+ m_writers.add(writer);
+
// Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(info);
-
+ addCommonServiceParams(writer);
+
// Generate Adapter Implementation
- info.addParam(Params.impl, m_className);
-
+ writer.put(EntryParam.impl, m_className);
+
// Parse resource filter
- String filter = annotation.get(Params.filter.toString());
+ String filter = annotation.get(EntryParam.filter.toString());
if (filter != null)
{
Verifier.verifyFilter(filter, 0);
- info.addParam(Params.filter, filter);
+ writer.put(EntryParam.filter, filter);
}
-
+
// Parse Adapter properties.
- parseParameters(annotation, Params.properties, info);
+ parseProperties(annotation, EntryParam.properties, writer);
// Parse the optional adapter service (use directly implemented interface by default).
- info.addClassParam(annotation, Params.service, m_interfaces);
-
+ writer.putClassArray(annotation, EntryParam.service, m_interfaces);
+
// Parse propagate attribute
- info.addParam(annotation, Params.propagate, Boolean.FALSE);
+ writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
}
/**
@@ -737,79 +570,79 @@
*/
private void parseFactoryConfigurationAdapterService(Annotation annotation)
{
- Info info = new Info(EntryTypes.FactoryConfigurationAdapterService);
- m_infos.add(info);
-
+ EntryWriter writer = new EntryWriter(EntryType.FactoryConfigurationAdapterService);
+ m_writers.add(writer);
+
// Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(info);
+ addCommonServiceParams(writer);
// Generate Adapter Implementation
- info.addParam(Params.impl, m_className);
+ writer.put(EntryParam.impl, m_className);
// Parse factory Pid
- info.addParam(annotation, Params.factoryPid, m_className);
-
+ writer.putString(annotation, EntryParam.factoryPid, m_className);
+
// Parse updated callback
- info.addParam(annotation, Params.updated, "updated");
-
+ writer.putString(annotation, EntryParam.updated, "updated");
+
// propagate attribute
- info.addParam(annotation, Params.propagate, Boolean.FALSE);
-
+ writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
+
// Parse the optional adapter service (use directly implemented interface by default).
- info.addClassParam(annotation, Params.service, m_interfaces);
+ writer.putClassArray(annotation, EntryParam.service, m_interfaces);
// Parse Adapter properties.
- parseParameters(annotation, Params.properties, info);
+ parseProperties(annotation, EntryParam.properties, writer);
// Parse optional meta types for configuration description.
- String factoryPid = get(annotation, Params.factoryPid.toString(), m_className);
- parseMetaTypes(annotation, factoryPid, true);
+ String factoryPid = get(annotation, EntryParam.factoryPid.toString(), m_className);
+ parseMetaTypes(annotation, factoryPid, true);
}
-
private void parseBundleDependencyAnnotation(Annotation annotation)
{
- Info info = new Info(EntryTypes.BundleDependency);
- m_infos.add(info);
+ EntryWriter writer = new EntryWriter(EntryType.BundleDependency);
+ m_writers.add(writer);
- String filter = annotation.get(Params.filter.toString());
+ String filter = annotation.get(EntryParam.filter.toString());
if (filter != null)
{
Verifier.verifyFilter(filter, 0);
- info.addParam(Params.filter, filter);
+ writer.put(EntryParam.filter, filter);
}
- info.addParam(annotation, Params.added, m_method);
- info.addParam(annotation, Params.changed, null); // TODO check if "changed" callback exists
- info.addParam(annotation, Params.removed, null); // TODO check if "removed" callback exists
- info.addParam(annotation, Params.required, null);
- info.addParam(annotation, Params.stateMask, null);
- info.addParam(annotation, Params.propagate, null);
+ writer.putString(annotation, EntryParam.added, m_method);
+ writer.putString(annotation, EntryParam.changed, null);
+ writer.putString(annotation, EntryParam.removed, null);
+ writer.putString(annotation, EntryParam.required, null);
+ writer.putString(annotation, EntryParam.stateMask, null);
+ writer.putString(annotation, EntryParam.propagate, null);
}
-
+
private void parseRersourceDependencyAnnotation(Annotation annotation)
{
- Info info = new Info(EntryTypes.ResourceDependency);
- m_infos.add(info);
+ EntryWriter writer = new EntryWriter(EntryType.ResourceDependency);
+ m_writers.add(writer);
- String filter = annotation.get(Params.filter.toString());
+ String filter = annotation.get(EntryParam.filter.toString());
if (filter != null)
{
Verifier.verifyFilter(filter, 0);
- info.addParam(Params.filter, filter);
+ writer.put(EntryParam.filter, filter);
}
- info.addParam(annotation, Params.added, m_method);
- info.addParam(annotation, Params.changed, null); // TODO check if "changed" callback exists
- info.addParam(annotation, Params.removed, null); // TODO check if "removed" callback exists
- info.addParam(annotation, Params.required, null);
- info.addParam(annotation, Params.propagate, null);
+ writer.putString(annotation, EntryParam.added, m_method);
+ writer.putString(annotation, EntryParam.changed, null);
+ writer.putString(annotation, EntryParam.removed, null);
+ writer.putString(annotation, EntryParam.required, null);
+ writer.putString(annotation, EntryParam.propagate, null);
}
/**
* Parse optional meta types annotation attributes
* @param annotation
*/
+ @SuppressWarnings("null")
private void parseMetaTypes(Annotation annotation, String pid, boolean factory)
{
if (annotation.get("metadata") != null)
@@ -818,13 +651,13 @@
String propertiesDesc = annotation.get("description");
MetaType.OCD ocd = new MetaType.OCD(pid, propertiesHeading, propertiesDesc);
- for (Object p : (Object[]) annotation.get("metadata"))
+ 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;
+ type = (type != null) ? Patterns.parseClass(type, Patterns.CLASS, 1) : null;
Object[] defaults = (Object[]) property.get("defaults");
String description = property.get("description");
Integer cardinality = property.get("cardinality");
@@ -832,16 +665,31 @@
MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description,
cardinality, required);
- Object[] options = property.get("options");
- if (options != null)
+
+ Object[] optionLabels = property.get("optionLabels");
+ Object[] optionValues = property.get("optionValues");
+
+ if (optionLabels == null
+ && optionValues != null
+ ||
+ optionLabels != null
+ && optionValues == null
+ ||
+ (optionLabels != null && optionValues != null && optionLabels.length != optionValues.length))
{
- for (Object o : (Object[]) property.get("options"))
+ throw new IllegalArgumentException("invalid option labels/values specified for property "
+ + id +
+ " in PropertyMetadata annotation from class " + m_className);
+ }
+
+ if (optionValues != null)
+ {
+ for (int i = 0; i < optionValues.length; i++)
{
- Annotation option = (Annotation) o;
- ad.add(new MetaType.Option((String) option.get("name"),
- (String) option.get("value")));
+ ad.add(new MetaType.Option(optionValues[i].toString(), optionLabels[i].toString()));
}
}
+
ocd.add(ad);
}
@@ -849,78 +697,63 @@
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).
+ * Parses a Property annotation (which represents a list of key-value pair).
* @param annotation the annotation where the Param annotation is defined
* @param attribute the attribute name which is of Param type
- * @param info the Info object where the parsed attributes are written
+ * @param writer the object where the parsed attributes are written
*/
- private void parseParameters(Annotation annotation, Params attribute, Info info) {
+ private void parseProperties(Annotation annotation, EntryParam attribute, EntryWriter writer)
+ {
Object[] parameters = annotation.get(attribute.toString());
+ Map<String, Object> properties = new HashMap<String, Object>();
if (parameters != null)
{
- for (Object p : parameters)
+ for (Object p: parameters)
{
- Annotation a = (Annotation) p;
- String prop = a.get("name") + ":" + a.get("value");
- info.addParam(attribute, prop);
+ Annotation a = (Annotation) p;
+ String name = (String) a.get("name");
+ String value = (String) a.get("value");
+ if (value != null)
+ {
+ properties.put(name, value);
+ }
+ else
+ {
+ Object[] values = a.get("values");
+ if (values != null)
+ {
+ // the values is an Object array of actual strings, and we must convert it into a String array.
+ properties.put(name, Arrays.asList(values).toArray(new String[values.length]));
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid Property attribyte \"" + attribute
+ + " from annotation " + annotation + " in class " + m_className);
+ }
+ }
}
- }
- }
-
- /**
- * Parses a class.
- * @param clazz the class to be parsed (the package is "/" separated).
- * @param pattern the pattern used to match the class.
- * @param group the pattern group index where the class can be retrieved.
- * @return the parsed class.
- */
- private String parseClass(String clazz, Pattern pattern, int group)
- {
- Matcher matcher = pattern.matcher(clazz);
- if (matcher.matches())
- {
- return matcher.group(group).replace("/", ".");
- }
- else
- {
- m_reporter.warning("Invalid class descriptor: %s", clazz);
- throw new IllegalArgumentException("Invalid class descriptor: " + clazz);
+ writer.putProperties(attribute, properties);
}
}
/**
- * Checks if a method descriptor matches a given pattern.
- * @param pattern the pattern used to check the method signature descriptor
- * @throws IllegalArgumentException if the method signature descriptor does not match the given pattern.
- */
- private void checkMethod(Pattern pattern)
- {
- Matcher matcher = pattern.matcher(m_descriptor);
- if (!matcher.matches())
- {
- m_reporter.warning("Invalid method %s : wrong signature: %s", m_method, m_descriptor);
- throw new IllegalArgumentException("Invalid method " + m_method + ", wrong signature: "
- + m_descriptor);
- }
- }
-
- /**
* Checks if the class is annotated with some given annotations. Notice that the Service
- * is always parsed at end of parsing, so, we have to check the last element of our m_infos
+ * is always parsed at end of parsing, so, we have to check the last element of our m_writers
* List.
* @return true if one of the provided annotations has been found from the parsed class.
*/
- private void checkServiceDeclared(EntryTypes ... types) {
+ private void checkServiceDeclared(EntryType... types)
+ {
boolean ok = false;
- if (m_infos.size() > 0)
+ if (m_writers.size() > 0)
{
- for (EntryTypes type : types)
+ for (EntryType type: types)
{
- if (m_infos.get(m_infos.size() - 1).m_entry == type)
+ if (m_writers.get(m_writers.size() - 1).getEntryType() == type)
{
ok = true;
break;
@@ -944,6 +777,7 @@
* @param defaultValue the default value to return if the attribute is not found in the annotation
* @return the annotation attribute value, or the defaultValue if not found
*/
+ @SuppressWarnings("unchecked")
private <T> T get(Annotation properties, String name, T defaultValue)
{
T value = (T) properties.get(name);
@@ -956,21 +790,22 @@
*/
public boolean finish()
{
- if (m_infos.size() == 0)
+ if (m_writers.size() == 0)
{
return false;
}
// We must have at least a Service or an AspectService annotation.
- checkServiceDeclared(EntryTypes.Service, EntryTypes.AspectService, EntryTypes.AdapterService, EntryTypes.BundleAdapterService,
- EntryTypes.ResourceAdapterService, EntryTypes.FactoryConfigurationAdapterService);
+ checkServiceDeclared(EntryType.Service, EntryType.AspectService, EntryType.AdapterService,
+ EntryType.BundleAdapterService,
+ EntryType.ResourceAdapterService, EntryType.FactoryConfigurationAdapterService);
StringBuilder sb = new StringBuilder();
sb.append("Parsed annotation for class ");
sb.append(m_className);
- for (int i = m_infos.size() - 1; i >= 0; i--)
+ for (int i = m_writers.size() - 1; i >= 0; i--)
{
- sb.append("\n\t").append(m_infos.get(i).toString());
+ sb.append("\n\t").append(m_writers.get(i).toString());
}
m_reporter.warning(sb.toString());
return true;
@@ -983,10 +818,10 @@
*/
public void writeTo(PrintWriter pw)
{
- // The last element our our m_infos list contains either the Service, or the AspectService descriptor.
- for (int i = m_infos.size() - 1; i >= 0; i--)
+ // The last element our our m_writers list contains either the Service, or the AspectService descriptor.
+ for (int i = m_writers.size() - 1; i >= 0; i--)
{
- pw.println(m_infos.get(i).toString());
+ pw.println(m_writers.get(i).toString());
}
}
}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
new file mode 100644
index 0000000..703effd
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
@@ -0,0 +1,38 @@
+package org.apache.felix.dm.annotation.plugin.bnd;
+
+/**
+ * The type of parameters which can be found in a component descriptor.
+ */
+public enum EntryParam
+{
+ init,
+ start,
+ stop,
+ destroy,
+ impl,
+ provide,
+ properties,
+ composition,
+ service,
+ filter,
+ defaultImpl,
+ required,
+ added,
+ changed,
+ removed,
+ autoConfig,
+ pid,
+ factoryPid,
+ propagate,
+ updated,
+ timeout,
+ adapterService,
+ adapterProperties,
+ adapteeService,
+ adapteeFilter,
+ stateMask,
+ ranking,
+ factory,
+ factoryConfigure,
+ name
+}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryType.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryType.java
new file mode 100644
index 0000000..bdbd005
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryType.java
@@ -0,0 +1,19 @@
+package org.apache.felix.dm.annotation.plugin.bnd;
+
+/**
+ * The type of each entry (lines) stored in a component descriptor.
+ */
+public enum EntryType
+{
+ Service,
+ AspectService,
+ AdapterService,
+ BundleAdapterService,
+ ResourceAdapterService,
+ FactoryConfigurationAdapterService,
+ ServiceDependency,
+ TemporalServiceDependency,
+ ConfigurationDependency,
+ BundleDependency,
+ ResourceDependency,
+}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java
new file mode 100644
index 0000000..3e5c8c7
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java
@@ -0,0 +1,251 @@
+package org.apache.felix.dm.annotation.plugin.bnd;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import aQute.lib.osgi.Annotation;
+
+/**
+ * This class encodes a component descriptor entry line, using json.
+ */
+public class EntryWriter
+{
+ // Every descriptor entries contains a type parameter for identifying the kind of entry
+ private final static String TYPE = "type";
+
+ /** All parameters as stored in a json object */
+ private JSONObject m_json;
+
+ /** The entry type */
+ private EntryType m_type;
+
+ /**
+ * Makes a new component descriptor entry.
+ */
+ public EntryWriter(EntryType type)
+ {
+ m_type = type;
+ m_json = new JSONObject();
+ try
+ {
+ m_json.put("type", type.toString());
+ }
+ catch (JSONException e)
+ {
+ throw new RuntimeException("could not initialize json object", e);
+ }
+ }
+
+ /**
+ * Returns this entry type.
+ */
+ EntryType getEntryType()
+ {
+ return m_type;
+ }
+
+ /**
+ * Returns a string representation for the given component descriptor entry.
+ */
+ @Override
+ public String toString()
+ {
+ return m_json.toString();
+ }
+
+ /**
+ * Put a String parameter in this descritor entry.
+ */
+ public void put(EntryParam param, String value)
+ {
+ checkType(param.toString());
+ try
+ {
+ m_json.put(param.toString(), value);
+ }
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("could not add param " + param + ":" + value, e);
+ }
+ }
+
+ /**
+ * Put a String[] parameter in this descriptor entry.
+ */
+ public void put(EntryParam param, String[] array)
+ {
+ checkType(param.toString());
+ try
+ {
+ m_json.put(param.toString(), new JSONArray(Arrays.asList(array)));
+ }
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("could not add param " + param + ":"
+ + Arrays.toString(array), e);
+ }
+ }
+
+ /**
+ * Put a Map parameter in the descriptor entry. The map values must be either Strings or Strings arrays.
+ */
+ public void putProperties(EntryParam param, Map<String, Object> properties)
+ {
+ checkType(param.toString());
+
+ try
+ {
+ JSONObject props = new JSONObject();
+ for (String key: properties.keySet())
+ {
+ Object value = properties.get(key);
+ if (value instanceof String)
+ {
+ props.put(key, value);
+ }
+ else if (value instanceof String[])
+ {
+ props.put(key, new JSONArray(Arrays.asList((String[]) value)));
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid property value: " + value);
+ }
+ }
+ m_json.put(param.toString(), props);
+ }
+
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("invalid properties for " + param + ": "
+ + properties, e);
+ }
+ }
+
+ /**
+ * Get a String attribute value from an annotation and write it into this descriptor entry.
+ */
+ public void putString(Annotation annotation, EntryParam param, String def)
+ {
+ checkType(param.toString());
+ Object value = annotation.get(param.toString());
+ if (value == null && def != null)
+ {
+ value = def;
+ }
+ if (value != null)
+ {
+ put(param, value.toString());
+ }
+ }
+
+ /**
+ * Get a String array attribute value from an annotation and write it into this descriptor entry.
+ */
+ public void putStringArray(Annotation annotation, EntryParam param, String[] def)
+ {
+ checkType(param.toString());
+ Object value = annotation.get(param.toString());
+ if (value == null && def != null)
+ {
+ value = def;
+ }
+ if (value != null)
+ {
+ for (Object v: ((Object[]) value))
+ {
+ try
+ {
+ m_json.append(param.toString(), v.toString());
+ }
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("Could not add param " + param + ":"
+ + value.toString(), e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get a class attribute value from an annotation and write it into this descriptor entry.
+ */
+ public void putClass(Annotation annotation, EntryParam param, Object def)
+ {
+ checkType(param.toString());
+
+ Pattern pattern = Patterns.CLASS;
+ Object value = annotation.get(param.toString());
+ if (value == null && def != null)
+ {
+ value = def;
+ pattern = null;
+ }
+ if (value != null)
+ {
+ if (pattern != null)
+ {
+ value = Patterns.parseClass(value.toString(), pattern, 1);
+ }
+ put(param, value.toString());
+ }
+ }
+
+ /**
+ * Get a class array attribute value from an annotation and write it into this descriptor entry.
+ */
+ public void putClassArray(Annotation annotation, EntryParam param, Object def)
+ {
+ checkType(param.toString());
+
+ Pattern pattern = Patterns.CLASS;
+ Object value = annotation.get(param.toString());
+ if (value == null && def != null)
+ {
+ value = def;
+ pattern = null;
+ }
+ if (value != null)
+ {
+ if (!(value instanceof Object[]))
+ {
+ throw new IllegalArgumentException("annotation parameter " + param
+ + " has not a class array type");
+ }
+
+ for (Object v: ((Object[]) value))
+ {
+ if (pattern != null)
+ {
+ v = Patterns.parseClass(v.toString(), pattern, 1);
+ }
+ try
+ {
+ m_json.append(param.toString(), v.toString());
+ }
+ catch (JSONException e)
+ {
+ throw new IllegalArgumentException("Could not add param " + param + ":"
+ + value.toString(), e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if the written key is not equals to "type" ("type" is an internal attribute we are using
+ * in order to identify a kind of descriptor entry (Service, ServiceDependency, etc ...).
+ */
+ private void checkType(String key)
+ {
+ if (TYPE.equals(key))
+ {
+ throw new IllegalArgumentException("\"" + TYPE + "\" parameter can't be overriden");
+ }
+ }
+}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Patterns.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Patterns.java
new file mode 100644
index 0000000..7e9b15b
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Patterns.java
@@ -0,0 +1,54 @@
+package org.apache.felix.dm.annotation.plugin.bnd;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Patterns
+{
+ // Pattern used to check if a method is void and does not take any params
+ public final static Pattern VOID = Pattern.compile("\\(\\)V");
+
+ // Pattern used to check if a method returns an array of Objects
+ public final static Pattern COMPOSITION = Pattern.compile("\\(\\)\\[Ljava/lang/Object;");
+
+ // Pattern used to parse the class parameter from the bind methods ("bind(Type)" or "bind(Map, Type)" or "bind(BundleContext, Type)"
+ public final static Pattern BIND_CLASS = Pattern.compile("\\((L[^;]+;)?L([^;]+);\\)V");
+
+ // Pattern used to parse classes from class descriptors;
+ public final static Pattern CLASS = Pattern.compile("L([^;]+);");
+
+ /**
+ * Parses a class.
+ * @param clazz the class to be parsed (the package is "/" separated).
+ * @param pattern the pattern used to match the class.
+ * @param group the pattern group index where the class can be retrieved.
+ * @return the parsed class.
+ */
+ public static String parseClass(String clazz, Pattern pattern, int group)
+ {
+ Matcher matcher = pattern.matcher(clazz);
+ if (matcher.matches())
+ {
+ return matcher.group(group).replace("/", ".");
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid class descriptor: " + clazz);
+ }
+ }
+
+ /**
+ * Checks if a method descriptor matches a given pattern.
+ * @param pattern the pattern used to check the method signature descriptor
+ * @throws IllegalArgumentException if the method signature descriptor does not match the given pattern.
+ */
+ public static void parseMethod(String method, String descriptor, Pattern pattern)
+ {
+ Matcher matcher = pattern.matcher(descriptor);
+ if (!matcher.matches())
+ {
+ throw new IllegalArgumentException("Invalid method " + method + ", wrong signature: "
+ + descriptor);
+ }
+ }
+}
diff --git a/dependencymanager/runtime/LICENSE.json b/dependencymanager/runtime/LICENSE.json
new file mode 100644
index 0000000..87d1411
--- /dev/null
+++ b/dependencymanager/runtime/LICENSE.json
@@ -0,0 +1,21 @@
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/dependencymanager/runtime/pom.xml b/dependencymanager/runtime/pom.xml
index 1d6bd15..b528af0 100644
--- a/dependencymanager/runtime/pom.xml
+++ b/dependencymanager/runtime/pom.xml
@@ -52,6 +52,12 @@
<artifactId>org.apache.felix.dependencymanager</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>20070829</version>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
@@ -77,7 +83,8 @@
<Bundle-Description>A bundle that loads components descriptors from started bundles</Bundle-Description>
<Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
<Private-Package>org.apache.felix.dm.runtime.*</Private-Package>
- <Import-Package>org.apache.felix.dm;org.apache.felix.dm.dependencies;org.apache.felix.dm.service;version="[3.0.0,4.0.0)", *</Import-Package>
+ <Import-Package>org.apache.felix.dm;org.apache.felix.dm.dependencies;org.apache.felix.dm.service;version="[3.0.0,4.0.0)",*</Import-Package>
+ <Embed-Dependency>json</Embed-Dependency>
<Bundle-Activator>org.apache.felix.dm.runtime.Activator</Bundle-Activator>
</instructions>
</configuration>
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Activator.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Activator.java
index 14a2c0d..82dde28 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Activator.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Activator.java
@@ -31,6 +31,9 @@
*/
public class Activator extends DependencyActivatorBase
{
+ /**
+ * Our bundle is starting: initialize our DependencyManager Runtime service.
+ */
@Override
public void init(BundleContext context, DependencyManager dm) throws Exception
{
@@ -42,15 +45,19 @@
boolean logActive = "true".equals(context.getProperty("dm.log"));
dm.add(createService()
- .setImplementation(ComponentManager.class)
+ .setImplementation(DependencyManagerRuntime.class)
.add(createServiceDependency()
.setService(LogService.class)
.setRequired(logActive)
- .setAutoConfig(true)));
+ .setAutoConfig("m_log")
+ .setCallbacks("bind", null)));
}
+ /**
+ * Our bundle is stopping: shutdown our Dependency Manager Runtime service.
+ */
@Override
public void destroy(BundleContext context, DependencyManager dm) throws Exception
{
- }
+ }
}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AdapterServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AdapterServiceBuilder.java
new file mode 100644
index 0000000..90a66d6
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AdapterServiceBuilder.java
@@ -0,0 +1,62 @@
+/*
+ * 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.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.dependencies.Dependency;
+import org.apache.felix.dm.service.Service;
+import org.osgi.framework.Bundle;
+
+/**
+ * Builded called when the JSON parser find an adapter service descriptor.
+ */
+public class AdapterServiceBuilder extends ServiceComponentBuilder
+{
+ /** The type attribute specified in the JSON descriptor */
+ private final static String TYPE = "AdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void buildService(MetaData serviceMetaData,
+ List<MetaData> serviceDependencies,
+ Bundle b, DependencyManager dm) throws Exception
+ {
+ Class<?> adapterImpl = b.loadClass(serviceMetaData.getString(Params.impl));
+ String[] adapterService = serviceMetaData.getStrings(Params.adapterService, null);
+ Dictionary<String, Object> adapterProperties = serviceMetaData.getDictionary(Params.adapterProperties, null);
+ Class<?> adapteeService = b.loadClass(serviceMetaData.getString(Params.adapteeService));
+ String adapteeFilter = serviceMetaData.getString(Params.adapteeFilter, null);
+ Service service = dm.createAdapterService(adapteeService, adapteeFilter, adapterService, adapterImpl, adapterProperties);
+ setCommonServiceParams(service, serviceMetaData);
+ for (MetaData dependencyMetaData: serviceDependencies)
+ {
+ Dependency dp = new DependencyBuilder(dependencyMetaData).build(b, dm);
+ service.add(dp);
+ }
+ dm.add(service);
+ }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AspectServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AspectServiceBuilder.java
new file mode 100644
index 0000000..e7ee17f
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AspectServiceBuilder.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.dependencies.Dependency;
+import org.apache.felix.dm.service.Service;
+import org.osgi.framework.Bundle;
+import org.osgi.service.log.LogService;
+
+public class AspectServiceBuilder extends ServiceComponentBuilder
+{
+ private final static String TYPE = "AspectService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void buildService(MetaData serviceMetaData,
+ List<MetaData> serviceDependencies,
+ Bundle b, DependencyManager dm) throws Exception
+ {
+ Log.instance().log(LogService.LOG_DEBUG, "building aspect service: service metadata=" + serviceMetaData
+ + ", dependencyMetaData=" + serviceDependencies);
+
+ Class<?> serviceInterface = b.loadClass(serviceMetaData.getString(Params.service));
+ String serviceFilter = serviceMetaData.getString(Params.filter, null);
+ Dictionary<String, Object> aspectProperties = serviceMetaData.getDictionary(Params.properties,
+ null);
+ int ranking = serviceMetaData.getInt(Params.ranking, 1);
+ String implClass = serviceMetaData.getString(Params.impl);
+ Object impl = b.loadClass(implClass);
+ // TODO add attribute name
+ Service service = dm.createAspectService(serviceInterface, serviceFilter, ranking, null)
+ .setImplementation(impl)
+ .setServiceProperties(aspectProperties);
+ setCommonServiceParams(service, serviceMetaData);
+ for (MetaData dependencyMetaData: serviceDependencies)
+ {
+ Dependency dp = new DependencyBuilder(dependencyMetaData).build(b, dm);
+ service.add(dp);
+ }
+ dm.add(service);
+ }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/BundleAdapterServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/BundleAdapterServiceBuilder.java
new file mode 100644
index 0000000..f41f6d4
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/BundleAdapterServiceBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.dependencies.Dependency;
+import org.apache.felix.dm.service.Service;
+import org.osgi.framework.Bundle;
+
+public class BundleAdapterServiceBuilder extends ServiceComponentBuilder
+{
+ private final static String TYPE = "BundleAdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void buildService(MetaData serviceMetaData,
+ List<MetaData> serviceDependencies,
+ Bundle b, DependencyManager dm) throws Exception
+ {
+ int stateMask = serviceMetaData.getInt(Params.stateMask, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
+ String filter = serviceMetaData.getString(Params.filter, null);
+ Class<?> adapterImpl = b.loadClass(serviceMetaData.getString(Params.impl));
+ String[] service = serviceMetaData.getStrings(Params.service, null);
+ Dictionary<String, Object> properties = serviceMetaData.getDictionary(Params.properties, null);
+ boolean propagate = "true".equals(serviceMetaData.getString(Params.propagate, "false"));
+ Service srv = dm.createBundleAdapterService(stateMask, filter, adapterImpl, service, properties, propagate);
+ setCommonServiceParams(srv, serviceMetaData);
+ for (MetaData dependencyMetaData: serviceDependencies)
+ {
+ Dependency dp = new DependencyBuilder(dependencyMetaData).build(b, dm);
+ srv.add(dp);
+ }
+ dm.add(srv);
+ }
+}
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
deleted file mode 100644
index 069a79b..0000000
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ComponentManager.java
+++ /dev/null
@@ -1,610 +0,0 @@
-/*
- * 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.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
-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;
-import org.apache.felix.dm.dependencies.ConfigurationDependency;
-import org.apache.felix.dm.dependencies.Dependency;
-import org.apache.felix.dm.dependencies.ResourceDependency;
-import org.apache.felix.dm.dependencies.ServiceDependency;
-import org.apache.felix.dm.dependencies.TemporalServiceDependency;
-import org.apache.felix.dm.service.Service;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.SynchronousBundleListener;
-import org.osgi.service.log.LogService;
-
-/**
- * This class parses service descriptors generated by the annotation bnd processor.
- * The descriptors are located under OSGI-INF/dependencymanager directory. Such files are actually
- * referenced by a specific "DependendencyManager-Component" manifest header.
- */
-public class ComponentManager implements SynchronousBundleListener
-{
- 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
- * as with our log service. We'll listen to bundle start/stop events (we implement the
- * SynchronousBundleListener interface).
- */
- protected void start()
- {
- for (Bundle b : m_bctx.getBundles())
- {
- if (b.getState() == Bundle.ACTIVE)
- {
- bundleChanged(new BundleEvent(Bundle.ACTIVE, b));
- }
- }
- m_bctx.addBundleListener(this);
- }
-
- /**
- * Stops our service. We'll stop all activated DependencyManager services.
- */
- protected void stop()
- {
- for (List<Service> services : m_services.values())
- {
- for (Service service : services)
- {
- service.stop();
- }
- }
- m_services.clear();
- }
-
- /**
- * Handle a bundle event, and eventually parse started bundles.
- */
- public void bundleChanged(BundleEvent event)
- {
- Bundle b = event.getBundle();
- if (b.getState() == Bundle.ACTIVE)
- {
- bundleStarted(b);
- }
- else if (b.getState() == Bundle.STOPPING)
- {
- bundleStopped(b);
- }
- }
-
- /**
- * Checks if a started bundle have some DependencyManager descriptors
- * referenced in the "DependencyManager-Component" OSGi header.
- * @param b the started bundle.
- */
- void bundleStarted(Bundle b)
- {
- String descriptorPaths = (String) b.getHeaders().get("DependencyManager-Component");
- if (descriptorPaths == null)
- {
- return;
- }
-
- for (String descriptorPath : descriptorPaths.split(","))
- {
- URL descriptorURL = b.getEntry(descriptorPath);
- if (descriptorURL == null)
- {
- m_logService.log(LogService.LOG_ERROR,
- "DependencyManager component descriptor not found: " + descriptorPath);
- continue;
- }
- loadDescriptor(b, descriptorURL);
- }
- }
-
- /**
- * Load a DependencyManager component descriptor from a given bundle.
- * @param b
- * @param descriptorURL
- */
- @SuppressWarnings("null")
- private void loadDescriptor(Bundle b, URL descriptorURL)
- {
- m_logService.log(LogService.LOG_DEBUG, "Parsing descriptor " + descriptorURL
- + " from bundle " + b.getSymbolicName());
-
- BufferedReader in = null;
- try
- {
- DescriptorParser parser = new DescriptorParser(m_logService);
- in = new BufferedReader(new InputStreamReader(descriptorURL.openStream()));
- DependencyManager dm = new DependencyManager(b.getBundleContext());
- Service service = null;
- String line;
- m_serviceFactory = null;
-
- while ((line = in.readLine()) != null)
- {
- Dependency dp = null;
- switch (parser.parse(line))
- {
- case Service:
- service = createService(b, dm, parser);
- break;
-
- case AspectService:
- service = createAspectService(b, dm, parser);
- break;
-
- case AdapterService:
- service = createAdapterService(b, dm, parser);
- break;
-
- case BundleAdapterService:
- service = createBundleAdapterService(b, dm, parser);
- break;
-
- case ResourceAdapterService:
- service = createResourceAdapterService(b, dm, parser);
- break;
-
- case FactoryConfigurationAdapterService:
- service = createFactoryConfigurationAdapterService(b, dm, parser);
- break;
-
- case ServiceDependency:
- dp = createServiceDependency(b, dm, parser, false);
- break;
-
- case TemporalServiceDependency:
- dp = createServiceDependency(b, dm, parser, true);
- break;
-
- case ConfigurationDependency:
- dp = createConfigurationDependency(b, dm, parser);
- break;
-
- case BundleDependency:
- dp = createBundleDependency(b, dm, parser);
- break;
-
- case ResourceDependency:
- 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)
- {
- services = new ArrayList<Service>();
- m_services.put(b, services);
- }
- services.add(service);
- dm.add(service);
- }
- catch (Throwable t)
- {
- m_logService.log(LogService.LOG_ERROR, "Error while parsing descriptor "
- + descriptorURL + " from bundle " + b.getSymbolicName(), t);
- }
- finally
- {
- if (in != null)
- {
- try
- {
- in.close();
- }
- catch (IOException ignored)
- {
- }
- }
- }
- }
-
- /**
- * Unregisters all services for a stopping bundle.
- * @param b
- */
- private void bundleStopped(Bundle b)
- {
- m_logService.log(LogService.LOG_INFO, "bundle stopped: " + b.getSymbolicName());
- List<Service> services = m_services.remove(b);
- if (services != null)
- {
- for (Service s : services)
- {
- m_logService.log(LogService.LOG_INFO, "stopping service " + s);
- s.stop();
- }
- }
- }
-
- /**
- * Check if we have already parsed the Service entry from a given DM component descriptor file.
- * Each descriptor must start with a Service definition entry.
- * @param service the parsed service
- * @throws IllegalArgumentException if the service has not been parsed.
- */
- private void checkServiceParsed(Service service)
- {
- if (service == null)
- {
- throw new IllegalArgumentException("Service not declared in the first descriptor line");
- }
- }
-
- /**
- * Creates a Service that we parsed from a component descriptor entry.
- * @param b the bundle from where the service is started
- * @param dm the DependencyManager framework
- * @param parser the parser that just parsed the descriptor "Service" entry
- * @return the created DependencyManager Service
- * @throws ClassNotFoundException if the service implementation could not be instantiated
- */
- private Service createService(Bundle b, DependencyManager dm, DescriptorParser parser)
- throws ClassNotFoundException
- {
- Service service = dm.createService();
- String factory = parser.getString(DescriptorParam.factory, null);
- 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)
- {
- 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
- {
- 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;
- }
-
- /**
- * Set common Service parameters, if provided from our Component descriptor
- * @param service
- * @param parser
- */
- private void setCommonServiceParams(Service service, DescriptorParser parser)
- {
- 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);
- service.setCallbacks(init, start, stop, destroy);
- String composition = parser.getString(DescriptorParam.composition, null);
- if (composition != null)
- {
- service.setComposition(composition);
- }
- }
-
- /**
- * Creates an Aspect Service.
- * @param b
- * @param dm
- * @param parser
- * @return
- */
- private Service createAspectService(Bundle b, DependencyManager dm, DescriptorParser parser)
- throws ClassNotFoundException
- {
- Service service = null;
-
- Class<?> serviceInterface = b.loadClass(parser.getString(DescriptorParam.service));
- String serviceFilter = parser.getString(DescriptorParam.filter, null);
- Dictionary<String, String> aspectProperties = parser.getDictionary(DescriptorParam.properties, null);
- int ranking = parser.getInt(DescriptorParam.ranking, 1);
- String implClass = parser.getString(DescriptorParam.impl);
- Object impl = b.loadClass(implClass);
- service = dm.createAspectService(serviceInterface, serviceFilter, ranking, impl, aspectProperties);
- setCommonServiceParams(service, parser);
- return service;
- }
-
- /**
- * Creates an Adapter Service.
- * @param b
- * @param dm
- * @param parser
- * @return
- */
- private Service createAdapterService(Bundle b, DependencyManager dm, DescriptorParser parser)
- throws ClassNotFoundException
- {
- Class<?> adapterImpl = b.loadClass(parser.getString(DescriptorParam.impl));
- 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);
- Service service = dm.createAdapterService(adapteeService, adapteeFilter, adapterService, adapterImpl, adapterProperties);
- setCommonServiceParams(service, parser);
- return service;
- }
-
- /**
- * Creates a Bundle Adapter Service.
- * @param b
- * @param dm
- * @param parser
- * @return
- */
- private Service createBundleAdapterService(Bundle b, DependencyManager dm, DescriptorParser parser)
- throws ClassNotFoundException
- {
- int stateMask = parser.getInt(DescriptorParam.stateMask, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
- String filter = parser.getString(DescriptorParam.filter, null);
- Class<?> adapterImpl = b.loadClass(parser.getString(DescriptorParam.impl));
- String service = parser.getString(DescriptorParam.service, null);
- Dictionary<String, String> properties = parser.getDictionary(DescriptorParam.properties, null);
- boolean propagate = "true".equals(parser.getString(DescriptorParam.propagate, "false"));
- Service srv = dm.createBundleAdapterService(stateMask, filter, adapterImpl, service, properties, propagate);
- setCommonServiceParams(srv, parser);
- return srv;
- }
-
- /**
- * Creates a Resource Adapter Service.
- * @param b
- * @param dm
- * @param parser
- * @return
- */
- private Service createResourceAdapterService(Bundle b, DependencyManager dm, DescriptorParser parser)
- throws ClassNotFoundException
- {
- String filter = parser.getString(DescriptorParam.filter, null);
- Class<?> impl = b.loadClass(parser.getString(DescriptorParam.impl));
- String service = parser.getString(DescriptorParam.service, null);
- Dictionary<String, String> properties = parser.getDictionary(DescriptorParam.properties, null);
- boolean propagate = "true".equals(parser.getString(DescriptorParam.propagate, "false"));
- Service srv = dm.createResourceAdapterService(filter, null, service, properties, impl, propagate);
- setCommonServiceParams(srv, parser);
- return srv;
- }
-
- /**
- * 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.
- */
- private Dependency createServiceDependency(Bundle b, DependencyManager dm, DescriptorParser parser, boolean temporal)
- throws ClassNotFoundException
- {
- String service = parser.getString(DescriptorParam.service);
- Class<?> serviceClass = b.loadClass(service);
- String serviceFilter = parser.getString(DescriptorParam.filter, null);
- String defaultServiceImpl = parser.getString(DescriptorParam.defaultImpl, null);
- 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);
- 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);
- }
- if (temporal)
- {
- // Set the timeout value for a temporal service dependency
- 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.
- sd.setRequired(required);
- }
- return sd;
- }
-
- /**
- * Creates a ConfigurationDependency that we parsed from a component descriptor entry.
- * @param b
- * @param dm
- * @param parser
- * @return
- */
- private Dependency createConfigurationDependency(Bundle b, DependencyManager dm,
- DescriptorParser parser)
- {
- 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);
- cd.setCallback(callback);
- cd.setPropagate(propagate);
- return cd;
- }
-
- /**
- * Creates a BundleDependency that we parsed from a component descriptor entry.
- * @param b
- * @param dm
- * @param parser
- * @return
- */
- private Dependency createBundleDependency(Bundle b, DependencyManager dm,
- DescriptorParser parser)
- {
- String added = parser.getString(DescriptorParam.added, null);
- String changed = parser.getString(DescriptorParam.changed, null);
- String removed = parser.getString(DescriptorParam.removed, null);
- 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);
- }
- if (stateMask != -1)
- {
- bd.setStateMask(stateMask);
- }
- return bd;
- }
-
- private Dependency createResourceDependency(@SuppressWarnings("unused") Bundle b, DependencyManager dm,
- DescriptorParser parser)
- {
- String added = parser.getString(DescriptorParam.added, null);
- String changed = parser.getString(DescriptorParam.changed, null);
- String removed = parser.getString(DescriptorParam.removed, null);
- 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);
- }
- rd.setPropagate(propagate);
- return rd;
- }
-}
\ No newline at end of file
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyBuilder.java
new file mode 100644
index 0000000..42fdf89
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyBuilder.java
@@ -0,0 +1,250 @@
+/*
+ * 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 org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.dependencies.BundleDependency;
+import org.apache.felix.dm.dependencies.ConfigurationDependency;
+import org.apache.felix.dm.dependencies.Dependency;
+import org.apache.felix.dm.dependencies.ResourceDependency;
+import org.apache.felix.dm.dependencies.ServiceDependency;
+import org.apache.felix.dm.dependencies.TemporalServiceDependency;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to build a concrete dependency from meta data.
+ */
+public class DependencyBuilder
+{
+ public enum DependencyType
+ {
+ ServiceDependency,
+ TemporalServiceDependency,
+ ConfigurationDependency,
+ BundleDependency,
+ ResourceDependency
+ }
+
+ private MetaData m_metaData;
+
+ public DependencyBuilder(MetaData dependencyMetaData)
+ {
+ m_metaData = dependencyMetaData;
+ }
+
+ public Dependency build(Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ return build(b, dm, false);
+ }
+
+ public Dependency build(Bundle b, DependencyManager dm, boolean instanceBound)
+ throws Exception
+ {
+ Dependency dp = null;
+ DependencyType type;
+
+ try
+ {
+ type = DependencyType.valueOf(m_metaData.getString(Params.type));
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new IllegalArgumentException("no \"type\" parameter found from metaData: " + m_metaData);
+ }
+
+ switch (type)
+ {
+ case ServiceDependency:
+ dp = createServiceDependency(b, dm, false, instanceBound);
+ break;
+
+ case TemporalServiceDependency:
+ dp = createServiceDependency(b, dm, true, instanceBound);
+ break;
+
+ case ConfigurationDependency:
+ dp = createConfigurationDependency(dm, instanceBound);
+ break;
+
+ case BundleDependency:
+ dp = createBundleDependency(dm, instanceBound);
+ break;
+
+ case ResourceDependency:
+ dp = createResourceDependency(dm, instanceBound);
+ break;
+ }
+ return dp;
+ }
+
+ private Dependency createServiceDependency(Bundle b, DependencyManager dm, boolean temporal,
+ boolean instanceBound)
+ throws ClassNotFoundException
+ {
+ String service = m_metaData.getString(Params.service);
+ Class<?> serviceClass = b.loadClass(service);
+ String serviceFilter = m_metaData.getString(Params.filter, null);
+ String defaultServiceImpl = m_metaData.getString(Params.defaultImpl, null);
+ Class<?> defaultServiceImplClass =
+ (defaultServiceImpl != null) ? b.loadClass(defaultServiceImpl) : null;
+ String added = m_metaData.getString(Params.added, null);
+ String changed = temporal ? null : m_metaData.getString(Params.changed, null);
+ String removed = temporal ? null : m_metaData.getString(Params.removed, null);
+ String autoConfigField = m_metaData.getString(Params.autoConfig, null);
+ boolean required = "true".equals(m_metaData.getString(Params.required, "true"));
+ String timeout = m_metaData.getString(Params.timeout, null);
+
+ Dependency dp = createServiceDependency(dm, temporal, serviceClass,
+ serviceFilter, defaultServiceImplClass, added, changed,
+ removed, autoConfigField, timeout, required, instanceBound);
+ 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,
+ boolean instanceBound)
+ {
+ 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);
+ }
+ if (temporal)
+ {
+ // Set the timeout value for a temporal service dependency
+ 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.
+ sd.setRequired(required);
+ }
+
+ sd.setInstanceBound(instanceBound);
+ return sd;
+ }
+
+ private Dependency createConfigurationDependency(DependencyManager dm, boolean instanceBound)
+ {
+ String pid = m_metaData.getString(Params.pid);
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+ String callback = m_metaData.getString(Params.updated, "updated");
+ Dependency dp = createConfigurationDependency(dm, pid, callback, propagate, instanceBound);
+ return dp;
+ }
+
+ private Dependency createConfigurationDependency(DependencyManager dm, String pid, String callback,
+ boolean propagate, boolean instanceBound)
+ {
+ if (pid == null)
+ {
+ throw new IllegalArgumentException(
+ "pid attribute not provided in ConfigurationDependency declaration");
+ }
+ ConfigurationDependency cd = dm.createConfigurationDependency();
+ cd.setPid(pid);
+ cd.setCallback(callback);
+ cd.setPropagate(propagate);
+ cd.setInstanceBound(instanceBound);
+ return cd;
+ }
+
+ /**
+ * Creates a BundleDependency that we parsed from a component descriptor entry.
+ * @param b
+ * @param dm
+ * @param parser
+ * @return
+ */
+ private Dependency createBundleDependency(DependencyManager dm, boolean instanceBound)
+ {
+ String added = m_metaData.getString(Params.added, null);
+ String changed = m_metaData.getString(Params.changed, null);
+ String removed = m_metaData.getString(Params.removed, null);
+ boolean required = "true".equals(m_metaData.getString(Params.required, "true"));
+ String filter = m_metaData.getString(Params.filter, null);
+ int stateMask = m_metaData.getInt(Params.stateMask, -1);
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+
+ Dependency dp = createBundleDependency(dm, added, changed, removed, required, propagate, filter,
+ stateMask, instanceBound);
+ return dp;
+ }
+
+ private Dependency createBundleDependency(DependencyManager dm, String added, String changed,
+ String removed, boolean required, boolean propagate, String filter, int stateMask, boolean instanceBound)
+ {
+ BundleDependency bd = dm.createBundleDependency();
+ bd.setCallbacks(added, changed, removed);
+ bd.setRequired(required);
+ bd.setPropagate(propagate);
+ if (filter != null)
+ {
+ bd.setFilter(filter);
+ }
+ if (stateMask != -1)
+ {
+ bd.setStateMask(stateMask);
+ }
+ bd.setInstanceBound(instanceBound);
+ return bd;
+ }
+
+ private Dependency createResourceDependency(DependencyManager dm, boolean instanceBound)
+ {
+ String added = m_metaData.getString(Params.added, null);
+ String changed = m_metaData.getString(Params.changed, null);
+ String removed = m_metaData.getString(Params.removed, null);
+ String filter = m_metaData.getString(Params.filter, null);
+ boolean required = "true".equals(m_metaData.getString(Params.required, "true"));
+ boolean propagate = "true".equals(m_metaData.getString(Params.propagate, "false"));
+
+ Dependency dp = createResourceDependency(dm, added, changed, removed, required, filter, propagate, instanceBound);
+ return dp;
+ }
+
+ private Dependency createResourceDependency(DependencyManager dm, String added,
+ String changed, String removed, boolean required, String filter, boolean propagate, boolean instanceBound)
+ {
+ ResourceDependency rd = dm.createResourceDependency();
+ rd.setCallbacks(added, changed, removed);
+ rd.setRequired(required);
+ if (filter != null)
+ {
+ rd.setFilter(filter);
+ }
+ rd.setPropagate(propagate);
+ rd.setInstanceBound(instanceBound);
+ return rd;
+ }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyManagerRuntime.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyManagerRuntime.java
new file mode 100644
index 0000000..92b4b62
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyManagerRuntime.java
@@ -0,0 +1,218 @@
+/*
+ * 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.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.service.Service;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.service.log.LogService;
+
+/**
+ * This class parses service descriptors generated by the annotation bnd processor.
+ * The descriptors are located under OSGI-INF/dependencymanager directory. Such files are actually
+ * referenced by a specific "DependendencyManager-Component" manifest header.
+ */
+public class DependencyManagerRuntime implements SynchronousBundleListener
+{
+ private ConcurrentHashMap<Bundle, DependencyManager> m_managers = new ConcurrentHashMap<Bundle, DependencyManager>();
+ private static LogService m_log; // Injected
+ private BundleContext m_bctx; // Injected
+ private DescriptorParser m_parser;
+
+ /**
+ * Starts our Service (at this point, we have been injected with our bundle context, as well
+ * as with our log service. We'll listen to bundle start/stop events (we implement the
+ * SynchronousBundleListener interface).
+ */
+ protected void start()
+ {
+ // Register our log service.
+ Log.instance().setLogService(m_log);
+
+ // Instantiates our descriptor parser, and register our service builders into it.
+ m_parser = new DescriptorParser();
+ m_parser.addBuilder(new ServiceBuilder());
+ m_parser.addBuilder(new AspectServiceBuilder());
+ m_parser.addBuilder(new AdapterServiceBuilder());
+ m_parser.addBuilder(new BundleAdapterServiceBuilder());
+ m_parser.addBuilder(new FactoryConfigurationAdapterServiceBuilder());
+ m_parser.addBuilder(new ResourceAdapterServiceBuilder());
+
+ // Load already started bundles
+ for (Bundle b: m_bctx.getBundles())
+ {
+ if (b.getState() == Bundle.ACTIVE)
+ {
+ bundleChanged(new BundleEvent(Bundle.ACTIVE, b));
+ }
+ }
+
+ // Track bundles
+ m_bctx.addBundleListener(this);
+ }
+
+ /**
+ * Stops our service. We'll stop all activated DependencyManager services.
+ */
+ @SuppressWarnings("unchecked")
+ protected void stop()
+ {
+ m_log.log(LogService.LOG_INFO, "DependencyManager Runtime stopping: removing services");
+
+ for (DependencyManager dm: m_managers.values())
+ {
+ List<Service> services = new ArrayList<Service>(dm.getServices());
+ for (Service service: services)
+ {
+ m_log.log(LogService.LOG_INFO, "DependencyManager Runtime stopping: removing service: "
+ + service);
+ dm.remove(service);
+ }
+ }
+
+ m_managers.clear();
+ }
+
+ /**
+ * Register a LogService, so other components may use it
+ */
+ protected void bind(LogService logService) {
+ m_log = logService;
+ Log.instance().setLogService(logService);
+ }
+
+ /**
+ * Handle a bundle event, and eventually parse started bundles.
+ */
+ public void bundleChanged(BundleEvent event)
+ {
+ Bundle b = event.getBundle();
+ if (b.getState() == Bundle.ACTIVE)
+ {
+ bundleStarted(b);
+ }
+ else if (b.getState() == Bundle.STOPPING)
+ {
+ bundleStopping(b);
+ }
+ }
+
+ /**
+ * Checks if a started bundle have some DependencyManager descriptors
+ * referenced in the "DependencyManager-Component" OSGi header.
+ * @param b the started bundle.
+ */
+ void bundleStarted(Bundle b)
+ {
+ String descriptorPaths = (String) b.getHeaders().get("DependencyManager-Component");
+ if (descriptorPaths == null)
+ {
+ return;
+ }
+
+ for (String descriptorPath: descriptorPaths.split(","))
+ {
+ URL descriptorURL = b.getEntry(descriptorPath);
+ if (descriptorURL == null)
+ {
+ m_log.log(LogService.LOG_ERROR,
+ "DependencyManager component descriptor not found: " + descriptorPath);
+ continue;
+ }
+ loadDescriptor(b, descriptorURL);
+ }
+ }
+
+ /**
+ * Unregisters all services for a stopping bundle.
+ * @param b
+ */
+ @SuppressWarnings("unchecked")
+ private void bundleStopping(Bundle b)
+ {
+ m_log.log(LogService.LOG_INFO, "Removing services from stopping bundle: " + b.getSymbolicName());
+ DependencyManager dm = m_managers.remove(b);
+ if (dm != null)
+ {
+ List<Service> services = new ArrayList(dm.getServices());
+ for (Service service: services)
+ {
+ m_log.log(LogService.LOG_INFO, "Removing service service: " + service);
+ dm.remove(service);
+ }
+ }
+ }
+
+ /**
+ * Load a DependencyManager component descriptor from a given bundle.
+ * @param b
+ * @param descriptorURL
+ */
+ private void loadDescriptor(Bundle b, URL descriptorURL)
+ {
+ m_log.log(LogService.LOG_DEBUG, "Parsing descriptor " + descriptorURL
+ + " from bundle " + b.getSymbolicName());
+
+ BufferedReader in = null;
+ try
+ {
+ in = new BufferedReader(new InputStreamReader(descriptorURL.openStream()));
+ DependencyManager dm = m_managers.get(b);
+ if (dm == null)
+ {
+ dm = new DependencyManager(b.getBundleContext());
+ m_managers.put(b, dm);
+ }
+
+ m_parser.parse(in, b, dm);
+ m_managers.put(b, dm);
+ }
+
+ catch (Throwable t)
+ {
+ m_log.log(LogService.LOG_ERROR, "Error while parsing descriptor "
+ + descriptorURL + " from bundle " + b.getSymbolicName(), t);
+ }
+
+ finally
+ {
+ if (in != null)
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+ }
+}
\ 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
deleted file mode 100644
index c208a9a..0000000
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParam.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.util.EnumSet;
-
-/**
- * List of all valid attribute a DependencyManager component descriptor may contain.
- * @see DescriptorParser for the descriptor syntax.
- */
-public enum DescriptorParam
-{
- /* Service attribute for the init method (the parsed value is a String) */
- init,
-
- /* Service attribute for the start method (the parsed value is a String) */
- start,
-
- /* Stop attribute for the stop method (the parsed value is a String) */
- stop,
-
- /* Service attribute for the destroy method (the parsed value is a String) */
- destroy,
-
- /* Service attribute for the impl service class name (the parsed value is a String) */
- impl,
-
- /* Service attribute for the provided services class names (the parsed value is a String[]) */
- provide,
-
- /* Service attribute for the provided service properties (the parsed value is a Hashtable) */
- properties,
-
- /* Service attribute for the composition method name (the parsed value is a String) */
- composition,
-
- /* ServiceDependency attribute for the interface we depend on (the parsed value is a String) */
- service,
-
- /* ServiceDependency attribute for the service filter (the parsed value is a String) */
- filter,
-
- /* ServiceDependency attribute for the service default impl (the parsed value is a String) */
- defaultImpl,
-
- /* ServiceDependency attribute for the required boolean (the parsed value is a String ("false"|"true") */
- required,
-
- /* ServiceDependency attribute for the added callback name (the parsed value is a String) */
- added,
-
- /* ServiceDependency attribute for the added changed name (the parsed value is a String) */
- changed,
-
- /* ServiceDependency attribute for the added removed name (the parsed value is a String) */
- removed,
-
- /* ServiceDependency attribute for the auto config field name (the parsed value is a String) */
- autoConfig,
-
- /* ConfigurationDependency attribute for the PID (the parsed value is a String) */
- pid,
-
- /* ConfigurationDependency attribute for the propagate boolean (the parsed value is a String ("false"|"true") */
- propagate,
-
- /* ConfigurationDependency attribute for the updated callback method (the parsed value is a String */
- updated,
-
- /* TemporalServiceDependency attribute for the timeout (the parsed value is a String) */
- timeout,
-
- /* AdapterService attribute for the adapter interface (the parsed value is a String) */
- adapterService,
-
- /* AdapterService attribute for the adapter service properties (the parsed value is a Hashtable) */
- adapterProperties,
-
- /* AdapterService attribute for the adaptee service (the parsed value is a String) */
- adapteeService,
-
- /* AdapterService attribute for the adaptee service filter (the parsed value is a String) */
- adapteeFilter,
-
- /* BundleDependency attribute for the state mask bundle (the parsed value is a string) */
- stateMask,
-
- /* The ranking of an aspect (the parsed value is an int) */
- ranking,
-
- /* The factory pid of an FactoryConfigurationAdapterService annotation (the parsed value is a string) */
- factoryPid,
-
- /* the factory attribute for the Service annotation (the parsed value is a string) */
- factory,
-
- /* 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/DescriptorParser.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParser.java
index 9804973..81728f8 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParser.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorParser.java
@@ -18,323 +18,58 @@
*/
package org.apache.felix.dm.runtime;
+import java.io.BufferedReader;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Dictionary;
import java.util.HashMap;
-import java.util.Hashtable;
import java.util.List;
import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import org.apache.felix.dm.DependencyManager;
+import org.json.JSONObject;
+import org.osgi.framework.Bundle;
import org.osgi.service.log.LogService;
/**
* This class parses files generated in OSGI-INF/*.dm by the DependencyManager bnd plugin.
- * Each descriptor contains the definition of a Service, along with its corresponding service dependency or configuration dependencies.
- * Here is an example of a typical descriptor syntax:
- *
- * Service: start=start; stop=stop; impl=org.apache.felix.dm.test.annotation.ServiceConsumer;
- * ServiceDependency: service=org.apache.felix.dm.test.annotation.ServiceInterface; autoConfig=m_service;
- * ServiceDependency: added=bind; service=org.apache.felix.dm.test.annotation.ServiceInterface;
- *
- * Notice that the descriptor must start with a "Service" definition. (Dependencies must be declared after the "Service" entry).
- * <p>
- *
- * Now, here is the formal BNF definition of the descriptor syntax:
- *
- * line := <type> ':' <params>
- *
- * type := service|aspectservice|serviceDependency|configurationDependency|temporalServiceDependency
- * service := 'Service'
- * aspectservice := 'AspectService'
- * serviceDependency := 'ServiceDependency'
- * configurationDependency := 'ConfigurationDependency'
- * temporalServiceDependency := 'TemporalServiceDependency'
- *
- * params := paramName '=' paramValue ( ';' paramName '=' paramValue )*
- *
- * paramName := init | start | stop | destroy | impl | provide | properties | factory | factoryMethod | composition | service | filter |
- * defaultImpl | required | added | changed | removed | autoConfig | pid | propagate | updated | timeout |
- * adapterService | adapterProperties | adapteeService | adapteeFilter
- * init := 'init'
- * start := 'start'
- * stop := 'stop'
- * destroy := 'destroy'
- * impl := 'impl'
- * provide := 'provide'
- * properties := 'properties'
- * factory := 'factory'
- * factoryMethod := 'factoryMethod'
- * composition := 'composition'
- * service := 'service'
- * filter := 'filter'
- * defaultImpl := 'defaultImpl'
- * required := 'required'
- * added := 'added'
- * changed := 'changed'
- * removed := 'removed'
- * autoConfig := 'autoConfig'
- * pid := 'pid'
- * propagate := 'propagate'
- * updated := 'updated'
- * timeout := 'timeout'
- * adapterService := 'adapterService'
- * adapterProperties := 'adapterProperties'
- * adapteeService := 'adapteeService'
- * adapteeFilter := 'adapteeFilter'
- *
- * paramValue := strings | attributes
- * strings := string ( ',' string )*
- * attributes := string ':' string ( ',' string : string )*
- * string := [alphanum string]
+ * Each descriptor contains a JSON definition of a Service, along with its corresponding service dependency or configuration dependencies.
*/
public class DescriptorParser
{
- private LogService m_logService;
- private Map<DescriptorParam, Object> m_params = new HashMap<DescriptorParam, Object>();
+ private Map<String, ServiceComponentBuilder> m_builders = new HashMap<String, ServiceComponentBuilder>();
- private final static Pattern linePattern = Pattern.compile("(\\w+):\\s*(.*)", Pattern.COMMENTS);
- private final static Pattern paramPattern = Pattern.compile("([^=]+)=([^;]+);?");
- private final static Pattern stringsPattern = Pattern.compile("([^,]+)");
- private final static Pattern attributesPattern = Pattern.compile("([^:]+):([^,]+),?");
-
- public DescriptorParser(LogService service)
+ public void addBuilder(ServiceComponentBuilder sb)
{
- m_logService = service;
+ m_builders.put(sb.getType(), sb);
}
- /*
- * Parses a DependencyManager component descriptor entry (either a Service, a ServiceDependency, or a ConfigurationDependency entry).
- * @return DescriptorEntry.Service, DescriptorEntry.ServiceDependency, or DescriptorEntry.ConfigurationDependency
- */
- public DescriptorEntry parse(String line)
+ public void parse(BufferedReader reader, Bundle b, DependencyManager dm) throws Exception
{
- m_params.clear();
- Matcher lineMatcher = linePattern.matcher(line);
- if (lineMatcher.matches())
- {
- DescriptorEntry entry = DescriptorEntry.valueOf(lineMatcher.group(1).trim());
- Matcher paramMatcher = paramPattern.matcher(lineMatcher.group(2).trim());
- while (paramMatcher.find())
- {
- DescriptorParam paramName = DescriptorParam.valueOf(paramMatcher.group(1).trim());
- String paramValue = paramMatcher.group(2).trim();
- Matcher attributesMatcher = attributesPattern.matcher(paramValue);
- boolean matched = false;
+ String line;
- Hashtable<String, String> attributes = new Hashtable<String, String>();
- while (attributesMatcher.find())
- {
- matched = true;
- attributes.put(attributesMatcher.group(1).trim(),
- attributesMatcher.group(2).trim());
- }
- m_params.put(paramName, attributes);
+ // The first line is a Service Component (a Service, an Aspect Service, etc ...)
+ line = reader.readLine();
+ Log.instance().log(LogService.LOG_DEBUG, "Parsing destriptor entry line: " + line);
+ JSONObject json = new JSONObject(line);
+ JSONMetaData serviceMetaData = new JSONMetaData(json);
- if (!matched)
- {
- Matcher stringsMatcher = stringsPattern.matcher(paramValue);
- if (stringsMatcher.groupCount() > 0)
- {
- List<String> strings = new ArrayList<String>();
- while (stringsMatcher.find())
- {
- strings.add(stringsMatcher.group(1).trim());
- }
- m_params.put(paramName, strings.toArray(new String[strings.size()]));
- }
- }
- }
+ String type = (String) json.get("type");
+ ServiceComponentBuilder builder = m_builders.get(type);
+ if (builder == null)
+ {
+ throw new IllegalArgumentException("Invalid descriptor"
+ + ": no \"type\" parameter found in first line");
+ }
- m_logService.log(LogService.LOG_DEBUG, "Parsed " + entry + ": " + toString());
- return entry;
- }
- else
+ // Parse the rest of the lines (dependencies)
+ List<MetaData> serviceDependencies = new ArrayList<MetaData>();
+ while ((line = reader.readLine()) != null)
{
- throw new IllegalArgumentException("Invalid descriptor entry: " + line);
+ Log.instance().log(LogService.LOG_DEBUG, "Parsing destriptor entry line: " + line);
+ JSONObject dep = new JSONObject(line);
+ serviceDependencies.add(new JSONMetaData(dep));
}
- }
- /**
- * Once a component descriptor entry line is parsed, you can retrieve entry attributes using this method.
- * @param param
- * @return
- */
- public String getString(DescriptorParam param)
- {
- Object value = m_params.get(param);
- if (value == null)
- {
- throw new IllegalArgumentException("Parameter " + param + " not found");
- }
- if (!(value instanceof String[]))
- {
- throw new IllegalArgumentException("Parameter " + param + " not a String array");
- }
- String[] array = (String[]) value;
- if (array.length < 1)
- {
- throw new IllegalArgumentException("Parameter " + param + " not found");
- }
- return (array[0]);
- }
-
- /**
- * Once a component descriptor entry line is parsed, you can retrieve entry attributes using this method.
- *
- * @param param
- * @param def
- * @return
- */
- public String getString(DescriptorParam param, String def)
- {
- try
- {
- return getString(param);
- }
- catch (IllegalArgumentException e)
- {
- return def;
- }
- }
-
- /**
- * Once a component descriptor entry line is parsed, you can retrieve entry attributes using this method.
- *
- * @param param
- * @param def
- * @return
- */
- public int getInt(DescriptorParam param)
- {
- String value = getString(param, null);
- if (value != null)
- {
- try
- {
- return Integer.parseInt(value);
- }
- catch (NumberFormatException e)
- {
- throw new IllegalArgumentException("parameter " + param + " is not an int value: "
- + value);
- }
- }
- else
- {
- throw new IllegalArgumentException("missing " + param + " parameter from annotation");
- }
- }
-
- /**
- * Once a component descriptor entry line is parsed, you can retrieve entry attributes using this method.
- *
- * @param param
- * @param def
- * @return
- */
- public int getInt(DescriptorParam param, int def)
- {
- String value = getString(param, null);
- if (value != null)
- {
- try
- {
- return Integer.parseInt(value);
- }
- catch (NumberFormatException e)
- {
- throw new IllegalArgumentException("parameter " + param + " is not an int value: "
- + value);
- }
- }
- else
- {
- return def;
- }
- }
-
- /**
- * Once a component descriptor entry line is parsed, you can retrieve entry attributes using this method.
- * @param param
- * @return
- */
- public String[] getStrings(DescriptorParam param)
- {
- Object value = m_params.get(param);
- if (value == null)
- {
- throw new IllegalArgumentException("Parameter " + param + " not found");
- }
- if (!(value instanceof String[]))
- {
- throw new IllegalArgumentException("Parameter " + param + " not a String array");
- }
- return (String[]) value;
- }
-
- /**
- * Once a component descriptor entry line is parsed, you can retrieve entry attributes using this method.
- * @param param
- * @return
- */
- public String[] getStrings(DescriptorParam param, String[] def)
- {
- try
- {
- return getStrings(param);
- }
- catch (IllegalArgumentException e)
- {
- return def;
- }
- }
-
- /**
- * Once a component descriptor entry line is parsed, you can retrieve entry attributes using this method.
- * @param param
- * @return
- */
- @SuppressWarnings("unchecked")
- public Dictionary<String, String> getDictionary(DescriptorParam param, Dictionary<String, String> def)
- {
- Object value = m_params.get(param);
- if (value == null)
- {
- return def;
- }
- if (!(value instanceof Dictionary))
- {
- throw new IllegalArgumentException("Parameter " + param + " not Dictionary");
- }
- return (Dictionary<String, String>) value;
- }
-
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- for (Map.Entry<DescriptorParam, Object> entry : m_params.entrySet())
- {
- sb.append(entry.getKey());
- sb.append("=");
- Object val = entry.getValue();
- if (val instanceof String || val instanceof Dictionary<?, ?>)
- {
- sb.append(val.toString());
- }
- else if (val instanceof String[])
- {
- sb.append(Arrays.toString((String[]) val));
- }
- else
- {
- sb.append(val.toString());
- }
- sb.append(";");
- }
- return sb.toString();
+ // and Invoke the builder
+ builder.buildService(serviceMetaData, serviceDependencies, b, dm);
}
}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
new file mode 100644
index 0000000..dc8ce75
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactoryConfigurationAdapterServiceBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.dependencies.Dependency;
+import org.apache.felix.dm.service.Service;
+import org.osgi.framework.Bundle;
+
+public class FactoryConfigurationAdapterServiceBuilder extends ServiceComponentBuilder
+{
+ private final static String TYPE = "FactoryConfigurationAdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void buildService(MetaData serviceMetaData,
+ List<MetaData> serviceDependencies,
+ Bundle b, DependencyManager dm) throws Exception
+ {
+ Class<?> impl = b.loadClass(serviceMetaData.getString(Params.impl));
+ String factoryPid = serviceMetaData.getString(Params.factoryPid);
+ String updated = serviceMetaData.getString(Params.updated);
+ String[] services = serviceMetaData.getStrings(Params.service, null);
+ Dictionary<String, Object> properties = serviceMetaData.getDictionary(Params.properties, null);
+ boolean propagate = "true".equals(serviceMetaData.getString(Params.propagate, "false"));
+ Service srv = dm.createFactoryConfigurationAdapterService(factoryPid, updated, impl, services, properties, propagate);
+ setCommonServiceParams(srv, serviceMetaData);
+ for (MetaData dependencyMetaData: serviceDependencies)
+ {
+ Dependency dp = new DependencyBuilder(dependencyMetaData).build(b, dm);
+ srv.add(dp);
+ }
+ dm.add(srv);
+ }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/JSONMetaData.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/JSONMetaData.java
new file mode 100644
index 0000000..7fc7054
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/JSONMetaData.java
@@ -0,0 +1,259 @@
+/*
+ * 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.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * A JSON MetaData.
+ */
+public class JSONMetaData implements MetaData, Cloneable
+{
+ /**
+ * The parsed Dependency or Service metadata. The map value is either a String, a String[],
+ * or a Dictionary, whose values are String or String[].
+ */
+ private HashMap<String, Object> m_metadata = new HashMap<String, Object>();
+
+ /**
+ * Decodes a JSON metadata for either a Service or a Dependency descriptor entry.
+ * The JSON object values contains either some of the following types: a String, a String[], or a Dictionary of String/String[].
+ * @param jso the JSON object that corresponds to a dependency manager descriptor entry line.
+ * @throws JSONException
+ */
+ @SuppressWarnings("unchecked")
+ public JSONMetaData(JSONObject jso) throws JSONException
+ {
+ // Decode json object into our internal map.
+ Iterator<String> it = jso.keys();
+ while (it.hasNext())
+ {
+ String key = it.next();
+ Object value = jso.get(key);
+ if (value instanceof String)
+ {
+ m_metadata.put(key, value);
+ }
+ else if (value instanceof JSONArray)
+ {
+ String[] array = decodeStringArray((JSONArray) value);
+ m_metadata.put(key, array);
+ }
+ else if (value instanceof JSONObject)
+ {
+ Hashtable<String, Object> h = new Hashtable<String, Object>();
+ JSONObject obj = ((JSONObject) value);
+ Iterator<String> it2 = obj.keys();
+ while (it2.hasNext())
+ {
+ String key2 = it2.next();
+ Object value2 = obj.get(key2);
+ if (value2 instanceof String)
+ {
+ h.put(key2, value2);
+ }
+ else if (value2 instanceof JSONArray)
+ {
+ String[] array = decodeStringArray((JSONArray) value2);
+ h.put(key2, array);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Could not decode JSON metadata: key " + key +
+ "contains an invalid dictionary key: " + key
+ + " (the value is neither a String nor a String[]).");
+ }
+ }
+ m_metadata.put(key, h);
+ }
+ }
+ }
+
+ /**
+ * Close this class instance to another one.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object clone() throws CloneNotSupportedException
+ {
+ JSONMetaData clone = (JSONMetaData) super.clone();
+ clone.m_metadata = (HashMap<String, Object>) m_metadata.clone();
+ return clone;
+ }
+
+ public String getString(Params key)
+ {
+ String value = (String) m_metadata.get(key.toString());
+ if (value == null)
+ {
+ throw new IllegalArgumentException("Parameter " + key + " not found");
+ }
+ return value;
+ }
+
+ public String getString(Params key, String def)
+ {
+ try
+ {
+ return getString(key);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return def;
+ }
+ }
+
+ public int getInt(Params key)
+ {
+ String value = getString(key, null);
+ if (value != null)
+ {
+ try
+ {
+ return Integer.parseInt(value);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not an int value: "
+ + value);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("missing " + key
+ + " parameter from annotation");
+ }
+ }
+
+ public int getInt(Params key, int def)
+ {
+ String value = getString(key, null);
+ if (value != null)
+ {
+ try
+ {
+ return Integer.parseInt(value);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("parameter " + key
+ + " is not an int value: "
+ + value);
+ }
+ }
+ else
+ {
+ return def;
+ }
+ }
+
+ public String[] getStrings(Params key)
+ {
+ Object array = m_metadata.get(key.toString());
+ if (array == null)
+ {
+ throw new IllegalArgumentException("Parameter " + key + " not found");
+ }
+
+ if (!(array instanceof String[]))
+ {
+ throw new IllegalArgumentException("Parameter " + key + " is not a String[] (" + array.getClass()
+ + ")");
+ }
+ return (String[]) array;
+ }
+
+ public String[] getStrings(Params key, String[] def)
+ {
+ try
+ {
+ return getStrings(key);
+ }
+ catch (IllegalArgumentException t)
+ {
+ return def;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public Dictionary<String, Object> getDictionary(Params key,
+ Dictionary<String, Object> def)
+ {
+ Object dictionary = m_metadata.get(key.toString());
+ if (dictionary == null)
+ {
+ return def;
+ }
+
+ if (!(dictionary instanceof Dictionary<?, ?>))
+ {
+ throw new IllegalArgumentException("Parameter " + key + " is not a Dictionary ("
+ + dictionary.getClass() + ")");
+ }
+
+ return (Dictionary<String, Object>) dictionary;
+ }
+
+ @Override
+ public String toString()
+ {
+ return m_metadata.toString();
+ }
+
+ public void setDictionary(Params key, Dictionary<String, Object> dictionary)
+ {
+ m_metadata.put(key.toString(), dictionary);
+ }
+
+ public void setString(Params key, String value)
+ {
+ m_metadata.put(key.toString(), value);
+ }
+
+ public void setStrings(Params key, String[] values)
+ {
+ m_metadata.put(key.toString(), values);
+ }
+
+ /**
+ * Decodes a JSONArray into a String array (all JSON array values are supposed to be strings).
+ */
+ private String[] decodeStringArray(JSONArray array) throws JSONException
+ {
+ String[] arr = new String[array.length()];
+ for (int i = 0; i < array.length(); i++)
+ {
+ Object value = array.get(i);
+ if (!(value instanceof String))
+ {
+ throw new IllegalArgumentException("JSON array is not an array of Strings: " + array);
+ }
+ arr[i] = value.toString();
+ }
+ return arr;
+ }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Log.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Log.java
new file mode 100644
index 0000000..198a6a9
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Log.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.dm.runtime;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+public class Log implements LogService
+{
+ /** The log service */
+ private static LogService m_logService;
+
+ /** Our sole instance */
+ private static Log m_instance = new Log();
+
+ public static Log instance()
+ {
+ return m_instance;
+ }
+
+ public void setLogService(LogService logService) {
+ m_logService = logService;
+ }
+
+ public void log(int level, String message, Throwable exception)
+ {
+ m_logService.log(level, message, exception);
+ }
+
+ public void log(int level, String message)
+ {
+ m_logService.log(level, message);
+ }
+
+ public void log(ServiceReference sr, int level, String message, Throwable exception)
+ {
+ m_logService.log(sr, level, message, exception);
+ }
+
+ public void log(ServiceReference sr, int level, String message)
+ {
+ m_logService.log(sr, level, message);
+ }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/MetaData.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/MetaData.java
new file mode 100644
index 0000000..7c56a30
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/MetaData.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.runtime;
+
+import java.util.Dictionary;
+
+/**
+ * This class represents the meta data parsed from a descriptor entry (json) line.
+ */
+public interface MetaData extends Cloneable
+{
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ String getString(Params key);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ String getString(Params key, String def);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ int getInt(Params key);
+
+ /**
+ * Returns a String descriptor entry parameter value.
+ */
+ int getInt(Params key, int def);
+
+ /**
+ * Returns a String array descriptor entry parameter value.
+ */
+ String[] getStrings(Params key);
+
+ /**
+ * Returns a String array descriptor entry parameter value.
+ */
+ String[] getStrings(Params key, String[] def);
+
+ /**
+ * Returns a descriptor entry value which is a complex value.
+ */
+ Dictionary<String, Object> getDictionary(Params key, Dictionary<String, Object> def);
+
+ /**
+ * Modifies a key Sring value
+ */
+ void setString(Params key, String value);
+
+ /**
+ * Modifies a String[] value.
+ */
+ void setStrings(Params key, String[] values);
+
+ /**
+ * Modifies a String[] value.
+ */
+ void setDictionary(Params key, Dictionary<String, Object> dictionary);
+}
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/Params.java
similarity index 63%
rename from dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DescriptorEntry.java
rename to dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Params.java
index 3a5e769..264d3da 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/Params.java
@@ -19,20 +19,39 @@
package org.apache.felix.dm.runtime;
/**
- * Type of each DependendencyManager component descriptor entry.
- * @see DescriptorParser for more informations about the descriptor syntax.
+ * List of descriptor parameters.
*/
-public enum DescriptorEntry
+public enum Params
{
- Service,
- AspectService,
- AdapterService,
- BundleAdapterService,
- ResourceAdapterService,
- FactoryConfigurationAdapterService,
- ServiceDependency,
- TemporalServiceDependency,
- ConfigurationDependency,
- BundleDependency,
- ResourceDependency
+ type,
+ init,
+ start,
+ stop,
+ destroy,
+ impl,
+ provide,
+ properties,
+ composition,
+ service,
+ filter,
+ defaultImpl,
+ required,
+ added,
+ changed,
+ removed,
+ autoConfig,
+ pid,
+ propagate,
+ updated,
+ timeout,
+ adapterService,
+ adapterProperties,
+ adapteeService,
+ adapteeFilter,
+ stateMask,
+ ranking,
+ factoryPid,
+ factory,
+ factoryConfigure,
+ name
}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ResourceAdapterServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ResourceAdapterServiceBuilder.java
new file mode 100644
index 0000000..e172f30
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ResourceAdapterServiceBuilder.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.dependencies.Dependency;
+import org.apache.felix.dm.service.Service;
+import org.osgi.framework.Bundle;
+
+public class ResourceAdapterServiceBuilder extends ServiceComponentBuilder
+{
+ private final static String TYPE = "ResourceAdapterService";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void buildService(MetaData serviceMetaData,
+ List<MetaData> serviceDependencies,
+ Bundle b, DependencyManager dm) throws Exception
+ {
+ String filter = serviceMetaData.getString(Params.filter, null);
+ Class<?> impl = b.loadClass(serviceMetaData.getString(Params.impl));
+ String[] service = serviceMetaData.getStrings(Params.service, null);
+ Dictionary<String, Object> properties = serviceMetaData.getDictionary(Params.properties, null);
+ boolean propagate = "true".equals(serviceMetaData.getString(Params.propagate, "false"));
+ Service srv = dm.createResourceAdapterService(filter, null, service, properties, impl, propagate);
+ setCommonServiceParams(srv, serviceMetaData);
+ for (MetaData dependencyMetaData: serviceDependencies)
+ {
+ Dependency dp = new DependencyBuilder(dependencyMetaData).build(b, dm);
+ srv.add(dp);
+ }
+ dm.add(srv);
+ }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceBuilder.java
new file mode 100644
index 0000000..5e51daa
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceBuilder.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.dm.runtime;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.dependencies.Dependency;
+import org.apache.felix.dm.service.Service;
+import org.osgi.framework.Bundle;
+import org.osgi.service.log.LogService;
+
+public class ServiceBuilder extends ServiceComponentBuilder
+{
+ private final static String TYPE = "Service";
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public void buildService(MetaData srvMeta, List<MetaData> srvDeps, Bundle b, DependencyManager dm)
+ throws Exception
+ {
+ Log.instance().log(LogService.LOG_DEBUG, "building service: service metadata=" + srvMeta
+ + ", dependencyMetaData=" + srvDeps);
+
+ // Get service parameters (lifecycle callbacks, composition, factory, etc ...)
+
+ Service service = dm.createService();
+ String factory = srvMeta.getString(Params.factory, null);
+ String factoryConfigure = srvMeta.getString(Params.factoryConfigure, null);
+ String impl = srvMeta.getString(Params.impl);
+ String init = srvMeta.getString(Params.init, null);
+ String start = srvMeta.getString(Params.start, null);
+ String stop = srvMeta.getString(Params.stop, null);
+ String destroy = srvMeta.getString(Params.destroy, null);
+ String composition = srvMeta.getString(Params.composition, null);
+ Dictionary<String, Object> serviceProperties = srvMeta.getDictionary(Params.properties, null);
+ String[] provide = srvMeta.getStrings(Params.provide, null);
+
+ // Check if we must provide a Set Factory.
+ if (factory == null)
+ {
+ // No: instantiate the service.
+ service.setImplementation(b.loadClass(impl));
+ if (composition != null)
+ {
+ service.setComposition(composition);
+ }
+ if (provide != null)
+ {
+ service.setInterface(provide, serviceProperties);
+ }
+
+ // Creates a ServiceHandler, which will filter all service lifecycle callbacks.
+ /*
+ ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(service, b, dm, srvMeta,
+ srvDeps);
+ service.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+ String confDependency = DependencyBuilder.DependencyType.ConfigurationDependency.toString();
+ */
+ // TODO REMOVE
+ for (MetaData depMeta: srvDeps)
+ {
+ Dependency dp = new DependencyBuilder(depMeta).build(b, dm);
+ service.add(dp);
+ }
+ }
+ else
+ {
+ // We don't instantiate the service, but instead we provide a Set in the registry.
+ // This Set will act as a factory and another component may use registers some
+ // service configurations into it in order to fire some service instantiations.
+
+ ServiceFactory serviceFactory = new ServiceFactory(b.loadClass(impl), init, start, stop, destroy,
+ composition, serviceProperties, provide,
+ factoryConfigure);
+ for (MetaData dependencyMetaData: srvDeps)
+ {
+ Dependency dp = new DependencyBuilder(dependencyMetaData).build(b, dm);
+ serviceFactory.addDependency(dp);
+ }
+ service.setImplementation(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);
+ }
+
+ dm.add(service);
+ }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceComponentBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceComponentBuilder.java
new file mode 100644
index 0000000..607375e
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceComponentBuilder.java
@@ -0,0 +1,63 @@
+/*
+ * 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.util.List;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.service.Service;
+import org.osgi.framework.Bundle;
+
+/**
+ * The builder which creates a DependencyManager Service (a Service and all its derived classes (Aspect/Adapters).
+ */
+public abstract class ServiceComponentBuilder
+{
+ /**
+ * Returns the service component type.
+ */
+ abstract String getType();
+
+ /**
+ * Builds the service component.
+ * @param serviceMetaData the service component metadata parsed from the descriptor file.
+ * @param serviceDependencies the service component dependencies metadata parsed from the descriptor file.
+ */
+ abstract void buildService(MetaData serviceMetaData, List<MetaData> serviceDependencies, Bundle b,
+ DependencyManager dm)
+ throws Exception;
+
+ /**
+ * Set common Service parameters, if provided from our Component descriptor
+ */
+ protected void setCommonServiceParams(Service service, MetaData serviceMetaData)
+ throws Exception
+ {
+ String init = serviceMetaData.getString(Params.init, null);
+ String start = serviceMetaData.getString(Params.start, null);
+ String stop = serviceMetaData.getString(Params.stop, null);
+ String destroy = serviceMetaData.getString(Params.destroy, null);
+ service.setCallbacks(init, start, stop, destroy);
+ String composition = serviceMetaData.getString(Params.composition, null);
+ if (composition != null)
+ {
+ service.setComposition(composition);
+ }
+ }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java
new file mode 100644
index 0000000..e8cc435
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java
@@ -0,0 +1,203 @@
+/*
+ * 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.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.dependencies.Dependency;
+import org.apache.felix.dm.service.Service;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class acts as a service implementation lifecycle handler. If the actual service implementation
+ * init method returns a Map, then this map will is inspected for dependency filters, which will be
+ * applied to the dependencies specified in the service.
+ */
+public class ServiceLifecycleHandler
+{
+ private String m_init;
+ private String m_start;
+ private String m_stop;
+ private String m_destroy;
+ private List<MetaData> m_dependencies = new ArrayList<MetaData>();
+ private Map<String, MetaData> m_namedDependencies = new HashMap<String, MetaData>();
+ private Bundle m_bundle;
+
+ public ServiceLifecycleHandler(Service srv, Bundle srvBundle, DependencyManager dm,
+ MetaData srvMetaData, List<MetaData> srvDep)
+ throws Exception
+ {
+ m_init = srvMetaData.getString(Params.init, null);
+ m_start = srvMetaData.getString(Params.start, null);
+ m_stop = srvMetaData.getString(Params.stop, null);
+ m_destroy = srvMetaData.getString(Params.destroy, null);
+ m_bundle = srvBundle;
+
+ String confDependency = DependencyBuilder.DependencyType.ConfigurationDependency.toString();
+ for (MetaData depMeta: srvDep)
+ {
+ if (depMeta.getString(Params.type).equals(confDependency))
+ {
+ // Register Configuration dependencies now
+ Dependency dp = new DependencyBuilder(depMeta).build(m_bundle, dm);
+ srv.add(dp);
+ }
+ else
+ {
+ String name = depMeta.getString(Params.name, null);
+ if (name != null)
+ {
+ m_namedDependencies.put(name, depMeta);
+ }
+ else
+ {
+ m_dependencies.add(depMeta);
+ }
+ }
+ }
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public void init(Object serviceInstance, DependencyManager dm, Service service)
+ throws Exception
+ {
+ // Invoke the service instance actual init method.
+ Object o = invokeMethod(serviceInstance, m_init, dm, service);
+
+ // If the init method returned a Map, then apply all filters found from it into
+ // the service dependencies. Keys = Dependency name / values = Dependency filter
+ if (o != null && Map.class.isAssignableFrom(o.getClass()))
+ {
+ Map<String, String> filters = (Map<String, String>) o;
+ for (Map.Entry<String, String> entry: filters.entrySet())
+ {
+ String name = entry.getKey();
+ String filter = entry.getValue();
+
+ MetaData dependency = m_namedDependencies.remove(name);
+ if (dependency != null)
+ {
+ dependency.setString(Params.filter, filter);
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Dependency d = depBuilder.build(m_bundle, dm, true);
+ service.add(d);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid filter Map: the filter key " + name
+ + " does not correspond " +
+ " to a known Dependency.");
+ }
+ }
+ }
+
+ plugDependencies(m_namedDependencies.values(), dm, service);
+ plugDependencies(m_dependencies, dm, service);
+ }
+
+ public void start(Object serviceInstance, DependencyManager dm, Service service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ invokeMethod(serviceInstance, m_start, dm, service);
+ }
+
+ public void stop(Object serviceInstance, DependencyManager dm, Service service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ invokeMethod(serviceInstance, m_stop, dm, service);
+ }
+
+ public void destroy(Object serviceInstance, DependencyManager dm, Service service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ invokeMethod(serviceInstance, m_destroy, dm, service);
+ }
+
+ private Object invokeMethod(Object serviceInstance, String method, DependencyManager dm, Service service)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ return invokeMethod(serviceInstance,
+ method,
+ new Class[][] {
+ { Object.class, DependencyManager.class, Service.class },
+ { DependencyManager.class, Service.class },
+ { Object.class },
+ {}
+ },
+ new Object[][] {
+ { serviceInstance, dm, service },
+ { dm, service },
+ { serviceInstance },
+ {}
+ });
+ }
+
+ private Object invokeMethod(Object instance, String method, Class<?>[][] signatures, Object[][] params)
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ if (method == null)
+ {
+ // The annotated class did not provide an annotation for this lifecycle callback.
+ return null;
+ }
+
+ Class clazz = instance.getClass();
+
+ while (clazz != null)
+ {
+ for (int i = 0; i < signatures.length; i++)
+ {
+ Class<?>[] signature = signatures[i];
+ try
+ {
+ // Don't use getMethod because getMethod only look for public methods !
+ Method m = instance.getClass().getDeclaredMethod(method, signature);
+ m.setAccessible(true);
+ return m.invoke(instance, params[i]);
+ }
+ catch (NoSuchMethodException e)
+ {
+ // ignore this and keep looking
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+
+ return null;
+ }
+
+ private void plugDependencies(Collection<MetaData> dependencies, DependencyManager dm, Service service)
+ throws Exception
+ {
+ for (MetaData dependency: dependencies)
+ {
+ DependencyBuilder depBuilder = new DependencyBuilder(dependency);
+ Dependency d = depBuilder.build(m_bundle, dm, true);
+ service.add(d);
+ }
+ }
+}