mirror of
https://github.com/apache/openjpa.git
synced 2025-02-21 01:15:30 +00:00
OPENJPA-952. Committing patch from Rick Curtis
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@780086 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
016d49b4c0
commit
2b942805f2
@ -1557,7 +1557,15 @@ public interface OpenJPAConfiguration
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public void setRuntimeUnenhancedClasses(int mode);
|
||||
|
||||
/**
|
||||
* Whether OpenJPA will attempt to dynamically load the enhancement agent.
|
||||
*/
|
||||
public boolean getDynamicEnhancementAgent();
|
||||
/**
|
||||
* Sets whether OpenJPA will attempt to dynamically load the enhancement
|
||||
* agent.
|
||||
*/
|
||||
public void setDynamicEnhancementAgent(boolean dynamic);
|
||||
/**
|
||||
* A comma-separted list of the plugin strings specifying the
|
||||
* {@link CacheMarshaller}s to use.
|
||||
|
@ -28,6 +28,7 @@ import org.apache.openjpa.datacache.ConcurrentQueryCache;
|
||||
import org.apache.openjpa.datacache.DataCacheManager;
|
||||
import org.apache.openjpa.datacache.DataCacheManagerImpl;
|
||||
import org.apache.openjpa.ee.ManagedRuntime;
|
||||
import org.apache.openjpa.enhance.PCEnhancerAgent;
|
||||
import org.apache.openjpa.enhance.RuntimeUnenhancedClasssesModes;
|
||||
import org.apache.openjpa.event.BrokerFactoryEventManager;
|
||||
import org.apache.openjpa.event.OrphanedKeyAction;
|
||||
@ -165,6 +166,8 @@ public class OpenJPAConfigurationImpl
|
||||
new StoreFacadeTypeRegistry();
|
||||
private BrokerFactoryEventManager _brokerFactoryEventManager =
|
||||
new BrokerFactoryEventManager(this);
|
||||
|
||||
public BooleanValue dynamicEnhancementAgent;
|
||||
|
||||
/**
|
||||
* Default constructor. Attempts to load global properties.
|
||||
@ -572,6 +575,10 @@ public class OpenJPAConfigurationImpl
|
||||
"getValidationFactoryInstance");
|
||||
validationFactory.setDynamic(true);
|
||||
|
||||
dynamicEnhancementAgent = addBoolean("DynamicEnhancementAgent");
|
||||
dynamicEnhancementAgent.setDefault("true");
|
||||
dynamicEnhancementAgent.set(true);
|
||||
|
||||
// initialize supported options that some runtimes may not support
|
||||
supportedOptions.add(OPTION_NONTRANS_READ);
|
||||
supportedOptions.add(OPTION_OPTIMISTIC);
|
||||
@ -1605,4 +1612,10 @@ public class OpenJPAConfigurationImpl
|
||||
public void setValidationFactory(Object factory) {
|
||||
validationFactory.set(factory);
|
||||
}
|
||||
public boolean getDynamicEnhancementAgent() {
|
||||
return dynamicEnhancementAgent.get();
|
||||
}
|
||||
public void setDynamicEnhancementAgent(boolean dynamic) {
|
||||
dynamicEnhancementAgent.set(dynamic);
|
||||
}
|
||||
}
|
||||
|
@ -60,14 +60,15 @@ public class ClassRedefiner {
|
||||
*/
|
||||
public static void redefineClasses(OpenJPAConfiguration conf,
|
||||
final Map<Class,byte[]> classes) {
|
||||
if (classes == null || classes.size() == 0 || !canRedefineClasses())
|
||||
Log log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
|
||||
if (classes == null || classes.size() == 0 || !canRedefineClasses(log))
|
||||
return;
|
||||
|
||||
Log log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
|
||||
Instrumentation inst = null;
|
||||
ClassFileTransformer t = null;
|
||||
try {
|
||||
inst = InstrumentationFactory.getInstrumentation();
|
||||
inst =
|
||||
InstrumentationFactory.getInstrumentation(log);
|
||||
|
||||
Class[] array = classes.keySet().toArray(new Class[classes.size()]);
|
||||
if (JavaVersions.VERSION >= 6) {
|
||||
@ -100,17 +101,7 @@ public class ClassRedefiner {
|
||||
classes.get(array[i]));
|
||||
inst.redefineClasses(defs);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new InternalException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new UserException(e.getCause());
|
||||
} catch (IOException e) {
|
||||
throw new InternalException(e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new InternalException(e);
|
||||
} catch (UnmodifiableClassException e) {
|
||||
} catch (Exception e) {
|
||||
throw new InternalException(e);
|
||||
} finally {
|
||||
if (inst != null && t != null)
|
||||
@ -126,11 +117,11 @@ public class ClassRedefiner {
|
||||
* only checks whether or not an instrumentation is available and
|
||||
* if retransformation is possible.
|
||||
*/
|
||||
public static boolean canRedefineClasses() {
|
||||
public static boolean canRedefineClasses(Log log) {
|
||||
if (_canRedefine == null) {
|
||||
try {
|
||||
Instrumentation inst = InstrumentationFactory
|
||||
.getInstrumentation();
|
||||
.getInstrumentation(log);
|
||||
if (inst == null) {
|
||||
_canRedefine = Boolean.FALSE;
|
||||
} else if (JavaVersions.VERSION == 5) {
|
||||
|
@ -18,12 +18,24 @@
|
||||
*/
|
||||
package org.apache.openjpa.enhance;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.instrument.*;
|
||||
import java.lang.management.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.zip.*;
|
||||
import org.apache.openjpa.lib.util.*;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.lib.util.JavaVersions;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
|
||||
|
||||
/**
|
||||
@ -35,7 +47,17 @@ import org.apache.openjpa.lib.util.*;
|
||||
public class InstrumentationFactory {
|
||||
private static Instrumentation _inst;
|
||||
private static boolean _dynamicallyInstall = true;
|
||||
|
||||
private static final String _name = InstrumentationFactory.class.getName();
|
||||
private static final Localizer _loc = Localizer.forPackage(
|
||||
InstrumentationFactory.class);
|
||||
|
||||
/**
|
||||
* This method is not synchronized because when the agent is loaded from
|
||||
* getInstrumentation() that method will cause agentmain(..) to be called.
|
||||
* Synchronizing this method would cause a deadlock.
|
||||
*
|
||||
* @param inst The instrumentation instance to be used by this factory.
|
||||
*/
|
||||
public static void setInstrumentation(Instrumentation inst) {
|
||||
_inst = inst;
|
||||
}
|
||||
@ -48,9 +70,13 @@ public class InstrumentationFactory {
|
||||
_dynamicallyInstall = val;
|
||||
}
|
||||
|
||||
public static synchronized Instrumentation getInstrumentation()
|
||||
throws IOException, NoSuchMethodException, IllegalAccessException,
|
||||
InvocationTargetException, ClassNotFoundException {
|
||||
/**
|
||||
* @param log OpenJPA log.
|
||||
* @return null if Instrumentation can not be obtained, or if any
|
||||
* Exceptions are encountered.
|
||||
*/
|
||||
public static synchronized Instrumentation
|
||||
getInstrumentation(final Log log) {
|
||||
if (_inst != null || !_dynamicallyInstall)
|
||||
return _inst;
|
||||
|
||||
@ -58,59 +84,30 @@ public class InstrumentationFactory {
|
||||
if (JavaVersions.VERSION < 6)
|
||||
return null;
|
||||
|
||||
String agentPath = getAgentJar();
|
||||
AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
||||
public Object run() {
|
||||
// If we can't find the tools.jar, we can't load the agent.
|
||||
File toolsJar = findToolsJar(log);
|
||||
if (toolsJar == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// first obtain the PID of the currently-running process
|
||||
// ### this relies on the undocumented convention of the RuntimeMXBean's
|
||||
// ### name starting with the PID, but there appears to be no other
|
||||
// ### way to obtain the current process' id, which we need for
|
||||
// ### the attach process
|
||||
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
|
||||
String pid = runtime.getName();
|
||||
if (pid.indexOf("@") != -1)
|
||||
pid = pid.substring(0, pid.indexOf("@"));
|
||||
|
||||
// JDK1.6: now attach to the current VM so we can deploy a new agent
|
||||
// ### this is a Sun JVM specific feature; other JVMs may offer
|
||||
// ### this feature, but in an implementation-dependent way
|
||||
Class vmClass = Class.forName("com.sun.tools.attach.VirtualMachine");
|
||||
Object vm = vmClass.getMethod("attach", new Class[] { String.class }).
|
||||
invoke(null, new String[] { pid });
|
||||
|
||||
// now deploy the actual agent, which will wind up calling agentmain()
|
||||
vm.getClass().getMethod("loadAgent", new Class[] { String.class }).
|
||||
invoke(vm, new Object[] { agentPath });
|
||||
|
||||
if (_inst != null)
|
||||
return _inst;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new jar file for the sole purpose of specifying an
|
||||
* Agent-Class to load into the JVM.
|
||||
*/
|
||||
private static String getAgentJar() throws IOException {
|
||||
File file = File.createTempFile(
|
||||
InstrumentationFactory.class.getName(), ".jar");
|
||||
file.deleteOnExit();
|
||||
|
||||
ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(file));
|
||||
zout.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
|
||||
|
||||
PrintWriter writer = new PrintWriter
|
||||
(new OutputStreamWriter(zout));
|
||||
|
||||
writer.println("Agent-Class: "
|
||||
+ InstrumentationFactory.class.getName());
|
||||
writer.println("Can-Redefine-Classes: true");
|
||||
writer.println("Can-Retransform-Classes: true");
|
||||
|
||||
writer.close();
|
||||
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
Class<?> vmClass = loadVMClass(toolsJar, log);
|
||||
if (vmClass == null) {
|
||||
return null;
|
||||
}
|
||||
String agentPath = getAgentJar(log);
|
||||
if (agentPath == null) {
|
||||
return null;
|
||||
}
|
||||
loadAgent(log, agentPath, vmClass);
|
||||
return null;
|
||||
}// end run()
|
||||
});
|
||||
// If the load(...) agent call was successful, this variable will no
|
||||
// longer be null.
|
||||
return _inst;
|
||||
}//end getInstrumentation()
|
||||
|
||||
/**
|
||||
* The method that is called when a jar is added as an agent at runtime.
|
||||
@ -120,4 +117,191 @@ public class InstrumentationFactory {
|
||||
public static void agentmain(String agentArgs, Instrumentation inst) {
|
||||
InstrumentationFactory.setInstrumentation(inst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new jar file for the sole purpose of specifying an Agent-Class
|
||||
* to load into the JVM.
|
||||
*
|
||||
* @return absolute path to the new jar file.
|
||||
*/
|
||||
private static String createAgentJar() throws IOException {
|
||||
File file =
|
||||
File.createTempFile(InstrumentationFactory.class.getName(), ".jar");
|
||||
file.deleteOnExit();
|
||||
|
||||
ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(file));
|
||||
zout.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
|
||||
|
||||
PrintWriter writer = new PrintWriter(new OutputStreamWriter(zout));
|
||||
|
||||
writer
|
||||
.println("Agent-Class: " + InstrumentationFactory.class.getName());
|
||||
writer.println("Can-Redefine-Classes: true");
|
||||
writer.println("Can-Retransform-Classes: true");
|
||||
|
||||
writer.close();
|
||||
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* This private worker method attempts to find [java_home]/lib/tools.jar.
|
||||
* Note: The tools.jar is a part of the SDK, it is not present in the JRE.
|
||||
*
|
||||
* @return If tools.jar can be found, a File representing tools.jar. <BR>
|
||||
* If tools.jar cannot be found, null.
|
||||
*/
|
||||
private static File findToolsJar(Log log) {
|
||||
String javaHome = System.getProperty("java.home");
|
||||
File javaHomeFile = new File(javaHome);
|
||||
|
||||
// IBM JDK hack -- for some reason when running on the IBM JDK, the JVM
|
||||
// appends /jre to the java.home SystemProperty. Remove the addition to
|
||||
// be consistent with Sun. Note: Not sure if this is something dependent
|
||||
// on my machine. Not really that big of a deal since this isn't
|
||||
// supported on the IBM JDK at this point.
|
||||
File toolsJarFile =
|
||||
new File(javaHomeFile, "lib" + File.separator + "tools.jar");
|
||||
if (toolsJarFile.exists() == false) {
|
||||
// If tools jar file isn't found, we may be on an IBM JDK. If the
|
||||
// java.home property ends in /jre, try removing it to look for the
|
||||
// tools.jar.
|
||||
String absPath = javaHomeFile.getAbsolutePath();
|
||||
if (absPath.endsWith(File.separator + "jre") == true) {
|
||||
javaHomeFile = javaHomeFile.getParentFile();
|
||||
toolsJarFile =
|
||||
new File(javaHomeFile, "lib" + File.separator +
|
||||
"tools.jar");
|
||||
}
|
||||
}
|
||||
|
||||
if (toolsJarFile.exists() == false) {
|
||||
String toolsJarPath = toolsJarFile.getAbsolutePath();
|
||||
if (log.isTraceEnabled() == true) {
|
||||
log.trace(_name + ".findToolsJar() -- couldn't find "
|
||||
+ toolsJarPath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return toolsJarFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* This private worker method will return a fully qualified path to a jar
|
||||
* that has this class defined as an Agent-Class in it's
|
||||
* META-INF/manifest.mf file. Under normal circumstances the path should
|
||||
* point to the OpenJPA jar. If running in a development environment a
|
||||
* temporary jar file will be created.
|
||||
*
|
||||
* @return absolute path to the agent jar.
|
||||
* @throws Exception
|
||||
* if this method is unable to detect where this class was
|
||||
* loaded from. It is unknown if this is actually possible.
|
||||
*/
|
||||
private static String getAgentJar(Log log) {
|
||||
// Find the name of the jar that this class was loaded from. That
|
||||
// jar *should* be the same location as our agent.
|
||||
File agentJarFile =
|
||||
new File(InstrumentationFactory.class.getProtectionDomain()
|
||||
.getCodeSource().getLocation().getFile());
|
||||
// We're deadmeat if we can't find a file that this class
|
||||
// was loaded from. Just return if this file doesn't exist.
|
||||
// Note: I'm not sure if this can really happen.
|
||||
if (agentJarFile.exists() == false) {
|
||||
if (log.isTraceEnabled() == true) {
|
||||
log.trace(_name + ".getAgentJar() -- Couldn't find where this "
|
||||
+ "class was loaded from!");
|
||||
}
|
||||
}
|
||||
String agentJar;
|
||||
if (agentJarFile.isDirectory() == true) {
|
||||
// This will happen when running in eclipse as an OpenJPA
|
||||
// developer. No one else should ever go down this path. We
|
||||
// should log a warning here because this will create a jar
|
||||
// in your temp directory that doesn't always get cleaned up.
|
||||
try {
|
||||
agentJar = createAgentJar();
|
||||
if (log.isInfoEnabled() == true) {
|
||||
log.info(_loc.get("temp-file-creation", agentJar));
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (log.isTraceEnabled() == true) {
|
||||
log.trace(_name + ".getAgentJar() caught unexpected "
|
||||
+ "exception.", ioe);
|
||||
}
|
||||
agentJar = null;
|
||||
}
|
||||
} else {
|
||||
agentJar = agentJarFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
return agentJar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach and load an agent class.
|
||||
*
|
||||
* @param log Log used if the agent cannot be loaded.
|
||||
* @param agentJar absolute path to the agent jar.
|
||||
* @param vmClass VirtualMachine.class from tools.jar.
|
||||
*/
|
||||
private static void loadAgent(Log log, String agentJar, Class<?> vmClass) {
|
||||
try {
|
||||
// first obtain the PID of the currently-running process
|
||||
// ### this relies on the undocumented convention of the
|
||||
// RuntimeMXBean's
|
||||
// ### name starting with the PID, but there appears to be no other
|
||||
// ### way to obtain the current process' id, which we need for
|
||||
// ### the attach process
|
||||
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
|
||||
String pid = runtime.getName();
|
||||
if (pid.indexOf("@") != -1)
|
||||
pid = pid.substring(0, pid.indexOf("@"));
|
||||
|
||||
// JDK1.6: now attach to the current VM so we can deploy a new agent
|
||||
// ### this is a Sun JVM specific feature; other JVMs may offer
|
||||
// ### this feature, but in an implementation-dependent way
|
||||
Object vm =
|
||||
vmClass.getMethod("attach", new Class<?>[] { String.class })
|
||||
.invoke(null, new String[] { pid });
|
||||
|
||||
// now deploy the actual agent, which will wind up calling
|
||||
// agentmain()
|
||||
vmClass.getMethod("loadAgent", new Class[] { String.class })
|
||||
.invoke(vm, new Object[] { agentJar });
|
||||
vmClass.getMethod("detach", new Class[] {}).invoke(vm,
|
||||
new Object[] {});
|
||||
} catch (Throwable t) {
|
||||
if (log.isTraceEnabled() == true) {
|
||||
// Log the message from the exception. Don't log the entire
|
||||
// stack as this is expected when running on a JDK that doesn't
|
||||
// support the Attach API.
|
||||
log.trace(_name + ".loadAgent() caught an exception. Message: "
|
||||
+ t.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This private method will create a new classloader and attempt to load the
|
||||
* com.sun.tools.attach.VirtualMachine class from the provided toolsJar
|
||||
* file.
|
||||
*
|
||||
* @return com.sun.tools.attach.VirtualMachine class <br>
|
||||
* or null if something unexpected happened.
|
||||
*/
|
||||
private static Class<?> loadVMClass(File toolsJar, Log log) {
|
||||
try {
|
||||
URLClassLoader loader =
|
||||
new URLClassLoader(new URL[] { toolsJar.toURI().toURL() },
|
||||
Thread.currentThread().getContextClassLoader());
|
||||
return loader.loadClass("com.sun.tools.attach.VirtualMachine");
|
||||
} catch (Exception e) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(_name
|
||||
+ ".loadVMClass() failed to load the VirtualMachine class");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,21 @@ public class ManagedClassSubclasser {
|
||||
if (!PersistenceCapable.class.isAssignableFrom(cls))
|
||||
unenhanced.add(cls);
|
||||
if (unenhanced.size() > 0) {
|
||||
if (PCEnhancerAgent.getLoadSuccessful() == true) {
|
||||
// This means that the enhancer has been ran but we
|
||||
// have some unenhanced classes. This can happen if an
|
||||
// entity is loaded by the JVM before the EntityManger
|
||||
// was created. Warn the user.
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn(_loc.get("entities-loaded-before-em"));
|
||||
}
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(ManagedClassSubclasser.class.getName()
|
||||
+ ".prepareUnenhancedClasses()"
|
||||
+ " - The following classes are unenhanced "
|
||||
+ unenhanced.toString());
|
||||
}
|
||||
}
|
||||
Message msg = _loc.get("runtime-optimization-disabled",
|
||||
unenhanced);
|
||||
if (conf.getRuntimeUnenhancedClassesConstant()
|
||||
@ -106,7 +121,7 @@ public class ManagedClassSubclasser {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean redefine = ClassRedefiner.canRedefineClasses();
|
||||
boolean redefine = ClassRedefiner.canRedefineClasses(log);
|
||||
if (redefine)
|
||||
log.info(_loc.get("enhance-and-subclass-and-redef-start",
|
||||
classes));
|
||||
|
@ -20,47 +20,109 @@ package org.apache.openjpa.enhance;
|
||||
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.security.AccessController;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||
import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
|
||||
import org.apache.openjpa.lib.conf.Configuration;
|
||||
import org.apache.openjpa.lib.conf.Configurations;
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.lib.util.J2DoPrivHelper;
|
||||
import org.apache.openjpa.lib.util.Options;
|
||||
import org.apache.openjpa.util.ClassResolver;
|
||||
|
||||
/**
|
||||
* <p>Java agent that makes persistent classes work with OpenJPA at runtime.
|
||||
* This is achieved by either running the enhancer on the classes as they
|
||||
* are loaded, or by redefining the classes on the fly.
|
||||
* The agent is launched at JVM startup from the command line:</p>
|
||||
*
|
||||
* <p><code>java -javaagent:openjpa.jar[=<options>]</code>
|
||||
* The options string should be formatted as a OpenJPA plugin, and may
|
||||
* contain any properties understood by the OpenJPA enhancer or any
|
||||
* configuration properties. For example:</p>
|
||||
*
|
||||
* <p><code>java -javaagent:openjpa.jar</code></p>
|
||||
*
|
||||
* <p>By default, if specified, the agent runs the OpenJPA enhancer on
|
||||
* all classes listed in the first persistence unit as they are loaded,
|
||||
* and redefines all other persistent classes when they are encountered.
|
||||
* To disable enhancement at class-load time and rely solely on the
|
||||
* redefinition logic, set the ClassLoadEnhancement flag to false. To
|
||||
* disable redefinition and rely solely on pre-deployment or class-load
|
||||
* enhancement, set the RuntimeRedefinition flag to false.
|
||||
* <p>
|
||||
* Java agent that makes persistent classes work with OpenJPA at runtime. This
|
||||
* is achieved by either running the enhancer on the classes as they are loaded,
|
||||
* or by redefining the classes on the fly. The agent is launched at JVM startup
|
||||
* from the command line:
|
||||
* </p>
|
||||
*
|
||||
* <p><code>java -javaagent:openjpa.jar=ClassLoadEnhancement=false</code></p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* <code>java -javaagent:openjpa.jar[=<options>]</code> The options string
|
||||
* should be formatted as a OpenJPA plugin, and may contain any properties
|
||||
* understood by the OpenJPA enhancer or any configuration properties. For
|
||||
* example:
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <code>java -javaagent:openjpa.jar</code>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* By default, if specified, the agent runs the OpenJPA enhancer on all classes
|
||||
* listed in the first persistence unit as they are loaded, and redefines all
|
||||
* other persistent classes when they are encountered. To disable enhancement at
|
||||
* class-load time and rely solely on the redefinition logic, set the
|
||||
* ClassLoadEnhancement flag to false. To disable redefinition and rely solely
|
||||
* on pre-deployment or class-load enhancement, set the RuntimeRedefinition flag
|
||||
* to false.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <code>java -javaagent:openjpa.jar=ClassLoadEnhancement=false</code>
|
||||
* </p>
|
||||
*
|
||||
* @author Abe White
|
||||
* @author Patrick Linskey
|
||||
*/
|
||||
public class PCEnhancerAgent {
|
||||
|
||||
private static boolean loadAttempted = false;
|
||||
private static boolean loadSuccessful = false;
|
||||
private static boolean disableDynamicAgent = false;
|
||||
|
||||
/**
|
||||
* @return True if the Agent has ran successfully. False otherwise.
|
||||
*/
|
||||
public static synchronized boolean getLoadSuccessful() {
|
||||
return loadSuccessful;
|
||||
}
|
||||
/**
|
||||
* @return True if the dynamic agent was disabled via configuration.
|
||||
*/
|
||||
public static void disableDynamicAgent(){
|
||||
disableDynamicAgent=true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param log
|
||||
* @return True if the agent is loaded successfully
|
||||
*/
|
||||
public static synchronized boolean loadDynamicAgent(Log log) {
|
||||
if (loadAttempted == false && disableDynamicAgent == false) {
|
||||
Instrumentation inst =
|
||||
InstrumentationFactory.getInstrumentation(log);
|
||||
if (inst != null) {
|
||||
premain("", inst);
|
||||
} else {
|
||||
// This needs to be set in this method AND in premain because
|
||||
// there are two different paths that we can attempt to load the
|
||||
// agent. One is when the user specifies a javaagent and the
|
||||
// other is when we try to dynamically enhance.
|
||||
loadAttempted = true;
|
||||
}
|
||||
}
|
||||
|
||||
return getLoadSuccessful();
|
||||
}
|
||||
|
||||
public static void premain(String args, Instrumentation inst) {
|
||||
// If the enhancer has already completed, noop. This can happen
|
||||
// if runtime enhancement is specified via javaagent, and
|
||||
// openJPA tries to dynamically enhance.
|
||||
// The agent will be disabled when running in an application
|
||||
// server.
|
||||
synchronized (PCEnhancerAgent.class) {
|
||||
if (loadAttempted == true) {
|
||||
return;
|
||||
}
|
||||
// See the comment in loadDynamicAgent as to why we set this to true
|
||||
// in multiple places.
|
||||
loadAttempted = true;
|
||||
}
|
||||
|
||||
Options opts = Configurations.parseProperties(args);
|
||||
|
||||
if (opts.containsKey("ClassLoadEnhancement") ||
|
||||
@ -88,6 +150,7 @@ public class PCEnhancerAgent {
|
||||
} else {
|
||||
InstrumentationFactory.setDynamicallyInstallAgent(false);
|
||||
}
|
||||
loadSuccessful = true;
|
||||
}
|
||||
|
||||
private static void registerClassLoadEnhancer(Instrumentation inst,
|
||||
|
@ -206,3 +206,9 @@ most-derived-unrelated: Methods "{0}" and "{1}" are defined in types that do \
|
||||
most-derived-unrelated-same-type: Methods "{0}" and "{1}" are defined in the same \
|
||||
type, but the method return types do not have an interface or superclass \
|
||||
inheritance relationship.
|
||||
entities-loaded-before-em: Unenhanced classes were detected even though the \
|
||||
enhancer has ran. Ensure that the EntityManagerFactory is created prior \
|
||||
to creating any Entities.
|
||||
temp-file-creation: The temporary file "{0}" was created and it may not get \
|
||||
cleaned up properly.
|
||||
|
||||
|
@ -47,6 +47,49 @@
|
||||
</properties>
|
||||
<profiles>
|
||||
|
||||
<!-- Profile for testing with test-dynamic-enhancer -->
|
||||
<profile>
|
||||
<id>test-dynamic-enhancer</id>
|
||||
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<build.enhance>false</build.enhance>
|
||||
<test.jvm.arguments>-Dopenjpa.RuntimeUnenhancedClasses=unsupported -Xmx512m </test.jvm.arguments>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
|
||||
<argLine>${test.jvm.arguments}</argLine>
|
||||
<includes><include>org/apache/openjpa/persistence/enhance/DynamicEnhancementSuite.java</include></includes>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>openjpa.Log</name>
|
||||
<value>DefaultLevel=${openjpa.loglevel}</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>openjpa.ConnectionDriverName</name>
|
||||
<value>org.apache.commons.dbcp.BasicDataSource</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>derby.stream.error.file</name>
|
||||
<value>target/derby.log</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>openjpa.ConnectionProperties</name>
|
||||
<value>DriverClassName=${connection.driver.name},Url=${connection.url},Username=${connection.username},Password=${connection.password},${dbcp.args}</value>
|
||||
</property>
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<!-- Profile for testing with Apache Derby -->
|
||||
<profile>
|
||||
<id>test-derby</id>
|
||||
@ -558,6 +601,7 @@
|
||||
<property name="outdir" value="${project.build.outputDirectory}" />
|
||||
<property name="project.build.testOutputDirectory" value="${project.build.testOutputDirectory}" />
|
||||
<property name="openjpa.loglevel" value="${openjpa.loglevel}" />
|
||||
<property name="build.enhance" value="${build.enhance}" />
|
||||
</ant>
|
||||
</tasks>
|
||||
</configuration>
|
||||
|
@ -31,8 +31,13 @@
|
||||
<istrue value="${maven.test.skip}" />
|
||||
</condition>
|
||||
|
||||
<condition property="test.isfalse">
|
||||
<condition property="skip.enhance">
|
||||
<or>
|
||||
<equals arg1="${test}" arg2="false" />
|
||||
<equals arg1="${build.enhance}" arg2="false" />
|
||||
<istrue value="${maven.test.skip}" />
|
||||
<istrue value="${skipTests}" />
|
||||
</or>
|
||||
</condition>
|
||||
|
||||
<!-- =================================
|
||||
@ -40,7 +45,7 @@
|
||||
================================= -->
|
||||
<target name="enhance"
|
||||
description="--> run the enhancer unless test=false"
|
||||
unless="test.isfalse">
|
||||
unless="skip.enhance">
|
||||
<antcall target="enhance.all.entities"
|
||||
inheritall="true"
|
||||
inheritrefs="true" />
|
||||
@ -51,7 +56,7 @@
|
||||
================================= -->
|
||||
<target name="enhance.all.entities"
|
||||
description="--> enhance the test entities"
|
||||
unless="maven.test.skip.istrue">
|
||||
unless="skip.enhance">
|
||||
<echo> running enhancer</echo>
|
||||
<!--
|
||||
Inherited references won't be present until the task is called.
|
||||
|
@ -26,11 +26,13 @@ import java.io.ByteArrayInputStream;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import java.lang.reflect.Field;
|
||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||
|
||||
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||
import org.apache.openjpa.persistence.OpenJPAEntityManager;
|
||||
import org.apache.openjpa.persistence.JPAFacadeHelper;
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.meta.AccessCode;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.util.ImplHelper;
|
||||
@ -40,6 +42,7 @@ import org.apache.openjpa.event.LifecycleEvent;
|
||||
public abstract class AbstractUnenhancedClassTest
|
||||
extends SingleEMFTestCase {
|
||||
|
||||
Log _log;
|
||||
// ##### To do:
|
||||
// - clearing in pnew property-access without redefinition
|
||||
// - figure out how to auto-test the redefinition code, either in Java 5
|
||||
@ -53,6 +56,7 @@ public abstract class AbstractUnenhancedClassTest
|
||||
setUp(getUnenhancedClass(), getUnenhancedSubclass(), CLEAR_TABLES);
|
||||
// trigger class redefinition
|
||||
emf.createEntityManager().close();
|
||||
_log = emf.getConfiguration().getLog(OpenJPAConfiguration.LOG_ENHANCE);
|
||||
}
|
||||
|
||||
protected abstract Class<? extends UnenhancedType> getUnenhancedClass();
|
||||
@ -72,8 +76,8 @@ public abstract class AbstractUnenhancedClassTest
|
||||
public void testMetaData() {
|
||||
ClassMetaData meta = JPAFacadeHelper.getMetaData(emf,
|
||||
getUnenhancedClass());
|
||||
assertEquals(ClassRedefiner.canRedefineClasses(),
|
||||
meta.isIntercepting());
|
||||
assertEquals(ClassRedefiner.canRedefineClasses(_log), meta
|
||||
.isIntercepting());
|
||||
}
|
||||
|
||||
public void testImplHelperCalls() {
|
||||
@ -308,7 +312,7 @@ public abstract class AbstractUnenhancedClassTest
|
||||
|
||||
// we only expect lazy loading to work when we can redefine classes
|
||||
// or when accessing a property-access record that OpenJPA created.
|
||||
if (ClassRedefiner.canRedefineClasses()
|
||||
if (ClassRedefiner.canRedefineClasses(_log)
|
||||
|| (!userDefined
|
||||
&& AccessCode.isProperty(sm.getMetaData().getAccessType()))) {
|
||||
|
||||
@ -360,7 +364,7 @@ public abstract class AbstractUnenhancedClassTest
|
||||
|
||||
// we only expect lazy loading to work when we can redefine classes
|
||||
// or when accessing a property-access record that OpenJPA created.
|
||||
if (ClassRedefiner.canRedefineClasses()
|
||||
if (ClassRedefiner.canRedefineClasses(_log)
|
||||
|| AccessCode.isProperty(sm.getMetaData().getAccessType())) {
|
||||
assertFalse(sm.getLoaded()
|
||||
.get(sm.getMetaData().getField("lazyField").getIndex()));
|
||||
|
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openjpa.persistence.enhance;
|
||||
|
||||
import javax.persistence.Persistence;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
import org.apache.openjpa.persistence.annotations.TestAnnotationBasics;
|
||||
import org.apache.openjpa.persistence.annotations.TestEmbeddedId;
|
||||
import org.apache.openjpa.persistence.annotations.TestEnumerated;
|
||||
import org.apache.openjpa.persistence.annotations.TestFlatInheritance;
|
||||
import org.apache.openjpa.persistence.annotations.TestGenerators;
|
||||
import org.apache.openjpa.persistence.annotations.TestJoinedInheritance;
|
||||
import org.apache.openjpa.persistence.annotations.TestManyToMany;
|
||||
import org.apache.openjpa.persistence.annotations.TestMapKey;
|
||||
import org.apache.openjpa.persistence.annotations.TestMappedSuperClass;
|
||||
import org.apache.openjpa.persistence.annotations.TestOneToOne;
|
||||
import org.apache.openjpa.persistence.annotations.TestPersistentCollection;
|
||||
import org.apache.openjpa.persistence.annotations.TestSerializedLobs;
|
||||
import org.apache.openjpa.persistence.annotations.TestTablePerClassInheritance;
|
||||
import org.apache.openjpa.persistence.datacache.TestDataCacheBehavesIdentical;
|
||||
import org.apache.openjpa.persistence.datacache.TestQueryResultSize;
|
||||
import org.apache.openjpa.persistence.detach.TestDetachNoCascade;
|
||||
import org.apache.openjpa.persistence.detachment.
|
||||
TestGetReferenceAndImplicitDetachment;
|
||||
import org.apache.openjpa.persistence.enhance.identity.
|
||||
TestMultipleLevelDerivedIdentity;
|
||||
import org.apache.openjpa.persistence.enhance.identity.
|
||||
TestMultipleLevelDerivedIdentity1;
|
||||
import org.apache.openjpa.persistence.identity.TestFloatingPointIds;
|
||||
import org.apache.openjpa.persistence.identity.TestGenerationType;
|
||||
import org.apache.openjpa.persistence.identity.TestSQLBigDecimalId;
|
||||
import org.apache.openjpa.persistence.identity.TestSQLBigIntegerId;
|
||||
import org.apache.openjpa.persistence.identity.TestSQLDateId;
|
||||
import org.apache.openjpa.persistence.jdbc.annotations.TestEJBEmbedded;
|
||||
import org.apache.openjpa.persistence.jdbc.annotations.TestEmbeddableSuperclass;
|
||||
import org.apache.openjpa.persistence.jdbc.annotations.TestOneToMany;
|
||||
import org.apache.openjpa.persistence.jdbc.annotations.TestVersion;
|
||||
import org.apache.openjpa.persistence.jdbc.mapping.TestPrecisionMapping;
|
||||
import org.apache.openjpa.persistence.jdbc.maps.m2mmapex2.TestMany2ManyMapEx2;
|
||||
import org.apache.openjpa.persistence.jdbc.maps.m2mmapex6.TestMany2ManyMapEx6;
|
||||
import org.apache.openjpa.persistence.jpql.clauses.TestEJBClauses;
|
||||
import org.apache.openjpa.persistence.jpql.clauses.TestEJBDeleteUpdateImpl;
|
||||
import org.apache.openjpa.persistence.jpql.expressions.TestEntityTypeExpression;
|
||||
import org.apache.openjpa.persistence.kernel.TestExtents;
|
||||
import org.apache.openjpa.persistence.kernel.TestProxies2;
|
||||
import org.apache.openjpa.persistence.kernel.TestSavepoints;
|
||||
import org.apache.openjpa.persistence.kernel.TestStateManagerImplData;
|
||||
import org.apache.openjpa.persistence.kernel.TestStoreBlob;
|
||||
import org.apache.openjpa.persistence.lockmgr.TestMixedLockManagerLockBasic;
|
||||
import org.apache.openjpa.persistence.lockmgr.
|
||||
TestMixedLockManagerLockPermutation;
|
||||
import org.apache.openjpa.persistence.meta.TestMetamodel;
|
||||
import org.apache.openjpa.persistence.query.TestComplexQueries;
|
||||
import org.apache.openjpa.persistence.query.TestNamedQueries;
|
||||
import org.apache.openjpa.persistence.query.TestQueryResults;
|
||||
import org.apache.openjpa.persistence.relations.
|
||||
TestBulkUpdatesAndEmbeddedFields;
|
||||
import org.apache.openjpa.persistence.relations.
|
||||
TestCascadingOneManyWithForeignKey;
|
||||
import org.apache.openjpa.persistence.relations.TestChainEntities;
|
||||
import org.apache.openjpa.persistence.relations.TestEagerBidiSQL;
|
||||
import org.apache.openjpa.persistence.relations.TestHandlerCollections;
|
||||
import org.apache.openjpa.persistence.relations.TestHandlerToHandlerMaps;
|
||||
import org.apache.openjpa.persistence.relations.TestHandlerToRelationMaps;
|
||||
import org.apache.openjpa.persistence.relations.TestIdOrderedOneMany;
|
||||
import org.apache.openjpa.persistence.relations.TestInverseEagerSQL;
|
||||
import org.apache.openjpa.persistence.relations.TestLRS;
|
||||
import org.apache.openjpa.persistence.relations.TestLazyManyToOne;
|
||||
import org.apache.openjpa.persistence.relations.TestManyEagerSQL;
|
||||
import org.apache.openjpa.persistence.relations.TestManyOneAsId;
|
||||
import org.apache.openjpa.persistence.relations.TestMapCollectionToBlob;
|
||||
import org.apache.openjpa.persistence.relations.
|
||||
TestMultipleSameTypedEmbeddedWithEagerRelations;
|
||||
import org.apache.openjpa.persistence.relations.TestOneOneNulls;
|
||||
import org.apache.openjpa.persistence.relations.
|
||||
TestRelationFieldAsPrimaryKeyAndForeignKey;
|
||||
import org.apache.openjpa.persistence.relations.TestRelationToHandlerMaps;
|
||||
import org.apache.openjpa.persistence.relations.TestRelationToRelationMaps;
|
||||
import org.apache.openjpa.persistence.relations.TestTargetedIFaceRelations;
|
||||
import org.apache.openjpa.persistence.simple.TestBasicAnnotation;
|
||||
import org.apache.openjpa.persistence.simple.TestCaseInsensitiveKeywordsInJPQL;
|
||||
import org.apache.openjpa.persistence.simple.TestEntityManagerClear;
|
||||
import org.apache.openjpa.persistence.simple.TestEntityManagerFactory;
|
||||
import org.apache.openjpa.persistence.simple.TestEntityManagerMerge;
|
||||
import org.apache.openjpa.persistence.simple.
|
||||
TestEntityManagerMethodsThrowAfterClose;
|
||||
import org.apache.openjpa.persistence.simple.TestFlushBeforeDetach;
|
||||
import org.apache.openjpa.persistence.simple.TestJoin;
|
||||
import org.apache.openjpa.persistence.simple.TestPersistence;
|
||||
import org.apache.openjpa.persistence.simple.TestPropertiesMethods;
|
||||
import org.apache.openjpa.persistence.simple.TestRefresh;
|
||||
import org.apache.openjpa.persistence.simple.TestSerializedFactory;
|
||||
import org.apache.openjpa.persistence.simple.
|
||||
TestTableNamesDefaultToEntityNames;
|
||||
import org.apache.openjpa.persistence.spring.TestLibService;
|
||||
import org.apache.openjpa.persistence.xml.TestSimpleXmlEntity;
|
||||
import org.apache.openjpa.persistence.xml.TestXmlOverrideEntity;
|
||||
|
||||
public class DynamicEnhancementSuite extends TestCase {
|
||||
static {
|
||||
Persistence.createEntityManagerFactory("test", System.getProperties());
|
||||
}
|
||||
|
||||
public static Test suite() throws Exception {
|
||||
TestSuite suite = new TestSuite();
|
||||
|
||||
// Setting the property -DdynamicTest allows you to run a single test
|
||||
// with the dynamic enhaner.
|
||||
String test = System.getProperty("dynamicTest");
|
||||
if (test != null) {
|
||||
suite.addTestSuite(Class.forName(test));
|
||||
} else {
|
||||
|
||||
// Subclassing failing tests
|
||||
suite.addTestSuite(TestComplexQueries.class);
|
||||
suite.addTestSuite(TestNamedQueries.class);
|
||||
suite.addTestSuite(TestQueryResults.class);
|
||||
suite.addTestSuite(TestMetamodel.class);
|
||||
suite.addTestSuite(TestLibService.class);
|
||||
suite.addTestSuite(TestOneOneNulls.class);
|
||||
suite.addTestSuite(TestProxies2.class);
|
||||
suite.addTestSuite(TestStoreBlob.class);
|
||||
suite.addTestSuite(TestEntityTypeExpression.class);
|
||||
suite.addTestSuite(TestSimpleXmlEntity.class);
|
||||
suite.addTestSuite(TestDataCacheBehavesIdentical.class);
|
||||
suite.addTestSuite(TestQueryResultSize.class);
|
||||
suite.addTestSuite(TestEJBClauses.class);
|
||||
suite.addTestSuite(TestEJBDeleteUpdateImpl.class);
|
||||
suite.addTestSuite(TestOneToMany.class);
|
||||
suite.addTestSuite(TestOneToOne.class);
|
||||
suite.addTestSuite(TestGetReferenceAndImplicitDetachment.class);
|
||||
suite.addTestSuite(TestMultipleLevelDerivedIdentity.class);
|
||||
suite.addTestSuite(TestMultipleLevelDerivedIdentity1.class);
|
||||
suite.addTestSuite(TestEJBEmbedded.class);
|
||||
suite.addTestSuite(TestEmbeddableSuperclass.class);
|
||||
suite.addTestSuite(TestFlatInheritance.class);
|
||||
suite.addTestSuite(TestVersion.class);
|
||||
suite.addTestSuite(TestMany2ManyMapEx2.class);
|
||||
suite.addTestSuite(TestMany2ManyMapEx6.class);
|
||||
suite.addTestSuite(TestOneOneNulls.class);
|
||||
suite.addTestSuite(TestTargetedIFaceRelations.class);
|
||||
suite.addTestSuite(TestExtents.class);
|
||||
suite.addTestSuite(TestProxies2.class);
|
||||
suite.addTestSuite(TestSavepoints.class);
|
||||
suite.addTestSuite(TestStateManagerImplData.class);
|
||||
suite.addTestSuite(TestStoreBlob.class);
|
||||
suite.addTestSuite(TestEntityTypeExpression.class);
|
||||
suite.addTestSuite(TestSimpleXmlEntity.class);
|
||||
suite.addTestSuite(TestXmlOverrideEntity.class);
|
||||
suite.addTestSuite(TestDataCacheBehavesIdentical.class);
|
||||
suite.addTestSuite(TestQueryResultSize.class);
|
||||
suite.addTestSuite(TestQueryResultSize.class);
|
||||
suite.addTestSuite(TestDetachNoCascade.class);
|
||||
suite.addTestSuite(TestMixedLockManagerLockBasic.class);
|
||||
suite.addTestSuite(TestMixedLockManagerLockPermutation.class);
|
||||
// end Subclassing failing tests
|
||||
|
||||
// org.apache.openjpa.persistence.enhance
|
||||
suite.addTestSuite(TestMultipleLevelDerivedIdentity.class);
|
||||
suite.addTestSuite(TestClone.class);
|
||||
|
||||
// excluded via pom
|
||||
// suite.addTestSuite(TestDynamicStorageGenerator.class);
|
||||
// suite.addTestSuite(TestNoNoArgs.class);
|
||||
// suite.addTestSuite(TestSubclassedBehavior.class);
|
||||
|
||||
// org.apache.openjpa.persistence.relations
|
||||
suite.addTestSuite(TestBulkUpdatesAndEmbeddedFields.class);
|
||||
suite.addTestSuite(TestCascadingOneManyWithForeignKey.class);
|
||||
suite.addTestSuite(TestChainEntities.class);
|
||||
suite.addTestSuite(TestEagerBidiSQL.class);
|
||||
suite.addTestSuite(TestHandlerCollections.class);
|
||||
suite.addTestSuite(TestHandlerToHandlerMaps.class);
|
||||
suite.addTestSuite(TestHandlerToRelationMaps.class);
|
||||
suite.addTestSuite(TestIdOrderedOneMany.class);
|
||||
suite.addTestSuite(TestInverseEagerSQL.class);
|
||||
suite.addTestSuite(TestLazyManyToOne.class);
|
||||
suite.addTestSuite(TestLRS.class);
|
||||
suite.addTestSuite(TestManyEagerSQL.class);
|
||||
suite.addTestSuite(TestManyOneAsId.class);
|
||||
suite.addTestSuite(TestMapCollectionToBlob.class);
|
||||
suite.addTestSuite(
|
||||
TestMultipleSameTypedEmbeddedWithEagerRelations.class);
|
||||
suite.addTestSuite(TestOneOneNulls.class);
|
||||
suite
|
||||
.addTestSuite(TestRelationFieldAsPrimaryKeyAndForeignKey.class);
|
||||
suite.addTestSuite(TestRelationToHandlerMaps.class);
|
||||
suite.addTestSuite(TestRelationToRelationMaps.class);
|
||||
suite.addTestSuite(TestTargetedIFaceRelations.class);
|
||||
// org.apache.openjpa.persistence.simple
|
||||
suite.addTestSuite(TestBasicAnnotation.class);
|
||||
suite.addTestSuite(TestCaseInsensitiveKeywordsInJPQL.class);
|
||||
suite.addTestSuite(TestEntityManagerClear.class);
|
||||
suite.addTestSuite(TestEntityManagerFactory.class);
|
||||
suite.addTestSuite(TestEntityManagerMerge.class);
|
||||
suite.addTestSuite(TestEntityManagerMethodsThrowAfterClose.class);
|
||||
suite.addTestSuite(TestFlushBeforeDetach.class);
|
||||
suite.addTestSuite(TestJoin.class);
|
||||
// TODO -- figure out why this test fails.
|
||||
// suite.addTestSuite(TestMissingMetaData.class);
|
||||
suite.addTestSuite(TestPersistence.class);
|
||||
suite.addTestSuite(TestPropertiesMethods.class);
|
||||
suite.addTestSuite(TestRefresh.class);
|
||||
suite.addTestSuite(TestSerializedFactory.class);
|
||||
suite.addTestSuite(TestTableNamesDefaultToEntityNames.class);
|
||||
|
||||
// org.apache.openjpa.persistence.jdbc.mapping
|
||||
// excluded via pom
|
||||
// suite.addTestSuite(TestCompositeIdTraversalInSQLMapping.class);
|
||||
// suite.addTestSuite(TestNativeQueries.class);
|
||||
suite.addTestSuite(TestPrecisionMapping.class);
|
||||
|
||||
// org.apache.openjpa.persistence.identity
|
||||
suite.addTestSuite(TestFloatingPointIds.class);
|
||||
suite.addTestSuite(TestGenerationType.class);
|
||||
suite.addTestSuite(TestSQLBigDecimalId.class);
|
||||
suite.addTestSuite(TestSQLBigIntegerId.class);
|
||||
suite.addTestSuite(TestSQLDateId.class);
|
||||
|
||||
// org.apache.openjpa.persistence.annotations
|
||||
suite.addTestSuite(TestAnnotationBasics.class);
|
||||
suite.addTestSuite(TestEmbeddableSuperclass.class);
|
||||
suite.addTestSuite(TestEmbeddedId.class);
|
||||
suite.addTestSuite(TestEnumerated.class);
|
||||
suite.addTestSuite(TestFlatInheritance.class);
|
||||
suite.addTestSuite(TestGenerators.class);
|
||||
suite.addTestSuite(TestJoinedInheritance.class);
|
||||
suite.addTestSuite(TestManyToMany.class);
|
||||
suite.addTestSuite(TestMapKey.class);
|
||||
suite.addTestSuite(TestMappedSuperClass.class);
|
||||
suite.addTestSuite(TestOneToMany.class);
|
||||
suite.addTestSuite(TestOneToOne.class);
|
||||
suite.addTestSuite(TestPersistentCollection.class);
|
||||
suite.addTestSuite(TestSerializedLobs.class);
|
||||
suite.addTestSuite(TestTablePerClassInheritance.class);
|
||||
|
||||
// excluded via pom
|
||||
// suite.addTestSuite(TestPropertyAccess.class);
|
||||
// suite.addTestSuite(TestVersion.class);
|
||||
// suite.addTestSuite(TestAdvAnnot.class);
|
||||
// suite.addTestSuite(TestDDCallbackMethods.class);
|
||||
// suite.addTestSuite(TestEJBEmbedded.class);
|
||||
// suite.addTestSuite(TestEntityListenerAnnot.class);
|
||||
// suite.addTestSuite(TestEntityOrderBy.class);
|
||||
}
|
||||
return suite;
|
||||
}
|
||||
}
|
@ -21,8 +21,10 @@ package org.apache.openjpa.persistence.jdbc.annotations;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||
import org.apache.openjpa.enhance.ClassRedefiner;
|
||||
import org.apache.openjpa.enhance.PersistenceCapable;
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||
|
||||
/**
|
||||
@ -31,15 +33,16 @@ import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||
* @author Steve Kim
|
||||
*/
|
||||
public class TestGenerators extends SingleEMFTestCase {
|
||||
|
||||
Log _log;
|
||||
public void setUp()
|
||||
throws Exception {
|
||||
setUp(Generator.class, CLEAR_TABLES);
|
||||
_log = emf.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
|
||||
}
|
||||
|
||||
public void testGet() {
|
||||
if (!PersistenceCapable.class.isAssignableFrom(Generator.class)
|
||||
&& !ClassRedefiner.canRedefineClasses())
|
||||
&& !ClassRedefiner.canRedefineClasses(_log))
|
||||
fail("This test requires a higher level of enhancement than"
|
||||
+ " is available in the current environment.");
|
||||
|
||||
|
@ -32,12 +32,12 @@ import org.apache.openjpa.conf.BrokerValue;
|
||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||
import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
|
||||
import org.apache.openjpa.enhance.PCClassFileTransformer;
|
||||
import org.apache.openjpa.enhance.PCEnhancerAgent;
|
||||
import org.apache.openjpa.kernel.Bootstrap;
|
||||
import org.apache.openjpa.kernel.BrokerFactory;
|
||||
import org.apache.openjpa.lib.conf.Configuration;
|
||||
import org.apache.openjpa.lib.conf.ConfigurationProvider;
|
||||
import org.apache.openjpa.lib.conf.Configurations;
|
||||
import org.apache.openjpa.lib.conf.ProductDerivations;
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
import org.apache.openjpa.meta.MetaDataModes;
|
||||
@ -61,6 +61,7 @@ public class PersistenceProviderImpl
|
||||
private static final Localizer _loc = Localizer.forPackage(
|
||||
PersistenceProviderImpl.class);
|
||||
|
||||
private static final String _name = PersistenceProviderImpl.class.getName();
|
||||
private Log _log;
|
||||
/**
|
||||
* Loads the entity manager specified by <code>name</code>, applying
|
||||
@ -82,11 +83,12 @@ public class PersistenceProviderImpl
|
||||
return null;
|
||||
|
||||
BrokerFactory factory = getBrokerFactory(cp, poolValue, null);
|
||||
_log = factory.getConfiguration()
|
||||
.getLog(OpenJPAConfiguration.LOG_RUNTIME);
|
||||
OpenJPAConfiguration conf = factory.getConfiguration();
|
||||
_log = conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
|
||||
if(pd.checkPuNameCollisions(_log,name)==true){
|
||||
;//return null;
|
||||
}
|
||||
loadAgent(_log, conf);
|
||||
return JPAFacadeHelper.toEntityManagerFactory(factory);
|
||||
} catch (Exception e) {
|
||||
throw PersistenceExceptions.toPersistenceException(e);
|
||||
@ -233,4 +235,15 @@ public class PersistenceProviderImpl
|
||||
throw new UnsupportedOperationException(
|
||||
"JPA 2.0 - Method not yet implemented");
|
||||
}
|
||||
/**
|
||||
* This private worker method will attempt load the PCEnhancerAgent.
|
||||
*/
|
||||
private void loadAgent(Log log, OpenJPAConfiguration conf) {
|
||||
if (conf.getDynamicEnhancementAgent() == true) {
|
||||
boolean res = PCEnhancerAgent.loadDynamicAgent(log);
|
||||
if(_log.isInfoEnabled() && res == true ){
|
||||
_log.info(_loc.get("dynamic-agent"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,16 +180,19 @@ unwrap-query-invalid: Query can not be unwrapped to an instance of "{0}".
|
||||
invalid_entity_argument: Object being locked must be an valid and not detached \
|
||||
entity.
|
||||
dup-pu: The persistence unit "{0}" was found multiple times in the following \
|
||||
resources "{1}", but persistence unit names should be unique. The first \
|
||||
persistence unit matching the provided name in "{2}" is being used.
|
||||
resources "{1}", but persistence unit names should be unique. The first \
|
||||
persistence unit matching the provided name in "{2}" is being used.
|
||||
bad-lock-level: Invalid lock mode/level. Valid values are \
|
||||
"none"(0), "read"(10), "optimistic"(15), "write"(20), \
|
||||
"optimistic-force-increment"(25), \
|
||||
"pessimistic-read"(30), "pessimistic-write"(40) or \
|
||||
"pessimistic-force-increment"(50). Specified value: {0}.
|
||||
access-invalid: "{0}" is not a valid access style. Valid access styles are \
|
||||
"PROPERTY" and "FIELD".
|
||||
"PROPERTY" and "FIELD".
|
||||
getter-unmatched: Getter method "{0}" has no matching setter method.
|
||||
invalid-orderBy: This is not a valid OrderBy annotation. The property or \
|
||||
field_name must be specified in the orederBy item of the orderBy list \
|
||||
for "{0}".
|
||||
for "{0}".
|
||||
dynamic-agent: OpenJPA dynamically loaded the class enhancer. Any classes \
|
||||
that were not enhanced at build time will be enhanced when the are \
|
||||
loaded by the JVM
|
||||
|
@ -2958,6 +2958,36 @@ serious problems.
|
||||
<xref linkend="ref_guide_pc_enhance_unenhanced_types"/>
|
||||
</para>
|
||||
</section>
|
||||
<section id="openjpa.DynamicEnhancementAgent">
|
||||
<title>openjpa.DynamicEnhancementAgent</title>
|
||||
<para>
|
||||
<emphasis role="bold">Property name: </emphasis>
|
||||
<literal>openjpa.DynamicEnhancementAgent</literal>
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Configuration API: </emphasis>
|
||||
<ulink url="../javadoc/org/apache/openjpa/conf/OpenJPAConfiguration.html#getDynamicEnhancementAgent()">org.apache.openjpa.conf.OpenJPAConfiguration.getDynamicEnhancementAgent</ulink>
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Resource adaptor config property:</emphasis>
|
||||
DynamicEnhancementAgent
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Default: </emphasis>
|
||||
<literal>true</literal>
|
||||
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Description:</emphasis>
|
||||
The DynamicEnhancementAgent property controls whether or not
|
||||
OpenJPA will attempt to dynamically load the PCEnhancer
|
||||
javaagent.
|
||||
</para>
|
||||
<para>
|
||||
See the reference guide for more information
|
||||
<xref linkend="ref_guide_pc_enhance_dynamic"/>
|
||||
</para>
|
||||
</section>
|
||||
<section id="openjpa.SavepointManager">
|
||||
<title>
|
||||
openjpa.SavepointManager
|
||||
|
@ -380,6 +380,55 @@ java -javaagent:/home/dev/openjpa/lib/openjpa.jar=addDefaultConstructor=false co
|
||||
</programlisting>
|
||||
</example>
|
||||
</section>
|
||||
<section id="ref_guide_pc_enhance_dynamic">
|
||||
<title>
|
||||
Enhancing Dynamically at Runtime
|
||||
</title>
|
||||
<para>
|
||||
If a javaagent is not provided via the command line and
|
||||
OpenJPA is running on the Sun 1.6 SDK (not the JRE), OpenJPA
|
||||
will attempt to dynamically load the Enhancer that was
|
||||
mentioned in the previous section. This support is
|
||||
provided as an ease of use feature and it is not recommended
|
||||
for use in a production system. Using this method of
|
||||
enhancement has the following caveats:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
As stated previously, this is only supported on
|
||||
the Sun 1.6 SDK.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The dynamic runtime enhancer is plugged into
|
||||
the JVM during creation of the
|
||||
EntityManagerFactory. Any Entity classes that
|
||||
are loaded before the EntityManagerFactory is
|
||||
created will not be enhanced.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The command line javaagent settings are not
|
||||
configurable when using this method of
|
||||
enhancement.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
When then dynamic enhancer is loaded, the following
|
||||
informational message is logged:
|
||||
<programlisting>
|
||||
[java] jpa.enhancement INFO [main] openjpa.Runtime - OpenJPA dynamically loaded the class enhancer. Any classes that were not enhanced at build time will be enhanced as they are loaded by the JVM.
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Setting the property openjpa.DynamicEnhancementAgent to false
|
||||
will disable this function.
|
||||
</para>
|
||||
</section>
|
||||
<section id="ref_guide_pc_enhance_unenhanced_types">
|
||||
<title>
|
||||
Omitting the OpenJPA enhancer
|
||||
|
@ -87,7 +87,7 @@
|
||||
</plugin>
|
||||
|
||||
<!--
|
||||
create enhancer pre-main attribute
|
||||
create enhancer pre-main and agent-main attributes
|
||||
-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@ -103,6 +103,11 @@
|
||||
org.apache.openjpa.enhance.PCEnhancerAgent
|
||||
</Premain-Class>
|
||||
<Can-Redefine-Classes>true</Can-Redefine-Classes>
|
||||
<Agent-Class>
|
||||
org.apache.openjpa.enhance.InstrumentationFactory
|
||||
</Agent-Class>
|
||||
<Can-Redefine-Classes>true</Can-Redefine-Classes>
|
||||
<Can-Retransform-Classes>true</Can-Retransform-Classes>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
|
25
pom.xml
25
pom.xml
@ -403,6 +403,31 @@
|
||||
<surefire.jvm.args>-Djava.security.manager -Djava.security.policy=${policy.file} ${test.env}</surefire.jvm.args>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>test-dynamic-enhancer</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<test.env>-Dtest.basedir=${basedir}/..</test.env>
|
||||
<policy.file>${basedir}/../openjpa-persistence-jdbc/src/test/resources/j2.security.test.policy</policy.file>
|
||||
<surefire.jvm.args>-Djava.security.manager -Djava.security.policy=${policy.file} ${test.env}</surefire.jvm.args>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>${test.jvm.arguments}</argLine>
|
||||
<includes>
|
||||
<include></include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user