openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dwo...@apache.org
Subject svn commit: r924003 - in /openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence: detach/Entity20.java detach/TestDetachNoProxy.java proxy/ proxy/TestProxyCollection.java proxy/TreeNode.java
Date Tue, 16 Mar 2010 21:43:23 GMT
Author: dwoods
Date: Tue Mar 16 21:43:23 2010
New Revision: 924003

URL: http://svn.apache.org/viewvc?rev=924003&view=rev
Log:
OPENJPA-1097 Tests showing how detach()/detachAll()/clear() are currently working in 1.2.x,
where entities serialized after calling detach()/detachAll() do not contain  class wrappers
as expected, but entities after calling clear() still contain  class wrappers.  Received off-line
approval from Mike to only include the junits in 1.2.x.

Added:
    openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java
  (with props)
    openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
  (with props)
    openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/
    openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java
  (with props)
    openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TreeNode.java
  (with props)

Added: openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java?rev=924003&view=auto
==============================================================================
--- openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java
(added)
+++ openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java
Tue Mar 16 21:43:23 2010
@@ -0,0 +1,107 @@
+/*
+ * 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.detach;
+
+import java.io.Serializable;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+@Entity
+@Table(name="Entity20_detach")
+public class Entity20 implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    private Integer id;
+    
+    @Column(name = "sqldate" ) 
+    @Temporal(TemporalType.DATE) 
+    private Date sqlDate; 
+
+    @Column(name = "sqltime") 
+    @Temporal(TemporalType.TIME)
+    private Time sqlTime; 
+
+    @Column(name = "sqltimestamp") 
+    @Temporal(TemporalType.TIMESTAMP)
+    private Timestamp sqlTimestamp; 
+
+    private String name;
+
+    public Entity20() {
+    }
+    
+    public Entity20(int id) {
+        this.id = new Integer(id);
+        this.name = this.id.toString();
+        Long time = System.currentTimeMillis();
+        this.sqlTime = new Time(time);
+        this.sqlDate = new Date(time);
+        this.sqlTimestamp = new Timestamp(time);
+    }
+    
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setDate(Date d) {
+        sqlDate = d;
+    }
+    
+    public Date getDate() {
+        return sqlDate;
+    }
+    
+    public void setTime(Time t) {
+        sqlTime = t;
+    }
+    
+    public Time getTime() {
+        return sqlTime;
+    }
+    
+    public void setTimestamp(Timestamp t) {
+        sqlTimestamp = t;
+    }
+    
+    public Timestamp getTimestamp() {
+        return sqlTimestamp;
+    }
+}

Propchange: openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java?rev=924003&view=auto
==============================================================================
--- openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
(added)
+++ openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
Tue Mar 16 21:43:23 2010
@@ -0,0 +1,306 @@
+/*
+ * 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.detach;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+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.test.SingleEMFTestCase;
+
+public class TestDetachNoProxy extends SingleEMFTestCase {
+    
+    private static final int numEntities = 3;
+    private static final String PROXY = new String("$proxy");
+    private Log log;
+    
+    public void setUp() {
+        setUp(DROP_TABLES, Entity20.class);
+        log = emf.getConfiguration().getLog("test");
+        
+        /* This code is only for 2.0 and later
+        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());
+        }
+        */
+        createEntities(numEntities);
+    }
+    
+    private void createEntities(int count) {
+        Entity20 e20 = null;
+        OpenJPAEntityManager em = emf.createEntityManager();
+        em.getTransaction().begin();
+        for (int i=0; i<count; i++) {
+            e20 = new Entity20(i);
+            em.persist(e20);
+        }
+        em.getTransaction().commit();
+        em.close();
+    }
+    
+    /* 
+     * Verify that returned copy of detached entity does not use the proxy classes.
+     */
+    public void testDetach() {
+        if (log.isTraceEnabled())
+            log.trace("***** testDetach() *****");
+        Integer id = new Integer(0);
+        OpenJPAEntityManager em = emf.createEntityManager();
+        
+        em.clear();
+        Entity20 e20 = em.find(Entity20.class, id);
+        if (log.isTraceEnabled())
+            log.trace("** after find");
+        assertTrue(em.contains(e20));
+        assertFalse(em.isDetached(e20));
+        verifySerializable(e20, true, false);
+        
+        // pre openjpa-2.0.0 behavior, where detach() returned the updated entity
+        Entity20 e20Copy = em.detach(e20);
+        if (log.isTraceEnabled())
+            log.trace("** after detach");
+        // original entity should have proxy classes and should not be detached
+        assertTrue(em.contains(e20));
+        assertFalse(em.isDetached(e20));
+        verifySerializable(e20, true, false);
+        // returned entity should not have any proxy classes and should be detached
+        assertFalse(em.contains(e20Copy));
+        assertTrue(em.isDetached(e20Copy));
+        verifySerializable(e20Copy, false, false);
+               
+        em.close();
+    }
+
+    /* 
+     * This only works on 2.0.0 and later - new method
+     * Verify that a detachCopy() returned entity does not contain any proxy classes.
+     *
+    public void testDetachCopy() {
+        if (log.isTraceEnabled())
+            log.trace("***** testDetachCopy() *****");
+        Integer id = new Integer(0);
+        OpenJPAEntityManager em = emf.createEntityManager();
+        em.clear();
+
+        Entity20 e20 = em.find(Entity20.class, id);
+        if (log.isTraceEnabled())
+            log.trace("** after find");
+        assertTrue(em.contains(e20));
+        assertFalse(em.isDetached(e20));
+        verifySerializable(e20, true, false);
+                        
+        // This only works on 2.0 and later - new method
+        Entity20 e20copy = em.detachCopy(e20);
+        if (log.isTraceEnabled())
+            log.trace("** after detachCopy");
+        // verify e20 is same as above
+        assertTrue(em.contains(e20));
+        assertFalse(em.isDetached(e20));
+        verifySerializable(e20, true);
+        // verify copy does not have any proxy classes (in-place updated) is detached
+        assertFalse(em.contains(e20copy));
+        assertTrue(em.isDetached(e20copy));
+        verifySerializable(e20copy, false, false);
+        
+        em.close();
+    }
+    */
+
+    /*
+     * Verify that returned copies of detachAll() entities do not use the proxy classes.
+     */
+    public void testDetachAll() {
+        if (log.isTraceEnabled())
+            log.trace("***** testDetachAll() *****");
+        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("** after find Entity20(" + i + ")");
+            assertTrue(em.contains(e20));
+            assertFalse(em.isDetached(e20));
+            verifySerializable(e20, true, false);
+        }
+        
+        // pre openjpa-2.0.0 behavior, where detachAll() returned the updated entities
+        ArrayList<Entity20> e20ListCopy = new ArrayList<Entity20>(em.detachAll(e20List));
+        for (int i=0; i<numEntities; i++) {
+            if (log.isTraceEnabled())
+                log.trace("** after EM.detachAll() verify e20List(" + i + ")");
+            Entity20 e20 = e20List.get(i);
+            // original entity should have proxy classes and should not be detached
+            assertFalse(em.isDetached(e20));
+            verifySerializable(e20, true, false);
+        }
+        for (int i=0; i<numEntities; i++) {
+            if (log.isTraceEnabled())
+                log.trace("** after EM.detachAll() verify e20ListCopy(" + i + ")");
+            Entity20 e20 = e20ListCopy.get(i);
+            // entity should not have any proxy classes and should be detached
+            assertFalse(em.contains(e20));
+            assertTrue(em.isDetached(e20));
+            verifySerializable(e20, false, false);
+        }
+
+        em.close();
+    }
+
+    /*
+     * Verify that after EM.clear() entities still contain proxy classes.
+     */
+    public void testClear() {
+        if (log.isTraceEnabled())
+            log.trace("***** testClear() *****");
+        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("** 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));
+            // entity should still have proxy classes and is detached,
+            // 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();
+    }
+
+    /**
+     * Test that the entity is/is not using our $proxy classes before
+     * and after serialization.
+     *
+     * @param e20 Entity to test.
+     * @param usesProxyBefore verify that the entity uses the $proxy classes
+     *        before serialization if true and does not if false.
+     * @param usesProxyAfter verify that the entity uses the $proxy classes
+     *        after serialization if true and does not if false.
+     */
+    private void verifySerializable(Entity20 e20, boolean usesProxyBefore,
+            boolean usesProxyAfter) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = null;
+        byte[] e20bytes = null;
+        
+        if (log.isTraceEnabled())
+            log.trace("verifySerializable() - before serialize");
+        verifyEntities(e20, usesProxyBefore);
+
+        // first serialize
+        try {
+            oos = new ObjectOutputStream(baos);
+            oos.writeObject(e20);
+            e20bytes = baos.toByteArray();
+        } catch (IOException e) {
+            fail(e.toString());
+        } finally {
+            try {
+                if (oos != null)
+                    oos.close();
+            } catch (IOException e) {
+            }
+        }
+        
+        // then deserialize and assert no $proxy classes exist
+        ByteArrayInputStream bais = new ByteArrayInputStream(e20bytes);
+        ObjectInputStream ois = null;
+        Entity20 e20new = null;
+        try {
+            ois = new ObjectInputStream(bais);
+            e20new = (Entity20) ois.readObject();
+            if (log.isTraceEnabled())
+                log.trace("verifySerializable() - after deserialize");
+            verifyEntities(e20new, usesProxyAfter);
+        } catch (IOException e) {
+            fail(e.toString());
+        } catch (ClassNotFoundException e) {
+            fail(e.toString());
+        } finally {
+            try {
+                if (ois != null)
+                    ois.close();
+            } catch (IOException e) {
+            }
+        }
+
+    }
+
+    private void verifyEntities(Entity20 e20, boolean usesProxy) {
+        if (log.isTraceEnabled()) {
+            log.trace("asserting expected proxy usage");
+            printClassNames(e20);
+        }
+        assertTrue("Expected sqlDate endsWith($proxy) to return " + usesProxy,
+            usesProxy == e20.getDate().getClass().getCanonicalName().endsWith(PROXY));
+        assertTrue("Expected sqlTime endsWith($proxy) to return " + usesProxy,
+            usesProxy == e20.getTime().getClass().getCanonicalName().endsWith(PROXY));
+        assertTrue("Expected sqlTimestamp endsWith($proxy) to return " + usesProxy,
+            usesProxy == e20.getTimestamp().getClass().getCanonicalName().endsWith(PROXY));
+        
+    }
+    
+    private void printClassNames(Entity20 e20) {
+        if (log.isTraceEnabled()) {
+            log.trace("sqlDate = " + e20.getDate().getClass().getCanonicalName());
+            log.trace("sqlTime = " + e20.getTime().getClass().getCanonicalName());
+            log.trace("sqlTimestamp = " + e20.getTimestamp().getClass().getCanonicalName());
+        }
+    }
+    
+}

Propchange: openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java?rev=924003&view=auto
==============================================================================
--- openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java
(added)
+++ openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java
Tue Mar 16 21:43:23 2010
@@ -0,0 +1,191 @@
+/*
+ * 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.proxy;
+
+import javax.persistence.EntityManager;
+
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+import org.apache.openjpa.util.ChangeTracker;
+import org.apache.openjpa.util.ProxyCollection;
+
+/**
+ * Tests proxying and change tracking of collection fields for modification in
+ * detached state.
+ * 
+ * Originally reported in
+ * <A HREF="https://issues.apache.org/jira/browse/OPENJPA-628">OPENJPA-628</A>
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class TestProxyCollection extends SingleEMFTestCase {
+	public void setUp() {
+		super.setUp(CLEAR_TABLES, TreeNode.class);
+	}
+	/**
+	 * Tests that a uniform tree is created with expected fan outs at each 
+	 * level. This is not a persistent operation, just in-memory. 
+	 */
+	public void testCreateTree() {
+		TreeNode root = new TreeNode();
+		root.setName("0");
+		int[] fanOuts = {1,2,3};
+		root.createTree(fanOuts);
+		assertArrayEquals(fanOuts, root.getFanOuts());
+	}
+	
+	/**
+	 * Tests that a uniform tree can be modified with different fan outs at each 
+	 * level. This is not a persistent operation, just in-memory. 
+	 */
+	public void testModifyTree() {
+		int[] fanOuts = {1,2,2,4};
+		int[] newFanOuts = {1,3,1,2};
+		TreeNode root = new TreeNode();
+		root.createTree(fanOuts);
+		assertArrayEquals(fanOuts, root.getFanOuts());
+		
+		root.modify(newFanOuts);
+		assertArrayEquals(newFanOuts, root.getFanOuts());
+	}
+	
+	/**
+	 * Tests that a uniform tree is persisted and later fetched back with same
+	 * number of children at every level.
+	 */
+	public void testPersistTree() {
+		int[] fanOuts = {2,3,4};
+		verify(create(fanOuts), fanOuts);
+	}
+	
+	public void testAddNodeAtLeaf() {
+		int[] original = {1,2,3};
+		int[] modifier = {1,2,4}; // add new child at Level 2
+		createModifyAndMerge(original, modifier);
+	}
+	
+	public void testAddNewLevel() {
+		int[] original = {1,2,3};
+		int[] modifier = {1,2,3,2}; // add 2 new children at new Level 
+		createModifyAndMerge(original, modifier);
+	}
+	
+	public void testAddAndRemove() {
+		int[] original = {2,3,4};
+		int[] modifier = {4,3,2}; // add 1 at Level 1 + remove 1 at Level 3
+		createModifyAndMerge(original, modifier);
+	}
+	
+	public void testAddAtAllLevel() {
+		int[] original = {2,3,4};
+		int[] modifier = {3,4,5}; // add 1 at each Level 
+		createModifyAndMerge(original, modifier);
+	}
+	
+	public void testRemoveAtAllLevel() {
+		int[] original = {2,3,4};
+		int[] modifier = {1,2,3}; // remove 1 from each Level 
+		createModifyAndMerge(original, modifier);
+	}
+	/**
+	 * Create a uniform tree with original fanout.
+	 * Persist.
+	 * Verify in a separate persistence context that the tree is stored.
+	 * Modify the tree by adding or deleting nodes according to the given 
+	 * modified fanouts outside a transaction.
+	 * Merge the changes.
+	 * Verify that the changes are merged by fetching the modified version.
+	 * 
+	 * @param original
+	 * @param modified
+	 */
+	void createModifyAndMerge(int[] original, int[] modifier) {
+		TreeNode root = create(original);
+		
+		EntityManager em = emf.createEntityManager();
+		em.getTransaction().begin();
+		TreeNode modified = em.find(TreeNode.class, root.getId());
+		modified.modify(modifier);
+		em.merge(modified);
+		em.getTransaction().commit();
+
+		em.clear();
+
+		assertProxyCollection(root.getNodes(), false);
+
+		verify(root, modifier);
+	}
+	
+	/**
+	 * Create a uniform tree with given fan out.
+	 * Persist.
+	 * Verify that the tree is stored by fetching it in a separate persistence
+	 * context.
+	 */
+	TreeNode create(int[] original) {
+		TreeNode root = new TreeNode();
+		root.createTree(original);
+		
+		EntityManager em = emf.createEntityManager();
+		em.getTransaction().begin();
+		em.persist(root);
+		em.getTransaction().commit();
+		em.clear();
+		
+		return root;
+	}
+	
+	void verify(TreeNode node, int[] fanOuts) {
+		EntityManager em = emf.createEntityManager();
+		em.getTransaction().begin();
+		TreeNode test = em.find(TreeNode.class, node.getId());
+		assertNotNull(test);
+		assertArrayEquals(fanOuts, test.getFanOuts());
+	}
+
+	/** Asserts the given arrays have exactly same elements at the same index.
+	*/
+	void assertArrayEquals(int[] a, int[] b) {
+		assertEquals(a.length, b.length);
+		for (int i = 0; i<a.length; i++)
+			assertEquals(a[i], b[i]);
+	}
+
+	/**
+	 * Asserts that the given object is a proxy collection and whether it is 
+	 * tracking changes.
+	 */
+	void assertProxyCollection(Object o, boolean tracking) {
+		assertTrue(o instanceof ProxyCollection);
+		ChangeTracker tracker = ((ProxyCollection)o).getChangeTracker();
+		if (tracking) {
+			assertNotNull(tracker);
+			assertTrue(tracker.isTracking());
+		} else {
+			assertFalse(tracker.isTracking());
+		}
+	}
+
+    /**
+     * Asserts that the given object is NOT a proxy collection.
+     */
+    void assertNotProxyCollection(Object o) {
+        assertFalse(o instanceof ProxyCollection);
+    }
+}

Propchange: openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TreeNode.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TreeNode.java?rev=924003&view=auto
==============================================================================
--- openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TreeNode.java
(added)
+++ openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TreeNode.java
Tue Mar 16 21:43:23 2010
@@ -0,0 +1,231 @@
+/*
+ * 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.proxy;
+
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.Version;
+
+import org.apache.openjpa.persistence.DetachedState;
+import org.apache.openjpa.persistence.ElementDependent;
+import org.apache.openjpa.persistence.jdbc.ElementJoinColumn;
+import org.apache.openjpa.persistence.jdbc.OrderColumn;
+
+/**
+ * Persistent entity for testing adding/removing elements of collection valued
+ * field while in detached state.
+ * 
+ * Node refers to a list of Nodes as children.
+ * 
+ * Contains recursive methods to create or modify uniform subtree. Uniform
+ * subtree implies that each child at a level L has equal number of
+ * grand children at level L+1.
+ * 
+ * @author Pinaki Poddar
+ * 
+ */
+@Entity
+@DetachedState
+public class TreeNode implements Serializable {
+	@Id
+	@GeneratedValue
+	private long id;
+
+	private String name;
+
+	@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+	@ElementJoinColumn(name = "ParentID")
+	@OrderColumn(name = "Sequence")
+	@ElementDependent
+	private List<TreeNode> childern = new ArrayList<TreeNode>();
+
+	@Version
+	private int version;
+
+	public long getId() {
+		return id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * Add a child node at the end of the current list of children.
+	 */
+	public void addNode(TreeNode node) {
+		addNode(node, childern.size());
+	}
+
+	/**
+	 * Insert a child node at the specified position in the list of children.
+	 */
+	public void addNode(TreeNode node, int position) {
+		checkSequenceRange(position);
+		childern.add(position, node);
+	}
+
+	public boolean removeNode(TreeNode node) {
+		return childern.remove(node);
+	}
+
+	public TreeNode removeNode(int sequence) {
+		checkSequenceRange(sequence);
+		return childern.remove(sequence);
+	}
+
+	public TreeNode getNode(int sequence) {
+		checkSequenceRange(sequence);
+		return childern.get(sequence);
+	}
+
+	public List<TreeNode> getNodes() {
+		return childern;
+	}
+
+	public void clearNodes() {
+		childern.clear();
+	}
+
+	public boolean isLeaf() {
+		return childern.isEmpty();
+	}
+
+	protected void checkSequenceRange(int sequence)
+			throws IllegalArgumentException {
+		int size = childern.size();
+		if (sequence < 0 || sequence > size)
+			throw new IllegalArgumentException("Sequence number is beyond "
+					+ "range of 0 to " + size + ".");
+	}
+
+	public int getVersion() {
+		return version;
+	}
+
+	/**
+	 * Create a uniform subtree below the receiver. Uniform subtree implies that
+	 * each child at a level L has equal number of grand children at level L+1.
+	 * 
+	 * @param fanOuts
+	 *            array of fan outs for children at every level.
+	 */
+	public void createTree(int[] fanOuts) {
+		if (fanOuts.length == 0)
+			return;
+		int[] nextFanOuts = new int[fanOuts.length];
+		System.arraycopy(fanOuts, 1, nextFanOuts, 0, fanOuts.length - 1);
+		for (int j = 0; j < fanOuts[0]; j++) {
+			TreeNode child = new TreeNode();
+			child.setName(getName() + "." + j);
+			addNode(child);
+			child.createTree(nextFanOuts);
+		}
+	}
+
+	/**
+	 * Add or remove subtree of the receiver to match the given fanOut.
+	 */
+	public void modify(int[] fanOuts) {
+		if (fanOuts == null || fanOuts.length == 0)
+			return;
+		int n = fanOuts[0];
+		int[] nextFanOuts = new int[fanOuts.length];
+		System.arraycopy(fanOuts, 1, nextFanOuts, 0, fanOuts.length - 1);
+		List<TreeNode> children = getNodes();
+		int diff = children.size() - n;
+		if (diff < 0) {
+			for (int i = 0; i < -diff; i++) {
+				TreeNode newChild = new TreeNode();
+				int position = getNodes().size();
+				newChild.setName(getName() + "." + position);
+				addNode(newChild);
+			}
+		} else if (diff > 0) {
+			for (int i = 0; i < diff; i++) {
+				int position = getNodes().size() - 1;
+				removeNode(position);
+			}
+		}
+		children = getNodes();
+		for (TreeNode child : children) {
+			child.modify(nextFanOuts);
+		}
+	}
+
+	/**
+	 * Get the fan outs of the given receiver. Assumes that the subtree is
+	 * uniform. Otherwise throws exception.
+	 */
+	public int[] getFanOuts() {
+		return getFanOuts(new int[] {});
+	}
+
+	private int[] getFanOuts(int[] list) {
+		List<TreeNode> children = getNodes();
+		if (children.isEmpty())
+			return list;
+		int[] fanOuts = new int[children.size()];
+		int i = 0;
+		for (TreeNode child : children) {
+			fanOuts[i++] = child.getNodes().size();
+		}
+		for (int j = 0; j < fanOuts.length - 1; j++)
+			if (fanOuts[j] != fanOuts[j + 1])
+				throw new RuntimeException("non-uniform fanouts for children "
+						+ " of " + getName());
+
+		int[] newList = new int[list.length + 1];
+		System.arraycopy(list, 0, newList, 0, list.length);
+		newList[list.length] = children.size();
+		return children.get(0).getFanOuts(newList);
+	}
+
+	/**
+	 * Prints this receiver and its subtree.
+	 */
+	public void print(PrintStream out) {
+		print(2, out);
+	}
+
+	private void print(int tab, PrintStream out) {
+		for (int i = 0; i < tab; i++)
+			out.print(" ");
+		out.println(getName());
+		for (TreeNode child : getNodes()) {
+			child.print(tab + 2, out);
+		}
+	}
+
+}

Propchange: openjpa/branches/1.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TreeNode.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message