OPENJPA-2911 ByteCodeWriter with ASM
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java
index 2fc77a3..cde3f04 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java
@@ -18,9 +18,10 @@
*/
package org.apache.openjpa.enhance;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -32,8 +33,8 @@
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.util.asm.AsmHelper;
import org.apache.openjpa.util.asm.BytecodeWriter;
-import org.apache.openjpa.lib.util.Files;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.Localizer.Message;
import org.apache.openjpa.meta.AccessCode;
@@ -46,8 +47,7 @@
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.MetaDataException;
import org.apache.openjpa.util.UserException;
-
-import serp.bytecode.BCClass;
+import org.apache.openjpa.util.asm.ClassNodeTracker;
/**
* Redefines the method bodies of existing unenhanced classes to make them
@@ -65,7 +65,7 @@
* OpenJPA to handle new instances of the unenhanced type. If this is
* invoked in a Java 6 environment, this method will redefine the methods
* for each class in the argument list such that field accesses are
- * intercepted in-line. If invoked in a Java 5 environment, this
+ * intercepted in-line. If invoked in a Java 5 environment or very new Java versions, this
* redefinition is not possible; in these contexts, when using field
* access, OpenJPA will need to do state comparisons to detect any change
* to any instance at any time, and when using property access, OpenJPA
@@ -136,8 +136,8 @@
enhancer.setBytecodeWriter(new BytecodeWriter() {
@Override
- public void write(BCClass bc) throws IOException {
- ManagedClassSubclasser.write(bc, enhancer, map, c, subs, ints);
+ public void write(ClassNodeTracker cnt) throws IOException {
+ ManagedClassSubclasser.write(cnt, enhancer, map, c, subs, ints);
}
});
if (redefine) {
@@ -174,9 +174,9 @@
}
}
- if (unspecified != null && !unspecified.isEmpty())
- throw new UserException(_loc.get("unspecified-unenhanced-types", Exceptions.toClassNames(classes),
- unspecified));
+ if (unspecified != null && !unspecified.isEmpty()) {
+ throw new UserException(_loc.get("unspecified-unenhanced-types", Exceptions.toClassNames(classes), unspecified));
+ }
ClassRedefiner.redefineClasses(conf, map);
for (Class<?> cls : map.keySet()) {
@@ -269,55 +269,61 @@
}
}
- private static void write(BCClass bc, PCEnhancer enhancer,
- Map<Class<?>, byte[]> map, Class<?> cls, List<Class<?>> subs, List<Class<?>> ints)
+ private static void write(ClassNodeTracker cnt, PCEnhancer enhancer, Map<Class<?>, byte[]> map,
+ Class<?> cls, List<Class<?>> subs, List<Class<?>> ints)
throws IOException {
- if (bc == enhancer.getManagedTypeBytecode()) {
+ if (cnt == enhancer.getManagedTypeBytecode()) {
// if it was already defined, don't put it in the map,
// but do set the metadata accordingly.
if (enhancer.isAlreadyRedefined())
- ints.add(bc.getType());
+ ints.add(cls);
else {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- AsmAdaptor.write(bc, baos);
- map.put(bc.getType(), baos.toByteArray());
- debugBytecodes(bc);
+ final byte[] byteArray = AsmHelper.toByteArray(cnt);
+ map.put(cls, byteArray);
+ debugBytecodes(cnt, byteArray);
}
} else {
if (!enhancer.isAlreadySubclassed()) {
- debugBytecodes(bc);
+ final byte[] byteArray = AsmHelper.toByteArray(cnt);
+ debugBytecodes(cnt, byteArray);
// this is the new subclass
- ClassLoader loader = GeneratedClasses.getMostDerivedLoader(
- cls, PersistenceCapable.class);
- subs.add(GeneratedClasses.loadBCClass(bc, loader));
+ ClassLoader loader = GeneratedClasses.getMostDerivedLoader(cls, PersistenceCapable.class);
+ String className = cnt.getClassNode().name.replace("/", ".");
+ final Class subclass = GeneratedClasses.loadAsmClass(className, byteArray, cls, loader);
+ try {
+ // Ugly workaround to trigger clinit static initializer block :(
+ subclass.newInstance();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ subs.add(subclass);
}
}
}
- public static void debugBytecodes(BCClass bc) throws IOException {
+ public static void debugBytecodes(ClassNodeTracker cnt, byte[] classBytes) throws IOException {
// Write the bytecodes to disk for debugging purposes.
- if ("true".equals(System.getProperty(
- ManagedClassSubclasser.class.getName() + ".dumpBytecodes")))
+ if ("true".equals(System.getProperty(ManagedClassSubclasser.class.getName() + ".dumpBytecodes")))
{
File tmp = new File(System.getProperty("java.io.tmpdir"));
File dir = new File(tmp, "openjpa");
dir = new File(dir, "pcsubclasses");
dir.mkdirs();
- dir = Files.getPackageFile(dir, bc.getPackageName(), true);
- File f = new File(dir, bc.getClassName() + ".class");
+ File f = new File(dir, cnt.getClassNode().name + ".class");
+
// START - ALLOW PRINT STATEMENTS
System.err.println("Writing to " + f);
// STOP - ALLOW PRINT STATEMENTS
- AsmAdaptor.write(bc, f);
+
+ Files.write(f.toPath(), classBytes, StandardOpenOption.WRITE);
}
}
- private static void setIntercepting(OpenJPAConfiguration conf,
- ClassLoader envLoader, Class<?> cls) {
- ClassMetaData meta = conf.getMetaDataRepositoryInstance()
- .getMetaData(cls, envLoader, true);
+ private static void setIntercepting(OpenJPAConfiguration conf, ClassLoader envLoader, Class<?> cls) {
+ ClassMetaData meta = conf.getMetaDataRepositoryInstance().getMetaData(cls, envLoader, true);
meta.setIntercepting(true);
}
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 0dc5123..d782996 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
@@ -212,7 +212,6 @@
private final ClassMetaData _meta;
private final Log _log;
- private Collection _oids = null;
private boolean _defCons = true;
private boolean _redefine = false;
private boolean _subclass = false;
@@ -389,8 +388,8 @@
* except when running the enhancer to redefine and subclass
* existing persistent types.
*/
- public BCClass getManagedTypeBytecode() {
- return _managedType;
+ public ClassNodeTracker getManagedTypeBytecode() {
+ return managedType;
}
/**
@@ -641,46 +640,41 @@
/**
* Write the generated bytecode.
*/
- public void record()
- throws IOException {
- if (_managedType != _pc && getRedefine()) {
- record(AsmHelper.toClassWriter(_managedType));
+ public void record() throws IOException {
+ if (managedType != pc && getRedefine()) {
+ record(managedType);
}
- record(AsmHelper.toClassWriter(_pc));
-
- if (_oids != null)
- for (Object oid : _oids) {
- record(AsmHelper.toClassWriter((BCClass) oid));
- }
+ record(pc);
}
/**
* Write the given class.
*/
- private void record(ClassWriterTracker cwt)
+ private void record(ClassNodeTracker cnt)
throws IOException {
- if (_writer != null)
- _writer.write(AsmHelper.toBCClass(cwt));
+ if (_writer != null) {
+ _writer.write(cnt);
+ }
else if (_dir == null) {
- String name = cwt.getName().replace(".", "/");
- ClassLoader cl = cwt.getClassLoader();
+ String name = cnt.getClassNode().name.replace(".", "/");
+ ClassLoader cl = cnt.getClassLoader();
if (cl == null) {
cl = Thread.currentThread().getContextClassLoader();
}
final URL resource = cl.getResource(name + ".class");
try (OutputStream out = new FileOutputStream(URLDecoder.decode(resource.getFile()))) {
- out.write(cwt.getCw().toByteArray());
+ out.write(AsmHelper.toByteArray(cnt));
out.flush();
}
}
else {
- String name = cwt.getName().replace(".", "/") + ".class";
+ String name = cnt.getClassNode().name.replace(".", "/") + ".class";
File targetFile = new File(_dir, name);
if (!targetFile.getParentFile().exists()) {
targetFile.getParentFile().mkdirs();
}
- java.nio.file.Files.write(targetFile.toPath(), cwt.getCw().toByteArray());
+ java.nio.file.Files.write(targetFile.toPath(), AsmHelper.toByteArray(cnt));
}
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassLoaderProxyService.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassLoaderProxyService.java
index 654d063..4ba872c 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassLoaderProxyService.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassLoaderProxyService.java
@@ -106,6 +106,7 @@
definePackageFor(pck, protectionDomain);
}
existing = super.defineClass(proxyClassName, proxyBytes, 0, proxyBytes.length);
+
resolveClass(existing);
classes.put(key, existing);
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneratedClasses.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneratedClasses.java
index b6ddf25..5073e5b 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneratedClasses.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneratedClasses.java
@@ -56,7 +56,7 @@
/**
* Load the class represented by the given bytecode.
- * @deprecated move to ASM
+ * @deprecated move to ASM {@link #loadAsmClass(String, byte[], Class, ClassLoader)}
*/
public static Class loadBCClass(BCClass bc, ClassLoader loader) {
BCClassLoader bcloader = AccessController
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 6bb378e..d86bb6b 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
@@ -111,6 +111,15 @@
}
/**
+ * Create a byte[] of that class represented by the ClassNodeTracker
+ */
+ public static byte[] toByteArray(ClassNodeTracker cnt) {
+ ClassWriter cw = new BCClassWriter(ClassWriter.COMPUTE_FRAMES, cnt.getClassLoader());
+ cnt.getClassNode().accept(cw);
+ return cw.toByteArray();
+ }
+
+ /**
* temporary helper class to convert ClassWriterTracker to BCClass
* @deprecated must get removed when done with migrating from Serp to ASM
*/
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/BytecodeWriter.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/BytecodeWriter.java
index c69a72e..06af49c 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/BytecodeWriter.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/BytecodeWriter.java
@@ -29,5 +29,5 @@
*/
public interface BytecodeWriter {
- void write(BCClass type) throws IOException;
+ void write(ClassNodeTracker type) throws IOException;
}
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 f139069..7189e79 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
@@ -32,6 +32,8 @@
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.xbean.asm9.Type;
import serp.bytecode.BCClass;
import serp.bytecode.Project;
@@ -84,9 +86,8 @@
Project project = new Project();
// make sure that the class is not already enhanced for some reason
- String className =
- "org.apache.openjpa.enhance.UnenhancedBootstrapInstance";
- BCClass bc = assertNotPC(loader, project, className);
+ String className = "org/apache/openjpa/enhance/UnenhancedBootstrapInstance";
+ assertNotPC(loader, project, className);
// build up a writer that just stores to a list so that we don't
// mutate the disk.
@@ -94,10 +95,9 @@
BytecodeWriter writer = new BytecodeWriter() {
@Override
- public void write(BCClass type) throws IOException {
- assertTrue(Arrays.asList(type.getInterfaceNames()).contains(
- PersistenceCapable.class.getName()));
- written.add(type.getName());
+ public void write(ClassNodeTracker cnt) throws IOException {
+ assertTrue(cnt.getClassNode().interfaces.contains(Type.getInternalName(PersistenceCapable.class)));
+ written.add(cnt.getClassNode().name);
}
};
@@ -135,9 +135,9 @@
BytecodeWriter writer = new BytecodeWriter() {
@Override
- public void write(BCClass type) throws IOException {
- assertTrue(Arrays.asList(type.getInterfaceNames()).contains(PersistenceCapable.class.getName()));
- written.add(type.getName());
+ public void write(ClassNodeTracker cnt) throws IOException {
+ assertTrue(cnt.getClassNode().interfaces.contains(Type.getInternalName(PersistenceCapable.class)));
+ written.add(cnt.getClassNode().name);
}
};
@@ -156,8 +156,8 @@
// ensure that we do process the classes listed in the PUs
assertTrue(written.contains(
- "org.apache.openjpa.enhance.UnenhancedBootstrapInstance"));
+ "org/apache/openjpa/enhance/UnenhancedBootstrapInstance"));
assertTrue(written.contains(
- "org.apache.openjpa.enhance.UnenhancedBootstrapInstance2"));
+ "org/apache/openjpa/enhance/UnenhancedBootstrapInstance2"));
}
}