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