Re-work on Felix-1518
Apply Guillaume patch, Fix the potential NPE
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@807115 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
index f6c94b4..90a1448 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
@@ -315,7 +315,11 @@
String name = m_metadata[m_metadata.length - 1].getAttribute("classname");
name = name.replace('.', '/');
name += ".class";
- m_components.add(new ComponentInfo(name, m_metadata[m_metadata.length - 1]));
+
+ // Creates the ComponentInfo and store bytecode
+ ComponentInfo info = new ComponentInfo(name, m_metadata[m_metadata.length - 1]);
+ info.m_bytecode = inC;
+ m_components.add(info);
}
}
}
@@ -348,11 +352,13 @@
Enumeration entries = m_inputJar.entries();
while (entries.hasMoreElements()) {
JarEntry curEntry = (JarEntry) entries.nextElement();
- // Check if we need to manipulate the class
+
+ // If the class was manipulated, write out the manipulated
+ // version of the bytecode
if (m_classes.containsKey(curEntry.getName())) {
JarEntry je = new JarEntry(curEntry.getName());
byte[] outClazz = (byte[]) m_classes.get(curEntry.getName());
- if (outClazz.length != 0) {
+ if (outClazz != null && outClazz.length != 0) {
jos.putNextEntry(je); // copy the entry header to jos
jos.write(outClazz);
jos.closeEntry();
@@ -452,79 +458,107 @@
}
}
-
- /**
- * Reads the entry to extract the byte array.
- * This method should be called only if the class has to be read.
- * The cost of this method is not negligible.
- * @param name name of the entry to read from the input jar
- * @return the read byte array
- * @throws IOException occurs when the entry cannot be read
- */
- private byte[] readEntry(String name) throws IOException {
- InputStream currIn = getInputStream(name);
- byte[] in = new byte[0];
- int c;
- while ((c = currIn.read()) >= 0) {
- byte[] in2 = new byte[in.length + 1];
- System.arraycopy(in, 0, in2, 0, in.length);
- in2[in.length] = (byte) c;
- in = in2;
- }
- currIn.close();
- return in;
- }
/**
* Manipulate classes of the input Jar.
*/
private void manipulateComponents() {
- Enumeration entries = getClassFiles();
- while (entries.hasMoreElements()) {
- String curName = (String) entries.nextElement();
- try {
- byte[] in = null; // Will store the bytes of the entry if required.
- if (!m_ignoreAnnotations) {
- // If we need to process annotations, all classes has to be read.
- in = readEntry(curName);
- computeAnnotations(in); // This method adds the class to the
- // component list.
+ // 1. Discover components described with annotations
+ // Only do this if annotations are enabled
+ if (!m_ignoreAnnotations) {
+ Enumeration entries = getClassFiles();
+
+ while (entries.hasMoreElements()) {
+ String curName = (String) entries.nextElement();
+ try {
+
+ // Need to load the bytecode for each .class entry
+ byte[] in = getBytecode(curName);
+
+ // This method adds the class to the component list
+ // if that bytecode is annotated with @Component.
+ computeAnnotations(in);
+ } catch (IOException e) {
+ error("Cannot read the class : " + curName);
+ return;
}
- // Check if we need to manipulate the class
- for (int i = 0; i < m_components.size(); i++) {
- ComponentInfo ci = (ComponentInfo) m_components.get(i);
- if (ci.m_classname.equals(curName)) {
- // So, we have to manipulate the class, if not already read, read the input class
- // Else reuse the same one.
- if (in == null) {
- in = readEntry(curName);
- }
- byte[] outClazz = manipulateComponent(in, ci);
- m_classes.put(ci.m_classname, outClazz);
-
- // Manipulate inner classes ?
- if (!ci.m_inners.isEmpty()) {
- for (int k = 0; k < ci.m_inners.size(); k++) {
- String innerCN = (String) ci.m_inners.get(k)
- + ".class";
- InputStream innerStream = getInputStream(innerCN);
- // manipulateInnerClass(inputJar, inner,
- // (String) ci.m_inners.get(k), ci);
- manipulateInnerClass(innerStream, innerCN, ci);
- }
- }
+ }
+ }
+
+ // 2. Iterates over the list of discovered components
+ // Note that this list includes components from metadata.xml AND from annotations
+
+ for (int i = 0; i < m_components.size(); i++) {
+ ComponentInfo info = (ComponentInfo) m_components.get(i);
+
+ // Get the bytecode if necessary
+ if (info.m_bytecode == null) {
+ try {
+ info.m_bytecode = getBytecode(info.m_classname);
+ } catch (IOException e) {
+ error("Cannot extract bytecode for component '" + info.m_classname + "'");
+ return;
+ }
+ }
+ // Manipulate the original bytecode and store the modified one
+ byte[] outClazz = manipulateComponent(info.m_bytecode, info);
+ m_classes.put(info.m_classname, outClazz);
+
+ // Are there any inner classes to be manipulated ?
+ if (!info.m_inners.isEmpty()) {
+ for (int k = 0; k < info.m_inners.size(); k++) {
+ String innerCN = (String) info.m_inners.get(k) + ".class";
+ try {
+ // Get the bytecode and start manipulation
+ byte[] innerClassBytecode = getBytecode(innerCN);
+ manipulateInnerClass(innerClassBytecode, innerCN, info);
+ } catch (IOException e) {
+ error("Cannot manipulate inner class '" + innerCN + "'");
+ return;
}
}
- } catch (IOException e) {
- error("Cannot read the class : " + curName);
- return;
}
-
}
}
/**
+ * Return a byte array that contains the bytecode of the given classname.
+ * @param classname name of a class to be read
+ * @return a byte array
+ * @throws IOException if the classname cannot be read
+ */
+ private byte[] getBytecode(final String classname) throws IOException {
+
+ InputStream currIn = null;
+ byte[] in = new byte[0];
+ try {
+ // Get the stream to read
+ currIn = getInputStream(classname);
+ int c;
+
+ // Fill the byte array with IS content
+ while ((c = currIn.read()) >= 0) {
+ byte[] in2 = new byte[in.length + 1];
+ System.arraycopy(in, 0, in2, 0, in.length);
+ in2[in.length] = (byte) c;
+ in = in2;
+ }
+ } finally {
+ // Close the stream
+ if (currIn != null) {
+ try {
+ currIn.close();
+ } catch (IOException e) {
+ // Ignored
+ }
+ }
+ }
+
+ return in;
+ }
+
+ /**
* Gets an input stream on the given class.
* This methods manages Jar files and directories.
* @param classname the class name
@@ -533,6 +567,7 @@
*/
private InputStream getInputStream(String classname) throws IOException {
if (m_inputJar != null) {
+ // Fix entry name if needed
if (! classname.endsWith(".class")) {
classname += ".class";
}
@@ -630,22 +665,15 @@
/**
* Manipulates an inner class.
- * @param clazz input stream on the inner file to manipulate
+ * @param in input bytecode of the inner file to manipulate
* @param cn the inner class name (ends with .class)
* @param ci component info of the component owning the inner class
* @throws IOException the inner class cannot be read
*/
- private void manipulateInnerClass(InputStream clazz, String cn, ComponentInfo ci) throws IOException {
- byte[] in = new byte[0];
- int c;
- while ((c = clazz.read()) >= 0) {
- byte[] in2 = new byte[in.length + 1];
- System.arraycopy(in, 0, in2, 0, in.length);
- in2[in.length] = (byte) c;
- in = in2;
- }
+ private void manipulateInnerClass(byte[] in, String cn, ComponentInfo ci) throws IOException {
// Remove '.class' from class name.
- InnerClassManipulator man = new InnerClassManipulator(ci.m_classname.substring(0, ci.m_classname.length() - 6), ci.m_fields);
+ String name = ci.m_classname.substring(0, ci.m_classname.length() - 6);
+ InnerClassManipulator man = new InnerClassManipulator(name, ci.m_fields);
byte[] out = man.manipulate(in);
m_classes.put(cn, out);
@@ -774,6 +802,12 @@
Set m_fields;
/**
+ * Initial (unmodified) bytecode of the component's class.
+ * May be null !!
+ */
+ byte[] m_bytecode;
+
+ /**
* Constructor.
* @param cn : class name
* @param met : component type metadata