You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by st...@apache.org on 2012/08/10 10:49:04 UTC

svn commit: r1371607 - /openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java

Author: struberg
Date: Fri Aug 10 08:49:03 2012
New Revision: 1371607

URL: http://svn.apache.org/viewvc?rev=1371607&view=rev
Log:
OPENJPA-2171 pickup existing ASM shades if possible

To prevent classpath clashes we try to pickup a few
known ASM shades. 

Modified:
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java?rev=1371607&r1=1371606&r2=1371607&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java Fri Aug 10 08:49:03 2012
@@ -18,26 +18,75 @@
  */
 package org.apache.openjpa.enhance;
 
+import serp.bytecode.BCClass;
+
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
 import java.net.URLDecoder;
 
-import serp.bytecode.BCClass;
-
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
-
 /**
  * Use ASM to add required StackMapTable attribute to the byte code generated by
  * Serp.
+ *
+ * This class contains a small hack to pickup different known shades of ASM
+ * to prevent classpath clashes.
+ * We first try to use standard ASM. If this is not available we try to pickup
+ * the shaded xbean-asm version used in OpenEJB, Geronimo or WAS.
+ * At last we try to use the shaded version from Spring.
  */
 public final class AsmAdaptor {
     private static final int Java7_MajorVersion = 51;
 
+    private static Class<?> cwClass;
+    private static Class<?> crClass;
+    private static int COMPUTE_FRAMES;
+    private static Method classReaderAccept;
+    private static Method classWritertoByteArray;
+    private static Constructor<?> classWriterConstructor;
+    private static Constructor<?> classReaderConstructor;
+
+    static {
+        // try the "real" asm first, then the others
+        tryClass("org.objectweb.asm.");
+        tryClass("org.apache.xbean.asm.");
+        tryClass("org.springframework.asm.");
+
+        // get needed stuff
+        try {
+            COMPUTE_FRAMES = cwClass.getField("COMPUTE_FRAMES").getInt(null);
+            classReaderAccept = crClass.getMethod("accept", cwClass.getInterfaces()[0], int.class);
+            classReaderConstructor = crClass.getConstructor(InputStream.class);
+            classWriterConstructor = cwClass.getConstructor(int.class);
+            classWritertoByteArray = cwClass.getMethod("toByteArray");
+        } catch (Exception e) {
+            throw new IllegalStateException("can't find all needed ASM stuff", e);
+        }
+    }
+
+    private static void tryClass(final String s) {
+        if (cwClass == null) {
+            try {
+                cwClass = AsmAdaptor.class.getClassLoader().loadClass(s + "ClassWriter");
+            } catch (Throwable t) {
+                //ignore
+            }
+        }
+        if (crClass == null) {
+            try {
+                crClass = AsmAdaptor.class.getClassLoader().loadClass(s + "ClassReader");
+            } catch (Throwable t) {
+                //ignore
+            }
+        }
+    }
+
     @SuppressWarnings("deprecation")
     public static void write(BCClass bc) throws IOException {
         if (bc.getMajorVersion() < Java7_MajorVersion) {
@@ -85,47 +134,24 @@ public final class AsmAdaptor {
         out.write(java7Bytes);
     }
 
-    private static byte[] toJava7ByteArray(BCClass bc, byte[] classBytes) throws IOException {
-        ByteArrayInputStream bais = new ByteArrayInputStream(classBytes);
-        BufferedInputStream bis = new BufferedInputStream(bais);
-
-        ClassWriter cw = new BCClassWriter(ClassWriter.COMPUTE_FRAMES, bc.getClassLoader());
-        ClassReader cr = new ClassReader(bis);
-        cr.accept(cw, 0);
-        return cw.toByteArray();
-    }
-
-    private static class BCClassWriter extends ClassWriter {
-        private final ClassLoader _loader;
-
-        BCClassWriter(int flags, ClassLoader loader) {
-            super(flags);
-            _loader = loader;
-        }
-
-        @Override
-        protected String getCommonSuperClass(String type1, String type2) {
-            Class<?> class1;
-            Class<?> class2;
-            try {
-                class1 = _loader.loadClass(type1.replace('/', '.'));
-                class2 = _loader.loadClass(type2.replace('/', '.'));
-            } catch (ClassNotFoundException ex) {
-                throw new RuntimeException(ex);
-            }
-            if (class1.isAssignableFrom(class2)) {
-                return type1;
-            }
-            if (class2.isAssignableFrom(class1)) {
-                return type2;
-            }
-            if (class1.isInterface() || class2.isInterface()) {
-                return "java/lang/Object";
-            }
-            do {
-                class1 = class1.getSuperclass();
-            } while (!class1.isAssignableFrom(class2));
-            return class1.getName().replace('.', '/');
+    private static byte[] toJava7ByteArray(final BCClass bc, final byte[] classBytes) throws IOException {
+        final ByteArrayInputStream bais = new ByteArrayInputStream(classBytes);
+        final BufferedInputStream bis = new BufferedInputStream(bais);
+
+        final ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        try {
+            final Object cw = classWriterConstructor.newInstance(COMPUTE_FRAMES);
+            final Object cr = classReaderConstructor.newInstance(bis);
+
+            // ClassWriter.getCommonSuperClass uses TCCL
+            Thread.currentThread().setContextClassLoader(bc.getClassLoader());
+            classReaderAccept.invoke(cr, cw, 0);
+
+            return (byte[]) classWritertoByteArray.invoke(cw);
+        } catch (Exception e) {
+            throw new IOException(e);
+        } finally {
+            Thread.currentThread().setContextClassLoader(cl);
         }
     }
 }