Integrate the constructor-injection branch into the trunk.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1052264 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/pom.xml b/ipojo/manipulator/pom.xml
index 5b0e68b..ea42af2 100644
--- a/ipojo/manipulator/pom.xml
+++ b/ipojo/manipulator/pom.xml
@@ -51,6 +51,11 @@
<artifactId>org.apache.felix.ipojo.metadata</artifactId>
<version>1.4.0</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.annotations</artifactId>
+ <version>1.7.0-SNAPSHOT</version>
+ </dependency>
</dependencies>
<build>
<plugins>
@@ -109,6 +114,16 @@
<configLocation>http://felix.apache.org/ipojo/dev/checkstyle_ipojo.xml</configLocation>
</configuration>
</plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+
</plugins>
<resources>
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java
index 41cca2f..7847464 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java
@@ -191,15 +191,14 @@
newDesc = "(Lorg/apache/felix/ipojo/InstanceManager;" + newDesc;
Type[] args = Type.getArgumentTypes(desc);
+
+ // TODO HERE ! => All constructor matches, no distinction between the different constructors.
+ generateConstructor(access, desc, signature, exceptions, md.getAnnotations(), md.getParameterAnnotations());
+
if (args.length == 0) {
- generateEmptyConstructor(access, signature, exceptions, md.getAnnotations()); // No parameters, so no annotations parameters
m_foundSuitableConstructor = true;
} else if (args.length == 1 && args[0].getClassName().equals("org.osgi.framework.BundleContext")) {
- generateBCConstructor(access, signature, exceptions, md.getAnnotations()); // One parameter, so no annotations parameters
m_foundSuitableConstructor = true;
- } else {
- // Do nothing, the constructor does not match.
- return cv.visitMethod(access, name, desc, signature, exceptions);
}
// Insert the new constructor
@@ -297,64 +296,56 @@
}
/**
- * Create a constructor to call the manipulated constructor.
- * This constructor does not have any argument. It will call the manipulated
- * constructor with a null instance manager.
+ * Modify the given constructor to be something like:
+ * <code>
+ * this(null, params...);
+ * return;
+ * </code>
+ * The actual constructor is modified to support the instance manager argument.
* @param access : access flag
+ * @param descriptor : the original constructor descriptor
* @param signature : method signature
* @param exceptions : declared exception
* @param annotations : the annotations to move to this constructor.
*/
- private void generateEmptyConstructor(int access, String signature, String[] exceptions, List annotations) {
- MethodVisitor mv = cv.visitMethod(access, "<init>", "()V", signature, exceptions);
- mv.visitCode();
- mv.visitVarInsn(ALOAD, 0);
- mv.visitInsn(ACONST_NULL);
- mv.visitMethodInsn(INVOKESPECIAL, m_owner, "<init>", "(Lorg/apache/felix/ipojo/InstanceManager;)V");
- mv.visitInsn(RETURN);
+ private void generateConstructor(int access, String descriptor, String signature, String[] exceptions, List annotations, Map paramAnnotations) {
+ GeneratorAdapter mv = new GeneratorAdapter(
+ cv.visitMethod(access, "<init>", descriptor, signature, exceptions),
+ access, "<init>", descriptor);
+ // Compute the new signature
+ String newDesc = descriptor.substring(1); // Remove the first (
+ newDesc = "(Lorg/apache/felix/ipojo/InstanceManager;" + newDesc;
- // Move annotations
- if (annotations != null) {
- for (int i = 0; i < annotations.size(); i++) {
- AnnotationDescriptor ad = (AnnotationDescriptor) annotations.get(i);
- ad.visitAnnotation(mv);
- }
- }
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ACONST_NULL);
+ mv.loadArgs();
+ mv.visitMethodInsn(INVOKESPECIAL, m_owner, "<init>", newDesc);
+ mv.visitInsn(RETURN);
- mv.visitMaxs(0, 0);
- mv.visitEnd();
- }
+ // Move annotations
+ if (annotations != null) {
+ for (int i = 0; i < annotations.size(); i++) {
+ AnnotationDescriptor ad = (AnnotationDescriptor) annotations.get(i);
+ ad.visitAnnotation(mv);
+ }
+ }
- /**
- * Create a constructor to call the manipulated constructor.
- * This constructor has one argument (the bundle context). It will call the manipulated
- * constructor with a null instance manager.
- * @param access : access flag
- * @param signature : method signature
- * @param exceptions : declared exception
- * @param annotations : the annotations to move to this constructor.
- */
- private void generateBCConstructor(int access, String signature, String[] exceptions, List annotations) {
- MethodVisitor mv = cv.visitMethod(access, "<init>", "(Lorg/osgi/framework/BundleContext;)V", signature, exceptions);
- mv.visitCode();
- Label l0 = new Label();
- mv.visitLabel(l0);
- mv.visitVarInsn(ALOAD, 0);
- mv.visitInsn(ACONST_NULL);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitMethodInsn(INVOKESPECIAL, m_owner, "<init>", "(Lorg/apache/felix/ipojo/InstanceManager;Lorg/osgi/framework/BundleContext;)V");
- mv.visitInsn(RETURN);
+ // Move parameter annotations if any
+ if (paramAnnotations != null && ! paramAnnotations.isEmpty()) {
+ Iterator ids = paramAnnotations.keySet().iterator();
+ while(ids.hasNext()) {
+ Integer id = (Integer) ids.next();
+ List ads = (List) paramAnnotations.get(id);
+ for (int i = 0; i < ads.size(); i++) {
+ AnnotationDescriptor ad = (AnnotationDescriptor) ads.get(i);
+ ad.visitParameterAnnotation(id.intValue(), mv);
+ }
+ }
+ }
- // Move annotations
- if (annotations != null) {
- for (int i = 0; i < annotations.size(); i++) {
- AnnotationDescriptor ad = (AnnotationDescriptor) annotations.get(i);
- ad.visitAnnotation(mv);
- }
- }
-
- mv.visitMaxs(0, 0);
- mv.visitEnd();
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
}
/**
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java
index fdd3ba2..a5e9f45 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -23,6 +23,7 @@
import org.apache.felix.ipojo.metadata.Attribute;
import org.apache.felix.ipojo.metadata.Element;
import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Type;
import org.objectweb.asm.commons.EmptyVisitor;
/**
@@ -31,8 +32,6 @@
*/
public class CustomAnnotationVisitor extends EmptyVisitor implements AnnotationVisitor {
- //TODO manage enum annotations.
-
/**
* Parent element.
*/
@@ -54,7 +53,7 @@
* Is the custom annotation a first-order annotation.
*/
private boolean m_root;
-
+
/**
* Is the visit annotation a class annotation?
*/
@@ -64,7 +63,22 @@
* Metadata collector.
*/
private MetadataCollector m_collector;
-
+
+ /**
+ * Flag sets to true for parameter annotation.
+ */
+ private boolean m_isParameterAnnotation = false;
+
+ /**
+ * For parameter annotations, the index of the parameter.
+ */
+ private int m_index = -1;
+
+ /**
+ * For parameter annotation, the descriptor of the method.
+ */
+ private String m_desc;
+
/**
* Constructor.
* @param elem the parent element
@@ -78,7 +92,26 @@
m_collector = collector;
m_classAnnotation = clazz;
}
-
+
+ /**
+ * Constructor used for parameter annotations
+ * @param elem the parent element
+ * @param collector the metadata collector
+ * @param root is the annotation a root
+ * @param clazz the annotation is a class annotation.
+ * @param index the index of the argument
+ * @param the descriptor of the method
+ */
+ public CustomAnnotationVisitor(Element elem, MetadataCollector collector, boolean root, boolean clazz, int index, String descriptor) {
+ m_elem = elem;
+ m_root = root;
+ m_collector = collector;
+ m_classAnnotation = clazz;
+ m_isParameterAnnotation = true;
+ m_index = index;
+ m_desc = descriptor;
+ }
+
/**
* Check if the given annotation descriptor is an iPOJO custom annotation.
* A valid iPOJO custom annotation must contains 'ipojo' or 'handler' in its qualified name.
@@ -92,7 +125,7 @@
}
return false;
}
-
+
/**
* Build the element object from the given descriptor.
* @param desc : annotation descriptor
@@ -109,7 +142,7 @@
/**
* Visit a 'simple' annotation attribute.
- * This method is used for primitive arrays too.
+ * This method is used for primitive arrays too.
* @param arg0 : attribute name
* @param arg1 : attribute value
* @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)
@@ -164,7 +197,7 @@
public AnnotationVisitor visitArray(String arg0) {
return new SubArrayVisitor(m_elem, arg0);
}
-
+
/**
* Visits an enumeration attribute.
* @param arg0 the attribute name
@@ -186,6 +219,7 @@
if (m_id != null) {
m_collector.getIds().put(m_id, m_elem);
} else {
+ m_id = m_elem.getNameSpace();
if (! m_collector.getIds().containsKey(m_elem.getNameSpace()) && m_classAnnotation) {
// If the namespace is not already used, add the annotation as the
// root element of this namespace.
@@ -197,8 +231,15 @@
}
}
}
-
+
m_collector.getElements().put(m_elem, m_parent);
+
+ if (m_isParameterAnnotation) {
+ String t = Type.getArgumentTypes(m_desc)[m_index].getClassName();
+ m_elem.addAttribute(new Attribute("type", t));
+ m_elem.addAttribute(
+ new Attribute("constructor-parameter", Integer.toString(m_index)));
+ }
}
}
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java
index f3a4836..7c54269 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java
@@ -210,7 +210,7 @@
* @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) {
- return new MethodCollector(name, this);
+ return new MethodCollector(name, desc, this);
}
/**
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java
index 770316a..13ced4c 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java
@@ -18,6 +18,8 @@
*/
package org.apache.felix.ipojo.manipulation.annotations;
+import java.awt.image.renderable.ParameterBlock;
+
import org.apache.felix.ipojo.metadata.Attribute;
import org.apache.felix.ipojo.metadata.Element;
import org.objectweb.asm.AnnotationVisitor;
@@ -41,16 +43,47 @@
private String m_name;
/**
+ * Method Descriptor.
+ */
+ private String m_descriptor;
+
+ /**
* Constructor.
* @param name : name of the method.
* @param collector : parent collector.
*/
- public MethodCollector(String name, MetadataCollector collector) {
+ public MethodCollector(String name, String descriptor, MetadataCollector collector) {
m_collector = collector;
m_name = name;
+ m_descriptor = descriptor;
}
/**
+ * Visit a parameter annotation.
+ * @see org.objectweb.asm.commons.EmptyVisitor#visitParameterAnnotation(int, java.lang.String, boolean)
+ */
+ public AnnotationVisitor visitParameterAnnotation(int index, String annotation,
+ boolean visible) {
+ if (m_name.equals("<init>")) {
+ if (annotation.equals("Lorg/apache/felix/ipojo/annotations/Property;")) {
+ return processProperty(true, index);
+ }
+ if (annotation.equals("Lorg/apache/felix/ipojo/annotations/Requires;")) {
+ return new BindAnnotationParser(index);
+ }
+
+ if (CustomAnnotationVisitor.isCustomAnnotation(annotation)) {
+ Element elem = CustomAnnotationVisitor.buildElement(annotation);
+ elem.addAttribute(new Attribute("index", "" + index));
+ return new CustomAnnotationVisitor(elem, m_collector, true, false, index, m_descriptor);
+ }
+ }
+ return super.visitParameterAnnotation(index, annotation, visible);
+ }
+
+
+
+ /**
* Visit method annotations.
* @param arg0 : annotation name.
* @param arg1 : is the annotation visible at runtime.
@@ -59,7 +92,7 @@
*/
public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
if (arg0.equals("Lorg/apache/felix/ipojo/annotations/Property;")) {
- return processProperty();
+ return processProperty(false, -1);
}
if (arg0.equals("Lorg/apache/felix/ipojo/annotations/Validate;")) {
return processValidate();
@@ -181,9 +214,11 @@
/**
* Process @property annotation.
+ * @param parameter true if we're processing a parameter
+ * @param index the index, meaningful only if parameter is true
* @return the visitor parsing the visited annotation.
*/
- private AnnotationVisitor processProperty() {
+ private AnnotationVisitor processProperty(boolean parameter, int index) {
Element prop = null;
if (! m_collector.getIds().containsKey("properties")) {
prop = new Element("Properties", "");
@@ -192,7 +227,7 @@
} else {
prop = (Element) m_collector.getIds().get("properties");
}
- return new PropertyAnnotationParser(prop, m_name);
+ return new PropertyAnnotationParser(prop, m_name, parameter, index);
}
/**
@@ -251,6 +286,12 @@
private String m_from;
/**
+ * For annotation parameter,
+ * the parameter index.
+ */
+ private int m_index = -1;
+
+ /**
* Constructor.
* @param bind : method name.
* @param type : is the callback a bind or an unbind method.
@@ -260,6 +301,10 @@
m_type = type;
}
+ private BindAnnotationParser(int index) {
+ m_index = index;
+ }
+
/**
* Visit annotation attribute.
* @param arg0 : annotation name
@@ -310,17 +355,20 @@
*/
public void visitEnd() {
if (m_id == null) {
- if (m_name.startsWith("bind")) {
+ if (m_name != null && m_name.startsWith("bind")) {
m_id = m_name.substring("bind".length());
- } else if (m_name.startsWith("unbind")) {
+ } else if (m_name != null && m_name.startsWith("unbind")) {
m_id = m_name.substring("unbind".length());
- } else if (m_name.startsWith("modified")) {
+ } else if (m_name != null && m_name.startsWith("modified")) {
m_id = m_name.substring("modified".length());
- } else {
+ } else if (m_index != -1) {
+ m_id = "" + m_index;
+ } else {
System.err.println("Cannot determine the id of the " + m_type + " method : " + m_name);
return;
}
}
+
// Check if it is a full-determined requirement
Element req = (Element) m_collector.getIds().get(m_id);
if (req == null) {
@@ -422,17 +470,22 @@
}
}
- Element method = new Element("callback", "");
- method.addAttribute(new Attribute("method", m_name));
- method.addAttribute(new Attribute("type", m_type));
- req.addElement(method);
+ if (m_name != null) {
+ Element method = new Element("callback", "");
+ method.addAttribute(new Attribute("method", m_name));
+ method.addAttribute(new Attribute("type", m_type));
+ req.addElement(method);
+ } else {
+ req.addAttribute(new Attribute("constructor-parameter", Integer.toString(m_index)));
+ }
+
m_collector.getIds().put(m_id, req);
m_collector.getElements().put(req, null);
return;
}
}
- private static final class PropertyAnnotationParser extends EmptyVisitor implements AnnotationVisitor {
+ private final class PropertyAnnotationParser extends EmptyVisitor implements AnnotationVisitor {
/**
* Parent element.
@@ -450,6 +503,11 @@
private String m_name;
/**
+ * Property id.
+ */
+ private String m_id;
+
+ /**
* Property value.
*/
private String m_value;
@@ -460,13 +518,27 @@
private String m_mandatory;
/**
+ * Flag set to true if we're processing an annotation parameter.
+ */
+ private boolean m_isParameterAnnotation = false;
+
+ /**
+ * If this is a parameter annotation, the index of the parameter.
+ */
+ private int m_index = -1;
+
+ /**
* Constructor.
* @param parent : parent element.
* @param method : attached method.
+ * @param param : we're processing a parameter
+ * @param index : the parameter index
*/
- private PropertyAnnotationParser(Element parent, String method) {
+ private PropertyAnnotationParser(Element parent, String method, boolean param, int index) {
m_parent = parent;
m_method = method;
+ m_isParameterAnnotation = param;
+ m_index = index;
}
/**
@@ -488,6 +560,10 @@
m_mandatory = arg1.toString();
return;
}
+ if (arg0.equals("id")) {
+ m_id = arg1.toString();
+ return;
+ }
}
/**
@@ -496,9 +572,17 @@
* @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
*/
public void visitEnd() {
- if (m_name == null && m_method.startsWith("set")) {
+ // If neither name not id, try to extract the name
+ if (m_name == null && m_id == null && m_method.startsWith("set")) {
m_name = m_method.substring("set".length());
+ m_id = m_name;
+ // Else align the two values
+ } else if (m_name != null && m_id == null) {
+ m_id = m_name;
+ } else if (m_id != null && m_name == null) {
+ m_name = m_id;
}
+
Element[] props = m_parent.getElements("Property");
Element prop = null;
for (int i = 0; props != null && prop == null && i < props.length; i++) {
@@ -516,7 +600,6 @@
}
}
- prop.addAttribute(new Attribute("method", m_method));
if (m_value != null) {
prop.addAttribute(new Attribute("value", m_value));
}
@@ -524,6 +607,14 @@
prop.addAttribute(new Attribute("mandatory", m_mandatory));
}
+ if (m_isParameterAnnotation) {
+ String t = Type.getArgumentTypes(m_descriptor)[m_index].getClassName();
+ prop.addAttribute(new Attribute("type", t));
+ prop.addAttribute(new Attribute("constructor-parameter", Integer.toString(m_index)));
+ } else {
+ prop.addAttribute(new Attribute("method", m_method));
+ }
+
}
}
}
diff --git a/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/ComponentInstance.java b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/ComponentInstance.java
new file mode 100644
index 0000000..a81dea8
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/ComponentInstance.java
@@ -0,0 +1,12 @@
+package org.apache.felix.ipojo;
+
+
+/**
+ * Component Instance Fake.
+ * We're using a fake to avoid the cyclic build dependency:
+ * manipulator -> ipojo -> maven-ipojo-plugin -> manipulator
+ */
+public interface ComponentInstance {
+
+
+}
diff --git a/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/InstanceManager.java b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/InstanceManager.java
new file mode 100644
index 0000000..04281df
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/InstanceManager.java
@@ -0,0 +1,20 @@
+package org.apache.felix.ipojo;
+
+import java.util.Set;
+
+/**
+ * Instance Manager Fake.
+ * We're using a fake to avoid the cyclic build dependency:
+ * manipulator -> ipojo -> maven-ipojo-plugin -> manipulator
+ */
+public class InstanceManager {
+
+ public Set getRegistredFields() {
+ return null;
+ }
+
+ public Set getRegistredMethods() {
+ return null;
+ }
+
+}
diff --git a/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/Pojo.java b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/Pojo.java
new file mode 100644
index 0000000..c7bcb59
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/Pojo.java
@@ -0,0 +1,10 @@
+package org.apache.felix.ipojo;
+
+/**
+ * POJO Interface fake.
+ * We're using a fake to avoid the cyclic build dependency:
+ * manipulator -> ipojo -> maven-ipojo-plugin -> manipulator
+ */
+public interface Pojo {
+
+}
diff --git a/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatorTest.java b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatorTest.java
new file mode 100644
index 0000000..03576b1
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/ManipulatorTest.java
@@ -0,0 +1,335 @@
+package org.apache.felix.ipojo.manipulation;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.Pojo;
+import org.apache.felix.ipojo.manipulation.annotations.MetadataCollector;
+import org.mockito.Mockito;
+import org.objectweb.asm.ClassReader;
+
+public class ManipulatorTest extends TestCase {
+
+ public void testManipulatingTheSimplePojo() throws Exception {
+ Manipulator manipulator = new Manipulator();
+ byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/SimplePojo.class")));
+ TestClassLoader classloader = new TestClassLoader("test.SimplePojo", clazz);
+ Class cl = classloader.findClass("test.SimplePojo");
+ Assert.assertNotNull(cl);
+ Assert.assertNotNull(manipulator.getManipulationMetadata());
+
+ System.out.println(manipulator.getManipulationMetadata());
+
+ // The manipulation add stuff to the class.
+ Assert.assertTrue(clazz.length > getBytesFromFile(new File("target/test-classes/test/SimplePojo.class")).length);
+
+
+ boolean found = false;
+ Constructor cst = null;
+ 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 &&
+ csts[i].getParameterTypes()[0].equals(InstanceManager.class)) {
+ found = true;
+ cst = csts[i];
+ }
+ }
+ Assert.assertTrue(found);
+
+ // We still have the empty constructor
+ found = false;
+ csts = cl.getDeclaredConstructors();
+ for (int i = 0; i < csts.length; i++) {
+ System.out.println(Arrays.asList(csts[i].getParameterTypes()));
+ if (csts[i].getParameterTypes().length == 0) {
+ found = true;
+ }
+ }
+ Assert.assertTrue(found);
+
+ // Check the POJO interface
+ Assert.assertTrue(Arrays.asList(cl.getInterfaces()).contains(Pojo.class));
+
+ cst.setAccessible(true);
+ Object pojo = cst.newInstance(new Object[] {new InstanceManager()});
+ Assert.assertNotNull(pojo);
+ Assert.assertTrue(pojo instanceof Pojo);
+
+ Method method = cl.getMethod("doSomething", new Class[0]);
+ Assert.assertTrue(((Boolean) method.invoke(pojo, new Object[0])).booleanValue());
+
+ }
+
+ public void testManipulatingChild() throws Exception {
+ Manipulator manipulator = new Manipulator();
+ byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/Child.class")));
+ TestClassLoader classloader = new TestClassLoader("test.Child", clazz);
+ Class cl = classloader.findClass("test.Child");
+ Assert.assertNotNull(cl);
+ Assert.assertNotNull(manipulator.getManipulationMetadata());
+
+ boolean found = false;
+ Constructor cst = null;
+ 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 &&
+ csts[i].getParameterTypes()[0].equals(InstanceManager.class)) {
+ found = true;
+ cst = csts[i];
+ }
+ }
+ Assert.assertTrue(found);
+
+ // We still have the regular constructor
+ found = false;
+ csts = cl.getDeclaredConstructors();
+ for (int i = 0; i < csts.length; i++) {
+ System.out.println(Arrays.asList(csts[i].getParameterTypes()));
+ if (csts[i].getParameterTypes().length == 2) {
+ found = true;
+ }
+ }
+ Assert.assertTrue(found);
+
+ // Check the POJO interface
+ Assert.assertTrue(Arrays.asList(cl.getInterfaces()).contains(Pojo.class));
+
+ InstanceManager im = (InstanceManager) Mockito.mock(InstanceManager.class);
+ cst.setAccessible(true);
+ Object pojo = cst.newInstance(new Object[] {im});
+ Assert.assertNotNull(pojo);
+ Assert.assertTrue(pojo instanceof Pojo);
+
+ Method method = cl.getMethod("doSomething", new Class[0]);
+ Assert.assertEquals(9, ((Integer) method.invoke(pojo, new Object[0])).intValue());
+
+ }
+
+ public void testManipulatingTheInner() throws Exception {
+ Manipulator manipulator = new Manipulator();
+ byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/PojoWithInner.class")));
+ TestClassLoader classloader = new TestClassLoader("test.PojoWithInner", clazz);
+ Class cl = classloader.findClass("test.PojoWithInner");
+ Assert.assertNotNull(cl);
+ Assert.assertNotNull(manipulator.getManipulationMetadata());
+ Assert.assertFalse(manipulator.getInnerClasses().isEmpty());
+
+
+ System.out.println(manipulator.getManipulationMetadata());
+
+ // The manipulation add stuff to the class.
+ Assert.assertTrue(clazz.length > getBytesFromFile(new File("target/test-classes/test/PojoWithInner.class")).length);
+
+
+ boolean found = false;
+ Constructor cst = null;
+ 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 &&
+ csts[i].getParameterTypes()[0].equals(InstanceManager.class)) {
+ found = true;
+ cst = csts[i];
+ }
+ }
+ Assert.assertTrue(found);
+
+ // We still have the empty constructor
+ found = false;
+ csts = cl.getDeclaredConstructors();
+ for (int i = 0; i < csts.length; i++) {
+ System.out.println(Arrays.asList(csts[i].getParameterTypes()));
+ if (csts[i].getParameterTypes().length == 0) {
+ found = true;
+ }
+ }
+ Assert.assertTrue(found);
+
+ // Check the POJO interface
+ Assert.assertTrue(Arrays.asList(cl.getInterfaces()).contains(Pojo.class));
+
+ InstanceManager im = (InstanceManager) Mockito.mock(InstanceManager.class);
+ cst.setAccessible(true);
+ Object pojo = cst.newInstance(new Object[] {im});
+ Assert.assertNotNull(pojo);
+ Assert.assertTrue(pojo instanceof Pojo);
+
+ Method method = cl.getMethod("doSomething", new Class[0]);
+ Assert.assertTrue(((Boolean) method.invoke(pojo, new Object[0])).booleanValue());
+
+ }
+
+ public void testManipulatingWithConstructorModification() throws Exception {
+ Manipulator manipulator = new Manipulator();
+ byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/Child.class")));
+ TestClassLoader classloader = new TestClassLoader("test.Child", clazz);
+ Class cl = classloader.findClass("test.Child");
+ Assert.assertNotNull(cl);
+ Assert.assertNotNull(manipulator.getManipulationMetadata());
+
+ boolean found = false;
+ Constructor cst = null;
+ 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 &&
+ csts[i].getParameterTypes()[0].equals(InstanceManager.class)) {
+ found = true;
+ cst = csts[i];
+ }
+ }
+ Assert.assertTrue(found);
+
+ // We still have the regular constructor
+ found = false;
+ csts = cl.getDeclaredConstructors();
+ for (int i = 0; i < csts.length; i++) {
+ System.out.println(Arrays.asList(csts[i].getParameterTypes()));
+ if (csts[i].getParameterTypes().length == 2) {
+ found = true;
+ }
+ }
+ Assert.assertTrue(found);
+
+ // Check that we have the IM, Integer, String constructor too
+ Constructor cst2 = cl.getDeclaredConstructor(new Class[] { InstanceManager.class, Integer.TYPE, String.class });
+ Assert.assertNotNull(cst2);
+
+ // Check the POJO interface
+ Assert.assertTrue(Arrays.asList(cl.getInterfaces()).contains(Pojo.class));
+
+
+ // Creation using cst
+ InstanceManager im = (InstanceManager) Mockito.mock(InstanceManager.class);
+ cst.setAccessible(true);
+ Object pojo = cst.newInstance(new Object[] {im});
+ Assert.assertNotNull(pojo);
+ Assert.assertTrue(pojo instanceof Pojo);
+
+ Method method = cl.getMethod("doSomething", new Class[0]);
+ Assert.assertEquals(9, ((Integer) method.invoke(pojo, new Object[0])).intValue());
+
+ // Try to create using cst2
+ im = (InstanceManager) Mockito.mock(InstanceManager.class);
+ cst2.setAccessible(true);
+ pojo = cst2.newInstance(new Object[] {im, new Integer(2), "bariton"});
+ Assert.assertNotNull(pojo);
+ Assert.assertTrue(pojo instanceof Pojo);
+
+ method = cl.getMethod("doSomething", new Class[0]);
+ Assert.assertEquals(10, ((Integer) method.invoke(pojo, new Object[0])).intValue());
+
+
+
+ }
+
+
+ public void testManipulatingWithNoValidConstructor() throws Exception {
+ Manipulator manipulator = new Manipulator();
+ byte[] clazz = manipulator.manipulate(getBytesFromFile(new File("target/test-classes/test/NoValidConstructor.class")));
+ TestClassLoader classloader = new TestClassLoader("test.NoValidConstructor", clazz);
+ Class cl = classloader.findClass("test.NoValidConstructor");
+ Assert.assertNotNull(cl);
+ Assert.assertNotNull(manipulator.getManipulationMetadata());
+
+ 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);
+
+
+ boolean found = false;
+ Constructor cst = null;
+ 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 &&
+ csts[i].getParameterTypes()[0].equals(InstanceManager.class)) {
+ found = true;
+ cst = csts[i];
+ }
+ }
+ Assert.assertTrue(found);
+
+ // Check the POJO interface
+ Assert.assertTrue(Arrays.asList(cl.getInterfaces()).contains(Pojo.class));
+
+ cst.setAccessible(true);
+ Object pojo = cst.newInstance(new Object[] {new InstanceManager()});
+ Assert.assertNotNull(pojo);
+ Assert.assertTrue(pojo instanceof Pojo);
+
+ }
+
+ public void test() throws Exception {
+
+
+ byte[] clazz = getBytesFromFile(new File("target/test-classes/test/Constructor.class"));
+ ClassReader cr = new ClassReader(clazz);
+ MetadataCollector collector = new MetadataCollector();
+ cr.accept(collector, 0);
+
+ System.out.println(collector.getComponentTypeDeclaration());
+
+ }
+
+ public static byte[] getBytesFromFile(File file) throws IOException {
+ InputStream is = new FileInputStream(file);
+ long length = file.length();
+ byte[] bytes = new byte[(int)length];
+
+ // Read in the bytes
+ int offset = 0;
+ int numRead = 0;
+ while (offset < bytes.length
+ && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
+ offset += numRead;
+ }
+
+ // Ensure all the bytes have been read in
+ if (offset < bytes.length) {
+ throw new IOException("Could not completely read file "+file.getName());
+ }
+
+ // Close the input stream and return bytes
+ is.close();
+ return bytes;
+ }
+
+ class TestClassLoader extends ClassLoader {
+
+ private String name;
+ private byte[] clazz;
+
+ public TestClassLoader(String name, byte[] clazz) {
+ this.name = name;
+ this.clazz = clazz;
+ }
+
+ public Class findClass(String name) throws ClassNotFoundException {
+ if (name.equals(this.name)) {
+ return defineClass(name, clazz, 0, clazz.length);
+ }
+ return super.findClass(name);
+ }
+
+ public Class loadClass(String arg0) throws ClassNotFoundException {
+ return super.loadClass(arg0);
+ }
+
+
+
+ }
+
+}
diff --git a/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/PojoizationTest.java b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/PojoizationTest.java
new file mode 100644
index 0000000..68d4b5e
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/org/apache/felix/ipojo/manipulation/PojoizationTest.java
@@ -0,0 +1,43 @@
+package org.apache.felix.ipojo.manipulation;
+
+import java.io.File;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.felix.ipojo.manipulator.Pojoization;
+
+public class PojoizationTest extends TestCase {
+
+ public void testJarManipulation() {
+ Pojoization pojoization = new Pojoization();
+ File in = new File("target/test-classes/tests.manipulation-no-annotations.jar");
+ File out = new File("target/test-classes/tests.manipulation-no-annotations-manipulated.jar");
+ out.delete();
+ File metadata = new File("target/test-classes/metadata.xml");
+ pojoization.pojoization(in, out, metadata);
+
+ Assert.assertTrue(out.exists());
+ }
+
+ public void testManipulationWithAnnotations() {
+ Pojoization pojoization = new Pojoization();
+ File in = new File("target/test-classes/tests.manipulator-annotations.jar");
+ File out = new File("target/test-classes/tests.manipulation-annotations-manipulated.jar");
+ out.delete();
+ pojoization.pojoization(in, out, (File) null);
+
+ Assert.assertTrue(out.exists());
+ }
+
+ public void testJarManipulationJava5() {
+ Pojoization pojoization = new Pojoization();
+ File in = new File("target/test-classes/tests.manipulation.java5.jar");
+ File out = new File("target/test-classes/tests.manipulation.java5-manipulated.jar");
+ out.delete();
+ pojoization.pojoization(in, out, (File) null);
+
+ Assert.assertTrue(out.exists());
+ }
+
+}
diff --git a/ipojo/manipulator/src/test/java/test/Child.java b/ipojo/manipulator/src/test/java/test/Child.java
new file mode 100644
index 0000000..1eda305
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/test/Child.java
@@ -0,0 +1,17 @@
+package test;
+
+public class Child extends Parent {
+
+ Child(int i, String f) {
+ super(i, f);
+ }
+
+ Child() {
+ super(5, "foo");
+ }
+
+ public int doSomething() {
+ return getIndex() + 1; // 9
+ }
+
+}
diff --git a/ipojo/manipulator/src/test/java/test/Constructor.java b/ipojo/manipulator/src/test/java/test/Constructor.java
new file mode 100644
index 0000000..ccbaebe
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/test/Constructor.java
@@ -0,0 +1,15 @@
+package test;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Property;
+import org.apache.felix.ipojo.annotations.Requires;
+
+@Component
+public class Constructor {
+
+ public Constructor(@Property(name="foo") String s, @Requires(id="t") Thread t) {
+ // plop
+
+ }
+
+}
diff --git a/ipojo/manipulator/src/test/java/test/NoValidConstructor.java b/ipojo/manipulator/src/test/java/test/NoValidConstructor.java
new file mode 100644
index 0000000..45d52cb
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/test/NoValidConstructor.java
@@ -0,0 +1,15 @@
+package test;
+
+public class NoValidConstructor {
+
+ String m_s;
+
+ public NoValidConstructor(String s) {
+ m_s = s;
+ }
+
+ public String getS() {
+ return m_s;
+ }
+
+}
diff --git a/ipojo/manipulator/src/test/java/test/Parent.java b/ipojo/manipulator/src/test/java/test/Parent.java
new file mode 100644
index 0000000..51ac622
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/test/Parent.java
@@ -0,0 +1,15 @@
+package test;
+
+public class Parent {
+
+ private int m_index = 0;
+
+ public Parent(int i, String s) {
+ m_index = i + s.length();
+ }
+
+ public int getIndex() {
+ return m_index;
+ }
+
+}
diff --git a/ipojo/manipulator/src/test/java/test/PojoWithInner.java b/ipojo/manipulator/src/test/java/test/PojoWithInner.java
new file mode 100644
index 0000000..ac5a052
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/test/PojoWithInner.java
@@ -0,0 +1,21 @@
+package test;
+
+public class PojoWithInner {
+
+ private MyInner m_result = new MyInner();
+
+ // This is a simple POJO
+
+ public boolean doSomething() {
+ return m_result.getInner();
+ }
+
+ public class MyInner {
+
+ public boolean getInner() {
+ return true;
+ }
+
+ }
+
+}
diff --git a/ipojo/manipulator/src/test/java/test/SimplePojo.java b/ipojo/manipulator/src/test/java/test/SimplePojo.java
new file mode 100644
index 0000000..3c1d6fa
--- /dev/null
+++ b/ipojo/manipulator/src/test/java/test/SimplePojo.java
@@ -0,0 +1,13 @@
+package test;
+
+public class SimplePojo {
+
+ private boolean m_result = true;
+
+ // This is a simple POJO
+
+ public boolean doSomething() {
+ return m_result;
+ }
+
+}
diff --git a/ipojo/manipulator/src/test/resources/metadata.xml b/ipojo/manipulator/src/test/resources/metadata.xml
new file mode 100644
index 0000000..b3e0262
--- /dev/null
+++ b/ipojo/manipulator/src/test/resources/metadata.xml
@@ -0,0 +1,37 @@
+<ipojo
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="org.apache.felix.ipojo http://felix.apache.org/ipojo/schemas/SNAPSHOT/core.xsd"
+ xmlns="org.apache.felix.ipojo"
+>
+ <!-- Simple provider used for manipulation analysis -->
+ <component
+ classname="org.apache.felix.ipojo.test.scenarios.component.FooProviderType1"
+ name="Manipulation-FooProviderType-1" architecture="true">
+ <provides />
+ </component>
+
+ <!-- Non lazzy service provider, to check instantiation -->
+ <component
+ classname="org.apache.felix.ipojo.test.scenarios.component.FooProviderType1"
+ name="Manipulation-ImmediateFooProviderType" immediate="true"
+ architecture="true">
+ <provides />
+ </component>
+
+ <!-- Nested & Inner classes -->
+ <component name="inners" classname="org.apache.felix.ipojo.test.scenarios.component.InnerClasses">
+ <provides>
+ <property field="privateObject"/>
+ <property field="privateInt"/>
+
+ <property field="protectedObject"/>
+ <property field="protectedInt"/>
+
+ <property field="packageObject"/>
+ <property field="packageInt"/>
+
+ <property field="publicObject"/>
+ <property field="publicInt"/>
+ </provides>
+ </component>
+</ipojo>
diff --git a/ipojo/manipulator/src/test/resources/tests.manipulation-no-annotations.jar b/ipojo/manipulator/src/test/resources/tests.manipulation-no-annotations.jar
new file mode 100644
index 0000000..a701fc0
--- /dev/null
+++ b/ipojo/manipulator/src/test/resources/tests.manipulation-no-annotations.jar
Binary files differ
diff --git a/ipojo/manipulator/src/test/resources/tests.manipulation.java5.jar b/ipojo/manipulator/src/test/resources/tests.manipulation.java5.jar
new file mode 100644
index 0000000..456e356
--- /dev/null
+++ b/ipojo/manipulator/src/test/resources/tests.manipulation.java5.jar
Binary files differ
diff --git a/ipojo/manipulator/src/test/resources/tests.manipulator-annotations.jar b/ipojo/manipulator/src/test/resources/tests.manipulator-annotations.jar
new file mode 100644
index 0000000..45322aa
--- /dev/null
+++ b/ipojo/manipulator/src/test/resources/tests.manipulator-annotations.jar
Binary files differ