/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.transaction;
import junit.framework.TestCase;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.jboss.cache.TreeCache;
import org.jboss.cache.Fqn;
import org.jboss.cache.DummyTransactionManagerLookup;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.lock.TimeoutException;
import EDU.oswego.cs.dl.util.concurrent.Latch;
import javax.transaction.Transaction;
import javax.transaction.SystemException;
import javax.transaction.NotSupportedException;
/**
* Tests READ_COMMITED isolation level.
*
* @author <a href="mailto:ovidiu@jboss.org">Ovidiu Feodorov</a>
*
* @version $Id: IsolationLevelReadCommittedNodeCreationRollbackTest.java 1300 2006-02-22 17:04:45Z msurtani $
*/
public class IsolationLevelReadCommittedNodeCreationRollbackTest extends TestCase
{
private TreeCache cache = null;
private final Fqn FQN = Fqn.fromString("/a/b/c");
private final String KEY = "key";
private final String VALUE = "value";
private volatile boolean writerFailed;
private volatile boolean readerFailed;
private volatile AssertionFailedError writerError;
private volatile AssertionFailedError readerError;
protected void setUp() throws Exception
{
super.setUp();
writerFailed = false;
readerFailed = false;
writerError = null;
readerError = null;
cache = new TreeCache();
cache.setCacheMode(TreeCache.LOCAL);
cache.setIsolationLevel(IsolationLevel.READ_COMMITTED);
cache.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
cache.startService();
}
protected void tearDown() throws Exception
{
super.tearDown();
cache.stopService();
cache.destroyService();
cache=null;
}
public void testNodeCreationRollback() throws Exception
{
final Latch secondCanWrite = new Latch();
final Latch secondCanRead = new Latch();
final Latch secondDone = new Latch();
final Latch firstCanRollback = new Latch();
final Latch firstDone = new Latch();
final Fqn PARENT = Fqn.fromString("/a");
// start a first thread and a transaction
Thread firstThread = new Thread(new Runnable()
{
public void run()
{
try
{
Transaction tx = startTransaction();
System.out.println("Writing /a/1");
// Create an empty parent node and a node with data
Fqn a1 = new Fqn(PARENT, "1");
cache.put(a1, KEY, VALUE);
// notify the second thread it can write
secondCanWrite.release();
// wait until the second thread writes and allows me to rollback or until I timeout
firstCanRollback.attempt(3000);
System.out.println("rolling back");
tx.rollback();
assertNull("a1 empty", cache.get(a1, KEY));
// notify the reading thread
secondCanRead.release();
}
catch (AssertionFailedError e)
{
writerError = e;
}
catch(Throwable t)
{
t.printStackTrace();
writerFailed = true;
}
finally
{
System.out.println("first thread exits");
secondCanWrite.release();
secondCanRead.release();
firstDone.release();
}
}
}, "FIRST");
firstThread.start();
// start a second thread; no transaction is necessary here
Thread secondThread = new Thread(new Runnable()
{
public void run()
{
try
{
// wait until the first thread has created PARENT and a child
secondCanWrite.acquire();
System.out.println("writing a2");
// create a second child under parent
Fqn a2 = new Fqn(PARENT, "2");
try
{
cache.put(a2, KEY, VALUE);
}
catch (TimeoutException good)
{
// first thread locked us out of parent
System.out.println("Prevented from writing a2 -- " +
good.getLocalizedMessage());
return;
}
// let the first thread know it can rollback
firstCanRollback.release();
// wait until the first thread rolls back.
secondCanRead.acquire();
// I should still see the value I put
assertEquals("Known issue JBCACHE-407 -- write lock not acquired on " +
"creation of an empty node", VALUE, cache.get(a2, KEY));
}
catch (AssertionFailedError e)
{
readerError = e;
}
catch(Throwable t)
{
t.printStackTrace();
readerFailed = true;
}
finally
{
System.out.println("second thread exits");
firstCanRollback.release();
secondDone.release();
}
}
}, "SECOND");
secondThread.start();
// wait for both threads to finish
secondDone.acquire();
firstDone.acquire();
// If any assertion failed, throw on the AssertionFailedError
if (readerError != null)
{
throw readerError;
}
if (writerError != null)
{
throw writerError;
}
if (readerFailed)
{
fail("The second thread exited incorrectly. Watch the log for previous stack traces");
}
if (writerFailed)
{
fail("The first thread exited incorrectly. Watch the log for previous stack traces");
}
}
private Transaction startTransaction() throws SystemException, NotSupportedException
{
DummyTransactionManager mgr=DummyTransactionManager.getInstance();
mgr.begin();
return mgr.getTransaction();
}
public static Test suite() {
return new TestSuite(IsolationLevelReadCommittedNodeCreationRollbackTest.class);
}
}