- aligned AspectService and AdapterService with new added/changed/removed callbacks introdoced in the API.
- improved annotation plugin logging: a "log" configuration can now be specified in the maven or bnd plugin.
- fixed bug when parsing ConfigurationDependency Annotation: the "updated" field was ignored.
- Generate DM metadata descriptors under META-INF instead of OSGI-INF



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1086716 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java
index 26507d5..5d0bffd 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java
@@ -84,4 +84,28 @@
      * By default, the default constructor of the annotated class is used.
      */
     String factoryMethod() default "";
+    
+    /**
+     * Sets the field name where to inject the original service. By default, the original service is injected
+     * in any attributes in the aspect implementation that are of the same type as the aspect interface.
+     */
+    String field() default "";
+    
+    /**
+     * The callback method to be invoked when the original service is available. This attribute can't be mixed with
+     * the field attribute.
+     */
+    String added() default "";
+
+    /**
+     * The callback method to be invoked when the original service properties have changed. When this attribute is used, 
+     * then the added attribute must also be used.
+     */
+    String changed() default "";
+
+    /**
+     * The callback method to invoke when the service is lost. When this attribute is used, then the added attribute 
+     * must also be used.
+     */
+    String removed() default "";
 }
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AspectService.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AspectService.java
index fcffe88..12cf328 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AspectService.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AspectService.java
@@ -87,6 +87,24 @@
     String field() default "";
     
     /**
+     * The callback method to be invoked when the original service is available. This attribute can't be mixed with
+     * the field attribute.
+     */
+    String added() default "";
+
+    /**
+     * The callback method to be invoked when the original service properties have changed. When this attribute is used, 
+     * then the added attribute must also be used.
+     */
+    String changed() default "";
+
+    /**
+     * The callback method to invoke when the service is lost. When this attribute is used, then the added attribute 
+     * must also be used.
+     */
+    String removed() default "";
+    
+    /**
      * Sets the static method used to create the AspectService implementation instance. The
      * default constructor of the annotated class is used. The factoryMethod can be used to provide a specific
      * aspect implements, like a DynamicProxy.
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
index a2e7720..6e25fe4 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
@@ -75,7 +75,7 @@
     private final static String A_RESOURCE_ADAPTER_SERVICE = "L" + ResourceAdapterService.class.getName().replace('.', '/') + ";";
     private final static String A_FACTORYCONFIG_ADAPTER_SERVICE = "L" + FactoryConfigurationAdapterService.class.getName().replace('.', '/') + ";";
 
-    private Reporter m_reporter;
+    private Logger m_logger;
     private String m_className;
     private String[] m_interfaces;
     private boolean m_isField;
@@ -103,22 +103,13 @@
      * Makes a new Collector for parsing a given class.
      * @param reporter the object used to report logs.
      */
-    public AnnotationCollector(Reporter reporter, MetaType metaType)
+    public AnnotationCollector(Logger reporter, MetaType metaType)
     {
-        m_reporter = reporter;
+        m_logger = reporter;
         m_metaType = metaType;
     }
 
     /**
-     * Returns the log reporter.
-     * @return the log reporter.
-     */
-    public Reporter getReporter()
-    {
-        return m_reporter;
-    }
-
-    /**
      * Parses the name of the class.
      * @param access the class access
      * @param name the class name (package are "/" separated).
@@ -127,7 +118,7 @@
     public void classBegin(int access, String name)
     {
         m_className = name.replace('/', '.');
-        m_reporter.trace("class name: " + m_className);
+        m_logger.debug("class name: %s", m_className);
     }
 
     /**
@@ -141,7 +132,7 @@
         {
             m_interfaces[i] = interfaces[i].replace('/', '.');
         }
-        m_reporter.trace("implements: %s", Arrays.toString(m_interfaces));
+        m_logger.debug("implements: %s", Arrays.toString(m_interfaces));
     }
 
     /**
@@ -150,7 +141,7 @@
     @Override
     public void method(int access, String name, String descriptor)
     {
-        m_reporter.trace("Parsed method %s, descriptor=%s", name, descriptor);
+        m_logger.debug("Parsed method %s, descriptor=%s", name, descriptor);
         m_isField = false;
         m_method = name;
         m_descriptor = descriptor;
@@ -163,7 +154,7 @@
     @Override
     public void field(int access, String name, String descriptor)
     {
-        m_reporter.trace("Parsed field %s, descriptor=%s", name, descriptor);
+        m_logger.debug("Parsed field %s, descriptor=%s", name, descriptor);
         m_isField = true;
         m_field = name;
         m_descriptor = descriptor;
@@ -175,7 +166,7 @@
     @Override
     public void annotation(Annotation annotation)
     {
-        m_reporter.trace("Parsed annotation: %s", annotation);
+        m_logger.debug("Parsed annotation: %s", annotation);
 
         if (annotation.getName().equals(A_COMPONENT))
         {
@@ -243,6 +234,47 @@
         }
     }
 
+    /**
+     * 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)
+        {
+            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());
+        }
+    }
+        
     private void parseComponentAnnotation(Annotation annotation)
     {
         EntryWriter writer = new EntryWriter(EntryType.Component);
@@ -397,6 +429,9 @@
         // 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);
 
@@ -435,8 +470,8 @@
         // Parse Aspect properties.
         parseProperties(annotation, EntryParam.properties, writer);
         
-        // Parse aspect impl field where to inject the original service.
-        writer.putString(annotation, EntryParam.field, null);
+        // Parse field/added/changed/removed attributes
+        parseAspectOrAdapterCallbackMethods(annotation, writer);
 
         // Parse service interface this aspect is applying to
         Object service = annotation.get(EntryParam.service.toString());
@@ -466,6 +501,36 @@
         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());
+
+        // "field" and "added/changed/removed" attributes can't be mixed
+        if (field != null && (added != null || changed != null || removed != null))
+        {
+            throw new IllegalStateException("Annotation " + annotation + "can't applied on " + m_className
+                    + " can't mix \"field\" attribute with \"added/changed/removed\" attributes");
+        }
+        
+        // changed/removed callbacks are allowed only if added callback is defined
+        if (field == null && added == null && (changed != null || removed != null))
+        {
+            throw new IllegalStateException("Annotation " + annotation + " applied on " + m_className
+                    + " must define an \"added\" callback");
+        }
+        
+        // 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);
+    }
+
     /**
      * Parses an AspectService annotation.
      * @param annotation
@@ -500,6 +565,9 @@
         
         // Parse factoryMethod attribute
         writer.putString(annotation, EntryParam.factoryMethod, null);
+        
+        // Parse field/added/changed/removed attributes
+        parseAspectOrAdapterCallbackMethods(annotation, writer);
     }
 
     /**
@@ -758,7 +826,7 @@
             m_metaType.add(ocd);
             MetaType.Designate designate = new MetaType.Designate(pid, factory);
             m_metaType.add(designate);
-            m_reporter.warning("Parsed MetaType Properties from class " + m_className);
+            m_logger.info("Parsed MetaType Properties from class " + m_className);
         }
     }
 
@@ -834,56 +902,15 @@
     /**
      * 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 properties The annotation we are parsing
+     * @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 properties, String name, T defaultValue)
+    private <T> T get(Annotation annotation, String name, T defaultValue)
     {
-        T value = (T) properties.get(name);
+        T value = (T) annotation.get(name);
         return value != null ? value : defaultValue;
-    }
-
-    /**
-     * 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)
-        {
-            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_reporter.warning(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());
-        }
-    }
-}
+    }        
+}
\ No newline at end of file
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
index 0709918..88343aa 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
@@ -18,19 +18,27 @@
  */
 package org.apache.felix.dm.annotation.plugin.bnd;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Iterator;
 import java.util.Map;
 
 import aQute.bnd.service.AnalyzerPlugin;
+import aQute.bnd.service.Plugin;
 import aQute.lib.osgi.Analyzer;
 import aQute.lib.osgi.Resource;
+import aQute.libg.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.
  */
-public class AnnotationPlugin implements AnalyzerPlugin
+public class AnnotationPlugin implements AnalyzerPlugin, Plugin
 {
+    private BndLogger m_logger;
+    private Reporter m_reporter;
+
     /**
      * 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 
@@ -43,27 +51,85 @@
      */
     public boolean analyzeJar(Analyzer analyzer) throws Exception
     {
-        // We'll do the actual parsing using a DescriptorGenerator object.
-        DescriptorGenerator generator = new DescriptorGenerator(analyzer);
-        if (generator.execute())
+        analyzer.setExceptions(true);
+        try {
+            // 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());
+
+                // 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());
+                }
+
+                // Inser the metatype resource, if any.
+                Resource metaType = generator.getMetaTypeResource();
+                if (metaType != null)
+                {
+                    analyzer.getJar().putResource("OSGI-INF/metatype/metatype.xml", metaType);
+                }
+            }
+        } 
+        
+        catch (Throwable t)
         {
-            // We have parsed some annotations: set the OSGi "DependencyManager-Component" header in the target bundle.
-            analyzer.setProperty("DependencyManager-Component", generator.getDescriptorPaths());
+            m_logger.error(parse(t));
+        }
 
-            // And insert the generated descriptors into the target bundle.
-            Map<String, Resource> resources = generator.getDescriptors();
-            for (Map.Entry<String, Resource> entry : resources.entrySet())
+        // Collect all logs and write it into the analyzer.
+        m_logger.getLogs(analyzer);
+        
+        // When some errors are present in the analyzer logs: it seems that the Bnd ANT task
+        // does not report it. So, to work around, we just log the errors ourself, and
+        // we throw an Error in case some errors are detected, in order to prevent the bnd ANT
+        // task from generating the target bundle.
+        if (analyzer.getWarnings().size() > 0)
+        {
+            for (Iterator<String> e = analyzer.getWarnings().iterator(); e.hasNext();)
             {
-                analyzer.getJar().putResource(entry.getKey(), entry.getValue());
-            }
-
-            // Inser the metatype resource, if any.
-            Resource metaType = generator.getMetaTypeResource();
-            if (metaType != null)
+                System.out.println(e.next());
+            }   
+            analyzer.getWarnings().clear();
+        }
+        
+        if (analyzer.getErrors().size() > 0)
+        {
+            for (Iterator<String> e = analyzer.getErrors().iterator(); e.hasNext();)
             {
-                analyzer.getJar().putResource("OSGI-INF/metatype/metatype.xml", metaType);
+                System.err.println(e.next());
             }
+            analyzer.getErrors().clear();
+            throw new Error("DM Annotation plugin failure");
         }
         return false;
     }
+
+    public void setProperties(Map<String, String> map)
+    {
+        String logLevel = map.get("log");
+        m_logger = new BndLogger(logLevel == null ? "error" : logLevel);
+    }
+
+    public void setReporter(Reporter reporter)
+    {
+        m_reporter = reporter;
+    }
+    
+    /**
+     * 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());
+    } 
 }
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java
new file mode 100644
index 0000000..2c868a8
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/BndLogger.java
@@ -0,0 +1,78 @@
+/*
+ * 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.lib.osgi.Analyzer;
+import aQute.lib.osgi.Processor;
+
+public class BndLogger extends Logger
+{
+    private Processor m_processor = new Processor();
+
+    private final int m_level;
+
+    public BndLogger(String level)
+    {
+        m_level = parseLogLevel(level);
+    }
+    
+    @Override
+    public void error(String msg, Object ... args)
+    {
+        m_processor.error(msg, args);
+    }
+    
+    @Override
+    public void error(String msg, Throwable err, Object ... args)
+    {
+        m_processor.error(msg, err, args);
+    }
+
+    @Override
+    public void warn(String msg , Object ... args)
+    {
+        if (m_level >= WARN)
+        {
+            m_processor.warning(msg, args);
+        }
+    }
+    
+    @Override
+    public void info(String msg, Object ... args)
+    {
+        if (m_level >= INFO)
+        {
+            m_processor.warning(msg, args);
+        }
+    }
+
+    @Override
+    public void debug(String msg, Object ... args)
+    {
+        if (m_level >= DEBUG)
+        {
+            m_processor.warning(msg, args);
+        }
+    }
+    
+    public void getLogs(Analyzer to)
+    {
+        to.getInfo(m_processor, "DependencyManager: ");
+    }
+}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
index 6b301cc..25c2be3 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
@@ -30,14 +30,13 @@
 import aQute.lib.osgi.Analyzer;
 import aQute.lib.osgi.Clazz;
 import aQute.lib.osgi.EmbeddedResource;
-import aQute.lib.osgi.Processor;
 import aQute.lib.osgi.Resource;
 import aQute.lib.osgi.Clazz.QUERY;
 
 /**
  * This helper parses all classes which contain DM annotations, and generates the corresponding component descriptors.
  */
-public class DescriptorGenerator extends Processor
+public class DescriptorGenerator
 {
     /**
      * This is the bnd analyzer used to lookup classes containing DM annotations.
@@ -57,13 +56,19 @@
     private Resource m_metaTypeResource;
 
     /**
+     * Object used to collect logs.
+     */
+    private final Logger m_logger;
+
+    /**
      * Creates a new descriptor generator.
      * @param analyzer The bnd analyzer used to lookup classes containing DM annotations.
+     * @param debug 
      */
-    public DescriptorGenerator(Analyzer analyzer)
+    public DescriptorGenerator(Analyzer analyzer, Logger logger)
     {
-        super(analyzer);
         m_analyzer = analyzer;
+        m_logger = logger;
     }
 
     /**
@@ -71,64 +76,41 @@
      * @return true if some annotations were successfully parsed, false if not. corresponding generated 
      * descriptors can then be retrieved by invoking the getDescriptors/getDescriptorPaths methods.
      */
-    public boolean execute()
+    public boolean execute() throws Exception
     {
         boolean annotationsFound = false;
         Clazz clazz = null;
-        try
-        {
-            // Try to locate any classes in the wildcarded universe
-            // that are annotated with the DependencyManager "Service" annotations.
-            Collection<Clazz> expanded = m_analyzer.getClasses("",
-                // Parse everything
-                QUERY.NAMED.toString(), "*");
+        // Try to locate any classes in the wildcarded universe
+        // that are annotated with the DependencyManager "Service" annotations.
+        Collection<Clazz> expanded = m_analyzer.getClasses("",
+                                                           // Parse everything
+                                                           QUERY.NAMED.toString(), "*");
 
-            // Create the object which will collect Config Admin MetaTypes.
-            MetaType metaType = new MetaType();
+        // Create the object which will collect Config Admin MetaTypes.
+        MetaType metaType = new MetaType();
             
-            for (Clazz c : expanded)
-            {
-                clazz = c;
-                // Let's parse all annotations from that class !
-                AnnotationCollector reader = new AnnotationCollector(this, metaType);
-                c.parseClassFileWithCollector(reader);
-                if (reader.finish()) 
-                {
-                    // And store the generated component descriptors in our resource list.
-                    String name = c.getFQN();
-                    Resource resource = createComponentResource(reader);
-                    m_resources.put("OSGI-INF/dependencymanager/" + name, resource);
-                    annotationsFound = true;
-                }
-            }
-
-            // If some Meta Types have been parsed, then creates the corresponding resource file.
-            if (metaType.getSize() > 0) {
-                m_metaTypeResource = createMetaTypeResource(metaType);
-            }
-            return annotationsFound;
-        }
-        catch (Throwable err)
+        for (Clazz c : expanded)
         {
-            StringBuilder sb = new StringBuilder();
-            sb.append("Error while scanning annotations");
-            if (clazz != null)
+            clazz = c;
+            // Let's parse all annotations from that class !
+            AnnotationCollector reader = new AnnotationCollector(m_logger, metaType);
+            c.parseClassFileWithCollector(reader);
+            if (reader.finish())
             {
-                sb.append(" from class " + clazz);
+                // And store the generated component descriptors in our resource list.
+                String name = c.getFQN();
+                Resource resource = createComponentResource(reader);
+                m_resources.put("META-INF/dependencymanager/" + name, resource);
+                annotationsFound = true;
             }
-            sb.append(": ");
-            sb.append(parse(err));
-            error(sb.toString());
-            return false;
         }
 
-        finally
+        // If some Meta Types have been parsed, then creates the corresponding resource file.
+        if (metaType.getSize() > 0)
         {
-            // Collect all logs (warns/errors) from our processor and store them into the analyze.
-            // Bnd will log them, if necessary.
-            m_analyzer.getInfo(this, "DependencyManager: ");
-            close();
+            m_metaTypeResource = createMetaTypeResource(metaType);
         }
+        return annotationsFound;
     }
 
     /**
@@ -196,17 +178,5 @@
         byte[] data = out.toByteArray();
         out.close();
         return new EmbeddedResource(data, 0);    
-    }
-    
-    /**
-     * 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());
-    } 
-}
+    }    
+}
\ No newline at end of file
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Logger.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Logger.java
new file mode 100644
index 0000000..a421aab
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Logger.java
@@ -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 org.apache.felix.dm.annotation.plugin.bnd;
+
+public abstract class Logger
+{    
+    protected final static int ERROR = 1;
+    protected final static int WARN = 2;
+    protected final static int INFO = 3;
+    protected final static int DEBUG = 4;
+    
+    protected int parseLogLevel(String level)
+    {
+        if (level == null || level.regionMatches(true, 0, "err", 0, "err".length())) 
+        {
+            return ERROR;
+        }
+        else if (level.regionMatches(true, 0, "warn", 0, "warn".length()))
+        {
+            return WARN;
+        }
+        else if (level.regionMatches(true, 0, "info", 0, "info".length()))
+        {
+            return INFO;
+        }
+        else if (level.regionMatches(true, 0, "debug", 0, "debug".length()))
+        {
+            return DEBUG;
+        }
+        else
+        {
+            throw new IllegalArgumentException("Invalid log level value: " + level + " (valid values are \"error\", \"warn\", \"debug\")");
+        }
+    }
+    
+    public abstract void error(String msg, Object ... args);
+    public abstract void error(String msg, Throwable err, Object ... args);
+    public abstract void warn(String msg , Object ... args);
+    public abstract void info(String msg , Object ... args);
+    public abstract void debug(String msg, Object ... args);    
+}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/mvn/AnnotationMojo.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/mvn/AnnotationMojo.java
index f65d5ac..f524a86 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/mvn/AnnotationMojo.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/mvn/AnnotationMojo.java
@@ -19,7 +19,6 @@
 package org.apache.felix.dm.annotation.plugin.mvn;
 
 import java.io.File;
-import java.util.Iterator;
 import java.util.Map;
 
 import org.apache.felix.dm.annotation.plugin.bnd.DescriptorGenerator;
@@ -59,6 +58,13 @@
      * @required
      */
     private String m_artifactExtension;
+    
+    /**
+     * If set, configures the log level.
+     *
+     * @parameter alias="log"
+     */
+    private String m_log;
 
     /**
      * Executes this mojo. We'll use the bnd library in order to scan classes from our target bundle.
@@ -80,7 +86,7 @@
             analyzer.analyze();
 
             // This helper class will parse classes using the analyzer we just created.
-            DescriptorGenerator generator = new DescriptorGenerator(analyzer);
+            DescriptorGenerator generator = new DescriptorGenerator(analyzer, new MvnLogger(getLog(), m_log));
 
             // Start scanning
             if (generator.execute())
@@ -104,25 +110,6 @@
                 }
                 copy(jar, target);
             }
-
-            // Check if some errors have to be logged.
-            if (analyzer.getErrors().size() != 0)
-            {
-                for (Iterator<String> e = analyzer.getErrors().iterator(); e.hasNext();)
-                {
-                    getLog().error(e.next());
-                }
-                throw new MojoExecutionException("Errors while generating dm descriptors");
-            }
-
-            // Check if some warnings have to be logged.
-            if (analyzer.getWarnings().size() != 0)
-            {
-                for (Iterator<String> e = analyzer.getWarnings().iterator(); e.hasNext();)
-                {
-                    getLog().info(e.next());
-                }
-            }
         }
 
         catch (MojoExecutionException e)
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/mvn/MvnLogger.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/mvn/MvnLogger.java
new file mode 100644
index 0000000..a36d716
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/mvn/MvnLogger.java
@@ -0,0 +1,70 @@
+/*
+ * 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.mvn;
+
+import org.apache.felix.dm.annotation.plugin.bnd.Logger;
+import org.apache.maven.plugin.logging.Log;
+
+public class MvnLogger extends Logger
+{
+    private final Log m_log;
+    private final int m_level;
+
+    public MvnLogger(Log log, String level)
+    {
+        m_log = log;
+        m_level = parseLogLevel(level);
+    }
+    
+    @Override
+    public void error(String msg, Object... args)
+    {
+        m_log.error("DependencyManager: " + String.format(msg, args));
+    }
+
+    @Override
+    public void error(String msg, Throwable err, Object... args)
+    {
+        m_log.error("DependencyManager: " + String.format(msg, args), err);
+    }
+
+    @Override
+    public void warn(String msg, Object... args)
+    {
+        m_log.warn("DependencyManager: " + String.format(msg, args));
+    }
+    
+    @Override
+    public void info(String msg, Object... args)
+    {
+        if (m_level >= INFO)
+        {
+            m_log.info("DependencyManager: " + String.format(msg, args));
+        }
+    }
+    
+    @Override
+    public void debug(String msg, Object... args)
+    {
+        if (m_level >= DEBUG)
+        {
+            m_log.info("DependencyManager: " + String.format(msg, args));
+        }
+    }
+}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AdapterServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AdapterServiceBuilder.java
index 98bd244..04864c8 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AdapterServiceBuilder.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AdapterServiceBuilder.java
@@ -47,8 +47,40 @@
         String[] provides = srvMeta.getStrings(Params.provides, null);
         Dictionary<String, Object> adapterProperties = srvMeta.getDictionary(Params.properties, null);
         Class<?> adapteeService = b.loadClass(srvMeta.getString(Params.adapteeService));
-        String adapteeFilter = srvMeta.getString(Params.adapteeFilter, null);     
-        Component service = dm.createAdapterService(adapteeService, adapteeFilter);
+        String adapteeFilter = srvMeta.getString(Params.adapteeFilter, null);   
+        String field = srvMeta.getString(Params.field, null);
+        String added = srvMeta.getString(Params.added, null);
+        String changed = srvMeta.getString(Params.changed, null);
+        String removed = srvMeta.getString(Params.removed, null);
+        
+        if (field != null && (added != null || changed != null || removed != null))
+        {
+            throw new IllegalArgumentException("autoconfig field " + field + " cant be defined with both added/changed/removed calllbacks");
+        }
+        if (field == null && added == null && (changed != null || removed != null))
+        {
+            throw new IllegalArgumentException("missing added callback");
+        }
+        
+        Component service;
+        
+        if (field != null)
+        {
+            service = dm.createAdapterService(adapteeService, adapteeFilter, field);
+        }
+        else
+        {
+            if (added != null)
+            {
+                service = dm.createAdapterService(adapteeService, adapteeFilter, added, changed, removed);
+
+            }
+            else
+            {
+                service = dm.createAdapterService(adapteeService, adapteeFilter);
+            }
+        }
+        
         service.setInterface(provides, adapterProperties);
         
         String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AspectServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AspectServiceBuilder.java
index 9c0c42a..4d3d507 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AspectServiceBuilder.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AspectServiceBuilder.java
@@ -50,10 +50,44 @@
         String implClassName = srvMeta.getString(Params.impl);
         Object implClass = b.loadClass(implClassName);
         String field = srvMeta.getString(Params.field, null);
-        String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
-        Component service =
-                dm.createAspectService(serviceInterface, serviceFilter, ranking, field)
+        String added = srvMeta.getString(Params.added, null);
+        String changed = srvMeta.getString(Params.changed, null);
+        String removed = srvMeta.getString(Params.removed, null);
+        
+        if (field != null && (added != null || changed != null || removed != null))
+        {
+            throw new IllegalArgumentException("autoconfig field " + field + " cant be defined with both added/changed/removed calllbacks");
+        }
+        if (field == null && added == null && (changed != null || removed != null))
+        {
+            throw new IllegalArgumentException("missing added callback");
+        }
+
+        Component service;
+        if (field != null)
+        {
+            service =
+                    dm.createAspectService(serviceInterface, serviceFilter, ranking, field)
+                            .setServiceProperties(aspectProperties);
+        } 
+        else
+        {
+            if (added != null)
+            {
+                service =
+                    dm.createAspectService(serviceInterface, serviceFilter, ranking, added, changed, removed)
                         .setServiceProperties(aspectProperties);
+            } 
+            else
+            {
+                service =
+                    dm.createAspectService(serviceInterface, serviceFilter, ranking)
+                        .setServiceProperties(aspectProperties);
+            }
+ 
+        }
+        
+        String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
         if (factoryMethod == null)
         {
             service.setImplementation(implClass);
diff --git a/dependencymanager/samples.annotation/directives.bnd b/dependencymanager/samples.annotation/directives.bnd
index 510e3f5..dc246ef 100644
--- a/dependencymanager/samples.annotation/directives.bnd
+++ b/dependencymanager/samples.annotation/directives.bnd
@@ -2,4 +2,4 @@
 Bundle-SymbolicName: org.apache.felix.dependencymanager.samples.annotation
 Import-Package: *
 Private-Package: org.apache.felix.dm.samples.annotation
--plugin org.apache.felix.dm.annotation.plugin.bnd.AnnotationPlugin
+-plugin org.apache.felix.dm.annotation.plugin.bnd.AnnotationPlugin;log=warn
diff --git a/dependencymanager/samples.annotation/pom.xml b/dependencymanager/samples.annotation/pom.xml
index c8be694..d6d4c68 100644
--- a/dependencymanager/samples.annotation/pom.xml
+++ b/dependencymanager/samples.annotation/pom.xml
@@ -66,6 +66,9 @@
 						<goals>

 							<goal>scan</goal>

 						</goals>

+						<configuration>

+							<log>warn</log>

+						</configuration>

 					</execution>

 				</executions>

 			</plugin>