/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.optimistic;
import EDU.oswego.cs.dl.util.concurrent.Latch;
import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArrayList;
import org.jboss.cache.Fqn;
import org.jboss.cache.OptimisticTreeNode;
import org.jboss.cache.TreeCache;
import org.jboss.cache.interceptors.Interceptor;
import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor;
import org.jboss.cache.interceptors.TxInterceptor;
import org.jboss.cache.marshall.JBCMethodCall;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jboss.cache.misc.TestingUtil;
import org.jgroups.blocks.MethodCall;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
*/
public class ConcurrentTransactionTest extends AbstractOptimisticTestCase
{
private TreeCache cache;
private Fqn f = Fqn.fromString("/a/b");
private List exceptions = new CopyOnWriteArrayList();
public ConcurrentTransactionTest(String name)
{
super(name);
}
public void setUp()
{
try
{
//cache = createCache();
cache = createCacheUnstarted();
cache.setUseRegionBasedMarshalling(true);
cache.startService();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void tearDown()
{
if (cache != null)
{
cache.stopService();
cache = null;
}
exceptions.clear();
}
public void testConcurrentTransactions() throws Exception
{
TransactionManager tm = cache.getTransactionManager();
cache.put("/a/b/c/d", key, value);
assertEquals(value, cache.get("/a/b/c/d", key));
tm.begin();
Transaction tx = tm.getTransaction();
cache.put("/a/b/x/y", key, value);
tm.suspend();
// a number of random puts in unrelated sub nodes.
cache.put("/a/b/c/d", key, value+value);
cache.put("/a/b/c/e", key, value);
cache.put("/a/b/c/f", key, value);
cache.put("/a/b/c/g", key, value);
assertEquals(value+value, cache.get("/a/b/c/d", key));
assertEquals(value, cache.get("/a/b/c/e", key));
assertEquals(value, cache.get("/a/b/c/f", key));
assertEquals(value, cache.get("/a/b/c/g", key));
tm.resume(tx);
tx.commit();
assertEquals(value, cache.get("/a/b/x/y", key));
OptimisticTreeNode n = (OptimisticTreeNode) cache.get(Fqn.ROOT);
System.out.println(n.getVersion());
}
public void testConcurrentCreationTestWithEmptyCache() throws Exception
{
doConcurrentCreationTest(false);
}
public void testConcurrentCreationTestWithEmptyCacheActivated() throws Exception
{
assertNotNull("Region manager is null!", cache.getRegionManager());
cache.activateRegion("/parent");
assertTrue(cache.exists("/parent"));
doConcurrentCreationTest(false);
}
public void testConcurrentCreationTestWithPopulatedCache() throws Exception
{
doConcurrentCreationTest(true);
}
public void testConcurrentReadAndRemove() throws Exception
{
final List exceptions = new LinkedList();
final Latch readerLatch = new Latch();
final Latch readerFinishedLatch = new Latch();
final Fqn fqn = Fqn.fromString("/parent/child");
cache.put(fqn, "k", "v");
class Reader extends Thread
{
public void run()
{
try
{
cache.getTransactionManager().begin();
cache.get(fqn, "k"); // read
readerFinishedLatch.release();
readerLatch.acquire(); // wait
cache.getTransactionManager().commit();
}
catch (Exception e)
{
e.printStackTrace();
exceptions.add(e);
}
}
}
Thread reader = new Reader();
reader.start();
readerFinishedLatch.acquire();
cache.remove(fqn.getParent());
assertFalse(cache.exists(fqn.getParent()));
readerLatch.release();
reader.join();
assertTrue("Should not have caught any exceptions!!", exceptions.isEmpty());
}
public void testConcurrentPutReadAndRemove() throws Exception
{
final List exceptions = new LinkedList();
final Latch readerLatch = new Latch();
final Latch readerFinishedLatch = new Latch();
final Fqn fqn = Fqn.fromString("/parent/child");
cache.put(fqn, "k", "v");
class Reader extends Thread
{
public void run()
{
try
{
cache.getTransactionManager().begin();
cache.put(Fqn.ROOT, "x", "y"); // a dummy put to ensure that validation occurs
cache.get(fqn, "k"); // read
readerFinishedLatch.release();
readerLatch.acquire(); // wait
cache.getTransactionManager().commit();
}
catch (Exception e)
{
e.printStackTrace();
exceptions.add(e);
}
}
}
Thread reader = new Reader();
reader.start();
readerFinishedLatch.acquire();
cache.remove(fqn.getParent());
assertFalse(cache.exists(fqn.getParent()));
readerLatch.release();
reader.join();
assertTrue("Should not have caught any exceptions!!", exceptions.isEmpty());
}
private void doConcurrentCreationTest(boolean prepopulateParent) throws Exception
{
if (prepopulateParent) cache.put("/parent/dummy", "k", "v");
final List exceptions = new LinkedList();
final Latch latch = new Latch();
class ConcurrentCreator extends Thread
{
private String name;
public ConcurrentCreator(String name)
{
this.name = name;
}
public void run()
{
try
{
cache.getTransactionManager().begin();
cache.put("/parent/child" + name, "key", "value");
latch.acquire();
cache.getTransactionManager().commit();
}
catch (Exception e)
{
e.printStackTrace();
exceptions.add(e);
}
}
}
Thread one = new ConcurrentCreator("one");
Thread two = new ConcurrentCreator("two");
one.start();
two.start();
latch.release();
one.join();
two.join();
assertTrue("Should not have caught any exceptions!!", exceptions.isEmpty());
}
public void testConcurrentCreationWithoutTx() throws Exception
{
final String slowThreadName = "SLOW";
Interceptor slowdownInterceptor = new Interceptor()
{
public Object invoke(MethodCall c) throws Throwable
{
if (Thread.currentThread().getName().equals(slowThreadName))
{
Thread.sleep(1000);
}
return super.invoke(c);
}
};
List interceptors = cache.getInterceptors();
int i=0;
for (i=0; i<interceptors.size(); i++)
{
if (interceptors.get(i) instanceof OptimisticCreateIfNotExistsInterceptor) break;
}
Interceptor prev = (Interceptor) interceptors.get(i);
Interceptor next = (Interceptor) interceptors.get(i+1);
slowdownInterceptor.setNext(next);
prev.setNext(slowdownInterceptor);
cache.setInterceptorChain((Interceptor) interceptors.get(0));
System.out.println(cache.getInterceptorChain());
// now create 2 threads to do concurrent puts.
Putter slow = new Putter(slowThreadName);
Putter fast = new Putter("FAST");
// start the slow putter first
slow.start();
TestingUtil.sleepThread(200);
fast.start();
fast.join();
slow.join();
for (Iterator e = exceptions.iterator() ; e.hasNext() ;) ((Exception) e.next()).printStackTrace();
assertEquals(0, exceptions.size());
}
public void testConcurrentDeletionWithoutTx() throws Exception
{
final String slowThreadName = "SLOW";
Interceptor slowdownInterceptor = new Interceptor()
{
public Object invoke(MethodCall c) throws Throwable
{
if (Thread.currentThread().getName().equals(slowThreadName) &&
((JBCMethodCall) c).getMethodId() == MethodDeclarations.optimisticPrepareMethod_id)
{
Thread.sleep(1000);
}
return super.invoke(c);
}
};
List interceptors = cache.getInterceptors();
int i=0;
for (i=0; i<interceptors.size(); i++)
{
if (interceptors.get(i) instanceof TxInterceptor) break;
}
Interceptor prev = (Interceptor) interceptors.get(i);
Interceptor next = (Interceptor) interceptors.get(i+1);
slowdownInterceptor.setNext(next);
prev.setNext(slowdownInterceptor);
cache.setInterceptorChain((Interceptor) interceptors.get(0));
System.out.println(cache.getInterceptorChain());
// now create 2 threads to do concurrent puts.
Remover slow = new Remover(slowThreadName);
Remover fast = new Remover("FAST");
cache.put(f, "hello", "world");
// start the slow putter first
slow.start();
TestingUtil.sleepThread(200);
fast.start();
fast.join();
slow.join();
for (Iterator e = exceptions.iterator() ; e.hasNext() ;) ((Exception) e.next()).printStackTrace();
assertEquals(0, exceptions.size());
}
public class Putter extends Thread
{
public Putter(String name)
{
super(name);
}
public void run()
{
try
{
assertFalse(cache.exists(f));
cache.put(new Fqn(f, getName()), "a", "b");
assertTrue(cache.exists(f));
}
catch (Exception e)
{
exceptions.add(e);
}
}
}
public class Remover extends Thread
{
public Remover(String name)
{
super(name);
}
public void run()
{
try
{
cache.getTransactionManager().begin();
cache.remove(f);
cache.getTransactionManager().commit();
assertFalse(cache.exists(f));
}
catch (Exception e)
{
exceptions.add(e);
}
}
}
}