You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by mi...@apache.org on 2009/05/29 22:06:33 UTC

svn commit: r780086 - in /openjpa/trunk: ./ openjpa-kernel/src/main/java/org/apache/openjpa/conf/ openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/ openjpa-persistence-jdbc/ openjpa-p...

Author: mikedd
Date: Fri May 29 20:06:29 2009
New Revision: 780086

URL: http://svn.apache.org/viewvc?rev=780086&view=rev
Log:
OPENJPA-952. Committing patch from Rick Curtis

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/DynamicEnhancementSuite.java   (with props)
Modified:
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ClassRedefiner.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/InstrumentationFactory.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerAgent.java
    openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/localizer.properties
    openjpa/trunk/openjpa-persistence-jdbc/pom.xml
    openjpa/trunk/openjpa-persistence-jdbc/src/main/ant/enhancer.xml
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestGenerators.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java
    openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
    openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml
    openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_pc.xml
    openjpa/trunk/openjpa/pom.xml
    openjpa/trunk/pom.xml

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java Fri May 29 20:06:29 2009
@@ -1557,7 +1557,15 @@
      * @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.

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java Fri May 29 20:06:29 2009
@@ -28,6 +28,7 @@
 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 @@
         new StoreFacadeTypeRegistry();
     private BrokerFactoryEventManager _brokerFactoryEventManager =
         new BrokerFactoryEventManager(this);
+        
+    public BooleanValue dynamicEnhancementAgent;
 
     /**
      * Default constructor. Attempts to load global properties.
@@ -572,6 +575,10 @@
             "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 void setValidationFactory(Object factory) {
         validationFactory.set(factory);                            
     }
+    public boolean getDynamicEnhancementAgent() {
+        return dynamicEnhancementAgent.get();
+    }
+    public void setDynamicEnhancementAgent(boolean dynamic) {
+        dynamicEnhancementAgent.set(dynamic);
+    }    
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ClassRedefiner.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ClassRedefiner.java?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ClassRedefiner.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ClassRedefiner.java Fri May 29 20:06:29 2009
@@ -60,14 +60,15 @@
      */
     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 @@
                         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 @@
      * 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) {

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/InstrumentationFactory.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/InstrumentationFactory.java?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/InstrumentationFactory.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/InstrumentationFactory.java Fri May 29 20:06:29 2009
@@ -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 @@
 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 @@
         _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,52 +84,58 @@
         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;
+                }
+
+                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()
 
-        // 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;
+    /**
+     *  The method that is called when a jar is added as an agent at runtime.
+     *  All this method does is store the {@link Instrumentation} for
+     *  later use.
+     */
+    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.
-     */
-    private static String getAgentJar() throws IOException {
-        File file = File.createTempFile(
-            InstrumentationFactory.class.getName(), ".jar");
+    /**
+     * 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));
+        PrintWriter writer = new PrintWriter(new OutputStreamWriter(zout));
 
-        writer.println("Agent-Class: "
-            + InstrumentationFactory.class.getName());
+        writer
+            .println("Agent-Class: " + InstrumentationFactory.class.getName());
         writer.println("Can-Redefine-Classes: true");
         writer.println("Can-Retransform-Classes: true");
 
@@ -113,11 +145,163 @@
     }
 
     /**
-     *  The method that is called when a jar is added as an agent at runtime.
-     *  All this method does is store the {@link Instrumentation} for
-     *  later use.
+     * 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.
      */
-    public static void agentmain(String agentArgs, Instrumentation inst) {
-        InstrumentationFactory.setInstrumentation(inst);
+    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;
     }
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java Fri May 29 20:06:29 2009
@@ -95,6 +95,21 @@
                 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 @@
             return null;
         }
 
-        boolean redefine = ClassRedefiner.canRedefineClasses();
+        boolean redefine = ClassRedefiner.canRedefineClasses(log);
         if (redefine)
             log.info(_loc.get("enhance-and-subclass-and-redef-start",
                 classes));

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerAgent.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerAgent.java?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerAgent.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerAgent.java Fri May 29 20:06:29 2009
@@ -20,47 +20,109 @@
 
 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[=&lt;options&gt;]</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[=&lt;options&gt;]</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 @@
         } else {
             InstrumentationFactory.setDynamicallyInstallAgent(false);
         }
+        loadSuccessful = true;
     }
 
     private static void registerClassLoadEnhancer(Instrumentation inst,

Modified: openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/localizer.properties?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/localizer.properties (original)
+++ openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/localizer.properties Fri May 29 20:06:29 2009
@@ -206,3 +206,9 @@
 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.
+    

Modified: openjpa/trunk/openjpa-persistence-jdbc/pom.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/pom.xml?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/pom.xml (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/pom.xml Fri May 29 20:06:29 2009
@@ -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>

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/ant/enhancer.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/ant/enhancer.xml?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/ant/enhancer.xml (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/ant/enhancer.xml Fri May 29 20:06:29 2009
@@ -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. 

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java Fri May 29 20:06:29 2009
@@ -26,11 +26,13 @@
 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 @@
 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 @@
         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 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 @@
 
         // 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 @@
 
         // 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()));

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/DynamicEnhancementSuite.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/DynamicEnhancementSuite.java?rev=780086&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/DynamicEnhancementSuite.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/DynamicEnhancementSuite.java Fri May 29 20:06:29 2009
@@ -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;
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/DynamicEnhancementSuite.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestGenerators.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestGenerators.java?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestGenerators.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestGenerators.java Fri May 29 20:06:29 2009
@@ -21,8 +21,10 @@
 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 @@
  * @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.");
 

Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java (original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java Fri May 29 20:06:29 2009
@@ -32,12 +32,12 @@
 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 @@
     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 @@
                 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 @@
         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"));
+            }
+        }
+    }
 }

Modified: openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties (original)
+++ openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties Fri May 29 20:06:29 2009
@@ -180,16 +180,19 @@
 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}".	
\ No newline at end of file
+    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

Modified: openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml (original)
+++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml Fri May 29 20:06:29 2009
@@ -2958,6 +2958,36 @@
                 <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

Modified: openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_pc.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_pc.xml?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_pc.xml (original)
+++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_pc.xml Fri May 29 20:06:29 2009
@@ -380,6 +380,55 @@
 </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

Modified: openjpa/trunk/openjpa/pom.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa/pom.xml?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/openjpa/pom.xml (original)
+++ openjpa/trunk/openjpa/pom.xml Fri May 29 20:06:29 2009
@@ -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>

Modified: openjpa/trunk/pom.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/pom.xml?rev=780086&r1=780085&r2=780086&view=diff
==============================================================================
--- openjpa/trunk/pom.xml (original)
+++ openjpa/trunk/pom.xml Fri May 29 20:06:29 2009
@@ -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>