You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomee.apache.org by Romain Manni-Bucau <rm...@gmail.com> on 2014/07/16 13:22:45 UTC

Fwd: svn commit: r1610966 - /tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/CmpJarBuilder.java

Please revert it since it is not finished. Pushed it on gist to share
my current status but it breaks more (=fastest so maybe good) tests.

Some more work needs to be done (and a little refactoring for perfs
but that's a detail) before we can use it.

Side note: we'll surely need to do the same for normal jpa entities -
didnt take time to evaluate it yet and can not be as mandatory as this
one for 1.7


Romain Manni-Bucau
Twitter: @rmannibucau
Blog: http://rmannibucau.wordpress.com/
LinkedIn: http://fr.linkedin.com/in/rmannibucau
Github: https://github.com/rmannibucau



---------- Forwarded message ----------
From:  <an...@apache.org>
Date: 2014-07-16 13:05 GMT+02:00
Subject: svn commit: r1610966 -
/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/CmpJarBuilder.java
To: commits@tomee.apache.org


Author: andygumbrecht
Date: Wed Jul 16 11:05:34 2014
New Revision: 1610966

URL: http://svn.apache.org/r1610966
Log:
Romains enhance patch

Modified:
    tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/CmpJarBuilder.java

Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/CmpJarBuilder.java
URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/CmpJarBuilder.java?rev=1610966&r1=1610965&r2=1610966&view=diff
==============================================================================
--- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/CmpJarBuilder.java
(original)
+++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/CmpJarBuilder.java
Wed Jul 16 11:05:34 2014
@@ -17,6 +17,8 @@

 package org.apache.openejb.assembler.classic;

+import org.apache.commons.lang3.JavaVersion;
+import org.apache.commons.lang3.SystemUtils;
 import org.apache.openejb.ClassLoaderUtil;
 import org.apache.openejb.core.cmp.CmpUtil;
 import org.apache.openejb.core.cmp.cmp2.Cmp1Generator;
@@ -27,9 +29,33 @@ import org.apache.openejb.loader.SystemI
 import org.apache.openejb.util.LogCategory;
 import org.apache.openejb.util.Logger;
 import org.apache.openejb.util.UrlCache;
-
+import org.apache.openejb.util.classloader.URLClassLoaderFirst;
+import org.apache.openjpa.enhance.PCEnhancer;
+import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
+import org.apache.openjpa.jdbc.meta.MappingRepository;
+import org.apache.openjpa.jdbc.meta.NoneMappingDefaults;
+import org.apache.openjpa.jdbc.sql.HSQLDictionary;
+import org.apache.openjpa.lib.util.BytecodeWriter;
+import org.apache.openjpa.meta.AccessCode;
+import org.apache.openjpa.meta.MetaDataModes;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.persistence.jdbc.PersistenceMappingFactory;
+import org.apache.xbean.asm5.ClassReader;
+import org.apache.xbean.asm5.ClassWriter;
+import serp.bytecode.BCClass;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
@@ -80,15 +106,53 @@ public class CmpJarBuilder {
             jarOutputStream = openJarFile(this);

             // Generate CMP implementation classes
+            final Map<String, Entry> classes = new HashMap<>();
             for (final EjbJarInfo ejbJar : appInfo.ejbJars) {
                 for (final EnterpriseBeanInfo beanInfo :
ejbJar.enterpriseBeans) {
                     if (beanInfo instanceof EntityBeanInfo) {
                         final EntityBeanInfo entityBeanInfo =
(EntityBeanInfo) beanInfo;
                         if
("CONTAINER".equalsIgnoreCase(entityBeanInfo.persistenceType)) {
-                            generateClass(jarOutputStream, entityBeanInfo);
+                            final Entry entry =
generateClass(jarOutputStream, entityBeanInfo);
+                            classes.put(entry.clazz, entry);
+                        }
+                    }
+                }
+            }
+
+            final URLClassLoaderFirst thisClassLoader = new
URLClassLoaderFirst(new URL[0], tempClassLoader) {
+                @Override
+                public Class<?> loadClass(final String name, final
boolean resolve) throws ClassNotFoundException {
+                    final Entry e = classes.get(name);
+                    if (e != null) {
+                        final Class<?> alreadyLoaded = findLoadedClass(name);
+                        if (alreadyLoaded != null) {
+                            if (resolve) {
+                                resolveClass(alreadyLoaded);
+                            }
+                            return alreadyLoaded;
                         }
+
+                        final Class<?> c = defineClass(e.clazz,
e.bytes, 0, e.bytes.length);
+                        if (resolve) {
+                            resolveClass(c);
+                        }
+                        return c;
                     }
+                    return super.loadClass(name, resolve);
+                }
+
+                @Override
+                public InputStream getResourceAsStream(final String name) {
+                    final String key = name.replace('/',
'.').replace(".class", "");
+                    final Entry e = classes.get(key);
+                    return e != null ? new
ByteArrayInputStream(e.bytes) : super.getResourceAsStream(name);
                 }
+            };
+
+
+            for (final Entry e : classes.values()) {
+                // add the generated class to the jar
+                addJarEntry(jarOutputStream, e.name,
enhance(thisClassLoader, e.clazz, e.bytes));
             }
             if (appInfo.cmpMappingsXml != null) {
                 // System.out.println(appInfo.cmpMappingsXml);
@@ -140,12 +204,12 @@ public class CmpJarBuilder {
      * @param entityBeanInfo  The descriptor for the entity bean we
need to wrapper.
      * @throws IOException
      */
-    private void generateClass(final JarOutputStream jarOutputStream,
final EntityBeanInfo entityBeanInfo) throws IOException {
+    private Entry generateClass(final JarOutputStream
jarOutputStream, final EntityBeanInfo entityBeanInfo) throws
IOException {
         // don't generate if there is aleady an implementation class
         final String cmpImplClass =
CmpUtil.getCmpImplClassName(entityBeanInfo.abstractSchemaName,
entityBeanInfo.ejbClass);
         final String entryName = cmpImplClass.replace(".", "/") + ".class";
         if (entries.contains(entryName) ||
tempClassLoader.getResource(entryName) != null) {
-            return;
+            return null;
         }

         // load the bean class, which is used by the generator
@@ -201,8 +265,7 @@ public class CmpJarBuilder {
             bytes = cmp2Generator.generate();
         }

-        // add the generated class to the jar
-        addJarEntry(jarOutputStream, entryName, bytes);
+        return new Entry(cmpImplClass, entryName, bytes);
     }

     /**
@@ -239,6 +302,81 @@ public class CmpJarBuilder {
         }
     }

+    private byte[] enhance(final ClassLoader thisClassLoader, final
String clazz, final byte[] bytes) {
+        try {
+            final ByteArrayOutputStream baos = new
ByteArrayOutputStream(bytes.length * 2);
+            final JDBCConfigurationImpl conf = new JDBCConfigurationImpl();
+            conf.setDBDictionary(new HSQLDictionary());
+            final MappingRepository repos = new MappingRepository();
+
+            final Class<?> tmpClass = thisClassLoader.loadClass(clazz);
+            repos.setConfiguration(conf);
+            repos.setMetaDataFactory(new PersistenceMappingFactory() {
+                @Override
+                public Set getPersistentTypeNames(boolean devpath,
ClassLoader envLoader) {
+                    return Collections.singleton(tmpClass);
+                }
+            });
+            repos.setMappingDefaults(NoneMappingDefaults.getInstance());
+            repos.setResolve(MetaDataModes.MODE_NONE);
+            repos.setValidate(MetaDataRepository.VALIDATE_NONE);
+            repos.addMetaData(tmpClass, AccessCode.PROPERTY);
+
+            final PCEnhancer.Flags flags = new PCEnhancer.Flags();
+            flags.tmpClassLoader = false;
+
+            final BytecodeWriter writer = new BytecodeWriter() {
+                @Override
+                public void write(final BCClass type) throws IOException {
+                    final byte[] b = type.toByteArray();
+                    if
(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_7)) {
+                        final ByteArrayInputStream bais = new
ByteArrayInputStream(b);
+                        final BufferedInputStream bis = new
BufferedInputStream(bais);
+
+                        final ClassWriter cw = new
ClassWriter(ClassWriter.COMPUTE_FRAMES) {
+                            protected String
getCommonSuperClass(String type1, String type2) {
+                                Class<?> class1;
+                                Class<?> class2;
+                                try {
+                                    class1 =
thisClassLoader.loadClass(type1.replace('/', '.'));
+                                    class2 =
thisClassLoader.loadClass(type2.replace('/', '.'));
+                                } catch (final 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('.', '/');
+                            }
+                        };
+                        final ClassReader cr = new ClassReader(bis);
+                        cr.accept(cw, 0);
+                        baos.write(cw.toByteArray());
+                    } else {
+                        baos.write(b);
+                    }
+                }
+            };
+            PCEnhancer.run(conf, null, flags, repos, writer, thisClassLoader);
+            final byte[] enhanced = baos.toByteArray();
+            if (enhanced.length > 0) {
+                return enhanced;
+            }
+        } catch (final Exception e) {
+            // no-op: we should surely log something, maybe a warning
+        }
+        return bytes;
+    }
+
     private static synchronized JarOutputStream openJarFile(final
CmpJarBuilder instance) throws IOException {

         if (instance.jarFile != null) {
@@ -286,4 +424,16 @@ public class CmpJarBuilder {
             }
         }
     }
-}
+
+    private static class Entry {
+        private final String clazz;
+        private final String name;
+        private final byte[] bytes;
+
+        private Entry(final String clazz, final String name, final
byte[] bytes) {
+            this.clazz = clazz;
+            this.name = name;
+            this.bytes = bytes;
+        }
+    }
+}
\ No newline at end of file