You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2018/09/26 16:45:39 UTC

svn commit: r1842034 [2/11] - in /felix/trunk/dependencymanager: ./ .gradle-wrapper/ cnf/ cnf/buildrepo/ cnf/ext/ cnf/localrepo/ cnf/localrepo/org.apache.felix.gogo.command/ cnf/localrepo/org.apache.felix.gogo.jline/ cnf/localrepo/org.apache.felix.gogo...

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java Wed Sep 26 16:45:35 2018
@@ -26,61 +26,77 @@ import java.util.Collection;
 import java.util.Dictionary;
 import java.util.Map;
 
-
 /**
- * Annotates a method for injecting a Configuration Dependency. A configuration dependency 
+ * Annotates a method for injecting a Configuration Dependency. 
+ * 
+ * <p> A configuration dependency 
  * is required by default, and allows you to depend on the availability of a valid configuration 
  * for your component. This dependency requires the OSGi Configuration Admin Service.
  * 
+ * Configuration Dependency callback is always invoked before any service dependency callbacks, and before init/start callbacks.
+ * 
  * The annotation can be applied on a callback method which accepts the following parameters:
  * 
- * <p><ul>
+ * <ul>
  * <li>callback(Dictionary) 
  * <li>callback(Component, Dictionary) 
- * <li>callback(Configuration interface) // type safe configuration
- * <li>callback(Component, Configuration interface) // type safe configuration
+ * <li>callback(Component, Configuration ... configTypes) // type safe configuration interface(s)
+ * <li>callback(Configuration ... configTypes) // type safe configuration interface(s)
+ * <li>callback(Dictionary, Configuration ... configTypes) // type safe configuration interfaces(s)
+ * <li>callback(Component, Dictionary, Configuration ... configTypes) // type safe configuration interfaces(s)
  * </ul>
  * 
  * <h3>Usage Examples</h3>
  * 
- * <p> In the following example, the "Printer" component depends on a configuration
- * whose PID name is "sample.PrinterConfiguration". This service will initialize
+ * <p> In the following example, the Printer components depends on a configuration
+ * whose PID name is "sample.Printer". This service will initialize
  * its ip/port number from the provided configuration.
  * 
- * <p> First, we define the configuration metadata, using standard bndtools metatatype annotations 
- * (see http://www.aqute.biz/Bnd/MetaType):
+ * <blockquote>
+ * <pre>
+ *
+ * package sample;
+ * 
+ * &#64;Component
+ * public class Printer {
+ *     &#64;ConfigurationDependency(propagate=true) // Will use the fqdn of the  Printer interface as the pid.
+ *     void updated(Dictionary cnf) {
+ *         if (cnf != null) {
+ *             String ip = cnf.get("address");
+ *             int port = Integer.parseInt(cnf.get("port"));
+ *         }
+ *     }
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * You can also define your own component properties using a custom type-safe interface:
  * 
  * <blockquote>
  * <pre>
  * package sample;
- * import aQute.bnd.annotation.metatype.Meta.AD;
- * import aQute.bnd.annotation.metatype.Meta.OCD;
- *
- * &#64;OCD(description = "Declare here the Printer Configuration.")
- * public interface PrinterConfiguration {
- *     &#64;AD(description = "Enter the printer ip address")
- *     String getAddress();
- *
- *     &#64;AD(description = "Enter the printer address port number.")
- *     default int getPort() { return 8080; }
+ * 
+ * interface PrinterConfig {
+ *     String getAddress();    	
+ *     int getPort();
  * }
  * </pre>
  * </blockquote>
  * 
- * Next, we define our Printer service which depends on the PrinterConfiguration:
+ * Next, we define our Printer service which depends on the PrinterConfig:
  * 
  * <blockquote>
  * <pre>
  * package sample;
- * import aQute.bnd.annotation.metatype.*;
  *
  * &#64;Component
  * public class Printer {
- *     &#64;ConfigurationDependency // Will use the fqdn of the  PrinterConfiguration interface as the pid.
- *     void updated(PrinterConfiguration cnf) {
- *         String ip = cnf.getAddress();
- *         int port = cnf.getPort();
- *         ...
+ *     &#64;ConfigurationDependency // Will use the fqdn of the  PrinterConfig interface as the pid.
+ *     void updated(PrinterConfig cnf) {
+ *         if (cnf != null) {
+ *             String ip = cnf.getAddress();
+ *             int port = cnf.getPort();
+ *         }
  *     }
  * }
  * </pre>
@@ -131,14 +147,14 @@ import java.util.Map;
  * 
  * <pre>{@code "map" => "{key1.value1, key2.value2}"}</pre> 
  * 
- * and a dictionary with <p>
+ * and a dictionary with
  * 
  * <pre>{@code "map.key1" => "value1", "map2.key2" => "value2"}</pre> 
  * 
  * result in the same map being returned.
  * Instead of a map, you could also define an interface with the methods <tt>getKey1()</tt> and <tt>getKey2</tt> and use
  * that interface as return type instead of a {@link Map}.
- * </p>
+ * 
  * <p>
  * In case a lookup does not yield a value from the underlying map or dictionary, the following rules are applied:
  * <ol>
@@ -147,8 +163,7 @@ import java.util.Map;
  * <li>for arrays, collections and maps, an empty array/collection/map is returned;
  * <li>for other interface types that are treated as configuration type a null-object is returned.
  * </ol>
- * </p>
- * 
+ *  
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 @Retention(RetentionPolicy.CLASS)
@@ -156,17 +171,15 @@ import java.util.Map;
 public @interface ConfigurationDependency
 {
     /**
-     * Returns the pid for a given service (by default, the pid is the service class name).
+     * Returns the pid for a given service (by default, the pid is the service class name, of the FQDN of 
+     * the configuration type found in the updated callback signature.
      * @return the pid for a given service (default = Service class name)
      */
     String pid() default "";
     
     /**
      * Returns the pid from a class name. The full class name will be used as the configuration PID.
-     * You can use this method when you use an interface annotated with standard bndtols metatype annotations.
-     * (see http://www.aqute.biz/Bnd/MetaType).
-     * @return the pid class
-     * @deprecated just define an updated callback which accepts as argument a configuration type.
+     * @return the pid class whose FQDN name is used as the configuration PID.
      */
     Class<?> pidClass() default Object.class;
     
@@ -176,8 +189,16 @@ public @interface ConfigurationDependenc
      * @return true if configuration must be published along with the service, false if not.
      */
     boolean propagate() default false;
-    
+        
     /**
+     * Sets the required flag which determines if this configuration dependency is required or not.
+     * A configuration dependency is required by default.
+     * 
+     * @return this service dependency
+     */
+	boolean required() default true;    
+
+	/**
      * The name for this configuration dependency. When you give a name a dependency, it won't be evaluated
      * immediately, but after the component's init method has been called, and from the init method, you can then return 
      * a map in order to dynamically configure the configuration dependency (the map has to contain a "pid" and/or "propagate" 
@@ -229,34 +250,4 @@ public @interface ConfigurationDependenc
      *  @return the dependency name used to configure the dependency dynamically from init callback
      */
     String name() default "";
-    
-    /**
-     * Sets the required flag which determines if this configuration dependency is required or not.
-     * A configuration dependency is required by default.
-     * 
-     * @param required the required flag
-     * @return this service dependency
-     */
-	boolean required() default true;
-    
-   /**
-     * The label used to display the tab name (or section) where the properties are displayed. Example: "Printer Service".
-     * @return The label used to display the tab name where the properties are displayed.
-     * @deprecated use standard bndtools metatype annotations instead (see http://www.aqute.biz/Bnd/MetaType)
-     */
-    String heading() default "";
-
-    /**
-     * A human readable description of the PID this annotation is associated with. Example: "Configuration for the PrinterService bundle".
-     * @return A human readable description of the PID this annotation is associated with.
-     * @deprecated use standard bndtools metatype annotations instead (see http://www.aqute.biz/Bnd/MetaType)
-     */
-    String description() default "";
-
-    /**
-     * The list of properties types used to expose properties in web console. 
-     * @return The list of properties types used to expose properties in web console.
-     * @deprecated use standard bndtools metatype annotations instead (see http://www.aqute.biz/Bnd/MetaType)
-     */
-    PropertyMetaData[] metadata() default {};
 }

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Property.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Property.java?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Property.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/Property.java Wed Sep 26 16:45:35 2018
@@ -25,8 +25,7 @@ import java.lang.annotation.RetentionPol
 import java.lang.annotation.Target;
 
 /**
- * Annotation used to describe a property key-value(s) pair. It is used for example when
- * declaring {@link Component#properties()} attribute.<p>
+ * Annotation used to describe a property key-value(s) pair. Such annotation can be applied on components.<p>
  * 
  * Property value(s) type is String by default, and the type is scalar if the value is single-valued, 
  * or an array if the value is multi-valued. You can apply this annotation on a component class multiple times

Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/PropertyType.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/PropertyType.java?rev=1842034&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/PropertyType.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/PropertyType.java Wed Sep 26 16:45:35 2018
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * When defining component service properties, one way to achieve this is to apply the {@link Property} annotation on your component class name.
+ * Now, you can also define your own component property type interfaces and apply them directly on your components (instead of the 
+ * {@link Property} annotation). The PropertyType annotation is closely similar to standard OSGi r7 declarative service @ComponentPropertyType 
+ * (which is also supported when dependency manager annotation API).
+ * 
+ * <h3>Usage Examples</h3>
+ * 
+ * Let’s assume your write an OSGi r7 jax rs servlet context which needs the two following service properties: 
+ * 
+ * <p><ul>
+ * <li> osgi.http.whiteboard.context.name
+ * <li> osgi.http.whiteboard.context.path
+ * </ul>
+ * 
+ * <p> Then you can first define your own annotation (but you could also reuse the default annotations provided by the jaxrs whiteboard r7 api):
+ * (notice that in the annotation, you can define default service property values):
+ * 
+ * <blockquote>
+ * <pre>
+ * &#64;PropertyType
+ * &#64;interface ServletContext {
+ *     String osgi_http_whiteboard_context_name() default AppServletContext.NAME;
+ *     String osgi_http_whiteboard_context_path();
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * In the above, the underscore is mapped to ".".
+ * Then you can apply the above annotation on top of your component like this:
+ * 
+ * <blockquote>
+ * <pre>
+ * &#64;Component
+ * &#64;ServletContext(osgi_http_whiteboard_context_path="/game")
+ * public class AppServletContext extends ServletContextHelper {
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * You can also use configuration admin service in order to override the default service properties:
+ * 
+ * <blockquote>
+ * <pre>
+ * &#64;Component
+ * &#64;ServletContext(osgi_http_whiteboard_context_path="/game")
+ * public class AppServletContext extends ServletContextHelper {
+ *     &#64;ConfigurationDependency(propagate=true, pid="my.pid")
+ *     void updated(ServletContext cnf) {
+ *        // if some properties are not present in the configuration, then the ones used in the annotation will be used.
+ *        // The configuration admin properties, if defined, will override the default configurations defined in the annotations
+ *     }
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * You can also define multiple property type annotations, and possibly single valued annotation. In this case, you can use
+ * the standard R7 PREFIX_ constants in order to specify the property prefix, and the property name will be derived from the
+ * single valued annotation (using camel case convention):
+ * 
+ * <blockquote>
+ * <pre>
+ * &#64;PropertyType
+ * &#64;interface ContextName { // will map to "osgi.http.whiteboard.context.name" property name
+ *     String PREFIX="osgi.http.whiteboard.";
+ *     String value();
+ * }
+ * 
+ * &#64;PropertyType
+ * &#64;interface ContextPath { // will map to "osgi.http.whiteboard.context.path" property name
+ *     String PREFIX="osgi.http.whiteboard.";
+ *     String value();
+ * }
+ * 
+ * &#64;Component
+ * &#64;ContextName(AppServletContext.NAME)
+ * &#64;ContextPath("/game")
+ * public class AppServletContext extends ServletContextHelper {
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * Same example as above, but also using configuration admin service in order to override default service properties: Here, as in OSGi r7 declarative service,
+ * you can define a callback method which accepts as arguments all (or some of) the defined property types:
+ * 
+ * <blockquote>
+ * <pre>
+ * &#64;Component
+ * &#64;ContextName(AppServletContext.NAME)
+ * &#64;ContextPath("/game")
+ * public class AppServletContext extends ServletContextHelper {
+ *     &#64;ConfigurationDependency(propagate=true, pid="my.pid")
+ *     void updated(ContextName ctxName, ContextPath ctxPath) {
+ *        // if some properties are not present in the configuration, then the ones used in the annotation will be used.
+ *        // The configuration admin properties, if defined, will override the default configurations defined in the annotations
+ *     }
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * The following is the same example as above, but this time the configuration callback can also define a Dictionary in the first argument
+ * (in case you want to also get the raw configuration dictionary:
+ * 
+ * <blockquote>
+ * <pre>
+ * &#64;Component
+ * &#64;ContextName(AppServletContext.NAME)
+ * &#64;ContextPath("/game")
+ * public class AppServletContext extends ServletContextHelper {
+ *     &#64;ConfigurationDependency(propagate=true, pid="my.pid")
+ *     void updated(Dictionary&lt;String, Object&gt; rawConfig, ContextName ctxName) {
+ *        // if some properties are not present in the configuration, then the ones used in the annotation will be used.
+ *        // The configuration admin properties, if defined, will override the default configurations defined in the annotations
+ *     }
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface PropertyType {
+	// meta-annotation
+}

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceDependency.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceDependency.java?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceDependency.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceDependency.java Wed Sep 26 16:45:35 2018
@@ -46,6 +46,8 @@ import java.lang.annotation.Target;
  * (Service service, Dictionary properties)
  * (Dictionary properties, Service service)
  * (Object service)
+ * (ServiceReference<T> service)
+ * (ServiceObjects<T> service)
  * }</pre>
  * 
  * <p> For "swap" callbacks, the following method signatures are supported:
@@ -61,18 +63,54 @@ import java.lang.annotation.Target;
  * (Component comp, ServiceReference old, Object old, ServiceReference replace, Object replace)
  * (ServiceReference old, ServiceReference replace)
  * (Component comp, ServiceReference old, ServiceReference replace)
+ * (ServiceObjects old, ServiceObjects replace)
+ * (Component comp, ServiceObjects old, ServiceObjects replace)
  * }</pre>
  * 
+ * <p> When the dependency is injected on a class field, the following field types are supported:
+ * 
+ * <ul>
+ * <li> a field having the same type as the dependency. If the field may be accessed by anythread, then the field should be declared volatile, in order to ensure visibility 
+ *      when the field is auto injected concurrently.
+ * <li> a field which is assignable to an  {@literal Iterable<T>} where T must match the dependency type. In this case, an Iterable will be injected by DependencyManager before the start 
+ *      callback is called. The Iterable field may then be traversed to inspect the currently available dependency services. The Iterable can possibly be set to a final value 
+ *      so you can choose the Iterable implementation of your choice (for example, a CopyOnWrite ArrayList, or a ConcurrentLinkedQueue).
+ * <li> a {@literal Map<K,V>} where K must match the dependency type and V must exactly equals Dictionary class. In this case, a ConcurrentHashMap will be injected by DependencyManager 
+ *      before the start callback is called. The Map may then be consulted to lookup current available dependency services, including the dependency service properties 
+ *      (the map key holds the dependency services, and the map value holds the dependency service properties). The Map field may be set to a final value so you can choose a Map of your choice (Typically a ConcurrentHashMap). A ConcurrentHashMap is "weakly consistent", meaning that when traversing the elements, you may or may not see any concurrent updates made on the map. So, take care to traverse the map using an iterator on the map entry set, which allows to atomically lookup pairs of Dependency service/Service properties.
+ * </ul>
+ * 
  * <h3>Usage Examples</h3>
  * Here, the MyComponent component is injected with a dependency over a "MyDependency" service
  * 
  * <blockquote><pre>
  * &#64;Component
  * class MyComponent {
- *     &#64;ServiceDependency(timeout=15000)
- *     MyDependency dependency;
+ *     &#64;ServiceDependency
+ *     volatile MyDependency dependency;
+ * }
  * </pre></blockquote>
  * 
+ * Another example, were we inject multiple dependencies to an Iterable field
+ * 
+ * <blockquote><pre>
+ * &#64;Component
+ * class MyComponent {
+ *     &#64;ServiceDependency
+ *     final Iterable&lt;Dependency&gt; dependencies = new CopyOnWriteArrayList&lt;&gt;();
+ * }
+ * </pre></blockquote>
+ * 
+ * Another example, were we inject multiple dependencies to a Map field, allowing to inspect service dependency properties
+ * (the keys hold the services, and the values hold the associated service properties):
+ * 
+ * <blockquote><pre>
+ * &#64;Component
+ * class MyComponent {
+ *     &#64;ServiceDependency
+ *     final Map&lt;MyDependency, Dictionary&lt;String, Object&gt;&gt; dependencies = new ConcurrentHashMap&lt;&gt;;
+ * }
+ * </pre></blockquote>
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 @Retention(RetentionPolicy.CLASS)
@@ -107,7 +145,8 @@ public @interface ServiceDependency
     Class<?> defaultImpl() default Object.class;
 
     /**
-     * Whether the Service dependency is required or not.
+     * Whether the Service dependency is required or not. When the dependency is optional and if the annotation is declared on a class field, then
+     * a Null Object will be injected in case the dependency is unavailable.
      * @return the required flag
      */
     boolean required() default true;
@@ -194,18 +233,5 @@ public @interface ServiceDependency
      * Any additional service properties specified directly are merged with these.
      * @return true if dependency service properties must be published along with the service, false if not.
      */
-    boolean propagate() default false;
-    
-    /**
-     * Configures whether or not this dependency should internally obtain the service object for all tracked service references.
-     * 
-     * By default, DM internally dereferences all discovered service references (using 
-     * <code>BundleContext.getService(ServiceReference ref)</code> methods. 
-     * However, sometimes, your callback only needs the ServiceReference, and sometimes you don't want to dereference the service.
-     * So, in this case you can use the <code>dereference(false)</code> method in order to tell to DM 
-     * that it should never internally dereference the service dependency internally.
-     * 
-     * @return false if the service must never be dereferenced by dependency manager (internally).
-     */
-    boolean dereference() default true;
+    boolean propagate() default false;    
 }

Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceScope.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceScope.java?rev=1842034&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceScope.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ServiceScope.java Wed Sep 26 16:45:35 2018
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.annotation.api;
+
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * Service scope for the {@link Component}/{@link AdapterService}/{@link AspectService}/{@link BundleAdapterService} annotations.
+ */
+public enum ServiceScope {
+	/**
+	 * When the component is registered as a service, it must be registered as a
+	 * bundle scope service but only a single instance of the component must be
+	 * used for all bundles using the service.
+	 */
+	SINGLETON,
+
+	/**
+	 * When the component is registered as a service, it must be registered as a
+	 * bundle scope service and an instance of the component must be created for
+	 * each bundle using the service.
+	 */
+	BUNDLE,
+
+	/**
+	 * When the component is registered as a service, it must be registered as a
+	 * prototype scope service and an instance of the component must be created
+	 * for each distinct request for the service.
+	 */
+	PROTOTYPE
+}

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/packageinfo?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/packageinfo (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/packageinfo Wed Sep 26 16:45:35 2018
@@ -1 +1 @@
-version 1.2.0
\ No newline at end of file
+version 2.0.0

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java Wed Sep 26 16:45:35 2018
@@ -18,15 +18,27 @@
  */
 package org.apache.felix.dm.annotation.plugin.bnd;
 
+import static aQute.bnd.osgi.Clazz.QUERY.ANNOTATED;
+
 import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.util.AbstractMap;
+import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Dictionary;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.apache.felix.dm.annotation.api.AdapterService;
 import org.apache.felix.dm.annotation.api.AspectService;
@@ -36,26 +48,31 @@ import org.apache.felix.dm.annotation.ap
 import org.apache.felix.dm.annotation.api.Composition;
 import org.apache.felix.dm.annotation.api.ConfigurationDependency;
 import org.apache.felix.dm.annotation.api.Destroy;
-import org.apache.felix.dm.annotation.api.FactoryConfigurationAdapterService;
 import org.apache.felix.dm.annotation.api.Init;
 import org.apache.felix.dm.annotation.api.Inject;
 import org.apache.felix.dm.annotation.api.LifecycleController;
 import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.PropertyType;
 import org.apache.felix.dm.annotation.api.Registered;
 import org.apache.felix.dm.annotation.api.RepeatableProperty;
-import org.apache.felix.dm.annotation.api.ResourceAdapterService;
-import org.apache.felix.dm.annotation.api.ResourceDependency;
 import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.ServiceScope;
 import org.apache.felix.dm.annotation.api.Start;
 import org.apache.felix.dm.annotation.api.Stop;
 import org.apache.felix.dm.annotation.api.Unregistered;
 import org.osgi.framework.Bundle;
 
+import aQute.bnd.osgi.Analyzer;
 import aQute.bnd.osgi.Annotation;
 import aQute.bnd.osgi.ClassDataCollector;
 import aQute.bnd.osgi.Clazz;
+import aQute.bnd.osgi.Clazz.FieldDef;
+import aQute.bnd.osgi.Clazz.MethodDef;
+import aQute.bnd.osgi.Descriptors;
 import aQute.bnd.osgi.Descriptors.TypeRef;
+import aQute.bnd.osgi.Instruction;
 import aQute.bnd.osgi.Verifier;
+import aQute.lib.collections.MultiMap;
 
 /**
  * This is the scanner which does all the annotation parsing on a given class.
@@ -79,12 +96,9 @@ public class AnnotationCollector extends
     private final static String A_SERVICE_DEP = ServiceDependency.class.getName();
     private final static String A_CONFIGURATION_DEPENDENCY = ConfigurationDependency.class.getName();
     private final static String A_BUNDLE_DEPENDENCY = BundleDependency.class.getName();
-    private final static String A_RESOURCE_DEPENDENCY = ResourceDependency.class.getName();
     private final static String A_ASPECT_SERVICE = AspectService.class.getName();
     private final static String A_ADAPTER_SERVICE = AdapterService.class.getName();
     private final static String A_BUNDLE_ADAPTER_SERVICE = BundleAdapterService.class.getName();
-    private final static String A_RESOURCE_ADAPTER_SERVICE = ResourceAdapterService.class.getName();
-    private final static String A_FACTORYCONFIG_ADAPTER_SERVICE = FactoryConfigurationAdapterService.class.getName();
     private final static String A_INJECT = Inject.class.getName();
     private final static String A_REGISTERED = Registered.class.getName();
     private final static String A_UNREGISTERED = Unregistered.class.getName();
@@ -93,11 +107,12 @@ public class AnnotationCollector extends
     private String[] m_interfaces;
     private boolean m_isField;
     private String m_field;
+	private FieldDef m_fieldDef;    
     private String m_method;
+	private MethodDef m_methodDef;
     private String m_descriptor;
     private final Set<String> m_dependencyNames = new HashSet<String>();
     private final List<EntryWriter> m_writers = new ArrayList<EntryWriter>();
-    private MetaType m_metaType;
     private String m_startMethod;
     private String m_stopMethod;
     private String m_initMethod;
@@ -110,11 +125,13 @@ public class AnnotationCollector extends
     private String m_bundleContextField;
     private String m_dependencyManagerField;
     private String m_registrationField;
+    private String m_bundleField;
     private String m_componentField;
     private String m_registeredMethod;
     private String m_unregisteredMethod;
 	private TypeRef	m_superClass;
 	private boolean m_baseClass = true;
+	private final static String[] EMPTY_STRING_ARRAY = new String[0];
 	
 	/**
 	 * Name of the class annotated with @Component (or other kind of components, like aspect, adapters).
@@ -152,15 +169,69 @@ public class AnnotationCollector extends
      */
     private final List<EntryType> m_componentTypes = Arrays.asList(EntryType.Component, EntryType.AspectService, EntryType.AdapterService,
         EntryType.BundleAdapterService, EntryType.ResourceAdapterService, EntryType.FactoryConfigurationAdapterService);
+    
+    
+    /**
+     * Class Analyzer used to scan component property type annotation. 
+     */
+	private final Analyzer m_analyzer;
+	
+	/**
+	 * List of component property type annotation applied on component class.
+	 */
+	private final List<Annotation> m_componentPropertyTypes = new ArrayList<>();
+	
+    /**
+     * List of all parsed methods. Key = method name, value is list of signatures.
+     * Example: if a component has two methods update(Dictionary) and updated(MyConfig), then the map will contain the following:
+     * "updated" -> ["(Ljava/util/Dictionary;)V", "(Lfoo.bar.MyConfig;)V"]
+     */
+    final Map<String, List<Descriptors.Descriptor>> m_methods = new HashMap<>();
+    	
+	/**
+	 * Object used to see if an annotation is matching a declarative service ComponentPropertyType annotation.
+	 */
+	private static final Instruction DS_PROPERTY_TYPE = new Instruction("org.osgi.service.component.annotations.ComponentPropertyType");
+	
+	/**
+	 * Object used to see if an annotation is matching a DM PropertyType annotation.
+	 */
+	private static final Instruction DM_PROPERTY_TYPE = new Instruction(PropertyType.class.getName());
+
+	/**
+	 * Mapping between type strings -> type class
+	 */	
+	private final static Map<String,Class<?>>m_types = Collections.unmodifiableMap(Stream.of(
+            new SimpleEntry<>("boolean", Boolean.class),
+            new SimpleEntry<>("byte", Byte.class),
+            new SimpleEntry<>("short", Short.class),
+            new SimpleEntry<>("char", Character.class),
+            new SimpleEntry<>("int", Integer.class),
+            new SimpleEntry<>("long", Long.class),
+            new SimpleEntry<>("float", Float.class),
+            new SimpleEntry<>("double", Double.class))
+			.collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue)));
 
+	/**
+	 * Marker used when parsing component property types annotations.
+	 * (To make sure the output is an array, we must make sure there is more than one entry)
+	 */
+	private final static String	MARKER = new String("|marker");
+	
+	/**
+	 * Pattern used when parsing component property type annotations.
+	 */
+	private static final Pattern IDENTIFIERTOPROPERTY = Pattern.compile("(__)|(_)|(\\$_\\$)|(\\$\\$)|(\\$)");
+	
     /**
      * Makes a new Collector for parsing a given class.
      * @param reporter the object used to report logs.
+     * @param m_analyzer 
      */
-    public AnnotationCollector(Logger reporter, MetaType metaType)
+    public AnnotationCollector(Logger reporter, Analyzer analyzer)
     {
         m_logger = reporter;
-        m_metaType = metaType;
+        m_analyzer = analyzer;
     }
     
     /**
@@ -215,10 +286,12 @@ public class AnnotationCollector extends
     @Override
     public void method(Clazz.MethodDef method)
     {
-        m_logger.debug("Parsed method %s, descriptor=%s", method.getName(), method.getDescriptor());
+        m_logger.debug("Parsed method %s descriptor=%s signature=%s", method.getName(), method.getDescriptor(), method.getSignature());
         m_isField = false;
         m_method = method.getName();
+        m_methodDef = method;
         m_descriptor = method.getDescriptor().toString();
+        m_methods.computeIfAbsent(method.getName(), k -> new ArrayList<Descriptors.Descriptor>()).add(method.getDescriptor());
     }
 
     /**
@@ -227,9 +300,10 @@ public class AnnotationCollector extends
     @Override
     public void field(Clazz.FieldDef field)
     {
-        m_logger.debug("Parsed field %s, descriptor=%s", field.getName(), field.getDescriptor());
+        m_logger.debug("Parsed field %s descriptor=%s", field.getName(), field.getDescriptor());
         m_isField = true;
         m_field = field.getName();
+        m_fieldDef = field;
         m_descriptor = field.getDescriptor().toString();
     }
 
@@ -277,14 +351,6 @@ public class AnnotationCollector extends
         {
             parseBundleAdapterService(annotation);
         }
-        else if (name.equals(A_RESOURCE_ADAPTER_SERVICE))
-        {
-            parseResourceAdapterService(annotation);
-        }
-        else if (name.equals(A_FACTORYCONFIG_ADAPTER_SERVICE))
-        {
-            parseFactoryConfigurationAdapterService(annotation);
-        }
         else if (name.equals(A_INIT))
         {
             checkAlreadyDeclaredSingleAnnot(() -> m_initMethod, "@Init");
@@ -337,10 +403,6 @@ public class AnnotationCollector extends
         {
             parseBundleDependencyAnnotation(annotation);
         }
-        else if (name.equals(A_RESOURCE_DEPENDENCY))
-        {
-            parseRersourceDependencyAnnotation(annotation);
-        } 
         else if (name.equals(A_INJECT))
         {
             parseInject(annotation);
@@ -352,10 +414,13 @@ public class AnnotationCollector extends
         else if (annotation.getName().getFQN().equals(A_PROPERTY))
         {
         	m_singleProperty = annotation;
-        }       
+        } 
+        else {
+			handlePossibleComponentPropertyAnnotation(annotation);
+        }
     }
 
-    /**
+	/**
      * Finishes up the class parsing. This method must be called once the parseClassFileWithCollector method has returned.
      * @return true if some annotations have been parsed, false if not.
      */
@@ -402,14 +467,119 @@ public class AnnotationCollector extends
             }
         }
         
-        // Handle a single Property declared on the component type (in this case, there is no @Repeatable annotation).
+        // handle component property types
+        parseComponentPropertyAnnotation(componentWriter);
         
+        // Handle a single Property declared on the component type (in this case, there is no @Repeatable annotation).        
         if (m_singleProperty != null) {
             parseProperty(m_singleProperty, componentWriter);
         }
+        
+        if (componentWriter.getEntryType() == EntryType.FactoryConfigurationAdapterService) {
+        	m_logger.debug("finishFactoryComponent ...");
+        	finishFactoryComponent(componentWriter);        	
+        }
     }
 
     /**
+     * Finish to parse a Factory Pid Component annotation.
+     * @param componentWriter the datastructure which holds the parsed annotation attributes.
+     */
+    private void finishFactoryComponent(EntryWriter componentWriter) {
+        
+    	if (componentWriter.getParameter(EntryParam.configType) == null) {
+    		// No configtype defined in the annotation, then try to detect if the method signature contains some config types.
+    		// First, see if there is an updated callback without a config type:    		
+    		String method = componentWriter.getParameter(EntryParam.updated).toString(); // can't be null
+    		List<Descriptors.Descriptor> methodSigs = m_methods.get(method);
+    		if (methodSigs == null) {
+    			throw new IllegalStateException("can't find callback " + method + " on class" +  m_componentClassName);
+    		}
+    		boolean parseConfigTypesFromArgs = true; 
+    		for (Descriptors.Descriptor desc : methodSigs) {
+    			Matcher m = Patterns.UPDATED_NO_CONFIG_TYPES.matcher(desc.toString());
+    			if (m.matches()) {
+    				// Found an updated callback which does not contain any config type
+    				m_logger.debug("updated callback %s does not contain any config type in accepted arguments", method); 
+    				parseConfigTypesFromArgs = false;
+    				break;
+    			}
+    		}
+    		// Now, try to find an updated callback having some config types accepted as arguments
+    		if (parseConfigTypesFromArgs) {
+				for (Descriptors.Descriptor desc : methodSigs) {
+					Matcher m = Patterns.UPDATED_CONFIG_TYPES.matcher(desc.toString());
+					String[] configTypes = parsePossibleConfigTypesFromUpdatedCallback(componentWriter,
+							desc.toString());
+					if (configTypes.length > 0) {
+						// If factory pid not specified, derive the factory pid from the config type
+						// (and if only one config type is specified)
+						if (componentWriter.getParameter(EntryParam.factoryPid) == null) {
+							if (configTypes.length == 1) {
+								componentWriter.put(EntryParam.factoryPid, configTypes[0]);
+							}
+						}
+						break;
+					}
+				}
+    		}
+    	}	
+    	
+    	// set factory pid to component class name if it has not been derived from any annotation attributes or from updated callback parameters.
+		if (componentWriter.getParameter(EntryParam.factoryPid) == null) {
+			componentWriter.put(EntryParam.factoryPid, m_componentClassName);
+		}
+	}
+    
+    /**
+     * Auto Detect if an updated callback method contains some configuration types.
+     * @param writer the writer where the detected configration types are written in case some are detected
+     * @param callbackDescriptor the descriptor of the udpated callback method.
+     * @param propagateConfigTypeDefVal true if any found config type default values should be parsed and propagated to service properties
+     * @return an empty string array in case no config types has been detected, or the array of parsed config types.
+     */
+    private String[] parsePossibleConfigTypesFromUpdatedCallback(EntryWriter writer, String callbackDescriptor) {
+		Matcher m = Patterns.UPDATED_CONFIG_TYPES.matcher(callbackDescriptor);
+		List<String> types = new ArrayList<>();
+		while (m.find()) {
+			String type = m.group(5);
+			if (type != null) {
+				m_logger.debug("checking if type %s is an interface or an annotation", type);
+				TypeRef typeRef = m_analyzer.getTypeRef(type);
+				try {
+					Clazz clazz = m_analyzer.findClass(typeRef);
+					if (! clazz.isAnnotation() && ! clazz.isInterface()) {
+						m_logger.debug("ignoring updated callback signature %s (argument type not an interface or an annotation", callbackDescriptor);
+						continue;
+					}
+					types.add(type.replace("/", "."));
+		                
+					/*
+					if (propagateConfigTypeDefVal) {
+					    // set component service properties with config type default values
+					    if (clazz.isAnnotation()) {
+					        clazz.parseClassFileWithCollector (new ComponentPropertyTypeDataCollector(writer));
+					    }
+					}
+					*/
+				} catch (Exception e) {
+					throw new IllegalStateException("could not find config type class " + type);
+				}
+			}
+		}
+		if (types.size() > 0) {
+			m_logger.debug("detected config types for updated callback %s", types);
+			if (types.size() == 1) {
+				writer.put(EntryParam.configType, types.get(0));
+			} else {
+				writer.put(EntryParam.configTypes, types);
+			}
+			return types.stream().toArray(String[]::new);
+		}
+		return EMPTY_STRING_ARRAY;
+    }
+
+	/**
      * Writes the generated component descriptor in the given print writer.
      * The first line must be the component descriptor (@Component or AspectService, etc ..).
      * @param pw the writer where the component descriptor will be written.
@@ -472,6 +642,12 @@ public class AnnotationCollector extends
     
     private void parseComponentAnnotation(Annotation annotation)
     {
+    	String factoryPid = annotation.get(EntryParam.factoryPid.toString());
+    	if (factoryPid != null) {
+    		parseFactoryComponent(annotation);
+    		return;
+    	}
+    	
         EntryWriter writer = new EntryWriter(EntryType.Component);
         m_writers.add(writer);
 
@@ -488,38 +664,12 @@ public class AnnotationCollector extends
             // and raise an error if true.
             checkRegisteredUnregisteredNotPresent();
         }
-
-        // factorySet attribute (deprecated, replaced by factoryName)
-        String factorySetName = writer.putString(annotation, EntryParam.factorySet, null);
-        if (factorySetName != null)
-        {
-            // When a component defines a factorySet, it means that a java.util.Set will 
-            // be provided into the OSGi registry, in order to let anoter component add
-            // some component instance configurations into it.
-            // So, we have to indicate that the Set is provided as a service, in the Export-Serviec
-            // header.
-            m_exportService.add("java.util.Set");
-        }
-
-        // factoryName attribute
-        String factoryName = writer.putString(annotation, EntryParam.factoryName, null);
-        if (factoryName != null)
-        {
-            // When a component defines a factoryName, it means that a ComponentFactory will 
-            // be provided into the OSGi registry, in order to let another component create some component instances.
-            // So, we have to indicate that the ComponentFactory is provided as a service, in the Export-Serviec
-            // header.
-            m_exportService.add("org.apache.felix.dependencymanager.runtime.api.ComponentFactory");
-        }
-
-        // factoryConfigure attribute
-        writer.putString(annotation, EntryParam.factoryConfigure, null);
         
         // factoryMethod attribute
         writer.putString(annotation, EntryParam.factoryMethod, null);
     }
 
-    private void addCommonServiceParams(EntryWriter writer)
+	private void addCommonServiceParams(EntryWriter writer)
     {
         if (m_initMethod != null)
         {
@@ -590,6 +740,15 @@ public class AnnotationCollector extends
         {
             writer.put(EntryParam.registrationField, m_registrationField);
         }
+        
+        if (m_bundleField != null)
+        {
+        	Object scope = writer.getParameter(EntryParam.scope);
+        	if (scope == null || scope == ServiceScope.SINGLETON.name()) {
+        		throw new IllegalStateException("can't inject a bundle field on a component without prototype or bundle scope");
+        	}        	
+            writer.put(EntryParam.bundle, m_bundleField);
+        }
     }
 
     /**
@@ -610,6 +769,8 @@ public class AnnotationCollector extends
     private void parseServiceDependencyAnnotation(Annotation annotation)
     {
         EntryWriter writer = new EntryWriter(EntryType.ServiceDependency);
+        boolean doDereference = true; // false means dependency manager must not internally dereference the service dependency
+        
         m_writers.add(writer);
 
         // service attribute
@@ -619,21 +780,34 @@ public class AnnotationCollector extends
             if (m_isField)
             {
                 checkDependencyAlreadyDeclaredInChild(annotation, m_field, false);
-                service = Patterns.parseClass(m_descriptor, Patterns.CLASS, 1);
+                //service = Patterns.parseClass(m_descriptor, Patterns.CLASS, 1);
+                m_logger.debug("parsing field service type %s", m_field);
+                service = FieldTypeGetter.determineFieldType(m_logger, m_fieldDef);
+                m_logger.debug("field service type=%s", service);
             }
             else
             {
                 // if we are parsing some inherited classes, detect if the bind method is already declared in child classes
-                checkDependencyAlreadyDeclaredInChild(annotation, m_method, true);                
+                checkDependencyAlreadyDeclaredInChild(annotation, m_method, true);   
+                
             	// parse "bind(Component, ServiceReference, Service)" signature
             	service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS1, 3, false);            		
             	
             	if (service == null) {
                 	// parse "bind(Component, Service)" signature
-                	service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS2, 2, false);            		
+                	service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS2, 2, false);    		
             	}
             	
             	if (service == null) {
+            		// parse "bind(ServiceReference<T>)" or "bind(ServiceObjects<T>)" signatures
+                	service = Patterns.inferTypeFromGenericType(m_methodDef.getDescriptor().toString(), 
+                												m_methodDef.getSignature(),
+                												m_logger);
+                	// dm must not internally dereference the service, since it is injected as a ServiceRef or a ServiceObjects
+                	doDereference = false;
+            	}
+
+            	if (service == null) {
             		// parse "bind(Component, Map, Service)" signature
                 	service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS3, 3, false);            		
             	}
@@ -666,7 +840,7 @@ public class AnnotationCollector extends
             	if (service == null) {
             		// parse "bind(Dictionary, Service)" signature
                 	service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS9, 2, true);            		
-            	}
+            	}            	
             }
         }
         
@@ -732,7 +906,7 @@ public class AnnotationCollector extends
         writer.putString(annotation, EntryParam.propagate, null);
         
         // dereference flag
-        writer.putString(annotation, EntryParam.dereference, null);
+        writer.putString(annotation, EntryParam.dereference, String.valueOf(doDereference));
     }
         
     /**
@@ -765,78 +939,51 @@ public class AnnotationCollector extends
      * Parses a ConfigurationDependency annotation.
      * @param annotation the ConfigurationDependency annotation.
      */
-    private void parseConfigurationDependencyAnnotation(Annotation annotation)
-    {
+    private void parseConfigurationDependencyAnnotation(Annotation annotation) {
         checkDependencyAlreadyDeclaredInChild(annotation, m_method, true);
 
         EntryWriter writer = new EntryWriter(EntryType.ConfigurationDependency);
         m_writers.add(writer);
+        
+        // propagate attribute
+        writer.putString(annotation, EntryParam.propagate, null);
+        
+        // required flag (true by default)
+        writer.putString(annotation, EntryParam.required, Boolean.TRUE.toString());
+        
+        // Parse possible config types specified in the updated callback.
+    	// First, check if the update callback does not contain any config type parameters
+        String[] configTypes = EMPTY_STRING_ARRAY;
+    	Matcher m = Patterns.UPDATED_NO_CONFIG_TYPES.matcher(m_descriptor.toString());
+    	if (! m.matches()) {
+    		// the updated callback may contain some configuration types.
+    		configTypes = parsePossibleConfigTypesFromUpdatedCallback(writer, m_descriptor);
+    	}
 
-        // The pid is either:
+        // Calculate the the pid, which value is either:
         //
-        // - the fqdn of the configuration proxy type, if the callback accepts an interface (not a Dictionary).
-        // - or the fqdn of the class specified by the pidFromClass attribute 
-        // - or the value of the pid attribute
+        // - the value of the pid attribute, if specified        
+        // - or the fqdn of the class specified by the pidFromClass attribute, if specified 
+        // - or the fqdn of one configuration proxy type found from the updated callback method (if multiple config types are specified in the callback,
+        //   then we can't derive the pid)
         // - or by default the fdqn of the class where the annotation is found
-
-        String pidFromClass = parseClassAttrValue(annotation.get(EntryParam.pidClass.toString()));
-        String pid = pidFromClass != null ? pidFromClass : get(annotation, EntryParam.pid.toString(), null);
-
-        // Check if annotation is applied on "updated(ConfigProxyType)"
-        String confProxyType =   Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS5, 1, false);
-        if (confProxyType != null)
-        {
-            if (! Dictionary.class.getName().equals(confProxyType)) 
-            {
-                // It's a conf proxy type.
-                writer.put(EntryParam.configType, confProxyType);
-            }
-            else
-            {
-                confProxyType = null;
-            }
-            
-        } 
-        else
-        {
-            // Check if annotation is applied on "updated(Component, ConfigProxyType)"
-            confProxyType = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS2, 2, false); 
-            if (! Dictionary.class.getName().equals(confProxyType)) 
-            {
-                // It's a conf proxy type.
-                writer.put(EntryParam.configType, confProxyType);
-            }
-            else
-            {
-                confProxyType = null;
-            }
-        }
-        
-        if (pid == null) 
-        {
-            if (confProxyType != null)
-            {
-                pid = confProxyType;
-            }
-            else 
-            {
-                pid = m_componentClassName;
-            }
-        }
+        String pid = get(annotation, EntryParam.pid.toString(), null);
+        if (pid == null) {
+        	pid = parseClassAttrValue(annotation.get(EntryParam.pidClass.toString()));
+        }
+        if (pid == null) {
+        	if (configTypes.length == 1) {
+        		pid = configTypes[0];
+        	}
+        }
+		if (pid == null) {
+			pid = m_componentClassName;
+		}
 
         writer.put(EntryParam.pid, pid);
         
         // the method on which the annotation is applied
         writer.put(EntryParam.updated, m_method);
-
-        // propagate attribute
-        writer.putString(annotation, EntryParam.propagate, null);
-        
-        // required flag (true by default)
-        writer.putString(annotation, EntryParam.required, Boolean.TRUE.toString());
-
-        // Property Meta Types
-        parseMetaTypes(annotation, pid, false);
         
         // name attribute
         parseDependencyName(writer, annotation);
@@ -1009,71 +1156,25 @@ public class AnnotationCollector extends
     }
 
     /**
-     * Parses a BundleAdapterService annotation.
-     * @param annotation
-     */
-    private void parseResourceAdapterService(Annotation annotation)
-    {
-        EntryWriter writer = new EntryWriter(EntryType.ResourceAdapterService);
-        m_writers.add(writer);
-
-        // Generate Adapter Implementation
-        writer.put(EntryParam.impl, m_componentClassName);
-
-        // Parse resource filter
-        String filter = annotation.get(EntryParam.filter.toString());
-        if (filter != null)
-        {
-            Verifier.verifyFilter(filter, 0);
-            writer.put(EntryParam.filter, filter);
-        }
-
-        // Parse Adapter properties, and other params common to all kind of component
-        parseCommonComponentAttributes(annotation, writer);
-
-        // Parse the provided adapter service (use directly implemented interface by default).
-        if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
-        {
-            checkRegisteredUnregisteredNotPresent();
-        }
-
-        // Parse propagate attribute
-        writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
-        
-        // Parse changed attribute
-        writer.putString(annotation, EntryParam.changed, null);
-    }
-
-    /**
-     * Parses a Factory Configuration Adapter annotation.
+     * Parses a Factory Pid Component 
      * @param annotation
      */
-    private void parseFactoryConfigurationAdapterService(Annotation annotation)
+    private void parseFactoryComponent(Annotation annotation)
     {
         EntryWriter writer = new EntryWriter(EntryType.FactoryConfigurationAdapterService);
         m_writers.add(writer);
 
         // Generate Adapter Implementation
         writer.put(EntryParam.impl, m_componentClassName);
+                       
+        String factoryPid = get(annotation, EntryParam.factoryPid.toString(), null);
 
-        // factory pid attribute (can be specified using the factoryPid attribute, or using the factoryPidClass attribute)
-        String factoryPidClass = parseClassAttrValue(annotation.get(EntryParam.factoryPidClass.toString()));
-        
-        // Test if a type safe configuration type is provided.
-        String configType = parseClassAttrValue(annotation.get(EntryParam.configType.toString()));
-        
-        if (configType != null) {
-            writer.put(EntryParam.configType, configType);
-        }
-        
-        String factoryPid = null;
-        
-        factoryPid = get(annotation, EntryParam.factoryPid.toString(), factoryPidClass);
-        if (factoryPid == null) {
-            factoryPid = configType != null ? configType : m_componentClassName;
+        if (factoryPid != null) {
+        	writer.put(EntryParam.factoryPid, factoryPid);
+        } else {
+        	// The finishFactoryComponent will set later the factoryPid (either to a config type specified in the updated callback, 
+        	// or to the component class name). See  finishFactoryComponent() method.
         }
-        
-        writer.put(EntryParam.factoryPid, factoryPid);
 
         // Parse updated callback
         writer.putString(annotation, EntryParam.updated, "updated");
@@ -1089,12 +1190,10 @@ public class AnnotationCollector extends
 
         // Parse Adapter properties, and other params common to all kind of component
         parseCommonComponentAttributes(annotation, writer);
-
-        // Parse optional meta types for configuration description.
-        parseMetaTypes(annotation, factoryPid, true);
         
         // Parse factoryMethod attribute
         writer.putString(annotation, EntryParam.factoryMethod, null);
+        m_logger.debug("Parsed factory configuration adapter annotations, methods=%s", m_methods);
     }
 
     private void parseBundleDependencyAnnotation(Annotation annotation)
@@ -1120,34 +1219,6 @@ public class AnnotationCollector extends
         parseDependencyName(writer, annotation);
     }
 
-    private void parseRersourceDependencyAnnotation(Annotation annotation)
-    {
-        checkDependencyAlreadyDeclaredInChild(annotation, ! m_isField ? m_method : m_field, ! m_isField);
-
-        EntryWriter writer = new EntryWriter(EntryType.ResourceDependency);
-        m_writers.add(writer);
-
-        String filter = annotation.get(EntryParam.filter.toString());
-        if (filter != null)
-        {
-            Verifier.verifyFilter(filter, 0);
-            writer.put(EntryParam.filter, filter);
-        }
-        
-        if (m_isField)
-        {
-            writer.put(EntryParam.autoConfig, m_field);
-        }
-
-        writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
-        writer.putString(annotation, EntryParam.changed, null);
-        writer.putString(annotation, EntryParam.removed, null);
-        writer.putString(annotation, EntryParam.required, null);
-        writer.putString(annotation, EntryParam.propagate, null);
-        writer.putString(annotation, EntryParam.factoryMethod, null);
-        parseDependencyName(writer, annotation);
-    }
-
     /**
      * Parse the name of a given dependency.
      * @param writer The writer where to write the dependency name
@@ -1186,66 +1257,6 @@ public class AnnotationCollector extends
     }
 
     /**
-     * Parse optional meta types annotation attributes
-     * @param annotation
-     */
-    private void parseMetaTypes(Annotation annotation, String pid, boolean factory)
-    {
-        if (annotation.get("metadata") != null)
-        {
-            String propertiesHeading = annotation.get("heading");
-            String propertiesDesc = annotation.get("description");
-
-            MetaType.OCD ocd = new MetaType.OCD(pid, propertiesHeading, propertiesDesc);
-            for (Object p: (Object[]) annotation.get("metadata"))
-            {
-                Annotation property = (Annotation) p;
-                String heading = property.get("heading");
-                String id = property.get("id");
-                String type = parseClassAttrValue(property.get("type"));
-                Object[] defaults = (Object[]) property.get("defaults");
-                String description = property.get("description");
-                Integer cardinality = property.get("cardinality");
-                Boolean required = property.get("required");
-
-                MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description,
-                    cardinality, required);
-
-                Object[] 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))
-                {
-                    throw new IllegalArgumentException("invalid option labels/values specified for property "
-                        + id +
-                        " in PropertyMetadata annotation from class " + m_currentClassName);
-                }
-
-                if (optionValues != null)
-                {
-                    for (int i = 0; i < optionValues.length; i++)
-                    {
-                        ad.add(new MetaType.Option(optionValues[i].toString(), optionLabels[i].toString()));
-                    }
-                }
-
-                ocd.add(ad);
-            }
-
-            m_metaType.add(ocd);
-            MetaType.Designate designate = new MetaType.Designate(pid, factory);
-            m_metaType.add(designate);
-            m_logger.info("Parsed MetaType Properties from class " + m_componentClassName);
-        }
-    }
-
-    /**
      * Parses attributes common to all kind of components.
      * First, Property annotation is parsed which represents a list of key-value pair.
      * The properties are encoded using the following json form:
@@ -1282,6 +1293,13 @@ public class AnnotationCollector extends
                 parseProperty(propertyAnnotation, writer);
             }             
         }
+                
+        // scope attribute
+        String scope = component.get("scope");
+        if (scope == null) {
+        	scope = ServiceScope.SINGLETON.name();
+        }
+        writer.put(EntryParam.scope, scope);
     }
     
     /**
@@ -1498,6 +1516,13 @@ public class AnnotationCollector extends
             }
             m_registrationField = m_field;
         }
+        else if (Patterns.BUNDLE.matcher(m_descriptor).matches())
+        {
+            if (m_bundleField != null) {
+                throw new IllegalStateException("detected multiple @Inject annotation from class " + m_currentClassName + " (on from child classes)");
+            }
+            m_bundleField = m_field;
+        }
         else
         {
             throw new IllegalArgumentException("@Inject annotation can't be applied on the field \"" + m_field
@@ -1540,4 +1565,316 @@ public class AnnotationCollector extends
         T value = (T) annotation.get(name);
         return value != null ? value : defaultValue;
     }
+    
+    /**
+     * Check if an annotation applied on the component class is a component property type.
+     * (the following is adapted from the original bnd DS AnnotationReader class)
+     */
+    private void handlePossibleComponentPropertyAnnotation(Annotation annotation) {
+    	m_logger.debug("Checking possible component property annotations %s", annotation);
+    	
+		try {
+			Clazz clazz = m_analyzer.findClass(annotation.getName());
+
+			if (clazz == null) {
+				m_logger.warn(
+						"Unable to determine whether the annotation %s applied to type %s is a component property type as it is not on the project build path. If this annotation is a component property type then it must be present on the build path in order to be processed",
+						annotation.getName().getFQN(), m_currentClassName);
+				return;
+			}
+
+			if (clazz.is(ANNOTATED, DS_PROPERTY_TYPE, m_analyzer) || clazz.is(ANNOTATED, DM_PROPERTY_TYPE, m_analyzer)) {
+				m_logger.debug("Found component property type %s", annotation);
+				m_componentPropertyTypes.add(annotation);
+			} else {
+	            m_logger.debug("The annotation %s on component type %s will not be used for properties as the annotation is not annotated with @ComponentPropertyType",
+						clazz.getFQN(), m_currentClassName);
+				return;
+			}
+		} catch (Exception e) {
+			m_logger.error("An error occurred when attempting to process annotation %s, applied to component %s", e, 
+					annotation.getName().getFQN(), m_currentClassName);
+		}		
+	}       
+
+    /**
+     * Parse a component property type annotation applied on the component class.
+     * (the following is adapted from the original bnd DS AnnotationReader class)
+     */
+    private void parseComponentPropertyAnnotation(EntryWriter componentWriter) {
+		for (Annotation annotation : m_componentPropertyTypes) {
+			try {
+				Clazz clazz = m_analyzer.findClass(annotation.getName());
+				if (clazz == null) {
+					m_logger.warn(
+							"Unable to determine whether the annotation %s applied to type %s is a component property type as it is not on the project build path. If this annotation is a component property type then it must be present on the build path in order to be processed",
+							annotation.getName().getFQN(), m_currentClassName);
+					continue;
+				}
+
+				m_logger.debug("Parsing component property type %s", annotation);
+				clazz.parseClassFileWithCollector(new ComponentPropertyTypeDataCollector(annotation, componentWriter));
+			} catch (Exception e) {
+				m_logger.error("An error occurred when attempting to process annotation %s, applied to component %s", e, 
+						annotation.getName().getFQN(), m_currentClassName);
+			}
+		}
+	}       
+    
+	private final class ComponentPropertyTypeDataCollector extends ClassDataCollector {
+		private final MultiMap<String,String>				props			= new MultiMap<String,String>();
+		private final Map<String,Class>					    propertyTypes	= new HashMap<>();
+		private int											hasNoDefault	= 0;
+		private boolean										hasValue		= false;
+		private boolean										hasMethods 		= false;
+		private FieldDef									prefixField		= null;
+		private TypeRef										typeRef			= null;
+		private final EntryWriter 							m_componentWriter;
+	
+		private ComponentPropertyTypeDataCollector(EntryWriter componentWriter) {
+		    m_componentWriter = componentWriter;
+		}
+
+		private ComponentPropertyTypeDataCollector(Annotation componentPropertyAnnotation, EntryWriter componentWriter) {
+			m_componentWriter = componentWriter;
+			// Add in the defined attributes
+			for (String key : componentPropertyAnnotation.keySet()) {
+				Object value = componentPropertyAnnotation.get(key);
+				m_logger.debug("ComponentPropertyTypeDataCollector: handle value %s %s", key, value);
+				handleValue(key, value, value instanceof TypeRef, null);
+			}
+		}
+	
+		@Override
+		public void classBegin(int access, TypeRef name) {
+			m_logger.debug("PropertyType: class begin %s", name);
+			typeRef = name;
+		}
+
+		@Override
+		public void field(FieldDef defined) {
+			m_logger.debug("PropertyType: field %s", defined);
+			if (defined.isStatic() && defined.getName().equals("PREFIX_")) {
+				prefixField = defined;
+			}
+		}
+	
+		@Override
+		public void method(MethodDef defined) {
+			m_logger.debug("PropertyType: method %s", defined);
+
+			if (defined.isStatic()) {
+				return;
+			}
+			hasMethods = true;
+			if (defined.getName().equals("value")) {
+				hasValue = true;
+			} else {
+				hasNoDefault++;
+			}
+		}
+	
+		@Override
+		public void annotationDefault(MethodDef defined, Object value) {
+			m_logger.debug("PropertyType: annotationDefault method %s value %s", defined, value);
+
+			if (!defined.getName().equals("value")) {
+				hasNoDefault--;
+			}
+			// check type, exit with warning if annotation
+			// or annotation array
+			boolean isClass = false;
+			Class< ? > typeClass = null;
+			TypeRef type = defined.getType().getClassRef();
+			if (!type.isPrimitive()) {
+				if (type == m_analyzer.getTypeRef("java/lang/Class")) {
+					isClass = true;
+				} else {
+					try {
+						Clazz r = m_analyzer.findClass(type);
+						if (r.isAnnotation()) {
+							m_logger.warn("Nested annotation type found in member %s, %s",
+									defined.getName(), type.getFQN());
+							return;
+						}
+					} catch (Exception e) {
+						m_logger.error("Exception looking at annotation type to lifecycle method with type %s", e, type);
+					}
+				}
+			} else {
+				m_logger.debug("PropertyType: type.getFQN()=%s", type.getFQN());
+				typeClass = m_types.get(type.getFQN());
+			}
+			if (value != null) {
+				String name = defined.getName();
+				// only add the default value if the user has not specified the property name
+				if (! props.containsKey(name)) {
+					handleValue(name, value, isClass, typeClass);
+				}
+			}
+		}
+	
+		private void handleValue(String name, Object value, boolean isClass, Class< ? > typeClass) {
+			if (value.getClass().isArray()) {
+				// add element individually
+				int len = Array.getLength(value);
+				for (int i = 0; i < len; i++) {
+					Object element = Array.get(value, i);
+					valueToProperty(name, element, isClass, typeClass);
+				}
+//				if (len == 1) {
+//					// To make sure the output is an array, we must make
+//					// sure there is more than one entry
+//					props.add(name, MARKER);
+//				}
+			} else {
+				valueToProperty(name, value, isClass, typeClass);
+			}
+		}
+		
+		@Override
+		public void classEnd() throws Exception {
+			m_logger.debug("PropertyType: classEnd");
+
+			String prefix = null;
+			if (prefixField != null) {
+				Object c = prefixField.getConstant();
+				if (prefixField.isFinal() && (prefixField.getType() == m_analyzer.getTypeRef("java/lang/String"))
+						&& (c instanceof String)) {
+					prefix = (String) c;
+				} else {
+					m_logger.warn(
+							"Field PREFIX_ in %s is not a static final String field with a compile-time constant value: %s",
+							typeRef.getFQN(), c);
+				}
+				m_logger.debug("PropertyType: classEnd prefix = %s", prefix);
+			}
+			
+			if (!hasMethods) {
+				// This is a marker annotation so treat it like it is a single
+				// element annotation with a value of Boolean.TRUE
+				hasValue = true;
+				handleValue("value", Boolean.TRUE, false, Boolean.class);
+			}
+			
+			String singleElementAnnotation = null;
+			if (hasValue && (hasNoDefault == 0)) {
+				m_logger.debug("PropertyType: classEnd hasValue && hasNoDefault == 0");
+
+				StringBuilder sb = new StringBuilder(typeRef.getShorterName());
+				boolean lastLowerCase = false;
+				for (int i = 0; i < sb.length(); i++) {
+					char c = sb.charAt(i);
+					if (Character.isUpperCase(c)) {
+						sb.setCharAt(i, Character.toLowerCase(c));
+						if (lastLowerCase) {
+							sb.insert(i++, '.');
+						}
+						lastLowerCase = false;
+					} else {
+						lastLowerCase = Character.isLowerCase(c);
+					}
+				}
+				singleElementAnnotation = sb.toString();
+				m_logger.debug("PropertyType: classEnd singleElementAnnotation=%s", singleElementAnnotation);
+			}
+			m_logger.debug("PropertyType: classEnd props=" + props);
+			for (Entry<String,List<String>> entry : props.entrySet()) {
+				String key = entry.getKey();
+				List<String> value = entry.getValue();
+				Class<?> type = propertyTypes.get(key);
+				if ((singleElementAnnotation != null) && key.equals("value")) {
+					key = singleElementAnnotation;
+				} else {
+					key = identifierToPropertyName(key);
+				}
+				if (prefix != null) {
+					key = prefix + key;
+				}	
+				
+				m_componentWriter.addProperty(key, value.toArray(), type);				
+				m_logger.info("PropertyType: parsed property key:%s, value:%s, type:%s", key, value, type);
+			}
+		}
+
+		private void valueToProperty(String name, Object value, boolean isClass, Class<?> typeClass) {
+			if (isClass)
+				value = ((TypeRef) value).getFQN();
+			if (typeClass == null)
+				typeClass = value.getClass();
+			// enums already come out as the enum name, no
+			// processing needed.
+			//String type = typeClass.getSimpleName();
+			String type = typeClass.getName();
+			propertyTypes.put(name, typeClass);
+			props.add(name, value.toString());
+		}
+	
+		private String identifierToPropertyName(String name) {
+		    name = derivePropertyNameUsingJavaBeanConvention(name);
+		    name = derivePropertyNameUsingCamelCaseConvention(name);
+		    name = derivePropertyNameUsingMetatypeConvention(name);
+			return name;
+		}
+		
+		// getFoo -> foo; isFoo -> foo
+        private String derivePropertyNameUsingJavaBeanConvention(String methodName) {
+            StringBuilder sb = new StringBuilder(methodName);
+            
+            if (methodName.startsWith("get")) {
+                sb.delete(0, 3);
+            } else if (methodName.startsWith("is")) {
+                sb.delete(0, 2);
+            }
+            
+            char c = sb.charAt(0);
+            if (Character.isUpperCase(c)) {
+                sb.setCharAt(0, Character.toLowerCase(c));
+            }
+            
+            return (sb.toString());
+        }
+        
+        // fooBarZoo -> foo.bar.zoo
+        private String derivePropertyNameUsingCamelCaseConvention(String methodName) {
+            StringBuilder sb = new StringBuilder(methodName);
+            for (int i = 0; i < sb.length(); i++) {
+                char c = sb.charAt(i);
+                if (Character.isUpperCase(c)) {
+                    // camel casing: replace fooBar -> foo.bar
+                    sb.setCharAt(i, Character.toLowerCase(c));
+                    sb.insert(i, ".");
+                }
+            }
+            return sb.toString();            
+        }
+        
+        // foo__bar -> foo_bar; foo_bar -> foo.bar
+        private String derivePropertyNameUsingMetatypeConvention(String name) {
+            Matcher m = IDENTIFIERTOPROPERTY.matcher(name);
+            StringBuffer b = new StringBuffer();
+            while (m.find()) {
+                String replace;
+                if (m.group(1) != null) // __ to _
+                    replace = "_";
+                else if (m.group(2) != null) // _ to .
+                    replace = ".";
+                else if (m.group(3) != null) { // $_$ to -
+                    replace = "-";
+                }
+                else if (m.group(4) != null) // $$ to $
+                    replace = "\\$";
+                else // $ removed.
+                    replace = "";
+                m.appendReplacement(b, replace);
+            }
+            m.appendTail(b);
+            return b.toString();
+        }        
+	}
+	
+    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
+        return new AbstractMap.SimpleEntry<>(key, value);
+    }
+
 }
\ No newline at end of file

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java Wed Sep 26 16:45:35 2018
@@ -111,7 +111,7 @@ public class AnnotationPlugin implements
         }
 
         catch (Throwable t) {
-            m_logger.error("error: " + t.toString(), t);
+            m_logger.error("Dependency Manager Annotation Errors: %s", t, t); 
         }
 
         finally {

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java Wed Sep 26 16:45:35 2018
@@ -135,14 +135,14 @@ public class BndLogger extends Logger {
         return logEnabled.ordinal() >= Level.Error.ordinal();
     }
 
-    public void error(String content, Object ... args) {
+    public void error(String content, Throwable err, Object ... args) {
         m_reporter.error(content, args);
-        logErr(String.format(content, args), null);
+        logErr(String.format(content, args), err);
     }
 
-    public void error(String content, Throwable err, Object ... args) {
+    public void error(String content, Object ... args) {
         m_reporter.error(content, args);
-        logErr(String.format(content, args), err);
+        logErr(String.format(content, args), null);
     }
 
     /**

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java Wed Sep 26 16:45:35 2018
@@ -98,13 +98,10 @@ public class DescriptorGenerator
         Collection<Clazz> expanded = m_analyzer.getClasses("",
                                                            // Parse everything
                                                            QUERY.NAMED.toString(), "*");
-
-        // Create the object which will collect Config Admin MetaTypes.
-        MetaType metaType = new MetaType();
             
         for (Clazz c : expanded)
         {
-            AnnotationCollector reader = new AnnotationCollector(m_logger, metaType);
+            AnnotationCollector reader = new AnnotationCollector(m_logger, m_analyzer);
             reader.baseClass(true);
             m_logger.debug("scanning class %s", c.getClassName());
             c.parseClassFileWithCollector(reader);
@@ -139,11 +136,6 @@ public class DescriptorGenerator
             }
         }
 
-        // If some Meta Types have been parsed, then creates the corresponding resource file.
-        if (metaType.getSize() > 0)
-        {
-            m_metaTypeResource = createMetaTypeResource(metaType);
-        }
         return annotationsFound;
     }
 
@@ -231,20 +223,4 @@ public class DescriptorGenerator
         return new EmbeddedResource(data, 0);
     }
     
-    /**
-     * Creates a bnd resource that contains the generated metatype descriptor.
-     * @param metaType the Object that has collected all meta type informations.
-     * @return the meta type resource
-     * @throws IOException on any errors
-     */
-    private Resource createMetaTypeResource(MetaType metaType) throws IOException
-    {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
-        metaType.writeTo(pw);
-        pw.close();
-        byte[] data = out.toByteArray();
-        out.close();
-        return new EmbeddedResource(data, 0);    
-    }
 }
\ No newline at end of file

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java Wed Sep 26 16:45:35 2018
@@ -45,6 +45,7 @@ public enum EntryParam
     pid,
     pidClass,
     configType, // inject a proxy configuration type 
+    configTypes, // inject multiple proxy configuration type interfaces 
     factoryPid,
     factoryPidClass,
     propagate, 
@@ -68,5 +69,7 @@ public enum EntryParam
     registrationField, 
     registered, 
     unregistered,
-    dereference
+    dereference,
+    scope,
+    bundle
 }

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java?rev=1842034&r1=1842033&r2=1842034&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java Wed Sep 26 16:45:35 2018
@@ -115,6 +115,22 @@ public class EntryWriter
         checkType(param.toString());
         m_params.put(param.toString(), Arrays.asList(values));
     }
+    
+    /**
+     * Adds a List of Strings in this descriptor entry.
+     */
+    public void put(EntryParam param, List<String> values)
+    {
+        checkType(param.toString());
+        m_params.put(param.toString(), values);
+    }
+
+    /**
+     * Returns a parameter value (either a String, or a List<String>).
+     */
+    public Object getParameter(EntryParam param) {
+    	return m_params.get(param.toString());
+    }
 
     /**
      * Adds a property in this descriptor entry.

Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/FieldTypeGetter.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/FieldTypeGetter.java?rev=1842034&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/FieldTypeGetter.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/FieldTypeGetter.java Wed Sep 26 16:45:35 2018
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.annotation.plugin.bnd;
+
+import aQute.bnd.osgi.Clazz.FieldDef;
+
+/**
+ * Class extracted from bndtools (aQute.bnd.component.AnnotationScanner), which
+ * allows to determine class field collection type.
+ * For example, given "private Collection<MyService> services;", this class allows to determine the
+ * "MyService" type. 
+ */
+public class FieldTypeGetter {
+	public enum FieldCollectionType {
+		service, properties, reference, serviceobjects, tuple
+	}
+
+	static String determineFieldType(Logger log, FieldDef member) {
+		String field = member.getName();
+		String sig = member.getSignature();
+		if (sig == null)
+			// no generics, the descriptor will be the class name.
+			sig = member.getDescriptor().toString();
+		String[] sigs = sig.split("[<;>]");
+		int sigLength = sigs.length;
+		int index = 0;
+		boolean isCollection = false;
+
+		if ("Ljava/lang/Iterable".equals(sigs[index]) || "Ljava/util/Collection".equals(sigs[index]) || "Ljava/util/List".equals(sigs[index])) {
+			index++;
+			isCollection = true;
+		}
+		// Along with determining the FieldCollectionType, the following
+		// code positions index to read the service type.
+		FieldCollectionType fieldCollectionType = null;
+		if (sufficientGenerics(index, sigLength, sig)) {
+			if ("Lorg/osgi/framework/ServiceReference".equals(sigs[index])) {
+				if (sufficientGenerics(index++, sigLength, sig)) {
+					fieldCollectionType = FieldCollectionType.reference;
+				}
+			} else if ("Lorg/osgi/service/component/ComponentServiceObjects".equals(sigs[index])) {
+				if (sufficientGenerics(index++, sigLength, sig)) {
+					fieldCollectionType = FieldCollectionType.serviceobjects;
+				}
+			} else if ("Ljava/util/Map".equals(sigs[index])) {
+				if (sufficientGenerics(index++, sigLength, sig)) {
+					fieldCollectionType = FieldCollectionType.properties;
+				}
+			} else if ("Ljava/util/Map$Entry".equals(sigs[index]) && sufficientGenerics(index++ + 5, sigLength, sig)) {
+				if ("Ljava/util/Map".equals(sigs[index++]) && "Ljava/lang/String".equals(sigs[index++])) {
+					if ("Ljava/lang/Object".equals(sigs[index]) || "+Ljava/lang/Object".equals(sigs[index])) {
+						fieldCollectionType = FieldCollectionType.tuple;
+						index += 3; // ;>;
+					} else if ("*".equals(sigs[index])) {
+						fieldCollectionType = FieldCollectionType.tuple;
+						index += 2; // >;
+					} else {
+						index = sigLength;// no idea what service might
+											// be.
+					}
+				}
+			} else {
+				fieldCollectionType = FieldCollectionType.service;
+			}
+		}
+		if (isCollection) {
+			// def.fieldCollectionType = fieldCollectionType;
+		}
+		String annoService = null;
+		if (annoService == null && index < sigs.length) {
+			annoService = sigs[index].substring(1).replace('/', '.');
+		}
+		return annoService;
+	}
+
+	private static boolean sufficientGenerics(int index, int sigLength, String sig) {
+		if (index + 1 > sigLength) {
+			// analyzer.error(
+			// "In component %s, method %s, signature: %s does not have sufficient generic
+			// type information",
+			// component.effectiveName(), def.name, sig);
+			return false;
+		}
+		return true;
+	}
+}