/* * Copyright (C) 2009 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.services.jcr.impl.core; import org.exoplatform.services.jcr.JcrImplBaseTest; import org.exoplatform.services.jcr.core.ExtendedNode; import org.exoplatform.services.jcr.datamodel.NodeData; import org.exoplatform.services.jcr.impl.Constants; import org.exoplatform.services.transaction.TransactionService; import java.util.NoSuchElementException; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RangeIterator; import javax.jcr.RepositoryException; import javax.transaction.UserTransaction; /** * Created by The eXo Platform SAS. * * @author <a href="mailto:nzamosenchuk@exoplatform.com">Nikolay Zamosenchuk</a> * @version $Id: TestGetNode.java 111 2009-11-11 11:11:11Z nzamosenchuk $ */ public class TestGetNodesLazily extends JcrImplBaseTest { private static String INDEX_PROPERTY = "indexNumber"; private NodeImpl testRoot; private int nodesCount; private TransactionService txService; @Override public void setUp() throws Exception { super.setUp(); testRoot = (NodeImpl)session.getRootNode().addNode("TestGetNodesLazily"); // add first 150 child nodes nodesCount = 350; for (int i = 0; i < nodesCount; i++) { Node newNode = testRoot.addNode("child" + i); newNode.setProperty(INDEX_PROPERTY, i); newNode.addMixin("exo:owneable"); } session.save(); txService = (TransactionService)container.getComponentInstanceOfType(TransactionService.class); } /** * Children nodes counting checking. */ public void testGetNodesCount() throws Exception { assertEquals(nodesCount, testRoot.getNodesCount()); } /** * Simple check, session log empty. */ public void testGetNodesLazilyBasicUsecase() throws Exception { // 150 nodes added in setup assertChildNodes(testRoot, nodesCount); } /** * All child they reordered one by one and though must be returned in same order */ public void testGetNodesLazilyReordered() throws Exception { // 150 nodes added in setup for (int i = 0; i < nodesCount; i++) { NodeImpl node = (NodeImpl)testRoot.getNode("child" + i); session.move(node.getPath(), node.getPath()); } session.save(); assertChildNodes(testRoot, nodesCount); } /** * All child they reordered one by one and though must be returned in same order */ public void testGetNodesLazilyReorderedBackwards() throws Exception { // 150 nodes added in setup // TODO : testcase fails for (int i = nodesCount - 1; i >= 0; i--) { NodeImpl node = (NodeImpl)testRoot.getNode("child" + i); session.move(node.getPath(), node.getPath()); } session.save(); assertChildNodes(testRoot, nodesCount, true); } /** * Session move with save */ public void testGetNodesLazilyRenamedParent() throws Exception { // 150 nodes added in setup // renaming parent String newName = "new Name"; session.move("/" + testRoot.getName(), "/" + newName); NodeImpl newTestRoot = (NodeImpl)root.getNode(newName); session.save(); assertChildNodes(newTestRoot, nodesCount); } //=============== Tests with non-empty changesLog =============== /** * New nodes added into session log and not save */ public void testGetNodesLazilySessionAddedNodes() throws Exception { // 150 nodes added in setup // adding 10 more nodes without save, so it is missing in persistent layer but exist in session changes log for (int i = 0; i < 10; i++) { testRoot.addNode("child" + nodesCount).setProperty(INDEX_PROPERTY, nodesCount); nodesCount++; } assertChildNodes(testRoot, nodesCount); } /** * New nodes moved into session log and not save */ public void testGetNodesLazilySessionMovedNodes() throws Exception { session.getRootNode().addNode("child" + nodesCount).setProperty(INDEX_PROPERTY, nodesCount); session.save(); nodesCount++; String newNodeName = "child" + (nodesCount - 1); session.move("/" + newNodeName, testRoot.getPath() + "/" + newNodeName); assertChildNodes(testRoot, nodesCount); testRoot.getNode(newNodeName).remove(); nodesCount--; assertChildNodes(testRoot, nodesCount); session.save(); assertChildNodes(testRoot, nodesCount); } /** * New nodes moved into session log and not save */ public void testGetNodesLazilySessionUpdatedNodes() throws Exception { testRoot.orderBefore("child110", "child0"); RangeIterator iterator = testRoot.getNodesLazily(); NodeImpl next = (NodeImpl)iterator.next(); assertEquals(next.getName(), "child110"); assertEquals(((NodeData)next.getData()).getOrderNumber(), 0); next = (NodeImpl)iterator.next(); assertEquals(next.getName(), "child0"); assertEquals(((NodeData)next.getData()).getOrderNumber(), 1); iterator.skip(108); next = (NodeImpl)iterator.next(); assertEquals(next.getName(), "child109"); assertEquals(((NodeData)next.getData()).getOrderNumber(), 110); } /** * Change mixin in node. */ public void testGetNodesLazilySessionMixinChanged() throws Exception { testRoot.getNode("child0").addMixin("mix:lockable"); NodeIterator iterator = testRoot.getNodesLazily(); NodeImpl node = (NodeImpl)iterator.nextNode(); assertTrue(node.isNodeType(Constants.MIX_LOCKABLE)); } /** * New nodes added into session log and not save */ public void testGetNodesLazilySessionNewAddedNodes() throws Exception { // adding 150 new nodes NodeImpl localRoot = (NodeImpl)testRoot.addNode("localRoot"); for (int i = 0; i < nodesCount; i++) { localRoot.addNode("child" + i).setProperty(INDEX_PROPERTY, i); } assertChildNodes(localRoot, nodesCount); } /** * Last 10 nodes removed. Session log has removed states for them */ public void testGetNodesLazilySessionRemovedNodes() throws Exception { // 150 nodes added in setup // removing 10 nodes without save for (int i = 0; i < 10; i++) { nodesCount--; testRoot.getNode("child" + nodesCount).remove(); } assertChildNodes(testRoot, nodesCount); } /** * Session move without save */ public void testGetNodesLazilySessionRenamedParent() throws Exception { // 150 nodes added in setup // renaming parent String newName = "new Name"; session.move("/" + testRoot.getName(), "/" + newName); NodeImpl newTestRoot = (NodeImpl)root.getNode(newName); assertChildNodes(newTestRoot, nodesCount); } /** * All child nodes are in session changes log and both in persisted layer. All they reordered one by one and though must * be returned in same order */ public void testGetNodesLazilySessionReordered() throws Exception { // 150 nodes added in setup // TODO : testcase fails for (int i = nodesCount - 1; i >= 0; i--) { NodeImpl node = (NodeImpl)testRoot.getNode("child" + i); session.move(node.getPath(), node.getPath()); } assertChildNodes(testRoot, nodesCount, true); } //=============== transactions related =============== public void testGetNodesLazilyTransaction() throws Exception { assertNotNull(txService); UserTransaction ut = txService.getUserTransaction(); ut.begin(); NodeImpl localRoot = (NodeImpl)testRoot.addNode("localRoot"); for (int i = 0; i < nodesCount; i++) { localRoot.addNode("child" + i).setProperty(INDEX_PROPERTY, i); } // assert within session changes log assertChildNodes(localRoot, nodesCount); session.save(); // assert within transaction changes log assertChildNodes(localRoot, nodesCount); ut.commit(); // assert within persistent layer assertChildNodes(localRoot, nodesCount); } public void testGetNodesLazilyTransactionRollbackAdded() throws Exception { assertNotNull(txService); UserTransaction ut = txService.getUserTransaction(); ut.begin(); int txNodesCount = nodesCount; // 150 nodes added in setup // adding 10 more nodes without save, so it is missing in persistent layer but exist in session changes log for (int i = 0; i < 10; i++) { testRoot.addNode("child" + txNodesCount).setProperty(INDEX_PROPERTY, txNodesCount); txNodesCount++; } session.save(); // assert within transaction changes log assertChildNodes(testRoot, txNodesCount); ut.rollback(); // assert within persistent layer, nodes should be rolled back assertChildNodes(testRoot, nodesCount); } public void testGetNodesLazilyTransactionRollbackRemoved() throws Exception { assertNotNull(txService); UserTransaction ut = txService.getUserTransaction(); ut.begin(); // 150 nodes added in setup // removing 10 nodes int txNodesCount = nodesCount; for (int i = 0; i < 10; i++) { txNodesCount--; testRoot.getNode("child" + txNodesCount).remove(); } session.save(); // assert within transaction changes log assertChildNodes(testRoot, txNodesCount); ut.rollback(); // assert within persistent layer, nodes should be rolled back assertChildNodes(testRoot, nodesCount); } //=============== test iterator =============== public void testGetNodesLazilyIterator() throws Exception { RangeIterator iterator = testRoot.getNodesLazily(); // there are 150 node, so it must have next assertTrue(iterator.hasNext()); // position is before first node assertEquals(0, iterator.getPosition()); // fetch first one (/child0) iterator.next(); assertEquals(1, iterator.getPosition()); // fetch second one (/child1) iterator.next(); assertEquals(2, iterator.getPosition()); // skip to /child12 iterator.skip(10); NodeImpl next = (NodeImpl)iterator.next(); assertEquals(13, iterator.getPosition()); assertEquals(12, next.getProperty(INDEX_PROPERTY).getLong()); iterator.skip(1); next = (NodeImpl)iterator.next(); assertEquals(15, iterator.getPosition()); assertEquals(14, next.getProperty(INDEX_PROPERTY).getLong()); iterator.skip(100); next = (NodeImpl)iterator.next(); assertEquals(116, iterator.getPosition()); assertEquals(115, next.getProperty(INDEX_PROPERTY).getLong()); iterator = testRoot.getNodesLazily(); long size = iterator.getSize(); iterator.skip(size); try { iterator.next(); fail("Exception should be thrown"); } catch (NoSuchElementException e) { } // remove nodes from 31..60 to make gap in interval of order numbers iterator = testRoot.getNodesLazily(); iterator.skip(30); for (int i = 0; i < 30; i++) { ((NodeImpl)iterator.next()).remove(); } testRoot.save(); iterator = testRoot.getNodesLazily(10); size = 0; while (iterator.hasNext()) { size++; iterator.next(); } assertEquals(320, size); } //=============== stuff =============== /** * Performs complex assert, that retrieves child nodes in a lazy way and asserts it's order, content * and quantity */ private void assertChildNodes(ExtendedNode testNode, int expectedSize) throws RepositoryException { assertChildNodes(testNode, expectedSize, false); } /** * Performs complex assert, that retrieves child nodes in a lazy way and asserts it's order, content * and quantity */ private void assertChildNodes(ExtendedNode testNode, int expectedSize, boolean backwardOrder) throws RepositoryException { NodeIterator lazyIterator = testNode.getNodesLazily(); // getSize shouldn't return actual size, since it works in a lazy manner and nodes are retrieved "on demand" // int actualSize = (int)lazyIterator.getSize(); // assertEquals("getSize should return -1, but returned " + actualSize, -1, actualSize); if (expectedSize == 0) { assertFalse(lazyIterator.hasNext()); } else { int number = 0; if (!backwardOrder) { int i = 0; while (lazyIterator.hasNext()) { Node node = lazyIterator.nextNode(); long actualNodeIndex = node.getProperty(INDEX_PROPERTY).getLong(); assertEquals("Iterator must return nodes ordered by \"order num\". Occurred at: child" + actualNodeIndex + " expecting <" + i + ">", i, actualNodeIndex); i++; number++; } } else { int i = expectedSize - 1; while (lazyIterator.hasNext()) { NodeImpl node = (NodeImpl)lazyIterator.nextNode(); long actualNodeIndex = node.getProperty(INDEX_PROPERTY).getLong(); assertEquals("Iterator must return nodes ordered by \"order num\". Occurred at: child" + actualNodeIndex + " expecting <" + i + ">", i, actualNodeIndex); i--; number++; } } // assert all returned assertEquals( "Iterator returned wrong number of nodes. Expected: " + expectedSize + ", but returned " + number, expectedSize, number); } NodeIterator it = testNode.getNodes(); if (expectedSize == 0) { assertFalse(it.hasNext()); return; } int number = 0; if (!backwardOrder) { int i = 0; while (it.hasNext()) { Node node = it.nextNode(); long actualNodeIndex = node.getProperty(INDEX_PROPERTY).getLong(); assertEquals("Iterator must return nodes ordered by \"order num\". Occurred at: child" + actualNodeIndex + " expecting <" + i + ">", i, actualNodeIndex); i++; number++; } } else { int i = expectedSize - 1; while (it.hasNext()) { NodeImpl node = (NodeImpl)it.nextNode(); long actualNodeIndex = node.getProperty(INDEX_PROPERTY).getLong(); assertEquals("Iterator must return nodes ordered by \"order num\". Occurred at: child" + actualNodeIndex + " expecting <" + i + ">", i, actualNodeIndex); i--; number++; } } int i = 0; for (;it.hasNext();i++) { Node node = it.nextNode(); long actualNodeIndex = node.getProperty(INDEX_PROPERTY).getLong(); assertEquals("Check the Property: Iterator must return nodes ordered by \"order num\". Occurred at: child" + actualNodeIndex + " expecting <" + i + ">", i, actualNodeIndex); } // assert all returned assertEquals( "Iterator returned wrong number of nodes. Expected: " + expectedSize + ", but returned " + number, expectedSize, number); } @Override protected void tearDown() throws Exception { session.refresh(false); testRoot.remove(); session.save(); super.tearDown(); } }