Fix:
* FELIX-4254 Specify the method id of methods from inner class
* FELIX-4255 Extend the inner class manipulation to allow method interception
* FELIX-4257 Allow the dependency handler to track the entry and exit of inner class methods

I think the leak is circumvented now, have to think about the test protocol to check it.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1528135 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/BndJarResourceStoreTestCase.java b/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/BndJarResourceStoreTestCase.java
index 100686e..3a425fd 100644
--- a/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/BndJarResourceStoreTestCase.java
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/BndJarResourceStoreTestCase.java
@@ -43,11 +43,7 @@
 import junit.framework.TestCase;
 
 /**
- * Created with IntelliJ IDEA.
- * User: guillaume
- * Date: 04/01/13
- * Time: 15:33
- * To change this template use File | Settings | File Templates.
+ * Checks the Resource Store from the BND plugin.
  */
 public class BndJarResourceStoreTestCase extends TestCase {
     @Mock
@@ -161,12 +157,13 @@
 
         plugin.analyzeJar(analyzer);
 
-        assertContains("component { $classname=\"org.apache.felix.ipojo.bnd.EmptyComponent\" manipulation { method { $name=\"$init\" }}}",
+        assertContains("component { $classname=\"org.apache.felix.ipojo.bnd.EmptyComponent\" manipulation { $classname=\"org.apache.felix.ipojo.bnd.EmptyComponent\" method { $name=\"$init\" }}}",
                 analyzer.getProperty("IPOJO-Components"));
         verify(dot).putResource(eq(path), any(Resource.class));
     }
 
     private void assertContains(String expected, String actual) {
+        System.out.println("Actual: " + actual);
         assertTrue(actual.contains(expected));
     }
 
diff --git a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/InnerClasses.java b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/InnerClasses.java
index 7cd6915..998b598 100644
--- a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/InnerClasses.java
+++ b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/InnerClasses.java
@@ -45,6 +45,11 @@
     
     private static int staticint = 6;
 
+    /**
+     * A fake service.
+     */
+    private Runnable runnable;
+
     public boolean check() {
         return true;
     }
diff --git a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/main/resources/metadata.xml b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/main/resources/metadata.xml
index 368eab8..3144b88 100644
--- a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/main/resources/metadata.xml
+++ b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/main/resources/metadata.xml
@@ -39,6 +39,7 @@
 	

 	<!-- Nested & Inner classes -->

 	<component name="inners" classname="org.apache.felix.ipojo.runtime.core.components.InnerClasses">

+        <requires field="runnable" optional="true"/>

 		<provides>

 			<property field="privateObject"/>

 			<property field="privateInt"/>

diff --git a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestNestedClasses.java b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestNestedClasses.java
index 9563ba4..e8a0fa4 100644
--- a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestNestedClasses.java
+++ b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestNestedClasses.java
@@ -124,7 +124,7 @@
     public void testPublicInnerClass() {
         Map data = (Map) service.getProps().get("publicInner");
         assertNotNull("Check data existence", data);
-
+        System.out.println(data);
         assertEquals("Check public object", "publicObject", data.get("publicObject"));
         assertEquals("Check public int", new Integer(0), data.get("publicInt"));
         assertEquals("Check protected object", "protectedObject", data.get("protectedObject"));
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 0fcba5f..c7bdaec 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
@@ -98,7 +98,7 @@
     public FieldVisitor visitField(int access, String name, String desc,

             String signature, Object value) {

 

-        if (access == ACC_PRIVATE && name.equals(MethodCreator.IM_FIELD)

+        if (access == 0 && name.equals(MethodCreator.IM_FIELD)

                 && desc.equals("Lorg/apache/felix/ipojo/InstanceManager;")) {

             m_isAlreadyManipulated = true;

         } else if (name.startsWith("class$")) { // Does not add class$* field generated by 'x.class'

@@ -191,9 +191,9 @@
             m_superClass = superName.replace('/', '.');

         }

 

-        for (int i = 0; i < interfaces.length; i++) {

-            if (! interfaces[i].equals("org/apache/felix/ipojo/Pojo")) {

-                m_itfs.add(interfaces[i].replace('/', '.'));

+        for (String anInterface : interfaces) {

+            if (!anInterface.equals("org/apache/felix/ipojo/Pojo")) {

+                m_itfs.add(anInterface.replace('/', '.'));

             }

         }

 

@@ -245,7 +245,7 @@
                 && Type.getType("Lorg/apache/felix/ipojo/InstanceManager;").equals(types[0]);

     }

 

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

+    public static boolean isGeneratedMethod(String name, String desc) {

         return isGetterMethod(name, desc)

                 || isSetterMethod(name, desc)

                 || isSetInstanceManagerMethod(name)

@@ -253,7 +253,7 @@
                 || isManipulatedMethod(name);

     }

 

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

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

         // TYPE __getXXX()

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

         return (name.startsWith("__get")

@@ -261,7 +261,7 @@
                 && !Type.VOID_TYPE.equals(Type.getReturnType(desc)));

     }

 

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

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

         // void __setXXX(TYPE)

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

         return (name.startsWith("__set")

@@ -269,16 +269,16 @@
                 && Type.VOID_TYPE.equals(Type.getReturnType(desc)));

     }

 

-    private boolean isSetInstanceManagerMethod(String name) {

+    private static boolean isSetInstanceManagerMethod(String name) {

         return name.startsWith("_setInstanceManager");

     }

 

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

+    private static 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) {

+    private static boolean isManipulatedMethod(String name) {

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

     }

 

diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassAdapter.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassAdapter.java
index c50effa..86de35f 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassAdapter.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassAdapter.java
@@ -20,7 +20,12 @@
 package org.apache.felix.ipojo.manipulation;
 
 import org.objectweb.asm.*;
+import org.objectweb.asm.commons.GeneratorAdapter;
+import org.objectweb.asm.tree.LocalVariableNode;
 
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -33,8 +38,22 @@
  */
 public class InnerClassAdapter extends ClassAdapter implements Opcodes {
 
+    /**
+     * The manipulator having manipulated the outer class.
+     * We add method descriptions to this manipulator.
+     */
     private final Manipulator m_manipulator;
+
+    /**
+     * The name of the inner class. This name is only define in the outer class.
+     */
     private final String m_name;
+
+    /**
+     * The ismple name of the class.
+     */
+    private final String m_simpleName;
+
     /**
      * Implementation class name.
      */
@@ -47,21 +66,22 @@
     /**
      * Creates the inner class adapter.
      *
-     * @param name      the inner class name
+     * @param name      the inner class name (internal name)
      * @param arg0       parent class visitor
-     * @param outerClass outer class (implementation class)
-     * @param fields     fields of the implementation class
+     * @param outerClassName outer class (implementation class)
      * @param manipulator the manipulator having manipulated the outer class.
      */
-    public InnerClassAdapter(String name, ClassVisitor arg0, String outerClass, Set<String> fields,
+    public InnerClassAdapter(String name, ClassVisitor arg0, String outerClassName,
                              Manipulator manipulator) {
         super(arg0);
         m_name = name;
-        m_outer = outerClass;
-        m_fields = fields;
+        m_simpleName = m_name.substring(m_name.indexOf("$") + 1);
+        m_outer = outerClassName;
         m_manipulator = manipulator;
+        m_fields = manipulator.getFields().keySet();
     }
 
+
     /**
      * Visits a method.
      * This methods create a code visitor manipulating outer class field accesses.
@@ -75,10 +95,6 @@
      * @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
      */
     public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
-
-        final MethodDescriptor md = new MethodDescriptor(name, desc, (access & ACC_STATIC) == ACC_STATIC);
-        m_manipulator.addMethodToInnerClass(m_name, md);
-
         // Do nothing on static methods, should not happen in non-static inner classes.
         if ((access & ACC_STATIC) == ACC_STATIC) {
             return super.visitMethod(access, name, desc, signature, exceptions);
@@ -89,14 +105,240 @@
             return super.visitMethod(access, name, desc, signature, exceptions);
         }
 
-        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+
+        // Do not re-manipulate.
         if (! m_manipulator.isAlreadyManipulated()) {
-            // Do not re-manipulate.
-            return new MethodCodeAdapter(mv, m_outer, access, name, desc, m_fields);
+
+            if (name.equals("<init>")) {
+                // We change the field access from the constructor, but we don't generate the wrapper.
+                MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+                return new MethodCodeAdapter(mv, m_outer, access, name, desc, m_fields);
+            }
+
+            // For all non constructor methods
+
+            MethodDescriptor md = getMethodDescriptor(name, desc);
+            if (md == null) {
+                generateMethodWrapper(access, name, desc, signature, exceptions, null, null,
+                        null);
+            } else {
+                generateMethodWrapper(access, name, desc, signature, exceptions,
+                        md.getArgumentLocalVariables(),
+                        md.getAnnotations(), md.getParameterAnnotations());
+            }
+
+            // The new name is the method name prefixed by the PREFIX.
+            MethodVisitor mv = super.visitMethod(access, MethodCreator.PREFIX + name, desc, signature,
+                    exceptions);
+            return new MethodCodeAdapter(mv, m_outer, access,  MethodCreator.PREFIX + name, desc, m_fields);
         } else {
-            return mv;
+            return super.visitMethod(access, name, desc, signature, exceptions);
         }
     }
 
+    private String getMethodFlagName(String name, String desc) {
+        return MethodCreator.METHOD_FLAG_PREFIX + getMethodId(name, desc);
+    }
+
+    private String getMethodId(String name, String desc) {
+        StringBuilder id = new StringBuilder(m_simpleName);
+        id.append("___"); // Separator
+        id.append(name);
+
+        Type[] args = Type.getArgumentTypes(desc);
+        for (Type type : args) {
+            String arg = type.getClassName();
+            if (arg.endsWith("[]")) {
+                // We have to replace all []
+                String acc = "";
+                while (arg.endsWith("[]")) {
+                    arg = arg.substring(0, arg.length() - 2);
+                    acc += "__";
+                }
+                id.append("$").append(arg.replace('.', '_')).append(acc);
+            } else {
+                id.append("$").append(arg.replace('.', '_'));
+            }
+        }
+        return id.toString();
+    }
+
+    /**
+     * Generate the method header of a POJO method.
+     * This method header encapsulate the POJO method call to
+     * signal entry exit and error to the container.
+     *
+     * The instance manager and flag are accessed using method calls.
+     * @param access : access flag.
+     * @param name : method name.
+     * @param desc : method descriptor.
+     * @param signature : method signature.
+     * @param exceptions : declared exceptions.
+     * @param localVariables : the local variable nodes.
+     * @param annotations : the annotations to move to this method.
+     * @param paramAnnotations : the parameter annotations to move to this method.
+     */
+    private void generateMethodWrapper(int access, String name, String desc, String signature, String[] exceptions,
+                                       List<LocalVariableNode> localVariables, List<ClassChecker.AnnotationDescriptor> annotations,
+                                       Map<Integer, List<ClassChecker.AnnotationDescriptor>> paramAnnotations) {
+        GeneratorAdapter mv = new GeneratorAdapter(cv.visitMethod(access, name, desc, signature, exceptions), access, name, desc);
+
+        // If we have variables, we wraps the code within labels. The `lifetime` of the variables are bound to those
+        // two variables.
+        boolean hasArgumentLabels = localVariables != null && !localVariables.isEmpty();
+        Label start = null;
+        if (hasArgumentLabels) {
+            start = new Label();
+            mv.visitLabel(start);
+        }
+
+        mv.visitCode();
+
+        Type returnType = Type.getReturnType(desc);
+
+        // Compute result and exception stack location
+        int result = -1;
+        int exception = -1;
+
+        //int arguments = mv.newLocal(Type.getType((new Object[0]).getClass()));
+
+        if (returnType.getSort() != Type.VOID) {
+            // The method returns something
+            result = mv.newLocal(returnType);
+            exception = mv.newLocal(Type.getType(Throwable.class));
+        } else {
+            exception = mv.newLocal(Type.getType(Throwable.class));
+        }
+
+        Label l0 = new Label();
+        Label l1 = new Label();
+        Label l2 = new Label();
+
+        mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Throwable");
+
+        // Access the flag from the outer class
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, m_name, "this$0", "L" + m_outer + ";");
+        mv.visitFieldInsn(GETFIELD, m_outer, getMethodFlagName(name, desc), "Z");
+        mv.visitJumpInsn(IFNE, l0);
+
+        mv.visitVarInsn(ALOAD, 0);
+        mv.loadArgs();
+        mv.visitMethodInsn(INVOKESPECIAL, m_name, MethodCreator.PREFIX + name, desc);
+        mv.visitInsn(returnType.getOpcode(IRETURN));
+
+        // end of the non intercepted method invocation.
+
+        mv.visitLabel(l0);
+
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, m_name, "this$0", "L" + m_outer + ";");
+        mv.visitFieldInsn(GETFIELD, m_outer, MethodCreator.IM_FIELD, "Lorg/apache/felix/ipojo/InstanceManager;");
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitLdcInsn(getMethodId(name, desc));
+        mv.loadArgArray();
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", MethodCreator.ENTRY,
+                "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V");
+
+        mv.visitVarInsn(ALOAD, 0);
+
+        // Do not allow argument modification : just reload arguments.
+        mv.loadArgs();
+        mv.visitMethodInsn(INVOKESPECIAL, m_name, MethodCreator.PREFIX + name, desc);
+
+        if (returnType.getSort() != Type.VOID) {
+            mv.visitVarInsn(returnType.getOpcode(ISTORE), result);
+        }
+
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, m_name, "this$0", "L" + m_outer + ";");
+        mv.visitFieldInsn(GETFIELD, m_outer, MethodCreator.IM_FIELD, "Lorg/apache/felix/ipojo/InstanceManager;");
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitLdcInsn(getMethodId(name, desc));
+        if (returnType.getSort() != Type.VOID) {
+            mv.visitVarInsn(returnType.getOpcode(ILOAD), result);
+            mv.box(returnType);
+        } else {
+            mv.visitInsn(ACONST_NULL);
+        }
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", MethodCreator.EXIT, "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V");
+
+        mv.visitLabel(l1);
+        Label l7 = new Label();
+        mv.visitJumpInsn(GOTO, l7);
+        mv.visitLabel(l2);
+
+        mv.visitVarInsn(ASTORE, exception);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, m_name, "this$0", "L" + m_outer + ";");
+        mv.visitFieldInsn(GETFIELD, m_outer, MethodCreator.IM_FIELD, "Lorg/apache/felix/ipojo/InstanceManager;");
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitLdcInsn(getMethodId(name, desc));
+        mv.visitVarInsn(ALOAD, exception);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", MethodCreator.ERROR, "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Throwable;)V");
+        mv.visitVarInsn(ALOAD, exception);
+        mv.visitInsn(ATHROW);
+
+        mv.visitLabel(l7);
+        if (returnType.getSort() != Type.VOID) {
+            mv.visitVarInsn(returnType.getOpcode(ILOAD), result);
+        }
+        mv.visitInsn(returnType.getOpcode(IRETURN));
+
+        // If we had arguments, we mark the end of the lifetime.
+        Label end = null;
+        if (hasArgumentLabels) {
+            end = new Label();
+            mv.visitLabel(end);
+        }
+
+        // Move annotations
+        if (annotations != null) {
+            for (int i = 0; i < annotations.size(); i++) {
+                ClassChecker.AnnotationDescriptor ad = annotations.get(i);
+                ad.visitAnnotation(mv);
+            }
+        }
+
+        // Move parameter annotations
+        if (paramAnnotations != null  && ! paramAnnotations.isEmpty()) {
+            Iterator<Integer> ids = paramAnnotations.keySet().iterator();
+            while(ids.hasNext()) {
+                Integer id = ids.next();
+                List<ClassChecker.AnnotationDescriptor> ads = paramAnnotations.get(id);
+                for (int i = 0; i < ads.size(); i++) {
+                    ClassChecker.AnnotationDescriptor ad = ads.get(i);
+                    ad.visitParameterAnnotation(id, mv);
+                }
+            }
+        }
+
+        // Write the arguments name.
+        if (hasArgumentLabels) {
+            for (LocalVariableNode var : localVariables) {
+                mv.visitLocalVariable(var.name, var.desc, var.signature, start, end, var.index);
+            }
+        }
+
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+    }
+
+    /**
+     * Gets the method descriptor for the specified name and descriptor.
+     * The method descriptor is looked inside the
+     * {@link MethodCreator#m_visitedMethods}
+     * @param name the name of the method
+     * @param desc the descriptor of the method
+     * @return the method descriptor or <code>null</code> if not found.
+     */
+    private MethodDescriptor getMethodDescriptor(String name, String desc) {
+        for (MethodDescriptor md : m_manipulator.getMethodsFromInnerClass(m_name)) {
+            if (md.getName().equals(name) && md.getDescriptor().equals(desc)) {
+                return md;
+            }
+        }
+        return null;
+    }
 
 }
diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassChecker.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassChecker.java
new file mode 100644
index 0000000..b30a5b7
--- /dev/null
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassChecker.java
@@ -0,0 +1,62 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+

+package org.apache.felix.ipojo.manipulation;

+

+import org.objectweb.asm.*;

+import org.objectweb.asm.commons.EmptyVisitor;

+

+import java.util.*;

+

+/**

+ * Analyze an inner class.

+ * This visit collects the methods from the inner class.

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

+ */

+public class InnerClassChecker extends EmptyVisitor implements ClassVisitor, Opcodes {

+

+    private final String m_name;

+    private final Manipulator m_manipulator;

+

+    public InnerClassChecker(String name, Manipulator manipulator) {

+        m_name = name;

+        m_manipulator = manipulator;

+    }

+

+    @Override

+    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {

+        // Do not collect static and native method.

+

+        if ((access & ACC_STATIC) == ACC_STATIC) {

+            return null;

+        }

+

+        if ((access & ACC_NATIVE) == ACC_NATIVE) {

+            return null;

+        }

+

+        // Don't add generated methods, and constructors

+        if (!ClassChecker.isGeneratedMethod(name, desc)  && ! name.endsWith("<init>")) {

+            final MethodDescriptor md = new MethodDescriptor(name, desc, (access & ACC_STATIC) == ACC_STATIC);

+            m_manipulator.addMethodToInnerClass(m_name, md);

+        }

+

+        return null;

+    }

+}

diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassManipulator.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassManipulator.java
deleted file mode 100644
index 0e09b6e..0000000
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/InnerClassManipulator.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.felix.ipojo.manipulation;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Set;
-
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Opcodes;
-
-/**
- * Manipulates inner class allowing outer class access. The manipulated class
- * has access to managed field of the outer class.
- * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
- */
-public class InnerClassManipulator {
-
-    /**
-     * The manipulator having manipulated the component class.
-     */
-    private final Manipulator m_manipulator;
-    private final String m_innerClassName;
-
-    /**
-     * Outer class class name.
-     */
-    private String m_outer;
-
-    /**
-     * Component class fields.
-     */
-    private Set<String> m_fields;
-
-    /**
-     * Creates an inner class manipulator.
-     * @param outerclassName : class name
-     * @param manipulator : fields
-     */
-    public InnerClassManipulator(String innerClassName, String outerclassName, Manipulator manipulator) {
-        m_outer = outerclassName;
-        m_innerClassName = innerClassName;
-        m_fields = manipulator.getFields().keySet();
-        m_manipulator = manipulator;
-    }
-
-    /**
-     * Manipulate the inner class.
-     * @param in input (i.e. original) class
-     * @return manipulated class
-     * @throws IOException the class cannot be read correctly
-     */
-    public byte[] manipulate(byte[] in, int version) throws IOException {
-        InputStream is1 = new ByteArrayInputStream(in);
-
-        ClassReader cr = new ClassReader(is1);
-        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-        InnerClassAdapter adapter = new InnerClassAdapter(m_innerClassName, cw, m_outer, m_fields, m_manipulator);
-        if (version >= Opcodes.V1_6) {
-            cr.accept(adapter, ClassReader.EXPAND_FRAMES);
-        } else {
-            cr.accept(adapter, 0);
-        }
-        is1.close();
-
-        return cw.toByteArray();
-    }
-
-}
diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java
index 7ebb8f4..ce533ce 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java
@@ -77,19 +77,18 @@
     private String m_className;
 
     /**
-     * Manipulate the given byte array.
-     * @param origin : original class.
-     * @return the manipulated class, if the class is already manipulated, the original class.
-     * @throws IOException : if an error occurs during the manipulation.
+     * Checks the given bytecode, determines if the class was already manipulated, and collect the metadata about the
+     * class.
+     * @param origin the bytecode
      */
-    public byte[] manipulate(byte[] origin) throws IOException {
-        InputStream is1 = new ByteArrayInputStream(origin);
+    public void prepare(byte[] origin) throws IOException {
+        InputStream is = new ByteArrayInputStream(origin);
 
         // First check if the class is already manipulated :
-        ClassReader ckReader = new ClassReader(is1);
+        ClassReader ckReader = new ClassReader(is);
         ClassChecker ck = new ClassChecker();
         ckReader.accept(ck, ClassReader.SKIP_FRAMES);
-        is1.close();
+        is.close();
 
         m_fields = ck.getFields(); // Get visited fields (contains only POJO fields)
         m_className = ck.getClassName();
@@ -101,36 +100,37 @@
         // Get the methods list
         m_methods = ck.getMethods();
 
+        // Methods are not yet collected, but the structure is ready.
         m_inners = ck.getInnerClassesAndMethods();
 
         m_version = ck.getClassVersion();
 
-        ClassWriter finalWriter = null;
-
         m_alreadyManipulated = ck.isAlreadyManipulated();
+    }
 
+    /**
+     * Manipulate the given byte array.
+     * @param origin : original class.
+     * @return the manipulated class, if the class is already manipulated, the original class.
+     * @throws IOException : if an error occurs during the manipulation.
+     */
+    public byte[] manipulate(byte[] origin) throws IOException {
+        ClassWriter finalWriter = null;
         if (!m_alreadyManipulated) {
-            // Manipulation ->
-            // Add the _setComponentManager method
-            // Instrument all fields
             InputStream is2 = new ByteArrayInputStream(origin);
             ClassReader cr0 = new ClassReader(is2);
             ClassWriter cw0 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
             //CheckClassAdapter ch = new CheckClassAdapter(cw0);
-            MethodCreator process = new MethodCreator(cw0, m_fields, m_methods);
-            if (ck.getClassVersion() >= Opcodes.V1_6) {
+            MethodCreator process = new MethodCreator(cw0, this);
+            if (m_version >= Opcodes.V1_6) {
                 cr0.accept(process, ClassReader.EXPAND_FRAMES);
             } else {
                 cr0.accept(process, 0);
             }
             is2.close();
-            finalWriter = cw0;
-        }
-        // The file is in the bundle
-        if (m_alreadyManipulated) {
-            return origin;
+            return cw0.toByteArray();
         } else {
-            return finalWriter.toByteArray();
+            return origin;
         }
     }
 
@@ -210,6 +210,10 @@
         return m_fields;
     }
 
+    public List<MethodDescriptor> getMethods() {
+        return m_methods;
+    }
+
     public Collection<String> getInnerClasses() {
         return new ArrayList<String>(m_inners.keySet());
     }
@@ -231,4 +235,61 @@
         }
         list.add(md);
     }
+
+    /**
+     * Analyzes the given inner class.
+     * @param inner the inner class name
+     * @param bytecode the bytecode of the inner class
+     */
+    public void prepareInnerClass(String inner, byte[] bytecode) throws IOException {
+        InputStream is = new ByteArrayInputStream(bytecode);
+        ClassReader ckReader = new ClassReader(is);
+        InnerClassChecker ck = new InnerClassChecker(inner, this);
+        ckReader.accept(ck, ClassReader.SKIP_FRAMES);
+        is.close();
+        // The metadata are collected during the visit.
+    }
+
+    /**
+     * Manipulates the inner class. If the outer class was already manipulated does not re-manipulate the inner class.
+     * We consider that the manipulation cycle of the outer and inner classes are the same.
+     * @param inner the inner class name
+     * @param bytecode input (i.e. original) class
+     * @return the manipulated class
+     * @throws IOException the class cannot be read correctly
+     */
+    public byte[] manipulateInnerClass(String inner, byte[] bytecode) throws IOException {
+        if (!m_alreadyManipulated) {
+            InputStream is1 = new ByteArrayInputStream(bytecode);
+
+            ClassReader cr = new ClassReader(is1);
+            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+            InnerClassAdapter adapter = new InnerClassAdapter(inner, cw, m_className, this);
+            if (m_version >= Opcodes.V1_6) {
+                cr.accept(adapter, ClassReader.EXPAND_FRAMES);
+            } else {
+                cr.accept(adapter, 0);
+            }
+            is1.close();
+
+            return cw.toByteArray();
+        } else {
+            // Return the unchanged inner class
+            return bytecode;
+        }
+    }
+
+    public List<MethodDescriptor> getMethodsFromInnerClass(String innerClassInternalName) {
+        return m_inners.get(innerClassInternalName);
+    }
+
+    public Map<String, List<MethodDescriptor>> getInnerClassesAndMethods() {
+        // Transform the map to use the simple name of the inner classes.
+        Map<String, List<MethodDescriptor>> map = new HashMap<String, List<MethodDescriptor>>();
+        for (Map.Entry<String, List<MethodDescriptor>> entry : m_inners.entrySet()) {
+            String name = extractInnerClassName(toQualifiedName(entry.getKey()));
+            map.put(name, entry.getValue());
+        }
+        return map;
+    }
 }
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 ca5de8e..cdb4ddf 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
@@ -31,6 +31,7 @@
 import org.objectweb.asm.Type;

 import org.objectweb.asm.commons.GeneratorAdapter;

 import org.objectweb.asm.tree.LocalVariableNode;

+import org.objectweb.asm.tree.MethodNode;

 

 /**

  * iPOJO Class Adapter.

@@ -67,17 +68,17 @@
     /**

      * onEntry method name.

      */

-    private static final  String ENTRY = "onEntry";

+    public static final  String ENTRY = "onEntry";

 

     /**

      * onExit method name.

      */

-    private static final  String EXIT = "onExit";

+    public static final  String EXIT = "onExit";

 

     /**

      * on Error method name.

      */

-    private static final  String ERROR = "onError";

+    public static final  String ERROR = "onError";

 

     /**

      * onGet method name.

@@ -90,6 +91,11 @@
     private static final  String SET = "onSet";

 

     /**

+     * The manipulator. It has already collected all the metadata about the class.

+     */

+    private final Manipulator m_manipulator;

+

+    /**

      * Name of the current manipulated class.

      */

     private String m_owner;

@@ -134,13 +140,13 @@
     /**

      * Constructor.

      * @param arg0 : class visitor.

-     * @param fields : fields map detected during the previous class analysis.

-     * @param methods : the list of the detected method during the previous class analysis.

+     * @param manipulator : the manipulator having analyzed the class.

      */

-    public MethodCreator(ClassVisitor arg0, Map<String, String> fields, List<MethodDescriptor> methods) {

+    public MethodCreator(ClassVisitor arg0, Manipulator manipulator) {

         super(arg0);

-        m_fields = fields.keySet();

-        m_visitedMethods = methods;

+        m_manipulator = manipulator;

+        m_fields = manipulator.getFields().keySet();

+        m_visitedMethods = manipulator.getMethods();

     }

 

     /**

@@ -161,6 +167,7 @@
         m_superclass = superName;

         addPOJOInterface(version, access, name, signature, superName, interfaces);

         addIMField();

+        addFlagsForInnerClassMethods();

     }

 

     /**

@@ -223,9 +230,10 @@
                     md.getAnnotations(), md.getParameterAnnotations());

         }

 

+        // TODO Also add the method flags for inner class methods.

         String id = generateMethodFlag(name, desc);

         if (! m_methodFlags.contains(id)) {

-            FieldVisitor flagField = cv.visitField(Opcodes.ACC_PRIVATE, id, "Z", null, null);

+            FieldVisitor flagField = cv.visitField(0, id, "Z", null, null);

             flagField.visitEnd();

             m_methodFlags.add(id);

         }

@@ -243,8 +251,7 @@
      * @return the method descriptor or <code>null</code> if not found.

      */

     private MethodDescriptor getMethodDescriptor(String name, String desc) {

-        for (int i = 0; i < m_visitedMethods.size(); i++) {

-            MethodDescriptor md = m_visitedMethods.get(i);

+        for (MethodDescriptor md : m_visitedMethods) {

             if (md.getName().equals(name) && md.getDescriptor().equals(desc)) {

                 return md;

             }

@@ -527,6 +534,10 @@
         return METHOD_FLAG_PREFIX + generateMethodId(name, desc);

     }

 

+    private String generateMethodFlagForMethodFromInnerClass(String name, String desc, String inner) {

+        return METHOD_FLAG_PREFIX + generateMethodIdForMethodFromInnerClass(name, desc, inner);

+    }

+

     /**

      * Generate the method id based on the given method name and method descriptor.

      * The method Id is unique for this method and serves to create the flag field (so

@@ -536,10 +547,10 @@
      * @return  method ID

      */

     private String generateMethodId(String name, String desc) {

-        StringBuffer id = new StringBuffer(name);

+        StringBuilder id = new StringBuilder(name);

         Type[] args = Type.getArgumentTypes(desc);

-        for (int i = 0; i < args.length; i++) {

-            String arg = args[i].getClassName();

+        for (Type type : args) {

+            String arg = type.getClassName();

             if (arg.endsWith("[]")) {

                 // We have to replace all []

                 String acc = "";

@@ -547,9 +558,9 @@
                     arg = arg.substring(0, arg.length() - 2);

                     acc += "__";

                 }

-                id.append("$" + arg.replace('.', '_') + acc);

+                id.append("$").append(arg.replace('.', '_')).append(acc);

             } else {

-                id.append("$" + arg.replace('.', '_'));

+                id.append("$").append(arg.replace('.', '_'));

             }

         }

         if (!m_methods.contains(id.toString())) {

@@ -558,15 +569,62 @@
         return id.toString();

     }

 

+    private String generateMethodIdForMethodFromInnerClass(String name, String desc, String inner) {

+        StringBuilder id = new StringBuilder(inner);

+        id.append("___"); // Separator

+        id.append(name);

+

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

+        for (Type type : args) {

+            String arg = type.getClassName();

+            if (arg.endsWith("[]")) {

+                // We have to replace all []

+                String acc = "";

+                while (arg.endsWith("[]")) {

+                    arg = arg.substring(0, arg.length() - 2);

+                    acc += "__";

+                }

+                id.append("$").append(arg.replace('.', '_')).append(acc);

+            } else {

+                id.append("$").append(arg.replace('.', '_'));

+            }

+        }

+

+        if (!m_methods.contains(id.toString())) {

+            m_methods.add(id.toString());

+        }

+

+        return id.toString();

+    }

+

     /**

      * Add the instance manager field (__im).

      */

     private void addIMField() {

-        FieldVisitor fv = super.visitField(ACC_PRIVATE, IM_FIELD, "Lorg/apache/felix/ipojo/InstanceManager;", null, null);

+        FieldVisitor fv = super.visitField(0, IM_FIELD, "Lorg/apache/felix/ipojo/InstanceManager;", null, null);

         fv.visitEnd();

     }

 

     /**

+     * Add the boolean flag fields for methods from inner classes.

+     */

+    private void addFlagsForInnerClassMethods() {

+        for (Map.Entry<String, List<MethodDescriptor>> entry : m_manipulator.getInnerClassesAndMethods().entrySet()) {

+            for (MethodDescriptor descriptor : entry.getValue()) {

+                String id = generateMethodFlagForMethodFromInnerClass(

+                        descriptor.getName(),

+                        descriptor.getDescriptor(),

+                        entry.getKey());

+                if (! m_methodFlags.contains(id)) {

+                    FieldVisitor flagField = cv.visitField(0, id, "Z", null, null);

+                    flagField.visitEnd();

+                    m_methodFlags.add(id);

+                }

+            }

+        }

+    }

+

+    /**

      * Add the POJO interface to the visited class.

      * @param version : class version

      * @param access : class access

diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ManipulationEngine.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ManipulationEngine.java
index 5d3eb72..c01fb81 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ManipulationEngine.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ManipulationEngine.java
@@ -19,13 +19,12 @@
 
 package org.apache.felix.ipojo.manipulator;
 
+import org.apache.felix.ipojo.manipulation.Manipulator;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.felix.ipojo.manipulation.InnerClassManipulator;
-import org.apache.felix.ipojo.manipulation.Manipulator;
-
 /**
  * A {@code ManipulationEngine} is responsible to drive the component's
  * classes manipulation.
@@ -106,9 +105,32 @@
             if (result != null) {
                 // Should always be the case
 
-                // Manipulate the original bytecode and store the modified one
+                // Manipulation preparation
                 Manipulator manipulator = new Manipulator();
                 try {
+                    manipulator.prepare(bytecode);
+                } catch (IOException e) {
+                    m_reporter.error("Cannot analyze the class " + info.getClassName() + " : " + e.getMessage());
+                    return;
+                }
+
+                // Inner class preparation
+                for (String inner : manipulator.getInnerClasses()) {
+                    // Get the bytecode and start manipulation
+                    String resourcePath = inner + ".class";
+                    String outerClassInternalName = info.getClassName().replace('.', '/');
+                    byte[] innerClassBytecode;
+                    try {
+                        innerClassBytecode = m_store.read(resourcePath);
+                        manipulator.prepareInnerClass(inner, innerClassBytecode);
+                    } catch (IOException e) {
+                        m_reporter.error("Cannot find or analyze inner class '" + resourcePath + "'");
+                        return;
+                    }
+                }
+
+                // Now manipulate the classes.
+                try {
                     byte[] out = manipulator.manipulate(bytecode);
                     // Call the visitor
                     result.visitManipulatedResource(info.getResourcePath(), out);
@@ -117,7 +139,6 @@
                     return;
                 }
 
-
                 // Visit inner classes
                 for (String inner : manipulator.getInnerClasses()) {
                     // Get the bytecode and start manipulation
@@ -136,10 +157,7 @@
                     // discovered in the main class instead of re-parsing the inner class to find
                     // its own class version
                     try {
-                        InnerClassManipulator innerManipulator = new InnerClassManipulator(inner,
-                                outerClassInternalName,
-                                manipulator);
-                        byte[] manipulated = innerManipulator.manipulate(innerClassBytecode, manipulator.getClassVersion());
+                        byte[] manipulated = manipulator.manipulateInnerClass(inner, innerClassBytecode);
                         // Propagate manipulated resource
                         result.visitManipulatedResource(resourcePath, manipulated);
                     } catch (IOException e) {
diff --git a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/InstanceManager.java b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/InstanceManager.java
index c29d3af..440151e 100644
--- a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/InstanceManager.java
+++ b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/InstanceManager.java
@@ -36,4 +36,16 @@
         return null;
     }
 
+    public void onEntry(Object pojo, String methodId, Object[] args) {
+
+    }
+
+    public void onExit(Object pojo, String methodId, Object[] args) {
+
+    }
+
+    public void onError(Object pojo, String methodId, Throwable error) {
+
+    }
+
 }
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
index 2d5be01..b6d8d6b 100644
--- 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
@@ -110,6 +110,7 @@
 
     private byte[] manipulate(byte[] input) throws Exception {
         Manipulator manipulator = new Manipulator();
+        manipulator.prepare(input);
         return manipulator.manipulate(input);
     }
 
diff --git a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/InnerClassAdapterTest.java b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/InnerClassAdapterTest.java
index 3298b57..421d0db 100644
--- a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/InnerClassAdapterTest.java
+++ b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/InnerClassAdapterTest.java
@@ -43,36 +43,136 @@
 
     public static String baseClassDirectory = "target/test-classes/";
 
-    private static ManipulatedClassLoader manipulate(String className, Manipulator manipulator) throws IOException {
+    public static ManipulatedClassLoader manipulate(String className, Manipulator manipulator) throws IOException {
+        byte[] bytecode = ManipulatorTest.getBytesFromFile(
+                new File(baseClassDirectory + className.replace(".", "/") + ".class"));
 
-        byte[] clazz = manipulator.manipulate(ManipulatorTest.getBytesFromFile(new File
-                (baseClassDirectory + className.replace(".", "/") + ".class")));
-        ManipulatedClassLoader classloader = new ManipulatedClassLoader(className, clazz);
-
-        // Manipulate all inner classes
-        for (String s : manipulator.getInnerClasses()) {
-            String outerClassInternalName = className.replace(".", "/");
-            byte[] innerClassBytecode = ManipulatorTest.getBytesFromFile(new File(baseClassDirectory + s + "" +
-                    ".class"));
-            String innerClassName = s.replace("/", ".");
-            InnerClassManipulator innerManipulator = new InnerClassManipulator(s, outerClassInternalName,
-                    manipulator);
-            byte[] manipulated = innerManipulator.manipulate(innerClassBytecode, manipulator.getClassVersion());
-            classloader.addInnerClass(innerClassName, manipulated);
+        // Preparation.
+        try {
+            manipulator.prepare(bytecode);
+        } catch (IOException e) {
+            Assert.fail("Cannot read " + className);
         }
 
+        // Inner class preparation
+        for (String inner : manipulator.getInnerClasses()) {
+            // Get the bytecode and start manipulation
+            String resourcePath = inner + ".class";
+            byte[] innerClassBytecode;
+            try {
+                innerClassBytecode = ManipulatorTest.getBytesFromFile(new File(baseClassDirectory + resourcePath));
+                manipulator.prepareInnerClass(inner, innerClassBytecode);
+            } catch (IOException e) {
+                Assert.fail("Cannot find or analyze inner class '" + resourcePath + "'");
+            }
+        }
+
+        // Now manipulate the classes.
+        byte[] out = new byte[0];
+        try {
+            out = manipulator.manipulate(bytecode);
+        } catch (IOException e) {
+            Assert.fail("Cannot manipulate the class " + className + " : " + e.getMessage());
+        }
+
+        ManipulatedClassLoader classloader = new ManipulatedClassLoader(className, out);
+
+        // Visit inner classes
+        for (String inner : manipulator.getInnerClasses()) {
+            // Get the bytecode and start manipulation
+            String resourcePath = inner + ".class";
+            byte[] innerClassBytecode;
+            try {
+                innerClassBytecode = ManipulatorTest.getBytesFromFile(new File(baseClassDirectory + resourcePath));
+                byte[] manipulated = manipulator.manipulateInnerClass(inner, innerClassBytecode);
+                classloader.addInnerClass(inner.replace("/", "."), manipulated);
+            } catch (IOException e) {
+                Assert.fail("Cannot find inner class '" + resourcePath + "'");
+            }
+        }
         return classloader;
     }
 
+    public static ManipulatedClassLoader manipulate(String className, Manipulator manipulator,
+                                                    ManipulatedClassLoader initial) throws IOException {
+        byte[] bytecode = initial.get(className);
+
+        // Preparation.
+        try {
+            manipulator.prepare(bytecode);
+        } catch (IOException e) {
+            Assert.fail("Cannot read " + className);
+        }
+
+        // Inner class preparation
+        for (String inner : manipulator.getInnerClasses()) {
+            // Get the bytecode and start manipulation
+            String resourcePath = inner + ".class";
+            byte[] innerClassBytecode;
+            try {
+                innerClassBytecode = initial.get(inner.replace("/", "."));
+                manipulator.prepareInnerClass(inner, innerClassBytecode);
+            } catch (IOException e) {
+                Assert.fail("Cannot find or analyze inner class '" + resourcePath + "'");
+            }
+        }
+
+        // Now manipulate the classes.
+        byte[] out = new byte[0];
+        try {
+            out = manipulator.manipulate(bytecode);
+        } catch (IOException e) {
+            Assert.fail("Cannot manipulate the class " + className + " : " + e.getMessage());
+        }
+
+        ManipulatedClassLoader classloader = new ManipulatedClassLoader(className, out);
+
+        // Visit inner classes
+        for (String inner : manipulator.getInnerClasses()) {
+            // Get the bytecode and start manipulation
+            String resourcePath = inner + ".class";
+            byte[] innerClassBytecode;
+            try {
+                innerClassBytecode = initial.get(inner.replace("/", "."));
+                byte[] manipulated = manipulator.manipulateInnerClass(inner, innerClassBytecode);
+                classloader.addInnerClass(inner.replace("/", "."), manipulated);
+            } catch (IOException e) {
+                Assert.fail("Cannot find inner class '" + resourcePath + "'");
+            }
+        }
+        return classloader;
+    }
+
+    private static Element getInnerClassMetadataByName(Element[] inners, String name) {
+        for (Element element : inners) {
+            if (name.equals(element.getAttribute("name"))) {
+                return element;
+            }
+        }
+        return null;
+    }
+
+    private static Element getMethodByName(Element[] methods, String name) {
+        for (Element element : methods) {
+            if (name.equals(element.getAttribute("name"))) {
+                return element;
+            }
+        }
+        return null;
+    }
+
     @Test
     public void testManipulatingTheInner() throws Exception {
         Manipulator manipulator = new Manipulator();
         String className = "test.PojoWithInner";
-        byte[] origin =  ManipulatorTest.getBytesFromFile(new File(baseClassDirectory + className.replace(".",
+        byte[] origin = ManipulatorTest.getBytesFromFile(new File(baseClassDirectory + className.replace(".",
                 "/") + ".class"));
 
         ManipulatedClassLoader classloader = manipulate(className, manipulator);
 
+
+
+
         Class cl = classloader.findClass(className);
         Assert.assertNotNull(cl);
         Assert.assertNotNull(manipulator.getManipulationMetadata());
@@ -89,7 +189,7 @@
         Constructor[] csts = cl.getDeclaredConstructors();
         for (int i = 0; i < csts.length; i++) {
             System.out.println(Arrays.asList(csts[i].getParameterTypes()));
-            if (csts[i].getParameterTypes().length == 1  &&
+            if (csts[i].getParameterTypes().length == 1 &&
                     csts[i].getParameterTypes()[0].equals(InstanceManager.class)) {
                 found = true;
                 cst = csts[i];
@@ -113,7 +213,7 @@
 
         InstanceManager im = Mockito.mock(InstanceManager.class);
         cst.setAccessible(true);
-        Object pojo = cst.newInstance(new Object[] {im});
+        Object pojo = cst.newInstance(new Object[]{im});
         Assert.assertNotNull(pojo);
         Assert.assertTrue(pojo instanceof Pojo);
         Method method = cl.getMethod("doSomething", new Class[0]);
@@ -121,13 +221,13 @@
 
     }
 
-
     @Test
     public void testInnerClasses() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
         Manipulator manipulator = new Manipulator();
         String className = "test.inner.ComponentWithInnerClasses";
         ManipulatedClassLoader classloader = manipulate(className, manipulator);
 
+
         Class clazz = classloader.findClass(className);
         Assert.assertNotNull(clazz);
         Assert.assertNotNull(manipulator.getManipulationMetadata());
@@ -139,7 +239,7 @@
         InstanceManager im = Mockito.mock(InstanceManager.class);
         Constructor constructor = clazz.getDeclaredConstructor(InstanceManager.class);
         constructor.setAccessible(true);
-        Object pojo = constructor.newInstance(new Object[] {im});
+        Object pojo = constructor.newInstance(new Object[]{im});
         Assert.assertNotNull(pojo);
         Assert.assertTrue(pojo instanceof Pojo);
         Method method = clazz.getMethod("doSomething", new Class[0]);
@@ -148,31 +248,14 @@
     }
 
     @Test
-    public void testRemanipulationOfInnerClasses() throws IOException, ClassNotFoundException, NoSuchMethodException,
+    public void testDoubleManipulation() throws IOException, ClassNotFoundException, NoSuchMethodException,
             IllegalAccessException, InvocationTargetException, InstantiationException {
         Manipulator manipulator = new Manipulator();
         String className = "test.inner.ComponentWithInnerClasses";
+        ManipulatedClassLoader classloader = manipulate(className, manipulator);
 
-        // Two manipulation of the outer class.
-        byte[] bytecode = manipulator.manipulate(ManipulatorTest.getBytesFromFile(new File
-                (baseClassDirectory + className.replace(".", "/") + ".class")));
-        bytecode = manipulator.manipulate(bytecode);
-
-        ManipulatedClassLoader classloader = new ManipulatedClassLoader(className, bytecode);
-
-        // Manipulate all inner classes
-        for (String s : manipulator.getInnerClasses()) {
-            String outerClassInternalName = className.replace(".", "/");
-            byte[] innerClassBytecode = ManipulatorTest.getBytesFromFile(new File(baseClassDirectory + s + "" +
-                    ".class"));
-            String innerClassName = s.replace("/", ".");
-            InnerClassManipulator innerManipulator = new InnerClassManipulator(s, outerClassInternalName,
-                    manipulator);
-            // Two manipulation of all inner classes.
-            byte[] manipulated = innerManipulator.manipulate(innerClassBytecode, manipulator.getClassVersion());
-            manipulated = innerManipulator.manipulate(manipulated, manipulator.getClassVersion());
-            classloader.addInnerClass(innerClassName, manipulated);
-        }
+        manipulator = new Manipulator();
+        classloader = manipulate(className, manipulator, classloader);
 
         Class clazz = classloader.findClass(className);
         Assert.assertNotNull(clazz);
@@ -185,7 +268,7 @@
         InstanceManager im = Mockito.mock(InstanceManager.class);
         Constructor constructor = clazz.getDeclaredConstructor(InstanceManager.class);
         constructor.setAccessible(true);
-        Object pojo = constructor.newInstance(new Object[] {im});
+        Object pojo = constructor.newInstance(new Object[]{im});
         Assert.assertNotNull(pojo);
         Assert.assertTrue(pojo instanceof Pojo);
         Method method = clazz.getMethod("doSomething", new Class[0]);
@@ -218,25 +301,25 @@
         inner = getInnerClassMetadataByName(inners, "1");
         assertThat(inner).isNotNull();
         assertThat(getMethodByName(inner.getElements("method"), "compute")).isNotNull();
-
     }
 
-    private static Element getInnerClassMetadataByName(Element[] inners, String name) {
-        for (Element element : inners) {
-            if (name.equals(element.getAttribute("name"))) {
-                return element;
-            }
-        }
-        return null;
-    }
+    @Test
+    public void testThatTheClassContainsTheFlagsForTheInnerMethods() throws IOException, ClassNotFoundException,
+            NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
+        Manipulator manipulator = new Manipulator();
+        String className = "test.inner.ComponentWithInnerClasses";
+        ManipulatedClassLoader classLoader = manipulate(className, manipulator);
 
-    private static Element getMethodByName(Element[] methods, String name) {
-        for (Element element : methods) {
-            if (name.equals(element.getAttribute("name"))) {
-                return element;
-            }
-        }
-        return null;
+        Class clazz = classLoader.findClass(className);
+
+        String flag = "__M" + "MyInnerWithANativeMethod" + "___" + "foo";
+        assertThat(clazz.getDeclaredField(flag)).isNotNull();
+
+        flag = "__M" + "MyInnerClass" + "___" + "foo";
+        assertThat(clazz.getDeclaredField(flag)).isNotNull();
+
+        flag = "__M" + "1" + "___" + "compute" + "$java_lang_String";
+        assertThat(clazz.getDeclaredField(flag)).isNotNull();
     }
 
 }
diff --git a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatedClassLoader.java b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatedClassLoader.java
index eae2eb7..6cf6329 100644
--- a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatedClassLoader.java
+++ b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatedClassLoader.java
@@ -19,6 +19,10 @@
 
 package org.apache.felix.ipojo.manipulation;
 
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
@@ -69,4 +73,19 @@
         }
         return super.loadClass(classname);
     }
+
+    public static final File DUMP_BASEDIR = new File("target/dump");
+
+    public void dump() throws IOException {
+        File outer = new File(DUMP_BASEDIR, name.replace(".", "/") + ".class");
+        FileUtils.writeByteArrayToFile(outer, clazz);
+        for (String name : inner.keySet()) {
+            File file = new File(DUMP_BASEDIR, name.replace(".", "/") + ".class");
+            FileUtils.writeByteArrayToFile(file, inner.get(name));
+        }
+    }
+
+    public Map<String, byte[]> getAllInnerClasses() {
+        return inner;
+    }
 }
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 ff0f772..5cc8828 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
@@ -39,7 +39,10 @@
 
     public void testClusterDaemon() throws Exception {
         Manipulator manipulator = new Manipulator();
-        byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/ClusterDaemon.class")));
+        byte[] origin = getBytesFromFile(new File("target/test-classes/test/ClusterDaemon.class"));
+        manipulator.prepare(origin);
+        byte[] clazz = manipulator.manipulate(origin);
+
         ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.ClusterDaemon", clazz);
 
         //Assert.assertNotNull(manipulator.getManipulationMetadata());
@@ -62,7 +65,9 @@
 
     public void testManipulatingTheSimplePojo() throws Exception {
         Manipulator manipulator = new Manipulator();
-        byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/SimplePojo.class")));
+        byte[] origin = getBytesFromFile(new File("target/test-classes/test/SimplePojo.class"));
+        manipulator.prepare(origin);
+        byte[] clazz = manipulator.manipulate(origin);
         ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.SimplePojo", clazz);
         Class cl = classloader.findClass("test.SimplePojo");
         Assert.assertNotNull(cl);
@@ -113,7 +118,10 @@
 
     public void testManipulatingTheNonSunPOJO() throws Exception {
         Manipulator manipulator = new Manipulator();
-        byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/NonSunClass.class")));
+        byte[] origin = getBytesFromFile(new File("target/test-classes/test/NonSunClass.class"));
+        manipulator.prepare(origin);
+        byte[] clazz = manipulator.manipulate(origin);
+
         ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.NonSunClass", clazz);
         Class cl = classloader.findClass("test.NonSunClass");
         Assert.assertNotNull(cl);
@@ -153,7 +161,10 @@
 
     public void testManipulatingChild() throws Exception {
         Manipulator manipulator = new Manipulator();
-        byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/Child.class")));
+        byte[] origin = getBytesFromFile(new File("target/test-classes/test/Child.class"));
+        manipulator.prepare(origin);
+        byte[] clazz = manipulator.manipulate(origin);
+
         ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.Child", clazz);
         Class cl = classloader.findClass("test.Child");
         Assert.assertNotNull(cl);
@@ -199,7 +210,9 @@
 
     public void testManipulatingWithConstructorModification() throws Exception {
         Manipulator manipulator = new Manipulator();
-        byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/Child.class")));
+        byte[] origin = getBytesFromFile(new File("target/test-classes/test/Child.class"));
+        manipulator.prepare(origin);
+        byte[] clazz = manipulator.manipulate(origin);
         ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.Child", clazz);
         Class cl = classloader.findClass("test.Child");
         Assert.assertNotNull(cl);
@@ -264,7 +277,9 @@
 
     public void testManipulatingWithNoValidConstructor() throws Exception {
         Manipulator manipulator = new Manipulator();
-        byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/NoValidConstructor.class")));
+        byte[] origin = getBytesFromFile(new File("target/test-classes/test/NoValidConstructor.class"));
+        manipulator.prepare(origin);
+        byte[] clazz = manipulator.manipulate(origin);
         ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.NoValidConstructor", clazz);
         Class cl = classloader.findClass("test.NoValidConstructor");
         Assert.assertNotNull(cl);
@@ -273,7 +288,7 @@
         System.out.println(manipulator.getManipulationMetadata());
 
         // The manipulation add stuff to the class.
-        Assert.assertTrue(clazz.length > getBytesFromFile(new File("target/test-classes/test/NoValidConstructor.class")).length);
+        Assert.assertTrue(clazz.length > origin.length);
 
 
         boolean found = false;
@@ -301,7 +316,9 @@
 
      public void testConstructor() throws Exception {
         Manipulator manipulator = new Manipulator();
-        byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/ConstructorCheck.class")));
+         byte[] origin = getBytesFromFile(new File("target/test-classes/test/ConstructorCheck.class"));
+         manipulator.prepare(origin);
+         byte[] clazz = manipulator.manipulate(origin);
 
 //        File out = new File("target/ManipulatedConstructorCheck.class");
 //        FileOutputStream fos = new FileOutputStream(out);
@@ -328,8 +345,9 @@
      */
     public void testManipulatingDoubleArray() throws Exception {
         Manipulator manipulator = new Manipulator();
-        byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/DoubleArray.class")
-        ));
+        byte[] origin = getBytesFromFile(new File("target/test-classes/test/DoubleArray.class"));
+        manipulator.prepare(origin);
+        byte[] clazz = manipulator.manipulate(origin);
         ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.DoubleArray", clazz);
         Class cl = classloader.findClass("test.DoubleArray");
         Assert.assertNotNull(cl);
@@ -339,7 +357,7 @@
         Assert.assertTrue(manipulator.getManipulationMetadata().toString().contains("arguments=\"{int[][]}\""));
 
         // The manipulation add stuff to the class.
-        Assert.assertTrue(clazz.length > getBytesFromFile(new File("target/test-classes/test/DoubleArray.class")).length);
+        Assert.assertTrue(clazz.length > origin.length);
 
 
         boolean found = false;
diff --git a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/RemanipulationTest.java b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/RemanipulationTest.java
index 74ddeb3..202bf40 100644
--- a/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/RemanipulationTest.java
+++ b/ipojo/manipulator/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/RemanipulationTest.java
@@ -53,15 +53,16 @@
         Reporter reporter = mock(Reporter.class);
         // Step 1 - First collection and manipulation
         //1.1 Metadata collection
+        byte[] origin = ManipulatorTest.getBytesFromFile(new File("target/test-classes/test/PlentyOfAnnotations.class"));
         MiniStore store = new MiniStore()
                 .addClassToStore("test.PlentyOfAnnotations",
-                        ManipulatorTest.getBytesFromFile(new File("target/test-classes/test/PlentyOfAnnotations.class")));
+                        origin);
         AnnotationMetadataProvider provider = new AnnotationMetadataProvider(store, reporter);
         List<Element> originalMetadata = provider.getMetadatas();
         // 1.2 Manipulation
         Manipulator manipulator = new Manipulator();
-        byte[] clazz = manipulator.manipulate(
-                ManipulatorTest.getBytesFromFile(new File("target/test-classes/test/PlentyOfAnnotations.class")));
+        manipulator.prepare(origin);
+        byte[] clazz = manipulator.manipulate(origin);
         Element originalManipulationMetadata = manipulator.getManipulationMetadata();
         // 1.3 Check that the class is valid
         ManipulatedClassLoader classloader = new ManipulatedClassLoader("test.PlentyOfAnnotations", clazz);
@@ -78,6 +79,7 @@
         List<Element> metadataAfterOneManipulation = provider.getMetadatas();
         // 2.2 Manipulation
         manipulator = new Manipulator();
+        manipulator.prepare(clazz);
         byte[] clazz2 = manipulator.manipulate(clazz);
         Element manipulationMetadataAfterSecondManipulation = manipulator.getManipulationMetadata();
         // 2.3 Check that the class is valid
@@ -95,6 +97,7 @@
         List<Element> metadataAfterTwoManipulation = provider.getMetadatas();
         // 3.2 Manipulation
         manipulator = new Manipulator();
+        manipulator.prepare(clazz2);
         byte[] clazz3 = manipulator.manipulate(clazz2);
         Element manipulationMetadataAfterThirdManipulation = manipulator.getManipulationMetadata();
         // 3.3 Check that the class is valid
diff --git a/ipojo/manipulator/manipulator/src/test/java/test/inner/ComponentWithInnerClasses.java b/ipojo/manipulator/manipulator/src/test/java/test/inner/ComponentWithInnerClasses.java
index 12927b6..f54cc95 100644
--- a/ipojo/manipulator/manipulator/src/test/java/test/inner/ComponentWithInnerClasses.java
+++ b/ipojo/manipulator/manipulator/src/test/java/test/inner/ComponentWithInnerClasses.java
@@ -39,6 +39,17 @@
         return nat.foo() + MyStaticInnerClass.foo() + inn.foo() + compute.compute("");
     }
 
+    private void doSomethingPrivately() {
+
+    }
+    private boolean flag;
+
+    boolean getFlag() {
+        return flag;
+    }
+
+    private String test = "";
+
     private String foo = "foo";
 
     private class MyInnerWithANativeMethod {
@@ -47,6 +58,12 @@
             return ComponentWithInnerClasses.this.foo;
         }
 
+        public void bar() {
+            if (! getFlag()) {
+                test.charAt(0);
+            }
+        }
+
         public native void baz();
 
     }
diff --git a/ipojo/manipulator/manipulator/src/test/java/test/inner/Example.java b/ipojo/manipulator/manipulator/src/test/java/test/inner/Example.java
new file mode 100644
index 0000000..2160838
--- /dev/null
+++ b/ipojo/manipulator/manipulator/src/test/java/test/inner/Example.java
@@ -0,0 +1,80 @@
+/*
+ * 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.inner;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: clement
+ * Date: 01/10/13
+ * Time: 14:57
+ * To change this template use File | Settings | File Templates.
+ */
+public class Example {
+
+    private String myString;
+
+    org.apache.felix.ipojo.InstanceManager __IM;
+    boolean __M1___run;
+    boolean __MFoo___run;
+
+    public void doSomething() {
+        Runnable runnable = new Runnable() {
+            public void run() {
+                if (! __M1___run) {
+                    __run();
+                } else {
+                    try {
+                        __IM.onEntry(Example.this, "__M1___run", new Object[0]);
+                        __run();
+                        __IM.onExit(Example.this, "__M1___run", new Object[0]);
+                    } catch (Throwable e) {
+                        __IM.onError(Example.this, "__M1___run", e);
+                    }
+                }
+            }
+
+            private void __run() {
+                System.out.println(myString);
+            }
+        };
+        runnable.run();
+    }
+
+    private class Foo {
+        public void run() {
+            if (! __MFoo___run) {
+                __run();
+            } else {
+                try {
+                    __IM.onEntry(Example.this, "__MFoo___run", new Object[0]);
+                    __run();
+                    __IM.onExit(Example.this, "__MFoo___run", new Object[0]);
+                } catch (Throwable e) {
+                    __IM.onError(Example.this, "__MFoo___run", e);
+                }
+            }
+        }
+
+        private void __run() {
+            System.out.println(myString);
+        }
+    }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
index 1589c68..9e86ec9 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
@@ -148,7 +148,7 @@
      * The Map storing the Method objects by ids.
      * [id=>{@link Method}].
      */
-    private Map m_methods = new Hashtable();
+    private Map m_methods = new HashMap();
 
 
     /**
@@ -1068,6 +1068,38 @@
     }
 
     /**
+     * Registers a method interceptor on a methods from an inner class.
+     * A method interceptor will be notified of method entries, exits
+     * and errors. Note that handlers are method interceptors.
+     * @param method the field to monitor
+     * @param innerClass the inner class name
+     * @param interceptor the field interceptor object
+     */
+    public void register(MethodMetadata method, String innerClass, MethodInterceptor interceptor) {
+        if (m_methodRegistration == null) {
+            m_methodRegistration = new HashMap();
+            m_methodRegistration.put(innerClass + "___" + method.getMethodIdentifier(),
+                    new MethodInterceptor[] { interceptor });
+        } else {
+            MethodInterceptor[] list = (MethodInterceptor[]) m_methodRegistration.get(method.getMethodIdentifier());
+            if (list == null) {
+                m_methodRegistration.put(innerClass + "___" + method.getMethodIdentifier(),
+                        new MethodInterceptor[] { interceptor });
+            } else {
+                for (int j = 0; j < list.length; j++) {
+                    if (list[j] == interceptor) {
+                        return;
+                    }
+                }
+                MethodInterceptor[] newList = new MethodInterceptor[list.length + 1];
+                System.arraycopy(list, 0, newList, 0, list.length);
+                newList[list.length] = interceptor;
+                m_methodRegistration.put(innerClass + "___" + method.getMethodIdentifier(), newList);
+            }
+        }
+    }
+
+    /**
      * Registers a constructor injector.
      * The constructor injector will be called when a pojo object is going to be
      * created.
@@ -1154,8 +1186,11 @@
         if (m_methodRegistration == null) { // Immutable field.
             return;
         }
+
         MethodInterceptor[] list = (MethodInterceptor[]) m_methodRegistration.get(methodId);
         Member method = getMethodById(methodId);
+        // We can't find the member object of anonymous methods.
+
         // In case of a constructor, the method is null, and the list is null too.
         for (int i = 0; list != null && i < list.length; i++) {
             list[i].onEntry(pojo, method, args); // Outside a synchronized block.
@@ -1222,7 +1257,42 @@
     private Member getMethodById(String methodId) {
         // Used a synchronized map.
         Member member = (Member) m_methods.get(methodId);
-        if (member == null  && m_clazz != null) {
+        if (! m_methods.containsKey(methodId) && m_clazz != null) {
+            // Is it a inner class method
+            if (methodId.contains("___")) { // Mark to detect a inner class method.
+                String[] split = methodId.split("___");
+                if (split.length != 2) {
+                    m_logger.log(Logger.INFO, "A methodID cannot be associated with a method from the POJO class: " + methodId);
+                    return null;
+                } else {
+                    String innerClassName = split[0];
+                    methodId = split[1];
+
+                    // We can't find the member objects from anonymous methods, identified by their numeric name
+                    // Just escaping in this case.
+                    if (innerClassName.matches("-?\\d+")) {
+                        m_methods.put(methodId, null);
+                        return null;
+                    }
+
+                    for (Class c : m_clazz.getDeclaredClasses()) {
+                        if (innerClassName.equals(c.getSimpleName())) {
+                            Method[] mets = c.getDeclaredMethods();
+                            for (Method met : mets) {
+                                if (MethodMetadata.computeMethodId(met).equals(methodId)) {
+                                    // Store the new methodId
+                                    m_methods.put(methodId, met);
+                                    return met;
+                                }
+                            }
+                        }
+                        m_logger.log(Logger.INFO, "Cannot find the member associated to " + methodId + " - reason: " +
+                                "cannot find the class " + innerClassName + " declared in " + m_clazz.getName());
+                    }
+                }
+            }
+
+
             // First try on methods.
             Method[] mets = m_clazz.getDeclaredMethods();
             for (int i = 0; i < mets.length; i++) {
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
index 8c07b76..b23ea03 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
@@ -19,23 +19,21 @@
 
 package org.apache.felix.ipojo.handlers.dependency;
 
-import java.util.*;
-
 import org.apache.felix.ipojo.*;
 import org.apache.felix.ipojo.architecture.HandlerDescription;
 import org.apache.felix.ipojo.metadata.Element;
-import org.apache.felix.ipojo.parser.FieldMetadata;
 import org.apache.felix.ipojo.parser.MethodMetadata;
 import org.apache.felix.ipojo.parser.PojoMetadata;
 import org.apache.felix.ipojo.util.*;
-import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
+
+import java.util.*;
 
 /**
  * The dependency handler manages a list of service dependencies.
+ *
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public class DependencyHandler extends PrimitiveHandler implements DependencyStateListener {
@@ -44,54 +42,81 @@
      * Proxy settings property.
      */
     public static final String PROXY_SETTINGS_PROPERTY = "ipojo.proxy";
-
     /**
      * Proxy type property.
      */
     public static final String PROXY_TYPE_PROPERTY = "ipojo.proxy.type";
-
     /**
      * Proxy type value: smart.
      */
     public static final String SMART_PROXY = "smart";
-
     /**
      * Proxy type value: dynamic-proxy.
      */
     public static final String DYNAMIC_PROXY = "dynamic-proxy";
-
     /**
      * Proxy settings value: enabled.
      */
     public static final String PROXY_ENABLED = "enabled";
-
     /**
      * Proxy settings value: disabled.
      */
     public static final String PROXY_DISABLED = "disabled";
-
     /**
      * List of dependencies of the component.
      */
     private final List<Dependency> m_dependencies = new ArrayList<Dependency>();
-
     /**
      * Is the handler started.
      */
     private boolean m_started;
-
     /**
      * The handler description.
      */
     private DependencyHandlerDescription m_description;
-
     /**
      * The instance configuration context source, updated once reconfiguration.
      */
     private InstanceConfigurationSource m_instanceConfigurationSource;
 
     /**
+     * Builds a description of this dependency to help the user to identify it. IT's not related to the Dependency
+     * Description, it's just a string containing dependency information to spot it easily in the code.
+     *
+     * @param dep the dependency
+     * @return the identifier containing (if defined) the id, the specification, the field and the callback.
+     * @since 1.10.1
+     */
+    public static String getDependencyIdentifier(Dependency dep) {
+        StringBuilder identifier = new StringBuilder("{");
+        if (dep.getId() != null) {
+            identifier.append("id=").append(dep.getId());
+        }
+        if (dep.getField() != null) {
+            if (identifier.length() > 1) {
+                identifier.append(", ");
+            }
+            identifier.append("field=").append(dep.getField());
+        }
+        if (dep.getCallbacks() != null && dep.getCallbacks().length > 0) {
+            if (identifier.length() > 1) {
+                identifier.append(", ");
+            }
+            identifier.append("method=").append(dep.getCallbacks()[0].getMethodName());
+        }
+        if (dep.getSpecification() != null) {
+            if (identifier.length() > 1) {
+                identifier.append(", ");
+            }
+            identifier.append("specification=").append(dep.getSpecification().getName());
+        }
+        identifier.append("}");
+        return identifier.toString();
+    }
+
+    /**
      * Get the list of managed dependency.
+     *
      * @return the dependency list
      */
     public Dependency[] getDependencies() {
@@ -100,6 +125,7 @@
 
     /**
      * Validate method. This method is invoked by an AbstractServiceDependency when this dependency becomes RESOLVED.
+     *
      * @param dep : the dependency becoming RESOLVED.
      * @see org.apache.felix.ipojo.util.DependencyStateListener#validate(org.apache.felix.ipojo.util.DependencyModel)
      */
@@ -109,6 +135,7 @@
 
     /**
      * Invalidate method. This method is invoked by an AbstractServiceDependency when this dependency becomes UNRESOLVED or BROKEN.
+     *
      * @param dep : the dependency becoming UNRESOLVED or BROKEN.
      * @see org.apache.felix.ipojo.util.DependencyStateListener#invalidate(org.apache.felix.ipojo.util.DependencyModel)
      */
@@ -156,43 +183,10 @@
     }
 
     /**
-     * Builds a description of this dependency to help the user to identify it. IT's not related to the Dependency
-     * Description, it's just a string containing dependency information to spot it easily in the code.
-     * @param dep the dependency
-     * @return the identifier containing (if defined) the id, the specification, the field and the callback.
-     * @since 1.10.1
-     */
-    public static String getDependencyIdentifier(Dependency dep) {
-        StringBuilder identifier = new StringBuilder("{");
-        if (dep.getId() != null) {
-            identifier.append("id=").append(dep.getId());
-        }
-        if (dep.getField() != null) {
-            if (identifier.length() > 1) {
-                identifier.append(", ");
-            }
-            identifier.append("field=").append(dep.getField());
-        }
-        if (dep.getCallbacks() != null  && dep.getCallbacks().length > 0) {
-            if (identifier.length() > 1) {
-                identifier.append(", ");
-            }
-            identifier.append("method=").append(dep.getCallbacks()[0].getMethodName());
-        }
-        if (dep.getSpecification() != null) {
-            if (identifier.length() > 1) {
-                identifier.append(", ");
-            }
-            identifier.append("specification=").append(dep.getSpecification().getName());
-        }
-        identifier.append("}");
-        return identifier.toString();
-    }
-
-    /**
      * Configure the handler.
+     *
      * @param componentMetadata : the component type metadata
-     * @param configuration : the instance configuration
+     * @param configuration     : the instance configuration
      * @throws ConfigurationException : one dependency metadata is not correct.
      * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
      */
@@ -256,8 +250,8 @@
             // Add the constructor parameter if needed
             String paramIndex = dependencyElement.getAttribute("constructor-parameter");
             if (paramIndex != null) {
-            	int index = Integer.parseInt(paramIndex);
-            	dep.addConstructorInjection(index);
+                int index = Integer.parseInt(paramIndex);
+                dep.addConstructorInjection(index);
             }
 
             // Check the dependency, throws an exception on error.
@@ -277,6 +271,19 @@
                     getInstanceManager().register(method, dep);
                 }
             }
+
+            // Also track the inner class methods
+            for (String inner : manipulation.getInnerClasses()) {
+                MethodMetadata[] meths = manipulation.getMethodsFromInnerClass(inner);
+                if (meths != null) {
+                    for (MethodMetadata method : meths) {
+                        for (Dependency dep : m_dependencies) {
+                            System.out.println("Registering " + method.getMethodName());
+                            getInstanceManager().register(method, inner, dep);
+                        }
+                    }
+                }
+            }
         }
 
         m_description = new DependencyHandlerDescription(this, getDependencies()); // Initialize the description.
@@ -286,6 +293,7 @@
 
     /**
      * Add internal context source to all dependencies.
+     *
      * @param configuration the instance configuration to creates the instance configuration source
      */
     private void manageContextSources(Dictionary<String, Object> configuration) {
@@ -353,7 +361,7 @@
             if (proxy.equals("false")) {
                 isProxy = false;
             } else if (proxy.equals("true")) {
-                if (! isProxy) { // The configuration overrides the system setting
+                if (!isProxy) { // The configuration overrides the system setting
                     warn("The configuration of a service dependency overrides the proxy mode");
                 }
                 isProxy = true;
@@ -446,39 +454,41 @@
     }
 
     /**
-	 * Gets the requires filter configuration from the given object.
-	 * The given object must come from the instance configuration.
-	 * This method was made to fix FELIX-2688. It supports filter configuration using
-	 * an array:
-	 * <code>{"myFirstDep", "(property1=value1)", "mySecondDep", "(property2=value2)"});</code>
-	 * @param requiresFiltersValue the value contained in the instance
-	 * configuration.
-	 * @return the dictionary. If the object in already a dictionary, just returns it,
-	 * if it's an array, builds the dictionary.
-	 * @throws ConfigurationException the dictionary cannot be built
-	 */
-	private Dictionary getRequiresFilters(Object requiresFiltersValue)
-			throws ConfigurationException {
-		if (requiresFiltersValue != null
-				&& requiresFiltersValue.getClass().isArray()) {
-			String[] filtersArray = (String[]) requiresFiltersValue;
-			if (filtersArray.length % 2 != 0) {
-				throw new ConfigurationException(
-						"A requirement filter is invalid : "
-								+ requiresFiltersValue);
-			}
-			Dictionary<String, Object> requiresFilters = new Hashtable<String, Object>();
-			for (int i = 0; i < filtersArray.length; i += 2) {
-				requiresFilters.put(filtersArray[i], filtersArray[i + 1]);
-			}
-			return requiresFilters;
-		}
+     * Gets the requires filter configuration from the given object.
+     * The given object must come from the instance configuration.
+     * This method was made to fix FELIX-2688. It supports filter configuration using
+     * an array:
+     * <code>{"myFirstDep", "(property1=value1)", "mySecondDep", "(property2=value2)"});</code>
+     *
+     * @param requiresFiltersValue the value contained in the instance
+     *                             configuration.
+     * @return the dictionary. If the object in already a dictionary, just returns it,
+     *         if it's an array, builds the dictionary.
+     * @throws ConfigurationException the dictionary cannot be built
+     */
+    private Dictionary getRequiresFilters(Object requiresFiltersValue)
+            throws ConfigurationException {
+        if (requiresFiltersValue != null
+                && requiresFiltersValue.getClass().isArray()) {
+            String[] filtersArray = (String[]) requiresFiltersValue;
+            if (filtersArray.length % 2 != 0) {
+                throw new ConfigurationException(
+                        "A requirement filter is invalid : "
+                                + requiresFiltersValue);
+            }
+            Dictionary<String, Object> requiresFilters = new Hashtable<String, Object>();
+            for (int i = 0; i < filtersArray.length; i += 2) {
+                requiresFilters.put(filtersArray[i], filtersArray[i + 1]);
+            }
+            return requiresFilters;
+        }
 
-		return (Dictionary) requiresFiltersValue;
-	}
+        return (Dictionary) requiresFiltersValue;
+    }
 
     /**
      * Handler start method.
+     *
      * @see org.apache.felix.ipojo.Handler#start()
      */
     public void start() {
@@ -494,6 +504,7 @@
 
     /**
      * Handler stop method.
+     *
      * @see org.apache.felix.ipojo.Handler#stop()
      */
     public void stop() {
@@ -505,6 +516,7 @@
 
     /**
      * Handler createInstance method. This method is override to allow delayed callback invocation.
+     *
      * @param instance : the created object
      * @see org.apache.felix.ipojo.PrimitiveHandler#onCreation(Object)
      */
@@ -516,6 +528,7 @@
 
     /**
      * Get the dependency handler description.
+     *
      * @return the dependency handler description.
      * @see org.apache.felix.ipojo.Handler#getDescription()
      */
@@ -525,6 +538,7 @@
 
     /**
      * The instance is reconfigured.
+     *
      * @param configuration the new instance configuration.
      */
     @Override