This is an automated email from the ASF dual-hosted git repository.

struberg pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openjpa.git


The following commit(s) were added to refs/heads/master by this push:
     new 0a81c3de7 OPENJPA-2911 move PCEnhancer off Serp
0a81c3de7 is described below

commit 0a81c3de7b8c590bf54b9d4138c51744684d369a
Author: Mark Struberg <strub...@apache.org>
AuthorDate: Wed Jul 19 13:14:16 2023 +0200

    OPENJPA-2911 move PCEnhancer off Serp
---
 .../internal/OpenJPADirectoriesEnhancer.java       |  7 +-
 .../openjpa/enhance/PCClassFileTransformer.java    |  9 ++-
 .../org/apache/openjpa/enhance/PCEnhancer.java     | 75 ++++++++++------------
 .../org/apache/openjpa/enhance/PCRegistry.java     |  8 +--
 .../openjpa/meta/InterfaceImplGenerator.java       | 58 ++++++++---------
 .../org/apache/openjpa/util/asm/AsmHelper.java     | 31 ++-------
 .../apache/openjpa/util/asm/ClassNodeTracker.java  | 39 ++++++++++-
 .../openjpa/util/asm/EnhancementClassLoader.java   | 11 +++-
 .../openjpa/util/asm/EnhancementProject.java       | 71 +++++++++++++++++---
 .../org/apache/openjpa/lib/util/JavaVersions.java  | 10 ++-
 .../enhance/TestEnhancementWithMultiplePUs.java    | 24 +++----
 11 files changed, 209 insertions(+), 134 deletions(-)

diff --git 
a/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/internal/OpenJPADirectoriesEnhancer.java
 
b/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/internal/OpenJPADirectoriesEnhancer.java
index 6bae9091c..9bb8bea09 100644
--- 
a/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/internal/OpenJPADirectoriesEnhancer.java
+++ 
b/openjpa-junit5/src/main/java/org/apache/openjpa/junit5/internal/OpenJPADirectoriesEnhancer.java
@@ -29,6 +29,7 @@ import org.apache.openjpa.meta.MetaDataRepository;
 import org.apache.openjpa.persistence.PersistenceMetaDataFactory;
 import org.apache.openjpa.util.asm.AsmHelper;
 import org.apache.openjpa.util.asm.ClassNodeTracker;
+import org.apache.openjpa.util.asm.EnhancementProject;
 import org.apache.xbean.asm9.AnnotationVisitor;
 import org.apache.xbean.asm9.ClassReader;
 import org.apache.xbean.asm9.Type;
@@ -265,18 +266,16 @@ public class OpenJPADirectoriesEnhancer implements 
Runnable {
             final Thread thread = Thread.currentThread();
             final ClassLoader old = thread.getContextClassLoader();
             thread.setContextClassLoader(tmpLoader);
-            try (final InputStream stream = new 
ByteArrayInputStream(classBytes)) {
+            try {
                 final PCEnhancer enhancer = new PCEnhancer(
                         repos.getConfiguration(),
-                        new Project().loadClass(stream, tmpLoader),
+                        new EnhancementProject().loadClass(classBytes, 
tmpLoader),
                         repos, tmpLoader);
                 if (enhancer.run() == PCEnhancer.ENHANCE_NONE) {
                     return null;
                 }
                 final ClassNodeTracker cnt = enhancer.getPCBytecode();
                 return AsmHelper.toByteArray(cnt);
-            } catch (final IOException e) {
-                throw new IllegalStateException(e);
             } finally {
                 thread.setContextClassLoader(old);
             }
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
index f46663f31..ba6c97074 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
@@ -18,7 +18,6 @@
  */
 package org.apache.openjpa.enhance;
 
-import java.io.ByteArrayInputStream;
 import java.lang.instrument.ClassFileTransformer;
 import java.lang.instrument.IllegalClassFormatException;
 import java.security.AccessController;
@@ -34,11 +33,11 @@ import org.apache.openjpa.meta.MetaDataRepository;
 import org.apache.openjpa.util.GeneralException;
 import org.apache.openjpa.util.asm.AsmHelper;
 import org.apache.openjpa.util.asm.ClassNodeTracker;
+import org.apache.openjpa.util.asm.EnhancementProject;
 import org.apache.xbean.asm9.ClassReader;
 import org.apache.xbean.asm9.ClassVisitor;
 import org.apache.xbean.asm9.Opcodes;
 
-import serp.bytecode.Project;
 import static java.util.Arrays.asList;
 
 
@@ -149,9 +148,9 @@ public class PCClassFileTransformer
             ClassLoader oldLoader = 
AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction());
             
AccessController.doPrivileged(J2DoPrivHelper.setContextClassLoaderAction(_tmpLoader));
             try {
-                PCEnhancer enhancer = new PCEnhancer(_repos.getConfiguration(),
-                        new Project().loadClass(new 
ByteArrayInputStream(bytes),
-                                _tmpLoader), _repos);
+                EnhancementProject project = new EnhancementProject();
+                final ClassNodeTracker bc = project.loadClass(bytes, 
_tmpLoader);
+                PCEnhancer enhancer = new 
PCEnhancer(_repos.getConfiguration(), bc, _repos);
                 
enhancer.setAddDefaultConstructor(_flags.addDefaultConstructor);
                 enhancer.setEnforcePropertyRestrictions
                         (_flags.enforcePropertyRestrictions);
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
index fb52bc3cc..bc3ef3934 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
@@ -100,14 +100,13 @@ import org.apache.openjpa.util.StringId;
 import org.apache.openjpa.util.UserException;
 import org.apache.openjpa.util.asm.AsmHelper;
 import org.apache.openjpa.util.asm.ClassNodeTracker;
+import org.apache.openjpa.util.asm.EnhancementProject;
 import org.apache.openjpa.util.asm.RedefinedAttribute;
 import org.apache.xbean.asm9.Attribute;
 import org.apache.xbean.asm9.Opcodes;
 import org.apache.xbean.asm9.Type;
 import org.apache.xbean.asm9.tree.*;
 
-import serp.bytecode.BCClass;
-import serp.bytecode.Project;
 
 /**
  * Bytecode enhancer used to enhance persistent classes from metadata. The
@@ -193,12 +192,15 @@ public class PCEnhancer {
         }
     }
 
-    private BCClass _pc;
-    private final BCClass _managedType;
     private final MetaDataRepository _repos;
+    private final ClassMetaData _meta;
+    private final Log _log;
+
     boolean _addVersionInitFlag = true;
 
 
+    private final EnhancementProject project;
+
     /**
      * represents the managed type.
      */
@@ -211,8 +213,6 @@ public class PCEnhancer {
      */
     private ClassNodeTracker pc;
 
-    private final ClassMetaData _meta;
-    private final Log _log;
     private boolean _defCons = true;
     private boolean _redefine = false;
     private boolean _subclass = false;
@@ -235,8 +235,7 @@ public class PCEnhancer {
      * repository.
      */
     public PCEnhancer(OpenJPAConfiguration conf, Class<?> type) {
-        this(conf, 
AccessController.doPrivileged(SerpPrivacyHelper.loadProjectClassAction(new 
Project(), type)),
-             (MetaDataRepository) null);
+        this(conf, new EnhancementProject().loadClass(type), 
(MetaDataRepository) null);
     }
 
     /**
@@ -245,8 +244,7 @@ public class PCEnhancer {
      * and then loading from <code>conf</code>'s repository.
      */
     public PCEnhancer(OpenJPAConfiguration conf, ClassMetaData meta) {
-        this(conf, 
AccessController.doPrivileged(SerpPrivacyHelper.loadProjectClassAction(new 
Project(), meta.getDescribedType())),
-             meta.getRepository());
+        this(conf, new 
EnhancementProject().loadClass(meta.getDescribedType()), meta.getRepository());
     }
 
     /**
@@ -260,12 +258,11 @@ public class PCEnhancer {
      *              because the configuration might be an
      *              implementation-specific subclass whose metadata
      *              required more than just base metadata files
-     * @deprecated use {@link #PCEnhancer(OpenJPAConfiguration, BCClass,
+     * @deprecated use {@link #PCEnhancer(OpenJPAConfiguration, 
ClassNodeTracker,
      * MetaDataRepository, ClassLoader)} instead.
      */
     @Deprecated
-    public PCEnhancer(OpenJPAConfiguration conf, BCClass type,
-                      MetaDataRepository repos) {
+    public PCEnhancer(OpenJPAConfiguration conf, ClassNodeTracker type, 
MetaDataRepository repos) {
         this(conf, type, repos, null);
     }
 
@@ -283,12 +280,11 @@ public class PCEnhancer {
      * @param loader the environment classloader to use for loading
      *               classes and resources.
      */
-    public PCEnhancer(OpenJPAConfiguration conf, BCClass type, 
MetaDataRepository repos, ClassLoader loader) {
-        _managedType = type;
-        _pc = type;
+    public PCEnhancer(OpenJPAConfiguration conf, ClassNodeTracker type, 
MetaDataRepository repos, ClassLoader loader) {
 
         // we assume that the original class and the enhanced class is the same
-        managedType = AsmHelper.toClassNode(type);
+        project = type.getProject();
+        managedType = type;
         pc = managedType;
 
         _log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
@@ -323,12 +319,10 @@ public class PCEnhancer {
      * @param meta  the metadata to use for processing this type.
      * @since 1.1.0
      */
-    public PCEnhancer(MetaDataRepository repos, BCClass type, ClassMetaData 
meta) {
-        _managedType = type;
-        _pc = type;
-
+    public PCEnhancer(MetaDataRepository repos, ClassNodeTracker type, 
ClassMetaData meta) {
         // we assume that the original class and the enhanced class is the same
-        managedType = AsmHelper.toClassNode(type);
+        project = type.getProject();
+        managedType = type;
         pc = managedType;
 
         _log = repos.getConfiguration()
@@ -597,7 +591,6 @@ public class PCEnhancer {
                 addCloningCode();
                 runAuxiliaryEnhancers();
 
-                AsmHelper.readIntoBCClass(pc, _pc);
                 return ENHANCE_PC;
             }
             return ENHANCE_AWARE;
@@ -631,19 +624,19 @@ public class PCEnhancer {
             if (getCreateSubclass()) {
                 PCSubclassValidator val = new PCSubclassValidator(_meta, 
managedType.getClassNode(), _log, _fail);
                 val.assertCanSubclass();
-                pc = AsmHelper.copyClassNode(managedType, 
toPCSubclassName(managedType));
-                _pc = 
_managedType.getProject().loadClass(toPCSubclassName(managedType));
-                _pc.setMajorVersion(_managedType.getMajorVersion());
-                _pc.setMinorVersion(_managedType.getMinorVersion());
-                if (_pc.getSuperclassBC() != _managedType) {
-                    _pc.setSuperclass(_managedType);
-                    _pc.setAbstract(_managedType.isAbstract());
-                    _pc.declareInterface(DynamicPersistenceCapable.class);
+                pc = project.loadClass(toPCSubclassName(managedType));
+                if (pc.getClassNode().superName.equals("java/lang/Object")) {
+                    // set the parent class
+                    pc.getClassNode().superName = 
managedType.getClassNode().name;
+                    if ((managedType.getClassNode().access & 
Opcodes.ACC_ABSTRACT) > 0) {
+                        pc.getClassNode().access |= Opcodes.ACC_ABSTRACT;
+                    }
+
+                    pc.declareInterface(DynamicPersistenceCapable.class);
                 }
                 else {
                     _isAlreadySubclassed = true;
                 }
-                pc = AsmHelper.toClassNode(_pc);
             }
 
             _bcsConfigured = true;
@@ -1408,7 +1401,7 @@ public class PCEnhancer {
         classNode.methods.add(newInstance);
         final InsnList instructions = newInstance.instructions;
 
-        if (_pc.isAbstract()) {
+        if ((pc.getClassNode().access & Opcodes.ACC_ABSTRACT) > 0) {
             instructions.add(throwException(USEREXCEP));
             return;
         }
@@ -3317,7 +3310,7 @@ public class PCEnhancer {
                 accessMode = Opcodes.ACC_PUBLIC;
                 access = "public";
             }
-            else if (_pc.isFinal()) {
+            else if ((pc.getClassNode().access & Opcodes.ACC_FINAL) > 0) {
                 accessMode = Opcodes.ACC_PRIVATE;
                 access = "private";
             }
@@ -3461,7 +3454,7 @@ public class PCEnhancer {
             instructions.add(new InsnNode(Opcodes.ACONST_NULL));
         }
 
-        if (_pc.isAbstract()) {
+        if ((pc.getClassNode().access & Opcodes.ACC_ABSTRACT) > 0) {
             instructions.add(new InsnNode(Opcodes.ACONST_NULL));
         }
         else {
@@ -4716,7 +4709,7 @@ public class PCEnhancer {
 
         // declare externalizable interface
         if (!Externalizable.class.isAssignableFrom(_meta.getDescribedType())) {
-            
pc.getClassNode().interfaces.add(Type.getInternalName(Externalizable.class));
+            pc.declareInterface(Externalizable.class);
         }
 
         // make sure the user doesn't already have custom externalization or
@@ -5602,8 +5595,8 @@ public class PCEnhancer {
             }
         }
 
-        Project project = new Project();
-        BCClass bc;
+        EnhancementProject project = new EnhancementProject();
+        ClassNodeTracker cnt;
         PCEnhancer enhancer;
         Collection persAwareClasses = new HashSet();
 
@@ -5614,12 +5607,12 @@ public class PCEnhancer {
             }
 
             if (o instanceof String) {
-                bc = project.loadClass((String) o, loader);
+                cnt = project.loadClass((String) o, loader);
             }
             else {
-                bc = project.loadClass((Class) o);
+                cnt = project.loadClass((Class) o);
             }
-            enhancer = new PCEnhancer(conf, bc, repos, loader);
+            enhancer = new PCEnhancer(conf, cnt, repos, loader);
             if (writer != null) {
                 enhancer.setBytecodeWriter(writer);
             }
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java
index 0a79ec12a..4ff6176cd 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java
@@ -248,10 +248,10 @@ public class PCRegistry {
      * Look up the metadata for a <code>PersistenceCapable</code> class.
      */
     private static Meta getMeta(Class<?> pcClass) {
-        Meta ret = (Meta) _metas.get(pcClass);
-        if (ret == null)
-            throw new IllegalStateException(_loc.get("no-meta", pcClass).
-                getMessage());
+        Meta ret = _metas.get(pcClass);
+        if (ret == null) {
+            throw new IllegalStateException(_loc.get("no-meta", 
pcClass).getMessage());
+        }
         return ret;
     }
 
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java
index 11324f9bc..07fc7ee80 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java
@@ -18,7 +18,6 @@
  */
 package org.apache.openjpa.meta;
 
-import java.io.ByteArrayInputStream;
 import java.lang.reflect.Method;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
@@ -28,14 +27,17 @@ import java.util.Set;
 import java.util.WeakHashMap;
 
 import org.apache.openjpa.enhance.PCEnhancer;
-import org.apache.openjpa.enhance.SerpPrivacyHelper;
 import org.apache.openjpa.lib.util.J2DoPrivHelper;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.lib.util.StringUtil;
 import org.apache.openjpa.util.InternalException;
+import org.apache.openjpa.util.asm.AsmHelper;
+import org.apache.openjpa.util.asm.ClassNodeTracker;
+import org.apache.openjpa.util.asm.EnhancementClassLoader;
+import org.apache.openjpa.util.asm.EnhancementProject;
+import org.apache.xbean.asm9.Type;
 
 import serp.bytecode.BCClass;
-import serp.bytecode.BCClassLoader;
 import serp.bytecode.BCField;
 import serp.bytecode.BCMethod;
 import serp.bytecode.Code;
@@ -49,16 +51,13 @@ import serp.bytecode.Project;
  * @author Steve Kim
  */
 class InterfaceImplGenerator {
-    private static final Localizer _loc = Localizer.forPackage
-        (InterfaceImplGenerator.class);
+    private static final Localizer _loc = 
Localizer.forPackage(InterfaceImplGenerator.class);
     private static final String POSTFIX = "openjpaimpl";
 
     private final MetaDataRepository _repos;
     private final Map<Class<?>,Class<?>> _impls = new WeakHashMap<>();
-    private final Project _project = new Project();
+    private final EnhancementProject _project = new EnhancementProject();
 
-    // distinct project / loader for enhanced version of class
-    private final Project _enhProject = new Project();
 
     /**
      * Constructor.  Supply repository.
@@ -79,45 +78,46 @@ class InterfaceImplGenerator {
         if (impl != null)
             return impl;
 
+        // distinct temp project / loader for enhancing
+        EnhancementProject _enhProject = new EnhancementProject();
+
         ClassLoader parentLoader = AccessController.doPrivileged(
             J2DoPrivHelper.getClassLoaderAction(iface));
-        BCClassLoader loader = AccessController
-            .doPrivileged(SerpPrivacyHelper.newBCClassLoaderAction(_project,
-                                                                   
parentLoader));
-        BCClassLoader enhLoader = AccessController
-            .doPrivileged(SerpPrivacyHelper.newBCClassLoaderAction(_enhProject,
-                                                                   
parentLoader));
-        BCClass bc = _project.loadClass(getClassName(meta));
+        EnhancementClassLoader loader = new EnhancementClassLoader(_project, 
parentLoader);
+        EnhancementClassLoader enhLoader = new 
EnhancementClassLoader(_enhProject, parentLoader);
+        ClassNodeTracker bc = _project.loadClass(getClassName(meta), loader);
         bc.declareInterface(iface);
         ClassMetaData sup = meta.getPCSuperclassMetaData();
         if (sup != null) {
-            bc.setSuperclass(sup.getInterfaceImpl());
-            enhLoader = AccessController
-                .doPrivileged(SerpPrivacyHelper.newBCClassLoaderAction(
-                    _enhProject, AccessController
-                        .doPrivileged(J2DoPrivHelper.getClassLoaderAction(sup
-                            .getInterfaceImpl()))));
+            bc.getClassNode().superName = 
Type.getInternalName(sup.getInterfaceImpl());
+            //X enhLoader = new EnhancementClassLoader(_enhProject, 
sup.getInterfaceImpl().getClassLoader());
         }
 
         FieldMetaData[] fields = meta.getDeclaredFields();
         Set<Method> methods = new HashSet<>();
+
+        //X TODO REMOVE
+        BCClass _bc = new Project().loadClass(getClassName(meta));
+        AsmHelper.readIntoBCClass(bc, _bc);
+
         for (FieldMetaData field : fields) {
-            addField(bc, iface, field, methods);
+            addField(_bc, iface, field, methods);
         }
-        invalidateNonBeanMethods(bc, iface, methods);
+        invalidateNonBeanMethods(_bc, iface, methods);
 
         // first load the base Class<?> as the enhancer requires the class
         // to be available
         try {
-            meta.setInterfaceImpl(Class.forName(bc.getName(), true, loader));
+            meta.setInterfaceImpl(Class.forName(_bc.getName(), true, loader));
         } catch (Throwable t) {
             throw new InternalException(_loc.get("interface-load", iface,
                 loader), t).setFatal(true);
         }
+
         // copy the BCClass<?> into the enhancer project.
-        bc = _enhProject.loadClass(new ByteArrayInputStream(bc.toByteArray()),
-            loader);
-        PCEnhancer enhancer = new PCEnhancer(_repos, bc, meta);
+        //X bc = _enhProject.loadClass(new 
ByteArrayInputStream(_bc.toByteArray()), loader);
+        ClassNodeTracker bcEnh = AsmHelper.toClassNode(_enhProject, _bc);
+        PCEnhancer enhancer = new PCEnhancer(_repos, bcEnh, meta);
 
         int result = enhancer.run();
         if (result != PCEnhancer.ENHANCE_PC)
@@ -128,8 +128,8 @@ class InterfaceImplGenerator {
             String pcClassName = 
enhancer.getPCBytecode().getClassNode().name.replace("/", ".");
             impl = Class.forName(pcClassName, true, enhLoader);
         } catch (Throwable t) {
-            throw new InternalException(_loc.get("interface-load2", iface,
-                enhLoader), t).setFatal(true);
+            //X throw new InternalException(_loc.get("interface-load2", iface, 
enhLoader), t).setFatal(true);
+            throw new InternalException(_loc.get("interface-load2", iface, 
loader), t).setFatal(true);
         }
         // cache the generated impl.
         _impls.put(iface, impl);
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
index 529ba4888..0e1b007fe 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
@@ -50,7 +50,7 @@ import serp.bytecode.Project;
  */
 public final class AsmHelper {
     private static final char[] PRIMITIVE_DESCRIPTORS = 
{'V','Z','C','B','S','I','F','J','D'};
-    private static final Attribute[] ATTRS = new Attribute[] {
+    public static final Attribute[] ATTRS = new Attribute[] {
             new RedefinedAttribute()
     };
 
@@ -79,40 +79,20 @@ public final class AsmHelper {
         }
     }
 
-    /**
-     * Copy the binary information from the ClassNodeTracker to a new one
-     * @param cntIn the original ASM class representation
-     * @param newClassName the class name of the new ClassNodeTracker
-     * @return a new ClassNodeTracker
-     */
-    public static ClassNodeTracker copyClassNode(ClassNodeTracker cntIn, 
String newClassName) {
-        final byte[] classBytes = toByteArray(cntIn);
-
-        ClassReader cr = new ClassReader(classBytes);
-        ClassNode classNode = new ClassNode(Opcodes.ASM9);
-        cr.accept(classNode, ATTRS, 0);
-
-        if ((classNode.version & 0xffff) < 49) {
-            classNode.version = 49;
-        }
-
-        return new ClassNodeTracker(classNode, cntIn.getClassLoader());
-    }
-
     /**
      * Read the binary bytecode from the class with the given name
      * @param classLoader the ClassLoader to use
      * @param className the fully qualified class name to read. e.g. 
"org.mycorp.mypackage.MyEntity"
      * @return the ClassNode constructed from that class
      */
-    public static ClassNode readClassNode(ClassLoader classLoader, String 
className) {
+    public static ClassNode readClassNode(ClassLoader classLoader, String 
className) throws ClassNotFoundException {
         ClassReader cr;
         final String classResourceName = className.replace(".", "/") + 
".class";
         try (final InputStream classBytesStream = 
classLoader.getResourceAsStream(classResourceName)) {
             cr = new ClassReader(classBytesStream);
         }
         catch (IOException e) {
-            throw new RuntimeException(e);
+            throw new ClassNotFoundException("Cannot read ClassNode for class 
" + className, e);
         }
         ClassNode classNode = new ClassNode();
         cr.accept(classNode, ATTRS, 0);
@@ -184,7 +164,7 @@ public final class AsmHelper {
      * temporary helper class to convert BCClass to ASM ClassNode
      * @deprecated must get removed when done with migrating from Serp to ASM
      */
-    public static ClassNodeTracker toClassNode(BCClass bcClass) {
+    public static ClassNodeTracker toClassNode(EnhancementProject project, 
BCClass bcClass) {
         ClassReader cr = new ClassReader(bcClass.toByteArray());
         ClassNode classNode = new ClassNode(Opcodes.ASM9);
         cr.accept(classNode, ATTRS, 0);
@@ -193,7 +173,8 @@ public final class AsmHelper {
             classNode.version = 49;
         }
 
-        return new ClassNodeTracker(classNode, bcClass.getClassLoader());
+        final ClassNodeTracker cnt = new ClassNodeTracker(project, classNode, 
bcClass.getClassLoader());
+        return cnt;
     }
 
     /**
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/ClassNodeTracker.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/ClassNodeTracker.java
index 1b08b1a52..acb615d7d 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/ClassNodeTracker.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/ClassNodeTracker.java
@@ -16,6 +16,9 @@
  */
 package org.apache.openjpa.util.asm;
 
+import java.util.ArrayList;
+
+import org.apache.xbean.asm9.Type;
 import org.apache.xbean.asm9.tree.ClassNode;
 
 /**
@@ -26,10 +29,31 @@ import org.apache.xbean.asm9.tree.ClassNode;
 public class ClassNodeTracker {
     private final ClassNode classNode;
     private final ClassLoader cl;
+    private final EnhancementProject project;
 
-    public ClassNodeTracker(ClassNode classNode, ClassLoader cl) {
+    public ClassNodeTracker(EnhancementProject project, ClassNode classNode, 
ClassLoader cl) {
+        this.project = project;
         this.classNode = classNode;
-        this.cl = cl;
+        if (hasEnhancementCl(cl)) {
+            this.cl = cl;
+        }
+        else {
+            this.cl = new EnhancementClassLoader(project, cl);
+        }
+        project.putClass(classNode.name.replace("/", "."), this);
+    }
+
+    private boolean hasEnhancementCl(ClassLoader cl) {
+        boolean hasEhCl = false;
+        do {
+            if (cl instanceof EnhancementClassLoader) {
+                hasEhCl = true;
+                break;
+            }
+            cl = cl.getParent();
+        } while (cl != null);
+
+        return hasEhCl;
     }
 
     public ClassNode getClassNode() {
@@ -40,6 +64,10 @@ public class ClassNodeTracker {
         return cl;
     }
 
+    public EnhancementProject getProject() {
+        return project;
+    }
+
     public Class<?> getType() {
         try {
             return Class.forName(classNode.name.replace("/", "."), false, cl);
@@ -48,4 +76,11 @@ public class ClassNodeTracker {
             throw new RuntimeException(e);
         }
     }
+
+    public void declareInterface(Class<?> iface) {
+        if (classNode.interfaces == null) {
+            classNode.interfaces = new ArrayList<>();
+        }
+        classNode.interfaces.add(Type.getInternalName(iface));
+    }
 }
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/EnhancementClassLoader.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/EnhancementClassLoader.java
index 8c971d513..462eabc13 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/EnhancementClassLoader.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/EnhancementClassLoader.java
@@ -32,7 +32,7 @@ public class EnhancementClassLoader extends ClassLoader {
         this.project = project;
     }
 
-    public EnhancementClassLoader(ClassLoader parent, EnhancementProject 
project) {
+    public EnhancementClassLoader(EnhancementProject project, ClassLoader 
parent) {
         super(parent);
         this.project = project;
     }
@@ -41,6 +41,15 @@ public class EnhancementClassLoader extends ClassLoader {
         return project;
     }
 
+    @Override
+    public Class<?> loadClass(String name) throws ClassNotFoundException {
+        return super.loadClass(name);
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
+        return super.loadClass(name, resolve);
+    }
 
     protected Class findClass(String name) throws ClassNotFoundException {
         byte[] bytes;
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/EnhancementProject.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/EnhancementProject.java
index 451ce039b..de93f1a18 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/EnhancementProject.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/EnhancementProject.java
@@ -18,8 +18,9 @@ package org.apache.openjpa.util.asm;
 
 import java.util.HashMap;
 
-import org.apache.openjpa.util.asm.AsmHelper;
-import org.apache.openjpa.util.asm.ClassNodeTracker;
+import org.apache.openjpa.lib.util.JavaVersions;
+import org.apache.xbean.asm9.ClassReader;
+import org.apache.xbean.asm9.Opcodes;
 import org.apache.xbean.asm9.tree.ClassNode;
 
 
@@ -30,26 +31,36 @@ import org.apache.xbean.asm9.tree.ClassNode;
  */
 public class EnhancementProject {
 
-    private HashMap<String, ClassNodeTracker> classNodTrackers = new 
HashMap<>();
+    private HashMap<String, ClassNodeTracker> classNodeTrackers = new 
HashMap<>();
 
 
     /**
      * Return true if the project already contains the given class.
      */
     public boolean containsClass(String type) {
-        return classNodTrackers.containsKey(type);
+        return classNodeTrackers.containsKey(type);
     }
 
 
     /**
      * Load a class with the given name.
      *
+     * @param name the fully qualified class name
      * @see #loadClass(String,ClassLoader)
      */
     public ClassNodeTracker loadClass(String name) {
         return loadClass(name, null);
     }
 
+    /**
+     * Load a class with the given type.
+     *
+     * @see #loadClass(String,ClassLoader)
+     */
+    public ClassNodeTracker loadClass(Class<?> type) {
+        return loadClass(type.getName(), type.getClassLoader());
+    }
+
     /**
      * Load the bytecode for the class with the given name.
      * If a {@link ClassNodeTracker} with the given name already exists in 
this project,
@@ -66,7 +77,7 @@ public class EnhancementProject {
      * @throws RuntimeException on parse error
      */
     public ClassNodeTracker loadClass(String name, ClassLoader loader) {
-        ClassNodeTracker cached = classNodTrackers.get(name);
+        ClassNodeTracker cached = classNodeTrackers.get(name);
         if (cached != null) {
             return cached;
         }
@@ -76,10 +87,54 @@ public class EnhancementProject {
             loader = Thread.currentThread().getContextClassLoader();
         }
 
-        final ClassNode classNode = AsmHelper.readClassNode(loader, name);
-        ClassNodeTracker cnt = new ClassNodeTracker(classNode, loader);
-        classNodTrackers.put(name, cnt);
+        ClassNode classNode;
+        try {
+            classNode = AsmHelper.readClassNode(loader, name);
+        }
+        catch (ClassNotFoundException e) {
+            // otherwise create a new ClassNode
+            classNode = new ClassNode(Opcodes.ASM9);
+            classNode.version = detectJavaBytecodeVersion();
+            classNode.name = name.replace(".", "/");
+            classNode.access = Opcodes.ACC_PUBLIC;
+            classNode.superName = "java/lang/Object";
+        }
+        ClassNodeTracker cnt = new ClassNodeTracker(this, classNode, loader);
+        return cnt;
+    }
+
+    /**
+     * 49 Java 1.5
+     * 50 Java 1.6
+     * 51 Java 1.7
+     * 52 Java 1.8
+     * 53 Java9
+     * 54 Java10
+     * 55 Java11
+     * etc
+     *
+     * @return the bytecode version of the current VM
+     */
+    private int detectJavaBytecodeVersion() {
+        return JavaVersions.VERSION + 44;
+    }
+
+    public ClassNodeTracker loadClass(byte[] bytes, ClassLoader loader) {
+        ClassReader cr = new ClassReader(bytes);
+        ClassNode classNode = new ClassNode();
+        cr.accept(classNode, AsmHelper.ATTRS, 0);
+        ClassNodeTracker cnt = new ClassNodeTracker(this, classNode, loader);
+        String name = classNode.name.replace("/", ".");
+        classNodeTrackers.put(name, cnt);
         return cnt;
     }
 
+    public void clear() {
+        classNodeTrackers.clear();
+    }
+
+    void putClass(String name, ClassNodeTracker cnt) {
+        classNodeTrackers.put(name, cnt);
+    }
+
 }
diff --git 
a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/JavaVersions.java 
b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/JavaVersions.java
index 222770d6e..45c737fe7 100644
--- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/JavaVersions.java
+++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/JavaVersions.java
@@ -58,7 +58,15 @@ public class JavaVersions {
         else if ("1.8".equals(specVersion))
             VERSION = 8;
         else {
-            VERSION = Integer.parseInt(specVersion);
+            int v;
+            try {
+                v = Integer.parseInt(specVersion);
+            }
+            catch (NumberFormatException nfe) {
+                // default to Java 8
+                v = 8;
+            }
+            VERSION = v;
         }
     }
 
diff --git 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestEnhancementWithMultiplePUs.java
 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestEnhancementWithMultiplePUs.java
index 38dc3cdfb..c61e0f2cd 100644
--- 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestEnhancementWithMultiplePUs.java
+++ 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestEnhancementWithMultiplePUs.java
@@ -21,7 +21,6 @@ package org.apache.openjpa.enhance;
 import java.io.IOException;
 import java.security.AccessController;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import org.apache.openjpa.conf.OpenJPAConfiguration;
@@ -33,26 +32,24 @@ import org.apache.openjpa.lib.util.Options;
 import org.apache.openjpa.meta.MetaDataRepository;
 import org.apache.openjpa.persistence.test.AbstractCachedEMFTestCase;
 import org.apache.openjpa.util.asm.ClassNodeTracker;
+import org.apache.openjpa.util.asm.EnhancementProject;
 import org.apache.xbean.asm9.Type;
 
-import serp.bytecode.BCClass;
-import serp.bytecode.Project;
 
 public class TestEnhancementWithMultiplePUs
     extends AbstractCachedEMFTestCase {
 
-    public void testExplicitEnhancementWithClassNotInFirstPU()
-        throws ClassNotFoundException {
+    public void testExplicitEnhancementWithClassNotInFirstPU() throws 
ClassNotFoundException {
         OpenJPAConfiguration conf = new OpenJPAConfigurationImpl();
         Configurations.populateConfiguration(conf, new Options());
         MetaDataRepository repos = conf.getMetaDataRepositoryInstance();
         ClassLoader loader = AccessController
             .doPrivileged(J2DoPrivHelper.newTemporaryClassLoaderAction(
                 getClass().getClassLoader()));
-        Project project = new Project();
+        EnhancementProject project = new EnhancementProject();
 
         String className = 
"org.apache.openjpa.enhance.UnenhancedBootstrapInstance";
-        BCClass bc = assertNotPC(loader, project, className);
+        ClassNodeTracker bc = assertNotPC(loader, project, className);
 
         PCEnhancer enhancer = new PCEnhancer(conf, bc, repos, loader);
 
@@ -61,11 +58,10 @@ public class TestEnhancementWithMultiplePUs
         
assertTrue(enhancer.getPCBytecode().getClassNode().interfaces.contains(Type.getInternalName(PersistenceCapable.class)));
     }
 
-    private BCClass assertNotPC(ClassLoader loader, Project project, String 
className) {
-        BCClass bc = project.loadClass(className, loader);
-        assertFalse(className + " must not be enhanced already; it was.",
-            Arrays.asList(bc.getInterfaceNames()).contains(
-                PersistenceCapable.class.getName()));
+    private ClassNodeTracker assertNotPC(ClassLoader loader, 
EnhancementProject project, String className) {
+        ClassNodeTracker bc = project.loadClass(className, loader);
+        assertTrue(className + " must not be enhanced already; it was.",
+            bc.getClassNode().interfaces == null || 
!bc.getClassNode().interfaces.contains(Type.getInternalName(PersistenceCapable.class)));
         return bc;
     }
 
@@ -80,7 +76,7 @@ public class TestEnhancementWithMultiplePUs
         ClassLoader loader = AccessController
             .doPrivileged(J2DoPrivHelper.newTemporaryClassLoaderAction(
                 getClass().getClassLoader()));
-        Project project = new Project();
+        EnhancementProject project = new EnhancementProject();
 
         // make sure that the class is not already enhanced for some reason
         String className = 
"org/apache/openjpa/enhance/UnenhancedBootstrapInstance";
@@ -118,7 +114,7 @@ public class TestEnhancementWithMultiplePUs
         ClassLoader loader = AccessController
             .doPrivileged(J2DoPrivHelper.newTemporaryClassLoaderAction(
                 getClass().getClassLoader()));
-        Project project = new Project();
+        EnhancementProject project = new EnhancementProject();
 
         // make sure that the classes is not already enhanced for some reason
         assertNotPC(loader, project,


Reply via email to