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,