/*
* 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();
}
}