// set up the initial structure
// /a, /b, /c
// one thread tries to move /c under /a, another tries to move /c under /b
Node<Object, Object> rootNode = treeCache.getRoot();
final Fqn FQN_A = A, FQN_B = B, FQN_C = C;
Node nodeA = rootNode.addChild(FQN_A);
Node nodeB = rootNode.addChild(FQN_B);
Node nodeC = rootNode.addChild(FQN_C);
final CountDownLatch nodeReadLatch = new CountDownLatch(1);
final CountDownLatch nodeMovedLatch = new CountDownLatch(1);
// tries to move C under B right after another thread already moved C under A
Callable<Object> moveCtoB = new Callable<Object>() {
public Object call() throws Exception {
tm().begin();
try {
// ensure we already 'see' node C in this tx. this is what actually triggers the issue.
assertEquals(asSet("a", "b", "c"), treeCache.getRoot().getChildrenNames());
assertEquals(Collections.emptySet(), treeCache.getNode(FQN_C).getChildrenNames());
nodeReadLatch.countDown();
nodeMovedLatch.await();
treeCache.move(FQN_C, FQN_B);
tm().commit(); //this is expected to fail
fail("Transaction should have failed");
} catch (Exception e) {
if (tm().getTransaction() != null) {
// the TX is most likely rolled back already, but we attempt a rollback just in case it isn't
try {
tm().rollback();
} catch (SystemException e1) {
log.error("Failed to rollback", e1);
}
}
}
return null;
}
};
// moves C under A successfully
Callable<Object> moveCtoA = new Callable<Object>() {
public Object call() throws Exception {
tm().begin();
try {
try {
nodeReadLatch.await();
treeCache.move(FQN_C, FQN_A);
tm().commit();
} finally {
nodeMovedLatch.countDown();
}
} catch (Exception e) {
if (tm().getTransaction() != null) {
// the TX is most likely rolled back already, but we attempt a rollback just in case it isn't
try {
tm().rollback();
} catch (SystemException e1) {
log.error("Failed to rollback", e1);
}
}
throw e;
}
return null;
}
};
runConcurrently(moveCtoB, moveCtoA);
log.trace("Tree: " + TreeStructureSupport.printTree(treeCache, true));
assertNoLocks();
assertFalse(nodeC.isValid());
assertEquals(asSet("a", "b"), rootNode.getChildrenNames());
assertEquals(asSet("c"), nodeA.getChildrenNames());
assertEquals(Collections.emptySet(), nodeB.getChildrenNames());
}