You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by dw...@apache.org on 2010/04/01 04:24:32 UTC

svn commit: r929784 - in /openjpa/branches/1.3.x: openjpa-kernel/src/main/java/org/apache/openjpa/conf/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-kernel/src/main/java/org/apache/openjpa/util/ openjpa-persistence-jdbc/src/test/java...

Author: dwoods
Date: Thu Apr  1 02:24:32 2010
New Revision: 929784

URL: http://svn.apache.org/viewvc?rev=929784&view=rev
Log:
OPENJPA-1597 Add Compatibility option to use new 2.0 behavior of removing proxy classes unless DetachedStateField=true, but default is still the old behavior.

Modified:
    openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java
    openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java
    openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java
    openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
    openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestDetachMerge.java

Modified: openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java?rev=929784&r1=929783&r2=929784&view=diff
==============================================================================
--- openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java (original)
+++ openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java Thu Apr  1 02:24:32 2010
@@ -57,6 +57,7 @@ public class Compatibility {
     private boolean _storeMapCollectionInEntityAsBlob = false;
     private boolean _flushBeforeDetach = true; 
     private boolean _reloadOnDetach = true;
+    private boolean _ignoreDetachedStateFieldForProxySerialization = true;
 
     /**
      * Whether to require exact identity value types when creating object
@@ -255,6 +256,39 @@ public class Compatibility {
     }
 
     /**
+     * Whether OpenJPA should ignore the DetachedStateField value when
+     * determining if our Proxy classes should be removed during serialization.
+     * <P>Now, when the DetachedStateFiled==true, the
+     * build time $proxy classes will not be removed.
+     * <P>Prior behavior, was that the DetachedStateFiled was not used and
+     * the $proxy classes were not being removed during serialization after
+     * the Persistence context was cleared.
+     * 
+     * @param ignoreDSF if true the old Proxy serialization behavior will be used.
+     * 
+     * @since 1.3.0
+     */
+    public void setIgnoreDetachedStateFieldForProxySerialization(boolean ignoreDSF) {
+        _ignoreDetachedStateFieldForProxySerialization = ignoreDSF;
+    }
+    
+    /**
+     * Whether OpenJPA should ignore the DetachedStateField value when
+     * determining if our Proxy classes should be removed during serialization.
+     * <P>Now, when the DetachedStateFiled==true, the
+     * build time $proxy classes will not be removed.
+     * <P>Prior behavior, was that the DetachedStateFiled was not used and
+     * the $proxy classes were not being removed during serialization after
+     * the Persistence context was cleared.
+     * 
+     * @since 1.3.0
+     * @return true (default) if the old Proxy serialization will be used, otherwise false.
+     */
+    public boolean getIgnoreDetachedStateFieldForProxySerialization() {
+        return _ignoreDetachedStateFieldForProxySerialization;
+    }
+
+    /**
      * Whether OpenJPA should flush changes before detaching or serializing an
      * entity. In JPA this is usually false, but other persistence frameworks
      * (ie JDO) may expect it to be true.

Modified: openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java?rev=929784&r1=929783&r2=929784&view=diff
==============================================================================
--- openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java (original)
+++ openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java Thu Apr  1 02:24:32 2010
@@ -25,6 +25,7 @@ import java.util.BitSet;
 import java.util.Collection;
 import java.util.Map;
 
+import org.apache.openjpa.conf.Compatibility;
 import org.apache.openjpa.enhance.PersistenceCapable;
 import org.apache.openjpa.enhance.StateManager;
 import org.apache.openjpa.lib.util.Localizer;
@@ -62,6 +63,7 @@ public class DetachedStateManager
     private final Object _oid;
     private final Object _version;
     private final ReentrantLock _lock;
+    private final boolean _useDSFForUnproxy;   // old releases will default to FALSE, which is the old behavior
 
     /**
      * Constructor.
@@ -89,6 +91,14 @@ public class DetachedStateManager
             _lock = new ReentrantLock();
         else
             _lock = null;
+        if (sm.getContext() != null && sm.getContext().getConfiguration() != null) {
+            Compatibility compat = sm.getContext().getConfiguration().getCompatibilityInstance();
+            if (compat != null && !compat.getIgnoreDetachedStateFieldForProxySerialization())
+                _useDSFForUnproxy = true;      // new behavior
+            else
+                _useDSFForUnproxy = false;
+        } else
+            _useDSFForUnproxy = false;
     }
 
     /////////////////////////////////
@@ -731,6 +741,15 @@ public class DetachedStateManager
         return _dirty;
     }
 
+    /**
+     * Should DetachedStateField be used by Proxies to determine when to remove
+     * $proxy wrappers during serialization.
+     * @since 1.3.0
+     */
+    public boolean getUseDSFForUnproxy() {
+        return _useDSFForUnproxy;
+    }
+
     public BitSet getFlushed() {
         throw new UnsupportedOperationException();
     }

Modified: openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java?rev=929784&r1=929783&r2=929784&view=diff
==============================================================================
--- openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java (original)
+++ openjpa/branches/1.3.x/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java Thu Apr  1 02:24:32 2010
@@ -20,9 +20,12 @@ package org.apache.openjpa.util;
 
 import java.security.AccessController;
 
+import org.apache.openjpa.conf.Compatibility;
+import org.apache.openjpa.kernel.DetachedStateManager;
 import org.apache.openjpa.kernel.OpenJPAStateManager;
 import org.apache.openjpa.lib.util.J2DoPrivHelper;
 import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.ClassMetaData;
 
 /**
  * Utility methods for managing proxies.
@@ -80,10 +83,87 @@ public class Proxies {
      * Used by proxy types to serialize non-proxy versions.
      */
     public static Object writeReplace(Proxy proxy, boolean detachable) {
-        if (detachable && (proxy == null || proxy.getOwner() == null 
-            || proxy.getOwner().isDetached()))
+        /* OPENJPA-1097 Remove $proxy classes during serialization based on:
+         *   1) No Proxy, then return as-is
+         *   2) Runtime created proxy (!detachable), then unproxy
+         *   3) No StateManager (DetachedStateField==false), then return as-is
+         *   Get the new IgnoreDetachedStateFieldForProxySerialization
+         *      Compatibility flag from either the metadata/configuration if
+         *      this is a normal StateManager, otherwise use the new flag
+         *      added to the DetachedStateManager
+         *   4) If new 2.0 behavior
+         *      4a) If ClassMetaData exists and DetachedStateField == TRUE
+         *          then do not remove the proxy and return as-is
+         *      4b) Else, using DetachedStateField of transient(default) or
+         *          false, so unproxy 
+         *   5) If 1.0 app or requested old 1.0 behavior
+         *      5a) If detached, then do not unproxy and return as-is
+         *      5b) Else, unproxy
+         * 
+         * Original code -
+         *   1) Runtime created proxy (!detachable), then unproxy
+         *   2) No Proxy, then return as-is
+         *   3) No StateManager (DetachedStateField==false), then return as-is
+         *   4) If detached, then return as-is <--- ERROR as EM.clear() marks
+         *      entity as detached but doesn't remove any $proxy usage
+         *   5) Else, unproxy
+         * 
+         *  if (detachable && (proxy == null || proxy.getOwner() == null 
+         *      || proxy.getOwner().isDetached()))
+         *      return proxy;
+         *
+         */
+        if (proxy == null) {
             return proxy;
+        } else if (!detachable) {
+            // OPENJPA-1571 - using our runtime generated proxies, so remove any $proxy
             return proxy.copy(proxy);
+        } else if (proxy.getOwner() == null) {
+            // no StateManager (DetachedStateField==false), so no $proxy to remove
+            return proxy;
+        } else {
+            // using a StateManager, so determine what DetachedState is being used
+            OpenJPAStateManager sm = proxy.getOwner();  // null checked for above
+            ClassMetaData meta = null;          // if null, no proxies?
+            boolean useDSFForUnproxy = false;   // default to false for old 1.0 behavior
+
+            // DetachedStateMnager has no context or metadata, so we can't get configuration settings
+            if (!proxy.getOwner().isDetached()) {
+                Compatibility compat = null;
+                meta = sm.getMetaData();
+                if (meta != null) {
+                    compat = meta.getRepository().getConfiguration().getCompatibilityInstance();
+                } else if (sm.getContext() != null && sm.getContext().getConfiguration() != null) {
+                    compat = sm.getContext().getConfiguration().getCompatibilityInstance();
+                } else {
+                    // no-op - using a StateManager, but no Compatibility settings available
+                }
+                if (compat != null) {
+                    // new 2.0 behavior of using DetachedStateField to determine unproxy during serialization
+                    useDSFForUnproxy = !compat.getIgnoreDetachedStateFieldForProxySerialization();
+                }
+            } else {
+                // Using a DetachedStateManager, so use the new flag since there is no context or metadata
+                useDSFForUnproxy = ((DetachedStateManager)sm).getUseDSFForUnproxy();
+            }
+            
+            if (useDSFForUnproxy) {
+                // use new 2.0 behavior
+                if ((meta != null) && (Boolean.TRUE.equals(meta.usesDetachedState()))) {
+                    // configured to always use and serialize a StateManger, so keep any $proxy
+                    return proxy;
+                } else {
+                    // already detached or using DetachedStateField==false or transient, so remove any $proxy
+                    return proxy.copy(proxy);
+                }
+            } else {
+                // use old 1.0 behavior
+                if (proxy.getOwner().isDetached())
+                    return proxy;
+                else
+                    return proxy.copy(proxy);
+            }
+        }
     }
 }
 

Modified: openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java?rev=929784&r1=929783&r2=929784&view=diff
==============================================================================
--- openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java (original)
+++ openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java Thu Apr  1 02:24:32 2010
@@ -186,9 +186,9 @@ public class TestDetachNoProxy extends S
     /*
      * Verify that after EM.clear() entities still contain proxy classes.
      */
-    public void testClear() {
+    public void testClear10() {
         if (log.isTraceEnabled())
-            log.trace("***** testClear() *****");
+            log.trace("***** testClear10() *****");
         OpenJPAEntityManager em = emf.createEntityManager();
         em.clear();
 
@@ -197,7 +197,7 @@ public class TestDetachNoProxy extends S
             Entity20 e20 = em.find(Entity20.class, new Integer(i));
             e20List.add(e20);
             if (log.isTraceEnabled())
-                log.trace("** after find Entity20(" + i + ")");
+                log.trace("** testClear10() - after find Entity20(" + i + ")");
             assertTrue(em.contains(e20));
             assertFalse(em.isDetached(e20));
             verifySerializable(e20, true, false);            
@@ -207,7 +207,7 @@ public class TestDetachNoProxy extends S
 
         for (int i=0; i<numEntities; i++) {
             if (log.isTraceEnabled())
-                log.trace("** after EM.clear() verify Entity20(" + i + ")");
+                log.trace("** testClear10() - after EM.clear() verify Entity20(" + i + ")");
             Entity20 e20 = e20List.get(i);
             assertFalse(em.contains(e20));
             assertTrue(em.isDetached(e20));
@@ -215,8 +215,46 @@ public class TestDetachNoProxy extends S
             // Current Behavior -
             //   the $proxy classes are not removed during serialization
             verifySerializable(e20, true, true);
-            // Proposed OPENJPA-1097 new behavior - $proxy classes are removed
-            // verifySerializable(e20, true, false);
+        }
+
+        em.close();
+    }
+
+    /*
+     * Verify that after EM.clear() entities do not contain proxy classes for 2.0 behavior.
+     */
+    public void testClear20() {
+        if (log.isTraceEnabled())
+            log.trace("***** testClear20() *****");
+        Compatibility compat = emf.getConfiguration().getCompatibilityInstance();
+        assertNotNull(compat);
+        // use new 2.0 behavior
+        compat.setIgnoreDetachedStateFieldForProxySerialization(false);
+        
+        OpenJPAEntityManager em = emf.createEntityManager();
+        em.clear();
+
+        ArrayList<Entity20> e20List = new ArrayList<Entity20>(numEntities);
+        for (int i=0; i<numEntities; i++) {
+            Entity20 e20 = em.find(Entity20.class, new Integer(i));
+            e20List.add(e20);
+            if (log.isTraceEnabled())
+                log.trace("** testClear20() - after find Entity20(" + i + ")");
+            assertTrue(em.contains(e20));
+            assertFalse(em.isDetached(e20));
+            verifySerializable(e20, true, false);            
+        }
+
+        em.clear();
+
+        for (int i=0; i<numEntities; i++) {
+            if (log.isTraceEnabled())
+                log.trace("** after EM.clear() verify Entity20(" + i + ")");
+            Entity20 e20 = e20List.get(i);
+            assertFalse(em.contains(e20));
+            assertTrue(em.isDetached(e20));
+            // Verify new OPENJPA-1097/1597 behavior - $proxy classes are removed
+            verifySerializable(e20, true, false);
         }
 
         em.close();

Modified: openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestDetachMerge.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestDetachMerge.java?rev=929784&r1=929783&r2=929784&view=diff
==============================================================================
--- openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestDetachMerge.java (original)
+++ openjpa/branches/1.3.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestDetachMerge.java Thu Apr  1 02:24:32 2010
@@ -21,15 +21,17 @@ package org.apache.openjpa.persistence.p
 import java.math.BigDecimal;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.persistence.EntityManager;
-//import org.apache.openjpa.conf.Compatibility;
+import org.apache.openjpa.conf.Compatibility;
 import org.apache.openjpa.lib.log.Log;
 import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
 import org.apache.openjpa.persistence.OpenJPAPersistence;
-//import org.apache.openjpa.persistence.test.AllowFailure;
+import org.apache.openjpa.persistence.test.AllowFailure;
 import org.apache.openjpa.persistence.test.SingleEMFTestCase;
 
 import org.apache.openjpa.persistence.proxy.entities.Address;
@@ -83,6 +85,15 @@ public class TestDetachMerge extends Sin
 
         Log log = emf1.getConfiguration().getLog("test");
 
+        if (log.isTraceEnabled()) {
+            Compatibility compat = emf1.getConfiguration().getCompatibilityInstance();
+            assertNotNull(compat);
+            log.trace("started testAnnuity1Compat()");
+            log.trace("FlushBeforeDetach=" + compat.getFlushBeforeDetach());
+            log.trace("IgnoreDetachedStateFieldForProxySerialization=" +
+                compat.getIgnoreDetachedStateFieldForProxySerialization());
+        }
+
         try {
             execute(emf1);
         } catch (RuntimeException e) {
@@ -93,12 +104,12 @@ public class TestDetachMerge extends Sin
     }
     
     /* 
-     * Test 2.0 behavior with Compatibility flag and DetachedStateField=true, which should FAIL
+     * Test default 2.0 compatibility behavior, which should PASS
      *
-    public void testAnnuity1Fail() throws Exception {
+    public void testAnnuity2Compat() throws Exception {
         OpenJPAEntityManagerFactorySPI emf2 = 
             (OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.createEntityManagerFactory(
-            "Annuity1Fail", "org/apache/openjpa/persistence/proxy/persistence1.xml");
+            "Annuity2Compat", "org/apache/openjpa/persistence/proxy/persistence2.xml");
         assertNotNull(emf2);
 
         Log log = emf2.getConfiguration().getLog("test");
@@ -106,26 +117,23 @@ public class TestDetachMerge extends Sin
         if (log.isTraceEnabled()) {
             Compatibility compat = emf2.getConfiguration().getCompatibilityInstance();
             assertNotNull(compat);
-            log.trace("started testAnnuity1Fail()");
+            log.trace("started testAnnuity2Compat()");
+            log.trace("FlushBeforeDetach=" + compat.getFlushBeforeDetach());
+            log.trace("CopyOnDetach=" + compat.getCopyOnDetach());
+            log.trace("CascadeWithDetach=" + compat.getCascadeWithDetach());
             log.trace("IgnoreDetachedStateFieldForProxySerialization=" +
                 compat.getIgnoreDetachedStateFieldForProxySerialization());
         }
 
         try {
             execute(emf2);
-            fail("testAnuity2Fail() should have caused an execption!");
         } catch (RuntimeException e) {
-            if (e.getMessage().startsWith("Annuity:")) {
-                // no-op caught our expected exception
-            } else {
-                fail("testAnuity2Fail() caught an unexpected execption!" + e);
-            }
+            fail("testAnuity2Compat() should not have caused an execption!" + e);
         } finally {
             emf2.close();
         }
     }
-     */
-    
+    */
     
     private void execute(OpenJPAEntityManagerFactorySPI myEMF) throws Exception {
         Log log = myEMF.getConfiguration().getLog("test");
@@ -545,7 +553,8 @@ public class TestDetachMerge extends Sin
         if (payors == null)
             throw new RuntimeException("Annuity: IPayor list not the same (payors was null)!");
         if (payors.size() != payors2.size())
-            throw new RuntimeException("Annuity: IPayor list not the same (payors size not the same)!");
+            throw new RuntimeException("Annuity: IPayor list not the same (payors size not the same)! payors=" +
+                payors.toArray().toString() + ", payors2=" + payors2.toString());
         for (int i = 0; i < payors.size(); i++) {
             IPayor payor = payors.get(i);
             boolean found = false;