diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java index 7896fdd39..fbff958b8 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java @@ -66,6 +66,7 @@ public class Compatibility { private boolean _isNonDefaultMappingAllowed = false; private boolean _reorderMetaDataResolution = true; private boolean _reloadOnDetach = false; + private boolean _ignoreDetachedStateFieldForProxySerialization = false; /** * Whether to require exact identity value types when creating object @@ -295,6 +296,39 @@ public class Compatibility { return _flushBeforeDetach; } + /** + * Whether OpenJPA should ignore the DetachedStateField value when + * determining if our Proxy classes should be removed during serialization. + *
Starting with version 2.0.0, when the DetachedStateFiled==true, the + * build time $proxy classes will not be removed. + *
Prior to version 2.0.0, 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 2.0.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. + *
Starting with version 2.0.0, when the DetachedStateFiled==true, the + * build time $proxy classes will not be removed. + *
Prior to version 2.0.0, the DetachedStateFiled was not used and
+ * the $proxy classes were not being removed during serialization after
+ * the Persistence context was cleared.
+ *
+ * @since 2.0.0
+ * @return true 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
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java
index b309ab908..33c6761e3 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java
@@ -708,10 +708,8 @@ public class DetachManager
* Set the owner of the field's proxy to the detached state manager.
*/
private Object reproxy(Object obj, int field) {
- if (obj != null && _detSM != null && obj instanceof Proxy) {
+ if (obj != null && _detSM != null && obj instanceof Proxy)
((Proxy) obj).setOwner(_detSM, field);
- return ((Proxy) obj).copy(obj);
- }
return obj;
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java
index b8f48a1ee..1c58524f3 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java
@@ -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 2.0 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 2.0.0
+ */
+ public boolean getUseDSFForUnproxy() {
+ return _useDSFForUnproxy;
+ }
+
public BitSet getFlushed() {
throw new UnsupportedOperationException();
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java
index 70f020a9f..ca944ae78 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java
@@ -20,6 +20,8 @@ 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;
@@ -85,10 +87,18 @@ public class Proxies {
* 1) No Proxy, then return as-is
* 2) Runtime created proxy (!detachable), then unproxy
* 3) No StateManager (DetachedStateField==false), then return as-is
- * 4) If detached, then unproxy
- * 5) If ClassMetaData exists and DetachedStateField != TRUE
- * (default of DetachedStateField==transient), then unproxy
- * 6) Else, 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
@@ -111,19 +121,47 @@ public class Proxies {
} else if (proxy.getOwner() == null) {
// no StateManager (DetachedStateField==false), so no $proxy to remove
return proxy;
- } else if (proxy.getOwner().isDetached()) {
- // already detached, so remove any $proxy
- return proxy.copy(proxy);
} else {
// using a StateManager, so determine what DetachedState is being used
- OpenJPAStateManager sm = proxy.getOwner(); // !null checked for above
- ClassMetaData meta = sm.getMetaData(); // if null, no proxies?
- if ((meta != null) && (!Boolean.TRUE.equals(meta.usesDetachedState()))) {
- // configured to use transient (null) or no (FALSE) StateManger, so remove any $proxy
- return proxy.copy(proxy);
+ 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 {
- // DetachedStateField==true, which means to keep the SM and $proxy in the serialized objects
- return proxy;
+ // 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);
}
}
}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestContainerSpecCompatibilityOptions.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestContainerSpecCompatibilityOptions.java
index 4bc3624be..73109aa4b 100644
--- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestContainerSpecCompatibilityOptions.java
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestContainerSpecCompatibilityOptions.java
@@ -71,6 +71,7 @@ public class TestContainerSpecCompatibilityOptions
Compatibility compat = emf1.getConfiguration().getCompatibilityInstance();
assertTrue(compat.getFlushBeforeDetach());
assertTrue(compat.getCopyOnDetach());
+ assertTrue(compat.getIgnoreDetachedStateFieldForProxySerialization());
assertTrue(compat.getPrivatePersistentProperties());
assertFalse(compat.isAbstractMappingUniDirectional());
assertFalse(compat.isNonDefaultMappingAllowed());
@@ -92,6 +93,7 @@ public class TestContainerSpecCompatibilityOptions
Compatibility compat = emf.getConfiguration().getCompatibilityInstance();
assertFalse(compat.getFlushBeforeDetach());
assertFalse(compat.getCopyOnDetach());
+ assertFalse(compat.getIgnoreDetachedStateFieldForProxySerialization());
assertFalse(compat.getPrivatePersistentProperties());
assertTrue(compat.isAbstractMappingUniDirectional());
assertTrue(compat.isNonDefaultMappingAllowed());
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java
index 9436dc78e..a2a0089ba 100644
--- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java
@@ -57,6 +57,7 @@ extends AbstractCachedEMFTestCase {
Compatibility compat = emf.getConfiguration().getCompatibilityInstance();
assertTrue(compat.getFlushBeforeDetach());
assertTrue(compat.getCopyOnDetach());
+ assertTrue(compat.getIgnoreDetachedStateFieldForProxySerialization());
assertTrue(compat.getPrivatePersistentProperties());
String vMode = emf.getConfiguration().getValidationMode();
assertEquals("NONE", vMode);
@@ -65,7 +66,6 @@ extends AbstractCachedEMFTestCase {
assertEquals(spec.getVersion(), 1);
emf.close();
-
}
/*
@@ -82,6 +82,7 @@ extends AbstractCachedEMFTestCase {
Compatibility compat = emf.getConfiguration().getCompatibilityInstance();
assertFalse(compat.getFlushBeforeDetach());
assertFalse(compat.getCopyOnDetach());
+ assertFalse(compat.getIgnoreDetachedStateFieldForProxySerialization());
assertFalse(compat.getPrivatePersistentProperties());
String vMode = emf.getConfiguration().getValidationMode();
assertEquals("AUTO", vMode);
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
index b2faf88d8..5f0e07155 100644
--- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
@@ -28,34 +28,19 @@ import java.util.ArrayList;
import org.apache.openjpa.conf.Compatibility;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestDetachNoProxy extends SingleEMFTestCase {
private static final int numEntities = 3;
private static final String PROXY = new String("$proxy");
- private Log log;
+ private Log _log;
public void setUp() {
setUp(DROP_TABLES, Entity20.class);
- log = emf.getConfiguration().getLog("test");
-
- // check and set Compatibility values to new 2.0 values
- Compatibility compat = emf.getConfiguration().getCompatibilityInstance();
- assertNotNull(compat);
- if (log.isTraceEnabled()) {
- log.trace("Before set, FlushBeforeDetach=" + compat.getFlushBeforeDetach());
- log.trace("Before set, CopyOnDetach=" + compat.getCopyOnDetach());
- log.trace("Before set, CascadeWithDetach=" + compat.getCascadeWithDetach());
- }
- compat.setFlushBeforeDetach(false);
- compat.setCopyOnDetach(false);
- compat.setCascadeWithDetach(false);
- if (log.isTraceEnabled()) {
- log.trace("After set, FlushBeforeDetach=" + compat.getFlushBeforeDetach());
- log.trace("After set, CopyOnDetach=" + compat.getCopyOnDetach());
- log.trace("After set, CascadeWithDetach=" + compat.getCascadeWithDetach());
- }
+ _log = emf.getConfiguration().getLog("test");
createEntities(numEntities);
}
@@ -74,16 +59,33 @@ public class TestDetachNoProxy extends SingleEMFTestCase {
/*
* Verify that an in-place detached entity does not use the proxy classes.
*/
- public void testDetach() {
- if (log.isTraceEnabled())
- log.trace("***** testDetach() *****");
+ public void testDetach20() {
Integer id = new Integer(0);
- OpenJPAEntityManager em = emf.createEntityManager();
+ OpenJPAEntityManagerFactorySPI emf2 =
+ (OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.createEntityManagerFactory(
+ "NoProxy2New", "org/apache/openjpa/persistence/detach/persistence2.xml");
+ assertNotNull(emf2);
+
+ Log log = emf2.getConfiguration().getLog("test");
+ if (log.isTraceEnabled())
+ log.trace("***** testDetach20() *****");
+ if (log.isTraceEnabled()) {
+ Compatibility compat = emf2.getConfiguration().getCompatibilityInstance();
+ assertNotNull(compat);
+ log.trace("FlushBeforeDetach=" + compat.getFlushBeforeDetach());
+ log.trace("CopyOnDetach=" + compat.getCopyOnDetach());
+ log.trace("CascadeWithDetach=" + compat.getCascadeWithDetach());
+ log.trace("IgnoreDetachedStateFieldForProxySerialization=" +
+ compat.getIgnoreDetachedStateFieldForProxySerialization());
+ }
+
+ OpenJPAEntityManager em = emf2.createEntityManager();
em.clear();
+
Entity20 e20 = em.find(Entity20.class, id);
if (log.isTraceEnabled())
- log.trace("** after find");
+ log.trace("** testDetach20() - after find");
assertTrue(em.contains(e20));
assertFalse(em.isDetached(e20));
verifySerializable(e20, true, false);
@@ -91,28 +93,46 @@ public class TestDetachNoProxy extends SingleEMFTestCase {
// new openjpa-2.0.0 behavior, where detach() doesn't return updated entity, but does it in-place
em.detach(e20);
if (log.isTraceEnabled())
- log.trace("** after detach");
+ log.trace("** testDetach20() - after detach");
// in-place updated entity should not have any proxy classes and should be detached
assertFalse(em.contains(e20));
assertTrue(em.isDetached(e20));
- verifySerializable(e20, false, false);
+ verifySerializable(e20, true, false);
em.close();
+ emf2.close();
}
/*
* Verify that a detachCopy() returned entity does not contain any proxy classes.
*/
- public void testDetachCopy() {
- if (log.isTraceEnabled())
- log.trace("***** testDetachCopy() *****");
+ public void testDetachCopy20() {
Integer id = new Integer(0);
- OpenJPAEntityManager em = emf.createEntityManager();
+ OpenJPAEntityManagerFactorySPI emf2 =
+ (OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.createEntityManagerFactory(
+ "NoProxy2New", "org/apache/openjpa/persistence/detach/persistence2.xml");
+ assertNotNull(emf2);
+
+ Log log = emf2.getConfiguration().getLog("test");
+ if (log.isTraceEnabled())
+ log.trace("***** testDetachCopy20() *****");
+
+ if (log.isTraceEnabled()) {
+ Compatibility compat = emf2.getConfiguration().getCompatibilityInstance();
+ assertNotNull(compat);
+ log.trace("FlushBeforeDetach=" + compat.getFlushBeforeDetach());
+ log.trace("CopyOnDetach=" + compat.getCopyOnDetach());
+ log.trace("CascadeWithDetach=" + compat.getCascadeWithDetach());
+ log.trace("IgnoreDetachedStateFieldForProxySerialization=" +
+ compat.getIgnoreDetachedStateFieldForProxySerialization());
+ }
+
+ OpenJPAEntityManager em = emf2.createEntityManager();
em.clear();
Entity20 e20 = em.find(Entity20.class, id);
if (log.isTraceEnabled())
- log.trace("** after find");
+ log.trace("** testDetachCopy20() - after find");
assertTrue(em.contains(e20));
assertFalse(em.isDetached(e20));
verifySerializable(e20, true, false);
@@ -120,7 +140,7 @@ public class TestDetachNoProxy extends SingleEMFTestCase {
// Test new detachCopy() method added in 2.0.0
Entity20 e20copy = em.detachCopy(e20);
if (log.isTraceEnabled())
- log.trace("** after detachCopy");
+ log.trace("** TestDetachCopy20() - after detachCopy");
// verify e20 is same as above
assertTrue(em.contains(e20));
assertFalse(em.isDetached(e20));
@@ -131,15 +151,33 @@ public class TestDetachNoProxy extends SingleEMFTestCase {
verifySerializable(e20copy, false, false);
em.close();
+ emf2.close();
}
/*
* Verify that in-place detachAll entities do not use the proxy classes.
*/
- public void testDetachAll() {
+ public void testDetachAll20() {
+ OpenJPAEntityManagerFactorySPI emf2 =
+ (OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.createEntityManagerFactory(
+ "NoProxy2New", "org/apache/openjpa/persistence/detach/persistence2.xml");
+ assertNotNull(emf2);
+
+ Log log = emf2.getConfiguration().getLog("test");
if (log.isTraceEnabled())
- log.trace("***** testDetachAll() *****");
- OpenJPAEntityManager em = emf.createEntityManager();
+ log.trace("***** testDetachAll20() *****");
+
+ if (log.isTraceEnabled()) {
+ Compatibility compat = emf2.getConfiguration().getCompatibilityInstance();
+ assertNotNull(compat);
+ log.trace("FlushBeforeDetach=" + compat.getFlushBeforeDetach());
+ log.trace("CopyOnDetach=" + compat.getCopyOnDetach());
+ log.trace("CascadeWithDetach=" + compat.getCascadeWithDetach());
+ log.trace("IgnoreDetachedStateFieldForProxySerialization=" +
+ compat.getIgnoreDetachedStateFieldForProxySerialization());
+ }
+
+ OpenJPAEntityManager em = emf2.createEntityManager();
em.clear();
ArrayList