| /* |
| * 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.util.LinkedHashSet; |
| |
| 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 { |
| @Override |
| public void setUp() { |
| super.setUp(CLEAR_TABLES, TreeNode.class, ConcreteEntity.class, AbstractEntity.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); |
| } |
| |
| public void testCreateCorrectType() { |
| ConcreteEntity ce = new ConcreteEntity(); |
| ce.addItem(ce); |
| |
| EntityManager em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| |
| em.persist(ce); |
| em.getTransaction().commit(); |
| em.clear(); |
| |
| ce = em.find(ConcreteEntity.class, ce.getId()); |
| assertNotNull(ce); |
| Class<?> proxyCls = ce.getItems().getClass(); |
| assertTrue(proxyCls + " is not assignableFrom " + LinkedHashSet.class, |
| LinkedHashSet.class.isAssignableFrom(proxyCls)); |
| } |
| /** |
| * 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); |
| } |
| } |
| |