felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pde...@apache.org
Subject svn commit: r1587054 [2/3] - in /felix/sandbox/pderop/dependencymanager-prototype/dm.annotations: ./ .settings/ generated/ src/ src/dm/ src/dm/annotation/ src/dm/annotation/api/ src/dm/annotation/plugin/ src/dm/annotation/plugin/bnd/
Date Sun, 13 Apr 2014 17:23:52 GMT
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ResourceAdapterService.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ResourceAdapterService.java?rev=1587054&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ResourceAdapterService.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ResourceAdapterService.java Sun Apr 13 17:23:51 2014
@@ -0,0 +1,158 @@
+/*
+ * 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 dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a class as a Resource adapter service. Resource adapters are things that 
+ * adapt a resource instead of a service, and provide an adapter service on top of this resource.
+ * Resources are an abstraction that is introduced by the dependency manager, represented as a URL. 
+ * They can be implemented to serve resources embedded in bundles, somewhere on a file system or in 
+ * an http content repository server, or database.<p>
+ * The adapter will be applied to any resource that matches the specified filter condition, which can
+ * match some part of the resource URL (with "path", "protocol", "port", or "host" filters). 
+ * For each matching resource an adapter will be created based on the adapter implementation class.
+ * The adapter will be registered with the specified interface and with any extra service properties 
+ * you supply here. Moreover, the following service properties will be propagated from the resource URL:
+ * 
+ * <ul><li> "host": this property exposes the host part of the resource URL
+ * <li>"path": the resource URL path
+ * <li>"protocol": the resource URL protocol
+ * <li>"port": the resource URL port
+ * </ul>
+ * 
+ * <h3>Usage Examples</h3>
+ * Here, the "VideoPlayer" service provides a video service on top of any movie resources, with service
+ * properties "host"/"port"/"protocol"/"path" extracted from the resource URL:
+ * <blockquote>
+ * <pre>
+ * 
+ * &#64;ResourceAdapterService(filter = "(&(path=/videos/*.mkv)(host=localhost))", propagate = true)
+ * public class VideoPlayerImpl implements VideoPlayer {
+ *     // Injected by reflection
+ *     URL resource;
+ *     
+ *     void play() {} // play video referenced by this.resource     
+ *     void stop() {} // stop playing the video
+ *     void transcode() {} // ...
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * And here is an example of a VideoProvider, which provides some videos using a web URL.
+ * Notice that Resource providers need to depend on the DependencyManager API:
+ * 
+ * <blockquote>
+ * <pre>
+ * import java.net.MalformedURLException;
+ * import java.net.URL;
+ * import java.util.HashMap;
+ * import java.util.Map;
+ * 
+ * import org.apache.felix.dm.ResourceHandler;
+ * import org.apache.felix.dm.ResourceUtil;
+ * import org.apache.felix.dm.annotation.api.Component;
+ * import org.apache.felix.dm.annotation.api.Init;
+ * import org.apache.felix.dm.annotation.api.ServiceDependency;
+ * import org.osgi.framework.BundleContext;
+ * import org.osgi.framework.Filter;
+ * import org.osgi.framework.InvalidSyntaxException;
+ * 
+ * &#64;Component
+ * public class VideoProvider
+ * {
+ *     // Injected by reflection
+ *     private volatile BundleContext context;
+ *     // List of known resource handlers
+ *     private Map&#60;ResourceHandler, Filter&#62; m_handlers = new HashMap&#60;ResourceHandler, Filter&#62;();
+ *     // List of known video resources
+ *     private URL[] m_videos;
+ * 
+ *     &#64;Init
+ *     void init() throws MalformedURLException
+ *     {
+ *        m_videos = new URL[] {
+ *                new URL("http://localhost:8080/videos/video1.mkv"),
+ *                new URL("http://localhost:8080/videos/video2.mkv"),
+ *         };
+ *     }
+ * 
+ *     // Track resource handlers
+ *     &#64;ServiceDependency(required = false)
+ *     public void add(Map&#60;String, String&#62; serviceProperties, ResourceHandler handler) throws InvalidSyntaxException
+ *     {
+ *         String filterString = serviceProperties.get("filter");
+ *         filterString = (filterString != null) ? filterString : "(path=*)";
+ *         Filter filter = context.createFilter(filterString);
+ *         synchronized (this)
+ *         {
+ *             m_handlers.put(handler, filter);
+ *         }
+ *         for (URL video : m_videos)
+ *         {
+ *             if (filter.match(ResourceUtil.createProperties(video)))
+ *             {
+ *                 handler.added(video);
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface ResourceAdapterService
+{
+    /**
+     * The interface(s) to use when registering adapters
+     */
+    Class<?>[] provides() default {};
+
+    /**
+     * Additional properties to use with the adapter service registration
+     */
+    Property[] properties() default {};
+
+   /**
+     * The filter condition to use with the resource.
+     */
+    String filter();
+
+    /**
+     * <code>true</code> if properties from the resource should be propagated to the service properties.
+     */
+    boolean propagate() default false;
+    
+    /**
+     * The callback method to be invoked when the Resource has changed.
+     */
+    String changed() default "";
+
+    /**
+     * Sets the static method used to create the AdapterService implementation instance.
+     */
+    String factoryMethod() default "";
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ResourceDependency.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ResourceDependency.java?rev=1587054&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ResourceDependency.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ResourceDependency.java Sun Apr 13 17:23:51 2014
@@ -0,0 +1,155 @@
+/*
+ * 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 dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method of field as a Resource Dependency. A resource dependency allows you to 
+ * depend on a resource. Resources are an abstraction that is introduced by the dependency manager, represented as a URL. 
+ * They can be implemented to serve resources embedded in bundles, somewhere on a file system or in 
+ * an http content repository server, or database. <p> A resource is a URL and you can use a filter condition based on 
+ * protocol, host, port, and path. 
+ * 
+ * <h3>Usage Examples</h3>
+ * Here, the "VideoPlayer" component plays any provided MKV video resources
+ * <blockquote>
+ * <pre>
+ * 
+ * &#64;Component
+ * public class VideoPlayer {
+ *     &#64;ResourceDependency(required=false, filter="(path=/videos/*.mkv)")
+ *     void playResource(URL video) { ... }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * And here is an example of a VideoProvider, which provides some videos using a web URL.
+ * Notice that Resource providers need to depend on the DependencyManager API:
+ * 
+ * <blockquote>
+ * <pre>
+ * import java.net.MalformedURLException;
+ * import java.net.URL;
+ * import java.util.HashMap;
+ * import java.util.Map;
+ * 
+ * import org.apache.felix.dm.ResourceHandler;
+ * import org.apache.felix.dm.ResourceUtil;
+ * import org.apache.felix.dm.annotation.api.Component;
+ * import org.apache.felix.dm.annotation.api.Init;
+ * import org.apache.felix.dm.annotation.api.ServiceDependency;
+ * import org.osgi.framework.BundleContext;
+ * import org.osgi.framework.Filter;
+ * import org.osgi.framework.InvalidSyntaxException;
+ * 
+ * &#64;Component
+ * public class VideoProvider
+ * {
+ *     // Injected by reflection
+ *     private volatile BundleContext context;
+ *     // List of known resource handlers
+ *     private Map&#60;ResourceHandler, Filter&#62; m_handlers = new HashMap&#60;ResourceHandler, Filter&#62;();
+ *     // List of known video resources
+ *     private URL[] m_videos;
+ * 
+ *     &#64;Init
+ *     void init() throws MalformedURLException
+ *     {
+ *        m_videos = new URL[] {
+ *                new URL("http://localhost:8080/videos/video1.mkv"),
+ *                new URL("http://localhost:8080/videos/video2.mkv"),
+ *         };
+ *     }
+ * 
+ *     // Track resource handlers
+ *     &#64;ServiceDependency(required = false)
+ *     public void add(Map&#60;String, String&#62; serviceProperties, ResourceHandler handler) throws InvalidSyntaxException
+ *     {
+ *         String filterString = serviceProperties.get("filter");
+ *         filterString = (filterString != null) ? filterString : "(path=*)";
+ *         Filter filter = context.createFilter(filterString);
+ *         synchronized (this)
+ *         {
+ *             m_handlers.put(handler, filter);
+ *         }
+ *         for (URL video : m_videos)
+ *         {
+ *             if (filter.match(ResourceUtil.createProperties(video)))
+ *             {
+ *                 handler.added(video);
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.FIELD})
+public @interface ResourceDependency
+{
+    /**
+     * Returns 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.
+     */
+    String changed() default "";
+
+    /**
+     * Returns the callback method to invoke when the service is lost.
+     */
+    String removed() default "";
+
+    /**
+     * Returns whether the Service dependency is required or not.
+     */
+    boolean required() default true;
+    
+    /**
+     * Returns the Service dependency OSGi filter.
+     */
+    String filter() default "";
+
+    /**
+     * Specifies if the resource URL properties must be propagated. If set to true, then the URL properties 
+     * ("protocol"/"host"/"port"/"path") will be propagated to the service properties of the component which 
+     * is using this dependency. 
+     */
+    boolean propagate() default false;
+    
+    /**
+     * The name used when dynamically configuring this dependency from the init method.
+     * Specifying this attribute allows to dynamically configure the dependency 
+     * <code>filter</code> and <code>required</code> flag from the Service's init method.
+     * All unnamed dependencies will be injected before the init() method; so from the init() method, you can
+     * then pick up whatever information needed from already injected (unnamed) dependencies, and configure dynamically
+     * your named dependencies, which will then be calculated once the init() method returns.
+     */
+    String name() default "";   
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ServiceDependency.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ServiceDependency.java?rev=1587054&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ServiceDependency.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/ServiceDependency.java Sun Apr 13 17:23:51 2014
@@ -0,0 +1,186 @@
+/*
+ * 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 dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method or a field for injecting a Service Dependency. When applied on a class 
+ * field, optional unavailable dependencies are injected with a NullObject.
+ * 
+ * <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;
+ * </pre></blockquote>
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.FIELD})
+public @interface ServiceDependency
+{
+    /**
+     * The type if the service this dependency is applying on. By default, the method parameter 
+     * (or the class field) is used as the type.
+     */
+    Class<?> service() default Object.class;
+
+    /**
+     * The Service dependency OSGi filter.
+     */
+    String filter() default "";
+
+    /**
+     * The class for the default implementation, if the dependency is not available.
+     */
+    Class<?> defaultImpl() default Object.class;
+
+    /**
+     * Whether the Service dependency is required or not.
+     */
+    boolean required() default true;
+
+    /**
+     * 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 "";
+
+    /**
+     * The callback method to be invoked when the service properties have changed.
+     */
+    String changed() default "";
+
+    /**
+     * The callback method to invoke when the service is lost.
+     */
+    String removed() default "";
+    
+    /** 
+     * The max time in millis to wait for the dependency availability. 
+     * Specifying a positive number allow to block the caller thread between service updates. Only
+     * useful for required stateless dependencies that can be replaced transparently.
+     * A Dynamic Proxy is used to wrap the actual service dependency (which must be an interface). 
+     * When the dependency goes away, an attempt is made to replace it with another one which satisfies 
+     * the service dependency criteria. If no service replacement is available, then any method invocation 
+     * (through the dynamic proxy) will block during a configurable timeout. On timeout, an unchecked 
+     * <code>IllegalStateException</code> exception is raised (but the service is not deactivated).<p>
+     * Notice that the changed/removed callbacks are not used when the timeout parameter is > -1.
+     * <p> 
+     * 
+     * -1 means no timeout at all (default). 0 means that invocation on a missing service will fail 
+     * immediately. A positive number represents the max timeout in millis to wait for the service availability.
+     * 
+     * <p> Sample Code:<p>
+     * <blockquote><pre>
+     * &#64;Component
+     * class MyServer implements Runnable {
+     *   &#64;ServiceDependency(timeout=15000)
+     *   MyDependency dependency;.
+     *   
+     *   &#64;Start
+     *   void start() {
+     *     (new Thread(this)).start();
+     *   }
+     *   
+     *   public void run() {
+     *     try {
+     *       dependency.doWork();
+     *     } catch (IllegalStateException e) {
+     *       t.printStackTrace();
+     *     }
+     *   }   
+     * </pre></blockquote>
+     */
+    long timeout() default -1;
+    
+    /**
+     * The name used when dynamically configuring this dependency from the init method.
+     * Specifying this attribute allows to dynamically configure the dependency 
+     * <code>filter</code> and <code>required</code> flag from the Service's init method.
+     * All unnamed dependencies will be injected before the init() method; so from the init() method, you can
+     * then pick up whatever information needed from already injected (unnamed) dependencies, and configure dynamically
+     * your named dependencies, which will then be calculated once the init() method returns.
+     * 
+     * <p> Usage example of a Service whose dependency filter is configured from ConfigAdmin:
+     * 
+     * <blockquote><pre>
+     *  &#47;**
+     *    * A Service whose service dependency "otherService" filter is configured from ConfigAdmin
+     *    *&#47;
+     *  &#64;Service
+     *  class X {
+     *      private Dictionary m_config;
+     *      
+     *      &#47;**
+     *       * Initialize our service from config ... and store the config for later usage (from our init method)
+     *       *&#47; 
+     *      &#64;ConfigurationDependency(pid="MyPid")
+     *      void configure(Dictionary conf) {
+     *           m_config = config;
+     *      }
+     * 
+     *      &#47;**
+     *       * All unnamed dependencies are injected: we can now configure other named
+     *       * dependencies, using the already injected configuration.
+     *       * The returned Map will be used to configure our "otherService" Dependency.
+     *       *&#47;
+     *      &#64;Init
+     *      Map init() {
+     *          return new HashMap() {{
+     *              put("otherService.filter", m_config.get("filter"));
+     *              put("otherService.required", m_config.get("required"));
+     *          }};
+     *      } 
+     *
+     *      &#47;**
+     *       * This named dependency filter/required flag will be configured by our init method (see above).
+     *       *&#47;
+     *      &#64;ServiceDependency(name="otherService") 
+     *      void bindOtherService(OtherService other) {
+     *      }
+     *      
+     *      &#47;**
+     *       * All dependencies are injected and our service is now ready to be published.
+     *       * Notice that you can also use the publisher service attribute if you need 
+     *       * to take control on service exposition.
+     *       *&#47;
+     *      &#64;Start
+     *      void start() {
+     *      }
+     *  }
+     *  </pre></blockquote>
+     */
+    String name() default "";
+    
+    /**
+     * Returns true if the dependency service properties must be published along with the service. 
+     * 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;
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Start.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Start.java?rev=1587054&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Start.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Start.java Sun Apr 13 17:23:51 2014
@@ -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 dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method which will be invoked when the component is started.
+ * The annotated method is invoked juste before registering the service into the OSGi registry 
+ * (if the service provides an interface). Notice that the start method may optionally return 
+ * a Map which will be propagated to the provided service properties.<p>
+ * Service activation/deactivation can be programatically controlled using {@link LifecycleController}.
+ *      
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ * 
+ * <pre>
+ * &#64;Component(properties={&#64;Property(name="foo", value="bar")})
+ * class X implements Z {
+ *     &#64;ServiceDependency
+ *     OtherService m_dependency;
+ *   
+ *     &#64;Start
+ *     Map start() {
+ *         // Our Z Service is ready (all required dependencies have been satisfied), and is about to be 
+ *         // registered into the OSGi registry. We return here an optional Map containing some extra-properties
+ *         // which will be appended to the properties supplied in the Component annotation.
+ *         return new HashMap() {{
+ *            put("foo2", "bar2");
+ *            put(Constants.SERVICE_RANKING, Integer.valueOf(10));
+ *         }};
+ *     }
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Start
+{
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Stop.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Stop.java?rev=1587054&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Stop.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Stop.java Sun Apr 13 17:23:51 2014
@@ -0,0 +1,54 @@
+/*
+ * 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 dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method which is invoked when the Service is being unregistered from the 
+ * OSGi registry.
+ * The method is called when the component's bundle is stopped, or when one of its
+ * required dependency is lost, or when a {@link LifecycleController} is programatically used to
+ * stop a service.
+ * </ul>
+ * 
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ * <pre>
+ * &#64;Component
+ * class MyComponent implements MyService {
+ *     &#64;ServiceDependency
+ *     private LogService logService; // Required dependency over the log service.
+ *     
+ *     &#64;Stop
+ *     void stop() {} // We are unregistering from the OSGi registry.     
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Stop
+{
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Unregistered.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Unregistered.java?rev=1587054&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Unregistered.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/Unregistered.java Sun Apr 13 17:23:51 2014
@@ -0,0 +1,57 @@
+/*
+ * 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 dm.annotation.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be used to be notified when a component is unregistered from the registry. 
+ * At this point, the component has been unregistered from the OSGI registry (if it provides some services).
+ * The method must not take any parameters.
+ * 
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ * 
+ * <pre>
+ * &#64;Component
+ * class X implements Z {     
+ *     &#64;Stop
+ *     void stop(ServiceRegistration sr) {
+ *        // Our service must stop because it is about to be unregistered from the registry.
+ *     }
+ *     
+ *     &#64;Unregistered
+ *     void unregistered() {
+ *        // At this point, our service has been unregistered from the OSGi registry
+ *     }
+ * }
+ * </pre>
+ * </blockquote>
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface Unregistered
+{
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/packageinfo
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/packageinfo?rev=1587054&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/packageinfo (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/api/packageinfo Sun Apr 13 17:23:51 2014
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/plugin/bnd/AnnotationCollector.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/plugin/bnd/AnnotationCollector.java?rev=1587054&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/plugin/bnd/AnnotationCollector.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/plugin/bnd/AnnotationCollector.java Sun Apr 13 17:23:51 2014
@@ -0,0 +1,1274 @@
+/*
+ * 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 dm.annotation.plugin.bnd;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.osgi.framework.Bundle;
+
+import dm.annotation.api.AdapterService;
+import dm.annotation.api.AspectService;
+import dm.annotation.api.BundleAdapterService;
+import dm.annotation.api.BundleDependency;
+import dm.annotation.api.Component;
+import dm.annotation.api.Composition;
+import dm.annotation.api.ConfigurationDependency;
+import dm.annotation.api.Destroy;
+import dm.annotation.api.FactoryConfigurationAdapterService;
+import dm.annotation.api.Init;
+import dm.annotation.api.Inject;
+import dm.annotation.api.LifecycleController;
+import dm.annotation.api.Registered;
+import dm.annotation.api.ResourceAdapterService;
+import dm.annotation.api.ResourceDependency;
+import dm.annotation.api.ServiceDependency;
+import dm.annotation.api.Start;
+import dm.annotation.api.Stop;
+import dm.annotation.api.Unregistered;
+import aQute.bnd.osgi.Annotation;
+import aQute.bnd.osgi.ClassDataCollector;
+import aQute.bnd.osgi.Clazz;
+import aQute.bnd.osgi.Descriptors.TypeRef;
+import aQute.bnd.osgi.Verifier;
+
+/**
+ * This is the scanner which does all the annotation parsing on a given class.
+ * To start the parsing, just invoke the parseClassFileWithCollector and finish methods.
+ * Once parsed, the corresponding component descriptors can be built using the "writeTo" method.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AnnotationCollector extends ClassDataCollector
+{
+    private final static String A_INIT = Init.class.getName();
+    private final static String A_START = Start.class.getName();
+    private final static String A_STOP = Stop.class.getName();
+    private final static String A_DESTROY = Destroy.class.getName();
+    private final static String A_COMPOSITION = Composition.class.getName();
+    private final static String A_LIFCLE_CTRL = LifecycleController.class.getName();
+
+    private final static String A_COMPONENT = Component.class.getName();
+    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();
+
+    private Logger m_logger;
+    private String m_className;
+    private String[] m_interfaces;
+    private boolean m_isField;
+    private String m_field;
+    private String m_method;
+    private String m_descriptor;
+    private Set<String> m_dependencyNames = new HashSet<String>();
+    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;
+    private String m_initMethod;
+    private String m_destroyMethod;
+    private String m_compositionMethod;
+    private String m_starter;
+    private String m_stopper;
+    private Set<String> m_importService = new HashSet<String>();
+    private Set<String> m_exportService = new HashSet<String>();
+    private String m_bundleContextField;
+    private String m_dependencyManagerField;
+    private String m_componentField;
+    private String m_registeredMethod;
+    private String m_unregisteredMethod;
+
+    /**
+     * This class represents a DependencyManager component descriptor entry.
+     * (Service, a ServiceDependency ... see EntryType enum).
+     */
+
+    /**
+     * Makes a new Collector for parsing a given class.
+     * @param reporter the object used to report logs.
+     */
+    public AnnotationCollector(Logger reporter, MetaType metaType)
+    {
+        m_logger = reporter;
+        m_metaType = metaType;
+    }
+
+    /**
+     * Parses the name of the class.
+     * @param access the class access
+     * @param name the class name (package are "/" separated).
+     */
+    @Override
+    public void classBegin(int access, TypeRef name)
+    {
+        m_className = name.getFQN();
+        m_logger.debug("class name: %s", m_className);
+    }
+
+    /**
+     * Parses the implemented interfaces ("/" separated).
+     */
+    @Override
+    public void implementsInterfaces(TypeRef[] interfaces)
+    {
+        List<String> result = new ArrayList<String>();
+        for (int i = 0; i < interfaces.length; i++)
+        {
+            if (!interfaces[i].getBinary().equals("scala/ScalaObject"))
+            {
+                result.add(interfaces[i].getFQN());
+            }
+        }
+         
+        m_interfaces = result.toArray(new String[result.size()]);
+        m_logger.debug("implements: %s", Arrays.toString(m_interfaces));
+    }
+
+    /**
+     * Parses a method. Always invoked BEFORE eventual method annotation.
+     */
+    @Override
+    public void method(Clazz.MethodDef method)
+    {
+        m_logger.debug("Parsed method %s, descriptor=%s", method.getName(), method.getDescriptor());
+        m_isField = false;
+        m_method = method.getName();
+        m_descriptor = method.getDescriptor().toString();
+    }
+
+    /**
+     * Parses a field. Always invoked BEFORE eventual field annotation
+     */
+    @Override
+    public void field(Clazz.FieldDef field)
+    {
+        m_logger.debug("Parsed field %s, descriptor=%s", field.getName(), field.getDescriptor());
+        m_isField = true;
+        m_field = field.getName();
+        m_descriptor = field.getDescriptor().toString();
+    }
+
+    /** 
+     * An annotation has been parsed. Always invoked AFTER the "method"/"field"/"classBegin" callbacks. 
+     */
+    @Override
+    public void annotation(Annotation annotation)
+    {
+        m_logger.debug("Parsing annotation: %s", annotation.getName());
+
+        if (annotation.getName().getFQN().equals(A_COMPONENT))
+        {
+            parseComponentAnnotation(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_ASPECT_SERVICE))
+        {
+            parseAspectService(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_ADAPTER_SERVICE))
+        {
+            parseAdapterService(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_BUNDLE_ADAPTER_SERVICE))
+        {
+            parseBundleAdapterService(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_RESOURCE_ADAPTER_SERVICE))
+        {
+            parseResourceAdapterService(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_FACTORYCONFIG_ADAPTER_SERVICE))
+        {
+            parseFactoryConfigurationAdapterService(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_INIT))
+        {
+            m_initMethod = m_method;
+        } 
+        else if (annotation.getName().getFQN().equals(A_START))
+        {
+            m_startMethod = m_method;
+        } 
+        else if (annotation.getName().getFQN().equals(A_REGISTERED))
+        {
+            m_registeredMethod = m_method;
+        }
+        else if (annotation.getName().getFQN().equals(A_STOP))
+        {
+            m_stopMethod = m_method;
+        }
+        else if (annotation.getName().getFQN().equals(A_UNREGISTERED))
+        {
+            m_unregisteredMethod = m_method;
+        }
+        else if (annotation.getName().getFQN().equals(A_DESTROY))
+        {
+            m_destroyMethod = m_method;
+        }
+        else if (annotation.getName().getFQN().equals(A_COMPOSITION))
+        {
+            Patterns.parseMethod(m_method, m_descriptor, Patterns.COMPOSITION);
+            m_compositionMethod = m_method;
+        } else if (annotation.getName().getFQN().equals(A_LIFCLE_CTRL)) 
+        {
+            parseLifecycleAnnotation(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_SERVICE_DEP))
+        {
+            parseServiceDependencyAnnotation(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_CONFIGURATION_DEPENDENCY))
+        {
+            parseConfigurationDependencyAnnotation(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_BUNDLE_DEPENDENCY))
+        {
+            parseBundleDependencyAnnotation(annotation);
+        }
+        else if (annotation.getName().getFQN().equals(A_RESOURCE_DEPENDENCY))
+        {
+            parseRersourceDependencyAnnotation(annotation);
+        } 
+        else if (annotation.getName().getFQN().equals(A_INJECT))
+        {
+            parseInject(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.
+     */
+    public boolean finish()
+    {
+        if (m_writers.size() == 0)
+        {
+            m_logger.info("No components found for class " + m_className);
+            return false;
+        }
+
+        // We must have at least a Service annotation.
+        checkServiceDeclared(EntryType.Component, 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_writers.size() - 1; i >= 0; i--)
+        {
+            sb.append("\n\t").append(m_writers.get(i).toString());
+        }
+        m_logger.info(sb.toString());
+        return true;
+    }
+
+    /**
+     * Writes the generated component descriptor in the given print writer.
+     * The first line must be the service (@Service or AspectService).
+     * @param pw the writer where the component descriptor will be written.
+     */
+    public void writeTo(PrintWriter pw)
+    {
+        // 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_writers.get(i).toString());
+        }
+    }
+        
+    /**
+     * Returns list of all imported services. Imported services are deduced from every
+     * @ServiceDependency annotations.
+     * @return the list of imported services, or null
+     */
+    public Set<String> getImportService()
+    {
+        return m_importService;
+    }
+
+    /**
+     * Returns list of all exported services. Imported services are deduced from every
+     * annotations which provides a service (@Component, etc ...)
+     * @return the list of exported services, or null
+     */
+    public Set<String> getExportService()
+    {
+        return m_exportService;
+    }
+
+    private void parseComponentAnnotation(Annotation annotation)
+    {
+        EntryWriter writer = new EntryWriter(EntryType.Component);
+        m_writers.add(writer);
+
+        // Register previously parsed annotations common to services (Init/Start/...)
+        addCommonServiceParams(writer);
+
+        // impl attribute
+        writer.put(EntryParam.impl, m_className);
+
+        // properties attribute
+        parseProperties(annotation, EntryParam.properties, writer);
+
+        // provides attribute.
+        if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+        {
+            // no service provided: check if @Registered/@Unregistered annotation are used
+            // and raise an error if true.
+            checkRegisteredUnregisteredNotPresent();
+        }
+
+        // factorySet attribute
+        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");
+        }
+
+        // factoryConfigure attribute
+        writer.putString(annotation, EntryParam.factoryConfigure, null);
+        
+        // factoryMethod attribute
+        writer.putString(annotation, EntryParam.factoryMethod, null);
+    }
+
+    private void addCommonServiceParams(EntryWriter writer)
+    {
+        if (m_initMethod != null)
+        {
+            writer.put(EntryParam.init, m_initMethod);
+        }
+
+        if (m_startMethod != null)
+        {
+            writer.put(EntryParam.start, m_startMethod);
+        }
+        
+        if (m_registeredMethod != null)
+        {
+            writer.put(EntryParam.registered, m_registeredMethod);
+        }
+
+        if (m_stopMethod != null)
+        {
+            writer.put(EntryParam.stop, m_stopMethod);
+        }
+        
+        if (m_unregisteredMethod != null)
+        {
+            writer.put(EntryParam.unregistered, m_unregisteredMethod);
+        }
+
+        if (m_destroyMethod != null)
+        {
+            writer.put(EntryParam.destroy, m_destroyMethod);
+        }
+
+        if (m_compositionMethod != null)
+        {
+            writer.put(EntryParam.composition, m_compositionMethod);
+        }       
+        
+        if (m_starter != null) 
+        {
+            writer.put(EntryParam.starter, m_starter);
+        }
+        
+        if (m_stopper != null)
+        {
+            writer.put(EntryParam.stopper, m_stopper);
+            if (m_starter == null)
+            {
+                throw new IllegalArgumentException("Can't use a @LifecycleController annotation for stopping a service without declaring a " +
+                                                   "@LifecycleController that starts the component in class " + m_className);
+            }
+        }   
+
+        if (m_bundleContextField != null)
+        {
+            writer.put(EntryParam.bundleContextField, m_bundleContextField);
+        }
+        
+        if (m_dependencyManagerField != null)
+        {
+            writer.put(EntryParam.dependencyManagerField, m_dependencyManagerField);
+        }
+        
+        if (m_componentField != null)
+        {
+            writer.put(EntryParam.componentField, m_componentField);
+        }
+    }
+
+    /**
+     * Parses a ServiceDependency Annotation.
+     * @param annotation the ServiceDependency Annotation.
+     */
+    private void parseServiceDependencyAnnotation(Annotation annotation)
+    {
+        EntryWriter writer = new EntryWriter(EntryType.ServiceDependency);
+        m_writers.add(writer);
+
+        // service attribute
+        String service = annotation.get(EntryParam.service.toString());
+        if (service != null)
+        {
+            service = Patterns.parseClass(service, Patterns.CLASS, 1);
+        }
+        else
+        {
+            if (m_isField)
+            {
+                service = Patterns.parseClass(m_descriptor, Patterns.CLASS, 1);
+            }
+            else
+            {
+                service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS, 2);
+            }
+        }
+        writer.put(EntryParam.service, service);
+
+        // Store this service in list of imported services.
+        m_importService.add(service);
+        
+        // autoConfig attribute
+        if (m_isField)
+        {
+            writer.put(EntryParam.autoConfig, m_field);
+        }
+
+        // filter attribute
+        String filter = annotation.get(EntryParam.filter.toString());
+        if (filter != null)
+        {
+            Verifier.verifyFilter(filter, 0);
+            writer.put(EntryParam.filter, filter);
+        }
+
+        // defaultImpl attribute
+        writer.putClass(annotation, EntryParam.defaultImpl, null);
+
+        // added callback
+        writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
+
+        // timeout parameter
+        writer.putString(annotation, EntryParam.timeout, null);
+        Long t = (Long) annotation.get(EntryParam.timeout.toString());
+        if (t != null && t.longValue() < -1)
+        {
+            throw new IllegalArgumentException("Invalid timeout value " + t + " in ServiceDependency annotation from class " + m_className);
+        }
+        
+        // required attribute (not valid if parsing a temporal service dependency)
+        writer.putString(annotation, EntryParam.required, null);
+
+        // changed callback
+        writer.putString(annotation, EntryParam.changed, null);
+
+        // removed callback
+        writer.putString(annotation, EntryParam.removed, null); 
+        
+        // name attribute
+        parseDependencyName(writer, annotation);
+        
+        // propagate attribute
+        writer.putString(annotation, EntryParam.propagate, null);
+    }
+
+    /**
+     * Parses a ConfigurationDependency annotation.
+     * @param annotation the ConfigurationDependency annotation.
+     */
+    private void parseConfigurationDependencyAnnotation(Annotation annotation)
+    {
+        EntryWriter writer = new EntryWriter(EntryType.ConfigurationDependency);
+        m_writers.add(writer);
+
+        // pid attribute
+        writer.putString(annotation, EntryParam.pid, m_className);
+
+        // the method on which the annotation is applied
+        writer.put(EntryParam.updated, m_method);
+
+        // propagate attribute
+        writer.putString(annotation, EntryParam.propagate, null);
+
+        // Property Meta Types
+        String pid = get(annotation, EntryParam.pid.toString(), m_className);
+        parseMetaTypes(annotation, pid, false);
+    }
+
+    /**
+     * Parses an AspectService annotation.
+     * @param annotation
+     */
+    private void parseAspectService(Annotation annotation)
+    {
+        EntryWriter writer = new EntryWriter(EntryType.AspectService);
+        m_writers.add(writer);
+
+        // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+        addCommonServiceParams(writer);
+
+        // Parse service filter
+        String filter = annotation.get(EntryParam.filter.toString());
+        if (filter != null)
+        {
+            Verifier.verifyFilter(filter, 0);
+            writer.put(EntryParam.filter, filter);
+        }
+
+        // Parse service aspect ranking
+        Integer ranking = annotation.get(EntryParam.ranking.toString());
+        writer.put(EntryParam.ranking, ranking.toString());
+
+        // Generate Aspect Implementation
+        writer.put(EntryParam.impl, m_className);
+
+        // Parse Aspect properties.
+        parseProperties(annotation, EntryParam.properties, writer);
+        
+        // Parse field/added/changed/removed attributes
+        parseAspectOrAdapterCallbackMethods(annotation, writer);
+
+        // Parse service interface this aspect is applying to
+        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");
+            }
+            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");
+            }
+
+            writer.put(EntryParam.service, m_interfaces[0]);
+        }
+        else
+        {
+            writer.putClass(annotation, EntryParam.service, null);
+        }
+        
+        // Parse factoryMethod attribute
+        writer.putString(annotation, EntryParam.factoryMethod, null);
+    }
+
+    private void parseAspectOrAdapterCallbackMethods(Annotation annotation, EntryWriter writer)
+    {
+        String field = annotation.get(EntryParam.field.toString());
+        String added = annotation.get(EntryParam.added.toString());
+        String changed = annotation.get(EntryParam.changed.toString());
+        String removed = annotation.get(EntryParam.removed.toString());
+        String swap = annotation.get(EntryParam.swap.toString());
+
+        // "field" and "added/changed/removed/swap" attributes can't be mixed
+        if (field != null && (added != null || changed != null || removed != null || swap != null))
+        {
+            throw new IllegalStateException("Annotation " + annotation + "can't applied on " + m_className
+                    + " can't mix \"field\" attribute with \"added/changed/removed\" attributes");
+        }
+                
+        // Parse aspect impl field where to inject the original service.
+        writer.putString(annotation, EntryParam.field, null);
+        
+        // Parse aspect impl callback methods.
+        writer.putString(annotation, EntryParam.added, null);
+        writer.putString(annotation, EntryParam.changed, null);
+        writer.putString(annotation, EntryParam.removed, null);
+        writer.putString(annotation, EntryParam.swap, null);
+    }
+
+    /**
+     * Parses an AspectService annotation.
+     * @param annotation
+     */
+    private void parseAdapterService(Annotation annotation)
+    {
+        EntryWriter writer = new EntryWriter(EntryType.AdapterService);
+        m_writers.add(writer);
+
+        // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+        addCommonServiceParams(writer);
+
+        // Generate Adapter Implementation
+        writer.put(EntryParam.impl, m_className);
+
+        // Parse adaptee filter
+        String adapteeFilter = annotation.get(EntryParam.adapteeFilter.toString());
+        if (adapteeFilter != null)
+        {
+            Verifier.verifyFilter(adapteeFilter, 0);
+            writer.put(EntryParam.adapteeFilter, adapteeFilter);
+        }
+
+        // Parse the mandatory adapted service interface.
+        writer.putClass(annotation, EntryParam.adapteeService, null);
+
+        // Parse Adapter properties.
+        parseProperties(annotation, EntryParam.properties, 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 factoryMethod attribute
+        writer.putString(annotation, EntryParam.factoryMethod, null);
+        
+        // Parse field/added/changed/removed attributes
+        parseAspectOrAdapterCallbackMethods(annotation, writer);
+    }
+
+    /**
+     * Parses a BundleAdapterService annotation.
+     * @param annotation
+     */
+    private void parseBundleAdapterService(Annotation annotation)
+    {
+        EntryWriter writer = new EntryWriter(EntryType.BundleAdapterService);
+        m_writers.add(writer);
+
+        // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+        addCommonServiceParams(writer);
+
+        // Generate Adapter Implementation
+        writer.put(EntryParam.impl, m_className);
+
+        // Parse bundle filter
+        String filter = annotation.get(EntryParam.filter.toString());
+        if (filter != null)
+        {
+            Verifier.verifyFilter(filter, 0);
+            writer.put(EntryParam.filter, filter);
+        }
+
+        // Parse stateMask attribute
+        writer.putString(annotation, EntryParam.stateMask, Integer.valueOf(
+            Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE).toString());
+
+        // Parse Adapter properties.
+        parseProperties(annotation, EntryParam.properties, writer);
+
+        // Parse the optional 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 factoryMethod attribute
+        writer.putString(annotation, EntryParam.factoryMethod, null);
+    }
+
+    /**
+     * Parses a BundleAdapterService annotation.
+     * @param annotation
+     */
+    private void parseResourceAdapterService(Annotation annotation)
+    {
+        EntryWriter writer = new EntryWriter(EntryType.ResourceAdapterService);
+        m_writers.add(writer);
+
+        // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+        addCommonServiceParams(writer);
+
+        // Generate Adapter Implementation
+        writer.put(EntryParam.impl, m_className);
+
+        // 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.
+        parseProperties(annotation, EntryParam.properties, 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.
+     * @param annotation
+     */
+    private void parseFactoryConfigurationAdapterService(Annotation annotation)
+    {
+        EntryWriter writer = new EntryWriter(EntryType.FactoryConfigurationAdapterService);
+        m_writers.add(writer);
+
+        // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+        addCommonServiceParams(writer);
+
+        // Generate Adapter Implementation
+        writer.put(EntryParam.impl, m_className);
+
+        // Parse factory Pid
+        writer.putString(annotation, EntryParam.factoryPid, m_className);
+
+        // Parse updated callback
+        writer.putString(annotation, EntryParam.updated, "updated");
+
+        // propagate attribute
+        writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
+
+        // Parse the provided adapter service (use directly implemented interface by default).
+        if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+        {
+            checkRegisteredUnregisteredNotPresent();
+        }
+
+        // Parse Adapter properties.
+        parseProperties(annotation, EntryParam.properties, writer);
+
+        // Parse optional meta types for configuration description.
+        String factoryPid = get(annotation, EntryParam.factoryPid.toString(), m_className);
+        parseMetaTypes(annotation, factoryPid, true);
+        
+        // Parse factoryMethod attribute
+        writer.putString(annotation, EntryParam.factoryMethod, null);
+    }
+
+    private void parseBundleDependencyAnnotation(Annotation annotation)
+    {
+        EntryWriter writer = new EntryWriter(EntryType.BundleDependency);
+        m_writers.add(writer);
+
+        String filter = annotation.get(EntryParam.filter.toString());
+        if (filter != null)
+        {
+            Verifier.verifyFilter(filter, 0);
+            writer.put(EntryParam.filter, filter);
+        }
+
+        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);
+        parseDependencyName(writer, annotation);
+    }
+
+    private void parseRersourceDependencyAnnotation(Annotation annotation)
+    {
+        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
+     * @param annotation the dependency to be parsed
+     */
+    private void parseDependencyName(EntryWriter writer, Annotation annotation)
+    {
+        String name = annotation.get(EntryParam.name.toString());
+        if (name != null) 
+        {
+            if(! m_dependencyNames.add(name))
+            {
+                throw new IllegalArgumentException("Duplicate dependency name " + name + " in Dependency " + annotation + " from class " + m_className);
+            }
+            writer.put(EntryParam.name, name);
+        }
+    }
+    
+    private void parseLifecycleAnnotation(Annotation annotation)
+    {
+        Patterns.parseField(m_field, m_descriptor, Patterns.RUNNABLE);
+        if ("true".equals(get(annotation,EntryParam.start.name(), "true")))
+        {
+            if (m_starter != null) {
+                throw new IllegalStateException("Lifecycle annotation already defined on field " + 
+                                                m_starter + " in class " + m_className);
+            }
+            m_starter = m_field;
+        } else {
+            if (m_stopper != null) {
+                throw new IllegalStateException("Lifecycle annotation already defined on field " + 
+                                                m_stopper + " in class " + m_className);
+            }
+            m_stopper = m_field;
+        }
+    }
+
+    /**
+     * Parse optional meta types annotation attributes
+     * @param annotation
+     */
+    private void parseMetaTypes(Annotation annotation, String pid, boolean factory)
+    {
+        if (annotation.get("metadata") != null)
+        {
+            String propertiesHeading = annotation.get("heading");
+            String propertiesDesc = annotation.get("description");
+
+            MetaType.OCD ocd = new MetaType.OCD(pid, propertiesHeading, propertiesDesc);
+            for (Object p: (Object[]) annotation.get("metadata"))
+            {
+                Annotation property = (Annotation) p;
+                String heading = property.get("heading");
+                String id = property.get("id");
+                String type = (String) property.get("type");
+                type = (type != null) ? Patterns.parseClass(type, Patterns.CLASS, 1) : null;
+                Object[] defaults = (Object[]) property.get("defaults");
+                String description = property.get("description");
+                Integer cardinality = property.get("cardinality");
+                Boolean required = property.get("required");
+
+                MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description,
+                    cardinality, required);
+
+                Object[] 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_className);
+                }
+
+                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_className);
+        }
+    }
+
+    /**
+     * Parses a Property annotation (which represents a list of key-value pair).
+     * The properties are encoded using the following json form:
+     * 
+     * properties       ::= key-value-pair*
+     * key-value-pair   ::= key value
+     * value            ::= String | String[] | value-type
+     * value-type       ::= jsonObject with value-type-info
+     * value-type-info  ::= "type"=primitive java type
+     *                      "value"=String|String[]
+     *                      
+     * Exemple:
+     * 
+     *  "properties" : {
+     *      "string-param" : "string-value",
+     *      "string-array-param" : ["str1", "str2],
+     *      "long-param" : {"type":"java.lang.Long", "value":"1"}}
+     *      "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
+     *  }
+     * }
+     * 
+     * @param annotation the annotation where the Param annotation is defined
+     * @param attribute the attribute name which is of Param type
+     * @param writer the object where the parsed attributes are written
+     */
+    private void parseProperties(Annotation annotation, EntryParam attribute, EntryWriter writer)
+    {
+        try
+        {
+            Object[] parameters = annotation.get(attribute.toString());
+            if (parameters != null)
+            {
+                JSONObject properties = new JSONObject();
+                for (Object p : parameters)
+                {
+                    Annotation a = (Annotation) p;
+                    String name = (String) a.get("name");
+
+                    String type = a.get("type");
+                    Class<?> classType;
+                    try
+                    {
+                        classType = (type == null) ? String.class : Class.forName(Patterns.parseClass(type,
+                            Patterns.CLASS, 1));
+                    }
+                    catch (ClassNotFoundException e)
+                    {
+                        // Theorically impossible
+                        throw new IllegalArgumentException("Invalid Property type " + type
+                            + " from annotation " + annotation + " in class " + m_className);
+                    }
+
+                    Object[] values;
+
+                    if ((values = a.get("value")) != null)
+                    {
+                        values = checkPropertyType(name, classType, values);
+                        addProperty(properties, name, values, classType);
+                    }
+                    else if ((values = a.get("values")) != null)
+                    { // deprecated
+                        values = checkPropertyType(name, classType, values);
+                        addProperty(properties, name, values, classType);
+                    }
+                    else if ((values = a.get("longValue")) != null)
+                    {
+                        addProperty(properties, name, values, Long.class);
+                    }
+                    else if ((values = a.get("doubleValue")) != null)
+                    {
+                        addProperty(properties, name, values, Double.class);
+                    }
+                    else if ((values = a.get("floatValue")) != null)
+                    {
+                        addProperty(properties, name, values, Float.class);
+                    }
+                    else if ((values = a.get("intValue")) != null)
+                    {
+                        addProperty(properties, name, values, Integer.class);
+                    }
+                    else if ((values = a.get("byteValue")) != null)
+                    {
+                        addProperty(properties, name, values, Byte.class);
+                    }
+                    else if ((values = a.get("charValue")) != null)
+                    {
+                        addProperty(properties, name, values, Character.class);
+                    }
+                    else if ((values = a.get("booleanValue")) != null)
+                    {
+                        addProperty(properties, name, values, Boolean.class);
+                    }
+                    else if ((values = a.get("shortValue")) != null)
+                    {
+                        addProperty(properties, name, values, Short.class);
+                    }
+                    else
+                    {
+                        throw new IllegalArgumentException("Missing Property value from annotation "
+                            + annotation + " in class " + m_className);
+                    }
+                }
+                writer.putJsonObject(attribute, properties);
+            }
+        }
+        catch (JSONException e)
+        {
+            throw new IllegalArgumentException("UNexpected exception while parsing Property from annotation "
+                + annotation + " in class " + m_className, e);
+        }
+    }
+    
+    /**
+     * Checks if a property contains values that are compatible with a give primitive type.
+     * 
+     * @param name the property name
+     * @param values the values for the property name
+     * @param type the primitive type.
+     * @return the same property values, possibly modified if the type is 'Character' (the strings are converted to their character integer values). 
+     */
+    private Object[] checkPropertyType(String name, Class<?> type, Object ... values)
+    {
+        if (type.equals(String.class))
+        {
+            return values;
+        } else if (type.equals(Long.class)) {
+            for (Object value : values) {
+                try {
+                    Long.valueOf(value.toString());
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+                        + " does not contain a valid Long value (" + value.toString() + ")");
+                }
+            }
+        } else if (type.equals(Double.class)) {
+            for (Object value : values) {
+                try {
+                    Double.valueOf(value.toString());
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+                        + " does not contain a valid Double value (" + value.toString() + ")");
+                }
+            }
+        } else if (type.equals(Float.class)) {
+            for (Object value : values) {
+                try {
+                    Float.valueOf(value.toString());
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+                        + " does not contain a valid Float value (" + value.toString() + ")");
+                }
+            }
+        } else if (type.equals(Integer.class)) {
+            for (Object value : values) {
+                try {
+                    Integer.valueOf(value.toString());
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+                        + " does not contain a valid Integer value (" + value.toString() + ")");
+                }
+            }
+        } else if (type.equals(Byte.class)) {
+            for (Object value : values) {
+                try {
+                    Byte.valueOf(value.toString());
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+                        + " does not contain a valid Byte value (" + value.toString() + ")");
+                }
+            }
+        } else if (type.equals(Character.class)) {
+            for (int i = 0; i < values.length; i++)
+            {
+                try
+                {
+                    // If the string is already an integer, don't modify it
+                    Integer.valueOf(values[i].toString());
+                }
+                catch (NumberFormatException e)
+                {
+                    // Converter the character string to its corresponding integer code.
+                    if (values[i].toString().length() != 1)
+                    {
+                        throw new IllegalArgumentException("Property \"" + name + "\" in class "
+                            + m_className + " does not contain a valid Character value (" + values[i] + ")");
+                    }
+                    try
+                    {
+                        values[i] = Integer.valueOf(values[i].toString().charAt(0));
+                    }
+                    catch (NumberFormatException e2)
+                    {
+                        throw new IllegalArgumentException("Property \"" + name + "\" in class "
+                            + m_className + " does not contain a valid Character value (" + values[i].toString()
+                            + ")");
+                    }
+                }
+            }
+        } else if (type.equals(Boolean.class)) {
+            for (Object value : values) {
+                if (! value.toString().equalsIgnoreCase("false") && ! value.toString().equalsIgnoreCase("true")) {
+                    throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+                        + " does not contain a valid Boolean value (" + value.toString() + ")");
+                }
+            }
+        } else if (type.equals(Short.class)) {
+            for (Object value : values) {
+                try {
+                    Short.valueOf(value.toString());
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+                        + " does not contain a valid Short value (" + value.toString() + ")");
+                }
+            }
+        }
+
+        return values;
+    }
+
+    /**
+     * Adds a key/value(s) pair in a properties map
+     * @param properties the target properties map
+     * @param name the property name
+     * @param value the property value(s)
+     * @param type the property type
+     * @throws JSONException 
+     */
+    private void addProperty(JSONObject props, String name, Object value, Class type) throws JSONException {
+        if (value.getClass().isArray())
+        {
+            Object[] array = (Object[]) value;
+            if (array.length == 1)
+            {
+                value = array[0];
+            }
+        }
+
+        if (type.equals(String.class))
+        {
+            props.put(name, value.getClass().isArray() ? new JSONArray(value) : value);
+        }
+        else
+        {
+            JSONObject jsonValue = new JSONObject();
+            jsonValue.put("type", type.getName());
+            jsonValue.put("value", value.getClass().isArray() ? new JSONArray(value) : value);
+            props.put(name, jsonValue);
+        }
+    }
+
+    /**
+     * Parse Inject annotation, used to inject some special classes in some fields
+     * (BundleContext/DependencyManager etc ...)
+     * @param annotation the Inject annotation
+     */
+    private void parseInject(Annotation annotation)
+    {      
+        if (Patterns.BUNDLE_CONTEXT.matcher(m_descriptor).matches())
+        {
+            m_bundleContextField = m_field;
+        }
+        else if (Patterns.DEPENDENCY_MANAGER.matcher(m_descriptor).matches())
+        {
+            m_dependencyManagerField = m_field;
+        }
+        else if (Patterns.COMPONENT.matcher(m_descriptor).matches())
+        {
+            m_componentField = m_field;
+        }
+        else
+        {
+            throw new IllegalArgumentException("@Inject annotation can't be applied on the field \"" + m_field
+                                               + "\" in class " + m_className);
+        }
+    }
+    
+    /**
+     * 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_writers
+     * List.
+     * @return true if one of the provided annotations has been found from the parsed class.
+     */
+    private void checkServiceDeclared(EntryType... types)
+    {
+        boolean ok = false;
+        if (m_writers.size() > 0)
+        {
+            for (EntryType type: types)
+            {
+                if (m_writers.get(m_writers.size() - 1).getEntryType() == type)
+                {
+                    ok = true;
+                    break;
+                }
+            }
+        }
+
+        if (!ok)
+        {
+            throw new IllegalStateException(
+                ": the class must be annotated with either one of the following types: "
+                    + Arrays.toString(types));
+        }
+    }
+
+    /**
+     * This method checks if the @Registered and/or @Unregistered annotations have been defined
+     * while they should not, because the component does not provide a service.
+     */
+    private void checkRegisteredUnregisteredNotPresent()
+    {
+        if (m_registeredMethod != null)
+        {
+            throw new IllegalStateException("@Registered annotation can't be used on a Component " +
+                                            " which does not provide a service (class=" + m_className + ")");
+
+        }
+        
+        if (m_unregisteredMethod != null)
+        {
+            throw new IllegalStateException("@Unregistered annotation can't be used on a Component " +
+                                            " which does not provide a service (class=" + m_className + ")");
+
+        }
+    }
+
+    /**
+     * Get an annotation attribute, and return a default value if its not present.
+     * @param <T> the type of the variable which is assigned to the return value of this method.
+     * @param annotation The annotation we are parsing
+     * @param name the attribute name to get from the annotation
+     * @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 annotation, String name, T defaultValue)
+    {
+        T value = (T) annotation.get(name);
+        return value != null ? value : defaultValue;
+    }
+}
\ No newline at end of file

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/plugin/bnd/AnnotationPlugin.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/plugin/bnd/AnnotationPlugin.java?rev=1587054&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/plugin/bnd/AnnotationPlugin.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.annotations/src/dm/annotation/plugin/bnd/AnnotationPlugin.java Sun Apr 13 17:23:51 2014
@@ -0,0 +1,159 @@
+/*
+ * 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 dm.annotation.plugin.bnd;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Map;
+import java.util.Set;
+
+import aQute.bnd.osgi.Analyzer;
+import aQute.bnd.osgi.Resource;
+import aQute.bnd.service.AnalyzerPlugin;
+import aQute.bnd.service.Plugin;
+import aQute.service.reporter.Reporter;
+
+/**
+ * This class is a BND plugin. It scans the target bundle and look for DependencyManager annotations.
+ * It can be directly used when using ant and can be referenced inside the ".bnd" descriptor, using
+ * the "-plugin" parameter.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AnnotationPlugin implements AnalyzerPlugin, Plugin {
+    private static final String IMPORT_SERVICE = "Import-Service";
+    private static final String EXPORT_SERVICE = "Export-Service";
+    private static final String LOGLEVEL = "log";
+    private static final String BUILD_IMPEXT = "build-import-export-service";
+    private BndLogger m_logger;
+    private Reporter m_reporter;
+    private boolean m_buildImportExportService;
+    private Map<String, String> m_properties;
+
+    public void setReporter(Reporter reporter) {
+        m_reporter = reporter;
+    }
+
+    public void setProperties(Map<String, String> map) {
+        m_properties = map;
+    }
+
+    /**
+     * This plugin is called after analysis of the JAR but before manifest
+     * generation. When some DM annotations are found, the plugin will add the corresponding 
+     * DM component descriptors under META-INF/ directory. It will also set the  
+     * "DependencyManager-Component" manifest header (which references the descriptor paths).
+     * 
+     * @param analyzer the object that is used to retrieve classes containing DM annotations.
+     * @return true if the classpath has been modified so that the bundle classpath must be reanalyzed
+     * @throws Exception on any errors.
+     */
+    public boolean analyzeJar(Analyzer analyzer) throws Exception {
+        m_logger = new BndLogger(m_reporter, analyzer.getBsn());
+
+        try {
+            init(analyzer);
+
+            // We'll do the actual parsing using a DescriptorGenerator object.
+            DescriptorGenerator generator = new DescriptorGenerator(analyzer, m_logger);
+
+            if (generator.execute()) {
+                // We have parsed some annotations: set the OSGi "DependencyManager-Component" header in the target bundle.
+                analyzer.setProperty("DependencyManager-Component", generator.getDescriptorPaths());
+
+                // Possibly set the Import-Service/Export-Service header
+                if (m_buildImportExportService) {
+                    // Don't override Import-Service header, if it is found from the bnd directives.
+                    if (analyzer.getProperty(IMPORT_SERVICE) == null) {
+                        buildImportExportService(analyzer, IMPORT_SERVICE, generator.getImportService());
+                    }
+
+                    // Don't override Export-Service header, if already defined
+                    if (analyzer.getProperty(EXPORT_SERVICE) == null) {
+                        buildImportExportService(analyzer, EXPORT_SERVICE, generator.getExportService());
+                    }
+                }
+
+                // And insert the generated descriptors into the target bundle.
+                Map<String, Resource> resources = generator.getDescriptors();
+                for (Map.Entry<String, Resource> entry : resources.entrySet()) {
+                    analyzer.getJar().putResource(entry.getKey(), entry.getValue());
+                }
+
+                // Insert the metatype resource, if any.
+                Resource metaType = generator.getMetaTypeResource();
+                if (metaType != null) {
+                    analyzer.getJar().putResource("OSGI-INF/metatype/metatype.xml", metaType);
+                }
+            }
+        }
+
+        catch (Throwable t) {
+            m_logger.error(parse(t));
+        }
+
+        finally {
+            m_logger.close();
+        }
+
+        return false; // do not reanalyze bundle classpath because our plugin has not changed it.
+    }
+
+    private void init(Analyzer analyzer) {
+        m_logger.setLevel(parseOption(m_properties, LOGLEVEL, BndLogger.Level.Warn.toString()));
+        m_buildImportExportService = parseOption(m_properties, BUILD_IMPEXT, true);
+        analyzer.setExceptions(true);
+        m_logger.info("Initialized Bnd DependencyManager plugin: buildImportExport=%b", m_buildImportExportService);
+    }
+
+    private String parseOption(Map<String, String> opts, String name, String def) {
+        String value = opts.get(name);
+        return value == null ? def : value;
+    }
+
+    private boolean parseOption(Map<String, String> opts, String name, boolean def) {
+        String value = opts.get(name);
+        return value == null ? def : Boolean.valueOf(value);
+    }
+
+    private void buildImportExportService(Analyzer analyzer, String header, Set<String> services) {
+        m_logger.info("building %s header with the following services: %s", header, services);
+        if (services.size() > 0) {
+            StringBuilder sb = new StringBuilder();
+            for (String service : services) {
+                sb.append(service);
+                sb.append(",");
+            }
+            sb.setLength(sb.length() - 1); // skip last comma
+            analyzer.setProperty(header, sb.toString());
+        }
+    }
+
+    /**
+     * Parse an exception into a string.
+     * @param e The exception to parse
+     * @return the parsed exception
+     */
+    private static String parse(Throwable e) {
+        StringWriter buffer = new StringWriter();
+        PrintWriter pw = new PrintWriter(buffer);
+        e.printStackTrace(pw);
+        return (buffer.toString());
+    }
+}



Mime
View raw message