Fix FELIX-3461 - Issue with collected metadata and manipulation metadata on re-manipulation

* Fix the constructor argument shift by not moving the annotations
* Fix the method name when it is a manipulated method
* Cleanup the manipulation metadata



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1327153 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/manipulator/pom.xml b/ipojo/manipulator/manipulator/pom.xml
index 99cb26d..a178fbe 100644
--- a/ipojo/manipulator/manipulator/pom.xml
+++ b/ipojo/manipulator/manipulator/pom.xml
@@ -55,12 +55,18 @@
             <artifactId>org.apache.felix.ipojo.annotations</artifactId>
             <version>1.8.0</version>
         </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
-            <version>1.8.5</version>
-            <scope>test</scope>
-        </dependency>
+      <dependency>
+          <groupId>org.mockito</groupId>
+          <artifactId>mockito-all</artifactId>
+          <version>1.8.5</version>
+          <scope>test</scope>
+      </dependency>
+      <dependency>
+          <groupId>org.osgi</groupId>
+          <artifactId>org.osgi.core</artifactId>
+          <version>4.2.0</version>
+          <scope>test</scope>
+      </dependency>
     </dependencies>
     <build>
         <plugins>
@@ -140,9 +146,10 @@
                 <directory>.</directory>
                 <targetPath>META-INF</targetPath>
                 <includes>
-                    <include>LICENSE*</include>
-                    <include>NOTICE*</include>
-                    <include>DEPENDENCIES*</include>
+                    <include>LICENSE</include>
+                    <include>LICENSE.asm</include>
+                    <include>NOTICE</include>
+                    <include>DEPENDENCIES</include>
                 </includes>
             </resource>
         </resources>
diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java
index fe073f3..8e23bf1 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java
@@ -102,6 +102,10 @@
             return null;

         }

 

+        if (isManipulatedField(name)) {

+            return null;

+        }

+

         Type type = Type.getType(desc);

         if (type.getSort() == Type.ARRAY) {

             if (type.getInternalName().startsWith("L")) {

@@ -120,6 +124,12 @@
         return null;

     }

 

+    private boolean isManipulatedField(String name) {

+        return ((MethodCreator.IM_FIELD.equals(name))

+                || (name.startsWith(MethodCreator.FIELD_FLAG_PREFIX))

+                || (name.startsWith(MethodCreator.METHOD_FLAG_PREFIX)));

+    }

+

     /**

      * Add the inner class to the list of inner class to manipulate.

      * The method checks that the inner class is really owned by the implementation class.

@@ -189,17 +199,16 @@
         if (!name.equals("<clinit>")) {

 

             if (name.equals("<init>")) {

-                final MethodDescriptor md = new MethodDescriptor("$init", desc);

-                m_methods.add(md);

-                if (m_supportAnnotation) {

-                    return new AnnotationCollector(md);

+                if (!isGeneratedConstructor(name, desc)) {

+                    final MethodDescriptor md = new MethodDescriptor("$init", desc);

+                    m_methods.add(md);

+                    if (m_supportAnnotation) {

+                        return new AnnotationCollector(md);

+                    }

                 }

             } else {

                 // no constructors.

-                if (!(name.startsWith("_get") || // Avoid getter method

-                        name.startsWith("_set") || // Avoid setter method

-                        name.equals("_setComponentManager") || // Avoid the set method

-                        name.equals("getComponentInstance"))) { // Avoid the getComponentInstance method

+                if (!isGeneratedMethod(name, desc)) {

                     final MethodDescriptor md = new MethodDescriptor(name, desc);

                     m_methods.add(md);

                     if (m_supportAnnotation) {

@@ -213,6 +222,56 @@
         return null;

     }

 

+    private boolean isGeneratedConstructor(String name, String desc) {

+        return ("<init>".equals(name) && isFirstArgumentInstanceManager(desc));

+    }

+

+    private boolean isFirstArgumentInstanceManager(String desc) {

+        Type[] types = Type.getArgumentTypes(desc);

+        if (types != null && (types.length >= 1)) {

+            return Type.getType("Lorg/apache/felix/ipojo/InstanceManager;")

+                            .equals(types[0]);

+        }

+        return false;

+    }

+

+    private boolean isGeneratedMethod(String name, String desc) {

+        return isGetterMethod(name, desc)

+                || isSetterMethod(name, desc)

+                || isSetInstanceManagerMethod(name)

+                || isGetComponentInstanceMethod(name, desc)

+                || isManipulatedMethod(name);

+    }

+

+    private boolean isGetterMethod(String name, String desc) {

+        // TYPE __getXXX()

+        Type[] arguments = Type.getArgumentTypes(desc);

+        return (name.startsWith("__get")

+                && (arguments.length == 0)

+                && !Type.VOID_TYPE.equals(Type.getReturnType(desc)));

+    }

+

+    private boolean isSetterMethod(String name, String desc) {

+        // void __setXXX(TYPE)

+        Type[] arguments = Type.getArgumentTypes(desc);

+        return (name.startsWith("__set")

+                && (arguments.length == 1)

+                && Type.VOID_TYPE.equals(Type.getReturnType(desc)));

+    }

+

+    private boolean isSetInstanceManagerMethod(String name) {

+        return name.startsWith("_setInstanceManager");

+    }

+

+    private boolean isGetComponentInstanceMethod(String name, String desc) {

+        return (name.startsWith("getComponentInstance")

+                && Type.getType("Lorg/apache/felix/ipojo/ComponentInstance;").equals(Type.getReturnType(desc)));

+    }

+

+    private boolean isManipulatedMethod(String name) {

+        return (name.startsWith(MethodCreator.PREFIX));

+    }

+

     /**

      * Get collected interfaces.

      * @return the interfaces implemented by the component class.

@@ -293,6 +352,18 @@
                 m_method.addParameterAnnotation(id, ann);

                 return ann;

             }

+            

+            /*

+             * It is harmless to keep injected parameter annotations on original constructor

+             * for correct property resolution in case of re-manipulation

+             */

+            if(m_method.getName().equals("$init"))

+            {

+            	AnnotationDescriptor ann = new AnnotationDescriptor(name, visible);

+                m_method.addParameterAnnotation(id, ann);

+                return ann;

+            }

+            

             return null;

         }

 

diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ConstructorCodeAdapter.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ConstructorCodeAdapter.java
index 144219c..4cc76ee 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ConstructorCodeAdapter.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ConstructorCodeAdapter.java
@@ -20,10 +20,14 @@
 

 import java.util.Set;

 

+import org.apache.felix.ipojo.manipulation.ClassChecker.AnnotationDescriptor;

+import org.apache.felix.ipojo.manipulation.annotations.CustomAnnotationVisitor;

+import org.apache.felix.ipojo.manipulation.annotations.MetadataCollector;

 import org.objectweb.asm.AnnotationVisitor;

 import org.objectweb.asm.Label;

 import org.objectweb.asm.MethodVisitor;

 import org.objectweb.asm.Opcodes;

+import org.objectweb.asm.Type;

 import org.objectweb.asm.commons.GeneratorAdapter;

 

 

@@ -32,6 +36,7 @@
  * This class adds an instance manager argument (so switch variable index).

  * Moreover, it adapts field accesses to delegate accesses to the instance

  * manager if needed.

+ *

  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

  */

 public class ConstructorCodeAdapter extends GeneratorAdapter implements Opcodes {

@@ -61,12 +66,13 @@
     /**

      * PropertyCodeAdapter constructor.

      * A new FiledCodeAdapter should be create for each method visit.

-     * @param mv the MethodVisitor

-     * @param owner the name of the class

+     *

+     * @param mv     the MethodVisitor

+     * @param owner  the name of the class

      * @param fields the list of contained fields

      * @param access the constructor access

-     * @param desc the constructor descriptor

-     * @param name the name

+     * @param desc   the constructor descriptor

+     * @param name   the name

      */

     public ConstructorCodeAdapter(final MethodVisitor mv, final String owner, Set<String> fields, int access, String name, String desc, String superClass) {

         super(mv, access, name, desc);

@@ -81,10 +87,11 @@
      * If the annotation is visible, the annotation is removed. In fact

      * the annotation was already moved to the method replacing this one.

      * If the annotation is not visible, this annotation is kept on this method.

-     * @param name the name of the annotation

+     *

+     * @param name    the name of the annotation

      * @param visible the annotation visibility

      * @return the <code>null</code> if the annotation is visible, otherwise returns

-     * {@link GeneratorAdapter#visitAnnotation(String, boolean)}

+     *         {@link GeneratorAdapter#visitAnnotation(String, boolean)}

      * @see org.objectweb.asm.MethodAdapter#visitAnnotation(java.lang.String, boolean)

      */

     public AnnotationVisitor visitAnnotation(String name, boolean visible) {

@@ -96,6 +103,37 @@
         }

     }

 

+    /**

+     * Visits a parameter annotation.

+     * Parameter annotations are moved to replacing constructor except

+     * they are injection annotations(-@Property and -@Requires).

+     * Because injection annotations shouldn't be copied to generated one

+     * in case of re-manipulation, since this is caused to wrong type resolution

+     * of injected parameters.

+     *

+     * @param parameter parameter index

+     * @param desc      annotation description(annotation name)

+     * @param visible   is parameter annotation visible

+     * @return @AnnotationVisitor

+     */

+    public AnnotationVisitor visitParameterAnnotation(

+            final int parameter,

+            final String desc,

+            final boolean visible) {

+

+        /*

+         * Generated constructor shouldn't inherit injection annotations

+         */

+        if (desc.equals("Lorg/apache/felix/ipojo/annotations/Property;")

+                || desc.equals("Lorg/apache/felix/ipojo/annotations/Requires;")

+                || CustomAnnotationVisitor.isCustomAnnotation(desc)) {

+            return null;

+        } else {

+            return super.visitParameterAnnotation(parameter, desc, visible);

+        }

+

+    }

+

 

     /**

      * Adapts field accesses.

@@ -104,11 +142,12 @@
      * <li><code>GETFIELD</code> are changed to a <code>__getX</code> invocation.</li>

      * <li><code>SETFIELD</code> are changed to a <code>__setX</code> invocation.</li>

      * </ul>

-     * @see org.objectweb.asm.MethodVisitor#visitFieldInsn(int, String, String, String)

+     *

      * @param opcode the visited operation code

-     * @param owner the owner of the field

-     * @param name the name of the field

-     * @param desc the descriptor of the field

+     * @param owner  the owner of the field

+     * @param name   the name of the field

+     * @param desc   the descriptor of the field

+     * @see org.objectweb.asm.MethodVisitor#visitFieldInsn(int, String, String, String)

      */

     public void visitFieldInsn(

             final int opcode,

@@ -120,12 +159,11 @@
                 String gDesc = "()" + desc;

                 mv.visitMethodInsn(INVOKEVIRTUAL, owner, "__get" + name, gDesc);

                 return;

-            } else

-                if (opcode == PUTFIELD) {

-                    String sDesc = "(" + desc + ")V";

-                    mv.visitMethodInsn(INVOKEVIRTUAL, owner, "__set" + name, sDesc);

-                    return;

-                }

+            } else if (opcode == PUTFIELD) {

+                String sDesc = "(" + desc + ")V";

+                mv.visitMethodInsn(INVOKEVIRTUAL, owner, "__set" + name, sDesc);

+                return;

+            }

         }

         super.visitFieldInsn(opcode, owner, name, desc);

     }

@@ -134,23 +172,24 @@
      * Visits a method invocation instruction.

      * After the super constructor invocation, insert the _setComponentManager invocation.

      * Otherwise, the method invocation doesn't change

+     *

      * @param opcode the opcode

-     * @param owner the class owning the invoked method

-     * @param name the method name

-     * @param desc the method descriptor

+     * @param owner  the class owning the invoked method

+     * @param name   the method name

+     * @param desc   the method descriptor

      * @see org.objectweb.asm.MethodAdapter#visitMethodInsn(int, java.lang.String, java.lang.String, java.lang.String)

      */

     public void visitMethodInsn(int opcode, String owner, String name, String desc) {

 

         // A method call is detected, check if it is the super call :

         // the first init is not necessary the super call, so check that it is really the super class.

-        if (!m_superDetected && name.equals("<init>")  && owner.equals(m_superClass)) {

+        if (!m_superDetected && name.equals("<init>") && owner.equals(m_superClass)) {

             m_superDetected = true;

             // The first invocation is the super call

             // 1) Visit the super constructor :

 

             //mv.visitVarInsn(ALOAD, 0); The ALOAD 0 was already visited. This previous visit allows

-                                         // Super constructor parameters.

+            // Super constructor parameters.

             mv.visitMethodInsn(opcode, owner, name, desc); // Super constructor invocation

 

             // 2) Load the object and the component manager argument

@@ -171,8 +210,9 @@
      * it is not <code>this</code> (i.e. 0). This increment

      * is due to the instance manager parameter added in the method

      * signature.

+     *

      * @param opcode the opcode

-     * @param var the variable index

+     * @param var    the variable index

      * @see org.objectweb.asm.MethodAdapter#visitVarInsn(int, int)

      */

     public void visitVarInsn(int opcode, int var) {

@@ -180,7 +220,7 @@
             mv.visitVarInsn(opcode, var); // ALOAD 0 (THIS)

         } else {

             mv.visitVarInsn(opcode, var + 1); // All other variable index must be incremented (due to

-                                              // the instance manager argument

+            // the instance manager argument

         }

 

     }

@@ -191,7 +231,8 @@
      * it is not <code>this</code> (i.e. 0). This increment

      * is due to the instance manager parameter added in the method

      * signature.

-     * @param var the variable index

+     *

+     * @param var       the variable index

      * @param increment the increment

      * @see org.objectweb.asm.MethodAdapter#visitIincInsn(int, int)

      */

@@ -208,12 +249,13 @@
      * Adds _manager and increment others variable indexes.

      * This variable has the same scope than <code>this</code> and

      * has the <code>1</code> index.

-     * @param name the variable name

-     * @param desc the variable descriptor

+     *

+     * @param name      the variable name

+     * @param desc      the variable descriptor

      * @param signature the variable signature

-     * @param start the beginning label

-     * @param end the ending label

-     * @param index the variable index

+     * @param start     the beginning label

+     * @param end       the ending label

+     * @param index     the variable index

      * @see org.objectweb.asm.MethodAdapter#visitLocalVariable(java.lang.String, java.lang.String, java.lang.String, org.objectweb.asm.Label, org.objectweb.asm.Label, int)

      */

     public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {

@@ -228,7 +270,8 @@
      * Visit max method.

      * The stack size is incremented of 1. The

      * local variable count is incremented of 2.

-     * @param maxStack the stack size.

+     *

+     * @param maxStack  the stack size.

      * @param maxLocals the local variable count.

      * @see org.objectweb.asm.MethodAdapter#visitMaxs(int, int)

      */

diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java
index 4bfb240..3b65dff 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java
@@ -59,12 +59,12 @@
     /**

      * Filed flag prefix.

      */

-    private static final  String FIELD_FLAG_PREFIX = "__F";

+    public static final  String FIELD_FLAG_PREFIX = "__F";

 

     /**

      * Method flag prefix.

      */

-    private static final  String METHOD_FLAG_PREFIX = "__M";

+    public static final  String METHOD_FLAG_PREFIX = "__M";

 

     /**

      * onEntry method name.

diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java
index e3e14f0..58399be 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java
@@ -100,7 +100,7 @@
      * @param root is the annotation a root

      * @param clazz the annotation is a class annotation.

      * @param index the index of the argument

-     * @param the descriptor of the method

+     * @param descriptor the descriptor of the method

      */

     public CustomAnnotationVisitor(Element elem, MetadataCollector collector, boolean root, boolean clazz, int index, String descriptor) {

         m_elem = elem;

diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java
index e7f18cd..fb9bb72 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.ipojo.manipulation.annotations;
 
+import org.apache.felix.ipojo.manipulation.MethodCreator;
 import org.apache.felix.ipojo.metadata.Attribute;
 import org.apache.felix.ipojo.metadata.Element;
 import org.objectweb.asm.AnnotationVisitor;
@@ -26,6 +27,7 @@
 
 /**
  * This class collects method annotations, and give them to the metadata collector.
+ *
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public class MethodCollector extends EmptyVisitor {
@@ -47,7 +49,8 @@
 
     /**
      * Constructor.
-     * @param name : name of the method.
+     *
+     * @param name      : name of the method.
      * @param collector : parent collector.
      */
     public MethodCollector(String name, String descriptor, MetadataCollector collector) {
@@ -58,10 +61,11 @@
 
     /**
      * Visit a parameter annotation.
+     *
      * @see org.objectweb.asm.commons.EmptyVisitor#visitParameterAnnotation(int, java.lang.String, boolean)
      */
     public AnnotationVisitor visitParameterAnnotation(int index, String annotation,
-            boolean visible) {
+                                                      boolean visible) {
         if (m_name.equals("<init>")) {
             if (annotation.equals("Lorg/apache/felix/ipojo/annotations/Property;")) {
                 return processProperty(true, index);
@@ -80,9 +84,9 @@
     }
 
 
-
     /**
      * Visit method annotations.
+     *
      * @param arg0 : annotation name.
      * @param arg1 : is the annotation visible at runtime.
      * @return the visitor paring the visited annotation.
@@ -119,7 +123,7 @@
 
         if (CustomAnnotationVisitor.isCustomAnnotation(arg0)) {
             Element elem = CustomAnnotationVisitor.buildElement(arg0);
-            elem.addAttribute(new Attribute("method", m_name));
+            elem.addAttribute(new Attribute("method", computeEffectiveMethodName(m_name)));
             return new CustomAnnotationVisitor(elem, m_collector, true, false);
         }
 
@@ -128,11 +132,12 @@
 
     /**
      * Process @Updated annotation.
+     *
      * @return null.
      */
     private AnnotationVisitor processUpdated() {
         Element parent = null;
-        if (! m_collector.getIds().containsKey("properties")) {
+        if (!m_collector.getIds().containsKey("properties")) {
             parent = new Element("Properties", "");
             m_collector.getIds().put("properties", parent);
             m_collector.getElements().put(parent, null);
@@ -147,6 +152,7 @@
 
     /**
      * Process @PostRegistration annotation.
+     *
      * @return null.
      */
     private AnnotationVisitor processPostRegistration() {
@@ -163,6 +169,7 @@
 
     /**
      * Process @PostRegistration annotation.
+     *
      * @return null.
      */
     private AnnotationVisitor processPostUnregistration() {
@@ -179,6 +186,7 @@
 
     /**
      * Process @bind, @modified, @unbind.
+     *
      * @param type : bind or unbind
      * @return the visitor parsing @bind & @unbind annotations.
      */
@@ -188,37 +196,40 @@
 
     /**
      * Process @validate annotation.
+     *
      * @return null.
      */
     private AnnotationVisitor processValidate() {
         Element cb = new Element("callback", "");
         cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("transition", "validate"));
-        cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("method", m_name));
+        cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("method", computeEffectiveMethodName(m_name)));
         m_collector.getElements().put(cb, null);
         return null;
     }
 
     /**
      * Process @invalidate annotation.
+     *
      * @return null.
      */
     private AnnotationVisitor processInvalidate() {
         Element cb = new Element("callback", "");
         cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("transition", "invalidate"));
-        cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("method", m_name));
+        cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("method", computeEffectiveMethodName(m_name)));
         m_collector.getElements().put(cb, null);
         return null;
     }
 
     /**
      * Process @property annotation.
+     *
      * @param parameter true if we're processing a parameter
-     * @param index the index, meaningful only if parameter is true
+     * @param index     the index, meaningful only if parameter is true
      * @return the visitor parsing the visited annotation.
      */
     private AnnotationVisitor processProperty(boolean parameter, int index) {
         Element prop = null;
-        if (! m_collector.getIds().containsKey("properties")) {
+        if (!m_collector.getIds().containsKey("properties")) {
             prop = new Element("Properties", "");
             m_collector.getIds().put("properties", prop);
             m_collector.getElements().put(prop, null);
@@ -291,6 +302,7 @@
 
         /**
          * Constructor.
+         *
          * @param bind : method name.
          * @param type : is the callback a bind or an unbind method.
          */
@@ -305,6 +317,7 @@
 
         /**
          * Visit annotation attribute.
+         *
          * @param arg0 : annotation name
          * @param arg1 : annotation value
          * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)
@@ -349,20 +362,23 @@
         /**
          * End of the visit.
          * Create or append the requirement info to a created or already existing "requires" element.
+         *
          * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
          */
         public void visitEnd() {
             if (m_id == null) {
-                if (m_name != null  && m_name.startsWith("bind")) {
-                    m_id = m_name.substring("bind".length());
-                } else if (m_name != null  && m_name.startsWith("unbind")) {
-                    m_id = m_name.substring("unbind".length());
-                } else if (m_name != null  && m_name.startsWith("modified")) {
-                    m_id = m_name.substring("modified".length());
+                String effectiveName = computeEffectiveMethodName(m_name);
+
+                if (effectiveName != null && effectiveName.startsWith("bind")) {
+                    m_id = effectiveName.substring("bind".length());
+                } else if (effectiveName != null && effectiveName.startsWith("unbind")) {
+                    m_id = effectiveName.substring("unbind".length());
+                } else if (effectiveName != null && effectiveName.startsWith("modified")) {
+                    m_id = effectiveName.substring("modified".length());
                 } else if (m_index != -1) {
                     m_id = "" + m_index;
                 } else {
-                    System.err.println("Cannot determine the id of the " + m_type + " method : " + m_name);
+                    System.err.println("Cannot determine the id of the " + m_type + " method : " + effectiveName);
                     return;
                 }
             }
@@ -407,7 +423,7 @@
                 if (m_specification != null) {
                     if (itf == null) {
                         req.addAttribute(new Attribute("specification", m_specification));
-                    } else if (! m_specification.equals(itf)) {
+                    } else if (!m_specification.equals(itf)) {
                         System.err.println("The required specification is not the same as previouly : " + m_specification + " & " + itf);
                         return;
                     }
@@ -416,7 +432,7 @@
                 if (m_optional != null) {
                     if (optional == null) {
                         req.addAttribute(new Attribute("optional", m_optional));
-                    } else if (! m_optional.equals(optional)) {
+                    } else if (!m_optional.equals(optional)) {
                         System.err.println("The optional attribute is not always the same");
                         return;
                     }
@@ -425,7 +441,7 @@
                 if (m_aggregate != null) {
                     if (aggregate == null) {
                         req.addAttribute(new Attribute("aggregate", m_aggregate));
-                    } else if (! m_aggregate.equals(aggregate)) {
+                    } else if (!m_aggregate.equals(aggregate)) {
                         System.err.println("The aggregate attribute is not always the same");
                         return;
                     }
@@ -434,7 +450,7 @@
                 if (m_filter != null) {
                     if (filter == null) {
                         req.addAttribute(new Attribute("filter", m_filter));
-                    } else if (! m_filter.equals(filter)) {
+                    } else if (!m_filter.equals(filter)) {
                         System.err.println("The filter attribute is not always the same");
                         return;
                     }
@@ -443,7 +459,7 @@
                 if (m_policy != null) {
                     if (policy == null) {
                         req.addAttribute(new Attribute("policy", m_policy));
-                    } else if (! m_policy.equals(policy)) {
+                    } else if (!m_policy.equals(policy)) {
                         System.err.println("The policy attribute is not always the same");
                         return;
                     }
@@ -452,7 +468,7 @@
                 if (m_comparator != null) {
                     if (comparator == null) {
                         req.addAttribute(new Attribute("comparator", m_comparator));
-                    } else if (! m_comparator.equals(comparator)) {
+                    } else if (!m_comparator.equals(comparator)) {
                         System.err.println("The comparator attribute is not always the same");
                         return;
                     }
@@ -461,7 +477,7 @@
                 if (m_from != null) {
                     if (from == null) {
                         req.addAttribute(new Attribute("from", m_from));
-                    } else if (! m_from.equals(from)) {
+                    } else if (!m_from.equals(from)) {
                         System.err.println("The from attribute is not always the same");
                         return;
                     }
@@ -470,7 +486,7 @@
             }
             if (m_name != null) {
                 Element method = new Element("callback", "");
-                method.addAttribute(new Attribute("method", m_name));
+                method.addAttribute(new Attribute("method", computeEffectiveMethodName(m_name)));
                 method.addAttribute(new Attribute("type", m_type));
                 req.addElement(method);
             } else {
@@ -527,10 +543,11 @@
 
         /**
          * Constructor.
+         *
          * @param parent : parent element.
          * @param method : attached method.
-         * @param param : we're processing a parameter
-         * @param index : the parameter index
+         * @param param  : we're processing a parameter
+         * @param index  : the parameter index
          */
         private PropertyAnnotationParser(Element parent, String method, boolean param, int index) {
             m_parent = parent;
@@ -541,6 +558,7 @@
 
         /**
          * Visit annotation attributes.
+         *
          * @param arg0 : annotation name
          * @param arg1 : annotation value
          * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)
@@ -567,17 +585,20 @@
         /**
          * End of the visit.
          * Append the computed element to the parent element.
+         *
          * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
          */
         public void visitEnd() {
+            m_method = computeEffectiveMethodName(m_method);
+
             // If neither name not id, try to extract the name
-            if (m_name == null && m_id == null  && m_method.startsWith("set")) {
+            if (m_name == null && m_id == null && m_method.startsWith("set")) {
                 m_name = m_method.substring("set".length());
                 m_id = m_name;
-            // Else align the two values
-            } else if (m_name != null  && m_id == null) {
+                // Else align the two values
+            } else if (m_name != null && m_id == null) {
                 m_id = m_name;
-            } else if (m_id != null  && m_name == null) {
+            } else if (m_id != null && m_name == null) {
                 m_name = m_id;
             }
 
@@ -615,4 +636,19 @@
 
         }
     }
+
+    /**
+     * Computes the real method name. This method is useful when the annotation is collected on an manipulated method
+     * (prefixed by <code>__M_</code>). This method just removes the prefix if found.
+     * @param name the collected method name
+     * @return the effective method name, can be the collected method name if the method name does not start with
+     * the prefix.
+     */
+    public static String computeEffectiveMethodName(String name) {
+        if (name != null && name.startsWith(MethodCreator.PREFIX)) {
+            return name.substring(MethodCreator.PREFIX.length());
+        } else {
+            return name;
+        }
+    }
 }
diff --git a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ClassCheckerTestCase.java b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ClassCheckerTestCase.java
new file mode 100644
index 0000000..40f4740
--- /dev/null
+++ b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ClassCheckerTestCase.java
@@ -0,0 +1,129 @@
+/*
+ * 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.ipojo.manipulation;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.apache.felix.ipojo.manipulator.util.Streams;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.EmptyVisitor;
+import org.osgi.framework.BundleContext;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ClassCheckerTestCase extends TestCase {
+
+    public void testIsAlreadyManipulatedWithNotManipulatedResource() throws Exception {
+        ClassChecker checker = check(resource("test/SimplePojo.class"));
+        assertFalse(checker.isalreadyManipulated());
+    }
+
+    public void testIsAlreadyManipulatedWithManipulatedResource() throws Exception {
+        ClassChecker checker = check(manipulate(resource("test/SimplePojo.class")));
+        assertTrue(checker.isalreadyManipulated());
+    }
+
+    public void testMetadataForAlreadyManipulatedClassAreCleaned() throws Exception {
+        ClassChecker checker = check(manipulate(resource("test/AnnotatedComponent.class")));
+
+        // Check implemented interfaces
+        List<String> interfaces = checker.getInterfaces();
+        assertTrue(interfaces.isEmpty());
+
+        // Check super class
+        assertNull(checker.getSuperClass());
+
+        // Check inner classes
+        List<String> inner = checker.getInnerClasses();
+        assertTrue(inner.isEmpty());
+
+        // Ensure fields are correctly filtered
+        Map<String, String> fields = checker.getFields();
+        assertEquals(1, fields.size());
+        assertEquals("java.lang.String", fields.get("prop"));
+
+        // Ensure methods are also correctly filtered
+        List<MethodDescriptor> descriptors = checker.getMethods();
+        assertEquals(2, descriptors.size());
+
+        // AnnotatedComponent(BundleContext)
+        MethodDescriptor constructor = searchMethod("$init", descriptors);
+        assertNotNull(constructor);
+        Type[] arguments = Type.getArgumentTypes(constructor.getDescriptor());
+        assertEquals(1, arguments.length);
+        assertEquals(Type.getType(BundleContext.class), arguments[0]);
+
+        // @FakeAnnotation
+        // AnnotatedComponent.annotatedMethod():Void
+        MethodDescriptor method = searchMethod("annotatedMethod", descriptors);
+        assertNotNull(method);
+        assertEquals("()V", method.getDescriptor()); // return void + no params
+        assertAnnotationIsAlone(method, "Ltest/FakeAnnotation;");
+
+
+    }
+
+    private void assertAnnotationIsAlone(MethodDescriptor method, String desc) {
+        List<ClassChecker.AnnotationDescriptor> annotations = method.getAnnotations();
+
+        assertEquals(1, annotations.size());
+        ClassChecker.AnnotationDescriptor annotationDescriptor = annotations.get(0);
+        MethodVisitor mv = mock(MethodVisitor.class);
+        when(mv.visitAnnotation(desc, true)).thenReturn(new EmptyVisitor());
+        annotationDescriptor.visitAnnotation(mv);
+    }
+
+    private MethodDescriptor searchMethod(String methodName, List<MethodDescriptor> descriptors) {
+        for (MethodDescriptor descriptor : descriptors) {
+            if (methodName.equals(descriptor.getName())) {
+                return descriptor;
+            }
+        }
+
+        return null;
+    }
+
+    private byte[] manipulate(byte[] input) throws Exception {
+        Manipulator manipulator = new Manipulator();
+        return manipulator.manipulate(input);
+    }
+
+    private byte[] resource(String name) throws Exception {
+        return ManipulatorTest.getBytesFromFile(new File("target/test-classes/" + name));
+    }
+
+    private ClassChecker check(byte[] resource) throws Exception {
+        ClassChecker checker = new ClassChecker();
+        ByteArrayInputStream is = new ByteArrayInputStream(resource);
+        try {
+            ClassReader classReader = new ClassReader(is);
+            classReader.accept(checker, ClassReader.SKIP_FRAMES);
+        } finally {
+            Streams.close(is);
+        }
+        return checker;
+    }
+}
diff --git a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatorTest.java b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatorTest.java
index f5964dd..9bd1b89 100644
--- a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatorTest.java
+++ b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatorTest.java
@@ -29,7 +29,6 @@
 
 import org.apache.felix.ipojo.InstanceManager;
 import org.apache.felix.ipojo.Pojo;
-import org.apache.felix.ipojo.manipulation.annotations.MetadataCollector;
 import org.mockito.Mockito;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.util.CheckClassAdapter;
@@ -39,7 +38,7 @@
     public void testClusterDaemon() throws Exception {
         Manipulator manipulator = new Manipulator();
         byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/ClusterDaemon.class")));
-        TestClassLoader classloader = new TestClassLoader("test.ClusterDaemon", clazz);
+        ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.ClusterDaemon", clazz);
 
         //Assert.assertNotNull(manipulator.getManipulationMetadata());
 
@@ -62,7 +61,7 @@
     public void testManipulatingTheSimplePojo() throws Exception {
         Manipulator manipulator = new Manipulator();
         byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/SimplePojo.class")));
-        TestClassLoader classloader = new TestClassLoader("test.SimplePojo", clazz);
+        ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.SimplePojo", clazz);
         Class cl = classloader.findClass("test.SimplePojo");
         Assert.assertNotNull(cl);
         Assert.assertNotNull(manipulator.getManipulationMetadata());
@@ -113,7 +112,7 @@
     public void testManipulatingChild() throws Exception {
         Manipulator manipulator = new Manipulator();
         byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/Child.class")));
-        TestClassLoader classloader = new TestClassLoader("test.Child", clazz);
+        ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.Child", clazz);
         Class cl = classloader.findClass("test.Child");
         Assert.assertNotNull(cl);
         Assert.assertNotNull(manipulator.getManipulationMetadata());
@@ -160,7 +159,7 @@
     public void _testManipulatingTheInner() throws Exception {
         Manipulator manipulator = new Manipulator();
         byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/PojoWithInner.class")));
-        TestClassLoader classloader = new TestClassLoader("test.PojoWithInner", clazz);
+        ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.PojoWithInner", clazz);
         Class cl = classloader.findClass("test.PojoWithInner");
         Assert.assertNotNull(cl);
         Assert.assertNotNull(manipulator.getManipulationMetadata());
@@ -214,7 +213,7 @@
     public void testManipulatingWithConstructorModification() throws Exception {
         Manipulator manipulator = new Manipulator();
         byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/Child.class")));
-        TestClassLoader classloader = new TestClassLoader("test.Child", clazz);
+        ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.Child", clazz);
         Class cl = classloader.findClass("test.Child");
         Assert.assertNotNull(cl);
         Assert.assertNotNull(manipulator.getManipulationMetadata());
@@ -279,7 +278,7 @@
     public void testManipulatingWithNoValidConstructor() throws Exception {
         Manipulator manipulator = new Manipulator();
         byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/NoValidConstructor.class")));
-        TestClassLoader classloader = new TestClassLoader("test.NoValidConstructor", clazz);
+        ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.NoValidConstructor", clazz);
         Class cl = classloader.findClass("test.NoValidConstructor");
         Assert.assertNotNull(cl);
         Assert.assertNotNull(manipulator.getManipulationMetadata());
@@ -322,7 +321,7 @@
 //        fos.write(clazz);
 //        fos.close();
 
-        TestClassLoader classloader = new TestClassLoader("test.ConstructorCheck", clazz);
+        ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.ConstructorCheck", clazz);
         Class cl = classloader.findClass("test.ConstructorCheck");
         Assert.assertNotNull(cl);
         Assert.assertNotNull(manipulator.getManipulationMetadata());
@@ -337,6 +336,8 @@
         Assert.assertEquals("toto", f.get(o));
      }
 
+
+
     public static byte[] getBytesFromFile(File file) throws IOException {
         InputStream is = new FileInputStream(file);
         long length = file.length();
@@ -360,29 +361,6 @@
         return bytes;
     }
 
-    class TestClassLoader extends ClassLoader {
 
-        private String name;
-        private byte[] clazz;
-
-        public TestClassLoader(String name, byte[] clazz) {
-            this.name = name;
-            this.clazz = clazz;
-        }
-
-        public Class findClass(String name) throws ClassNotFoundException {
-            if (name.equals(this.name)) {
-                return defineClass(name, clazz, 0, clazz.length);
-            }
-            return super.findClass(name);
-        }
-
-        public Class loadClass(String arg0) throws ClassNotFoundException {
-            return super.loadClass(arg0);
-        }
-
-
-
-    }
 
 }
diff --git a/ipojo/manipulator/manipulator/src/test/java/test/AnnotatedComponent.java b/ipojo/manipulator/manipulator/src/test/java/test/AnnotatedComponent.java
index 569a100..ccd8d9c 100644
--- a/ipojo/manipulator/manipulator/src/test/java/test/AnnotatedComponent.java
+++ b/ipojo/manipulator/manipulator/src/test/java/test/AnnotatedComponent.java
@@ -20,10 +20,19 @@
 
 import org.apache.felix.ipojo.annotations.Component;
 import org.apache.felix.ipojo.annotations.Property;
+import org.apache.felix.ipojo.annotations.Requires;
+import org.osgi.framework.BundleContext;
 
 @Component
 public class AnnotatedComponent {
 
     @Property
     private String prop;
+
+    public AnnotatedComponent(BundleContext bundleContext) {}
+
+    @FakeAnnotation
+    public void annotatedMethod() {
+
+    }
 }
diff --git a/ipojo/manipulator/manipulator/src/test/java/test/FakeAnnotation.java b/ipojo/manipulator/manipulator/src/test/java/test/FakeAnnotation.java
new file mode 100644
index 0000000..21d2076
--- /dev/null
+++ b/ipojo/manipulator/manipulator/src/test/java/test/FakeAnnotation.java
@@ -0,0 +1,35 @@
+/*
+ * 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 test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A {@code FakeAnnotation} is ...
+ *
+ * @author Guillaume Sauthier
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface FakeAnnotation {
+}