/*
*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.lock;
import org.apache.commons.logging.Log;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.transaction.TransactionSetup;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.fail;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import javax.transaction.UserTransaction;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Verifies that there are no read locks held when a transaction ends.
*
* @author Bela Ban
* @version $Id: LockReleaseTest.java 5506 2008-04-07 09:51:48Z manik.surtani@jboss.com $
*/
@Test(groups = "functional")
public class LockReleaseTest
{
CacheSPI<Object, Object> cache = null;
UserTransaction tx = null;
Log log;
final Fqn<String> NODE1 = Fqn.fromString("/test");
final Fqn<String> NODE2 = Fqn.fromString("/my/test");
final String KEY = "key";
final String VAL1 = "val1";
final String VAL2 = "val2";
@BeforeMethod(alwaysRun = true)
public void setUp() throws Exception
{
tx = TransactionSetup.getUserTransaction();
}
@AfterMethod(alwaysRun = true)
public void tearDown() throws Exception
{
if (cache != null)
{
cache.stop();
}
// BW. kind of a hack to destroy jndi binding and thread local tx before next run.
TransactionSetup.cleanup();
if (tx != null)
{
try
{
tx.rollback();
}
catch (Throwable t)
{
}
tx = null;
}
}
CacheSPI<Object, Object> createCache(IsolationLevel level) throws Exception
{
CacheSPI<Object, Object> c = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(false);
c.getConfiguration().setClusterName("test");
c.getConfiguration().setStateRetrievalTimeout(10000);
c.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup");
c.getConfiguration().setLockAcquisitionTimeout(500);
c.getConfiguration().setIsolationLevel(level);
c.create();
c.start();
return c;
}
public void testReadWithReadUncommitted() throws Exception
{
testReadLockRelease(IsolationLevel.READ_UNCOMMITTED);
}
public void testWriteWithReadUncommitted() throws Exception
{
testWriteLockRelease(IsolationLevel.READ_UNCOMMITTED);
}
public void testReadWithReadCommitted() throws Exception
{
testReadLockRelease(IsolationLevel.READ_COMMITTED);
}
public void testWriteWithReadCommitted() throws Exception
{
testWriteLockRelease(IsolationLevel.READ_COMMITTED);
}
public void testReadWithRepeatableRead() throws Exception
{
testReadLockRelease(IsolationLevel.REPEATABLE_READ);
}
public void testWriteWithRepeatableRead() throws Exception
{
testWriteLockRelease(IsolationLevel.REPEATABLE_READ);
}
public void testReadWithSerialzable() throws Exception
{
testReadLockRelease(IsolationLevel.SERIALIZABLE);
}
public void testWriteWithSerializable() throws Exception
{
testWriteLockRelease(IsolationLevel.SERIALIZABLE);
}
public void testGetKeys() throws Exception
{
cache = createCache(IsolationLevel.REPEATABLE_READ);
// add initial values outside of TX
cache.put(NODE1, KEY, VAL1);
cache.put(NODE2, KEY, VAL1);
assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld());
Set<Object> keys = cache.getNode(NODE1).getKeys();
System.out.println("keys of " + NODE1 + " are " + keys);
assertEquals("getKeys() called outside the TX should have released all locks", 0, cache.getNumberOfLocksHeld());
tx.begin();
keys = cache.getNode(NODE1).getKeys();
assertEquals("we should hold 1 read locks now: ", 2, cache.getNumberOfLocksHeld());
keys = cache.getNode(NODE2).getKeys();
assertEquals("we should hold 3 read locks now: ", 4, cache.getNumberOfLocksHeld());
tx.commit();
assertEquals("we should have released all 3 read locks: ", 0, cache.getNumberOfLocksHeld());
}
public void testGetChildrenNames() throws Exception
{
cache = createCache(IsolationLevel.REPEATABLE_READ);
// add initial values outside of TX
cache.put(NODE1, KEY, VAL1);
cache.put(NODE2, KEY, VAL1);
assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld());
Set<Object> keys = cache.getNode(NODE2).getChildrenNames();
System.out.println("keys of " + NODE2 + " are " + keys);
assertEquals("getChildrenNames() called outside the TX should have released all locks", 0,
cache.getNumberOfLocksHeld());
tx.begin();
keys = cache.getNode(NODE1).getChildrenNames();
assertEquals("we should hold 1 read locks now: ", 2, cache.getNumberOfLocksHeld());
keys = cache.getNode(NODE2).getChildrenNames();
assertEquals("we should hold 3 read locks now: ", 4, cache.getNumberOfLocksHeld());
tx.commit();
assertEquals("we should have released all 3 read locks: ", 0, cache.getNumberOfLocksHeld());
}
public void testPrint() throws Exception
{
cache = createCache(IsolationLevel.REPEATABLE_READ);
// add initial values outside of TX
cache.put(NODE1, KEY, VAL1);
cache.put(NODE2, KEY, VAL1);
assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld());
System.out.println(cache.getNode(NODE1));
assertEquals("print() called outside the TX should have released all locks", 0, cache.getNumberOfLocksHeld());
tx.begin();
System.out.println(cache.getNode(NODE1));
assertEquals("we should hold 1 read locks now (for print()): ", 2, cache.getNumberOfLocksHeld());
System.out.println(cache.getNode(NODE2));
assertEquals("we should hold 3 read locks now (for print()): ", 4, cache.getNumberOfLocksHeld());
tx.commit();
assertEquals("we should have released all 3 read locks: ", 0, cache.getNumberOfLocksHeld());
}
private void testReadLockRelease(IsolationLevel level) throws Exception
{
cache = createCache(level);
// add initial values outside of TX
cache.put(NODE1, KEY, VAL1);
cache.put(NODE2, KEY, VAL1);
assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld());
tx.begin();
assertEquals(VAL1, cache.get(NODE1, KEY));
assertEquals(VAL1, cache.get(NODE2, KEY));
assertEquals("we should hold 3 read locks now: ", 4, cache.getNumberOfLocksHeld());
tx.commit();
assertEquals("we should have released all 3 read locks: ", 0, cache.getNumberOfLocksHeld());
}
private void testWriteLockRelease(IsolationLevel level) throws Exception
{
cache = createCache(level);
// add initial values outside of TX
cache.put(NODE1, KEY, VAL1);
cache.put(NODE2, KEY, VAL1);
assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld());
tx.begin();
cache.put(NODE1, KEY, VAL1);
cache.put(NODE2, KEY, VAL1);
assertEquals("we should hold 3 write locks now: ", 4, cache.getNumberOfLocksHeld());
tx.commit();
assertEquals("we should have released all 3 write locks: ", 0, cache.getNumberOfLocksHeld());
}
/**
* Tests that when an acquisition timeout occurs locks are being released.
*/
@Test(invocationCount = 10)
public void testNodeReleaseOnAcquisitionTimeout() throws Exception
{
cache = createCache(IsolationLevel.REPEATABLE_READ);
cache.put("/a/b", "key", "value");
cache.put("/c", "key", "value");
final CountDownLatch rLockAcquired = new CountDownLatch(1);
final CountDownLatch wlTimeouted = new CountDownLatch(1);
final CountDownLatch txLocksReleased = new CountDownLatch(1);
Thread thread = new Thread()
{
public void run()
{
try
{
cache.getTransactionManager().begin();
cache.get("/a/b", "key"); //at this point we have an RL on /c and /c/d
rLockAcquired.countDown();
wlTimeouted.await(60, TimeUnit.SECONDS); //wait a long time but die eventually
cache.getTransactionManager().commit();//here we are releasing locks
txLocksReleased.countDown();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
};
thread.start();
rLockAcquired.await();
try
{
cache.move("/a/b", "c"); //acquired RL on /a and /a/b
fail("expected timeout here");
}
catch (TimeoutException e)
{
wlTimeouted.countDown();
}
txLocksReleased.await(); //wait for tx locks to be released
assertEquals(0, cache.getNumberOfLocksHeld());
System.out.println("LockReleaseTest.testNodeReleaseOnAcquisitionTimeout finished!");
}
}