/*
* Created on 17-Feb-2005
*
*/
package org.jboss.cache.optimistic;
import junit.framework.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.GlobalTransaction;
import org.jboss.cache.OptimisticTransactionEntry;
import org.jboss.cache.TransactionTable;
import org.jboss.cache.TreeCache;
import org.jboss.cache.config.Option;
import org.jboss.cache.interceptors.Interceptor;
import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor;
import org.jboss.cache.interceptors.OptimisticNodeInterceptor;
import org.jboss.cache.interceptors.OptimisticReplicationInterceptor;
import org.jboss.cache.interceptors.TxInterceptor;
import org.jboss.cache.loader.SamplePojo;
import org.jboss.cache.marshall.MethodCallFactory;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jboss.cache.transaction.DummyTransactionManager;
import org.jgroups.Address;
import org.jgroups.blocks.MethodCall;
import javax.transaction.RollbackException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.List;
import java.util.HashMap;
/**
* Tests cache operations on optimistic cache
*
* @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
* @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a>
*/
public class CacheTest extends AbstractOptimisticTestCase
{
Log log = LogFactory.getLog(CacheTest.class);
public CacheTest(String s)
{
super(s);
}
public void testExplicitTxFailure() throws Exception
{
final TreeCache c = createCache();
// explicit.
TransactionManager mgr = c.getTransactionManager();
try
{
mgr.begin();
c.put("/a", "k", "v");
Transaction t = mgr.suspend();
c.put("/a", "k2", "v2");
mgr.resume(t);
mgr.commit();
Assert.assertTrue("Expecting a rollback exception!", false);
}
catch (RollbackException re)
{
Assert.assertTrue("Expecting a rollback exception!", true);
}
c.stopService();
}
public void testImplicitTxFailure() throws Exception
{
final TreeCache c = createCache();
// implicit (much harder to orchestrate...
int numThreads = 50;
ExceptionThread thread[] = new ExceptionThread[numThreads];
for (int i = 0; i < numThreads; i++)
{
thread[i] = new ExceptionThread()
{
public void run()
{
try
{
c.put("/a", "k", "v");
}
catch (Exception e)
{
log.fatal("*** Thew an exception!!", e);
setException(e);
}
}
};
}
for (int i = 0; i < numThreads; i++) thread[i].start();
for (int i = 0; i < numThreads; i++) thread[i].join();
// test exceptions.
for (int i = 0; i < numThreads; i++)
{
Assert.assertNull("Thread " + thread[i].getName() + " threw exception!", thread[i].getException());
}
c.stopService();
}
public void testLocalTransaction() throws Exception
{
TreeCache cache = createCacheWithListener();
Interceptor txInterceptor = new TxInterceptor();
txInterceptor.setCache(cache);
Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
replicationInterceptor.setCache(cache);
Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
createInterceptor.setCache(cache);
Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
nodeInterceptor.setCache(cache);
MockInterceptor dummy = new MockInterceptor();
dummy.setCache(cache);
txInterceptor.setNext(replicationInterceptor);
replicationInterceptor.setNext(createInterceptor);
createInterceptor.setNext(nodeInterceptor);
nodeInterceptor.setNext(dummy);
cache.setInterceptorChain(txInterceptor);
DummyTransactionManager mgr = DummyTransactionManager.getInstance();
assertNull(mgr.getTransaction());
mgr.begin();
assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());
SamplePojo pojo = new SamplePojo(21, "test");
cache.put("/one/two", "key1", pojo);
mgr.commit();
assertNull(mgr.getTransaction());
assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());
//make sure all calls were done in right order
List calls = dummy.getAllCalled();
assertEquals(MethodDeclarations.optimisticPrepareMethod, calls.get(0));
assertEquals(MethodDeclarations.commitMethod, calls.get(1));
//flesh this out a bit more
destroyCache(cache);
}
public void testRollbackTransaction() throws Exception
{
TreeCache cache = createCacheWithListener();
Interceptor txInterceptor = new TxInterceptor();
txInterceptor.setCache(cache);
Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
replicationInterceptor.setCache(cache);
Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
createInterceptor.setCache(cache);
Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
nodeInterceptor.setCache(cache);
MockInterceptor dummy = new MockInterceptor();
dummy.setCache(cache);
txInterceptor.setNext(replicationInterceptor);
replicationInterceptor.setNext(createInterceptor);
createInterceptor.setNext(nodeInterceptor);
nodeInterceptor.setNext(dummy);
cache.setInterceptorChain(txInterceptor);
DummyTransactionManager mgr = DummyTransactionManager.getInstance();
assertNull(mgr.getTransaction());
assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());
SamplePojo pojo = new SamplePojo(21, "test");
mgr.begin();
cache.put("/one/two", "key1", pojo);
mgr.rollback();
assertNull(mgr.getTransaction());
assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());
//make sure all calls were done in right order
List calls = dummy.getAllCalled();
assertEquals(1, calls.size());
assertEquals(MethodDeclarations.rollbackMethod, calls.get(0));
destroyCache(cache);
}
public void testRemotePrepareTransaction() throws Exception
{
TreeCache cache = createCacheWithListener();
Interceptor txInterceptor = new TxInterceptor();
txInterceptor.setCache(cache);
Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
replicationInterceptor.setCache(cache);
Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
createInterceptor.setCache(cache);
Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
nodeInterceptor.setCache(cache);
MockInterceptor dummy = new MockInterceptor();
dummy.setCache(cache);
txInterceptor.setNext(replicationInterceptor);
replicationInterceptor.setNext(createInterceptor);
createInterceptor.setNext(nodeInterceptor);
nodeInterceptor.setNext(dummy);
cache.setInterceptorChain(txInterceptor);
DummyTransactionManager mgr = DummyTransactionManager.getInstance();
//start local transaction
mgr.begin();
Transaction tx = mgr.getTransaction();
//this sets
cache.getCurrentTransaction(tx);
SamplePojo pojo = new SamplePojo(21, "test");
cache.put("/one/two", "key1", pojo);
GlobalTransaction gtx = cache.getCurrentTransaction(tx);
TransactionTable table = cache.getTransactionTable();
OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);
assertNotNull(mgr.getTransaction());
mgr.commit();
GlobalTransaction remoteGtx = new GlobalTransaction();
remoteGtx.setAddress(new Address()
{
public int compareTo(Object arg0)
{
return 0;
}
public void readFrom(DataInputStream arg0) throws IOException,
IllegalAccessException, InstantiationException
{
}
public void writeTo(DataOutputStream arg0) throws IOException
{
}
public void readExternal(ObjectInput arg0) throws IOException,
ClassNotFoundException
{
}
public void writeExternal(ObjectOutput arg0) throws IOException
{
}
public int size()
{
return 0;
}
public boolean isMulticastAddress()
{
return false;
}
});
//hack the method call to make it have the remote gtx
MethodCall meth = (MethodCall) entry.getModifications().get(0);
meth.getArgs()[0] = remoteGtx;
//call our remote method
MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{remoteGtx, injectDataVersion(entry.getModifications()), null, remoteGtx.getAddress(), Boolean.FALSE});
try
{
cache._replicate(prepareMethod);
}
catch (Throwable t)
{
fail();
}
//our thread should be null
assertNull(mgr.getTransaction());
// there should be a registration for the remote gtx
assertNotNull(table.get(remoteGtx));
assertNotNull(table.getLocalTransaction(remoteGtx));
//assert that this is populated
assertEquals(1, table.get(remoteGtx).getModifications().size());
//assert that the remote prepare has populated the local workspace
OptimisticTransactionEntry opEntry = (OptimisticTransactionEntry) table.get(gtx);
assertEquals(3, entry.getTransactionWorkSpace().getNodes().size());
assertEquals(1, entry.getModifications().size());
List calls = dummy.getAllCalled();
assertEquals(MethodDeclarations.optimisticPrepareMethod, calls.get(2));
assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions());
assertEquals(1, cache.getTransactionTable().getNumLocalTransactions());
destroyCache(cache);
}
public void testRemoteCacheBroadcast() throws Exception
{
TreeCache cache = createReplicatedCache(TreeCache.REPL_SYNC);
TreeCache cache2 = createReplicatedCache(TreeCache.REPL_SYNC);
DummyTransactionManager mgr = DummyTransactionManager.getInstance();
//start local transaction
mgr.begin();
Transaction tx = mgr.getTransaction();
//this sets
GlobalTransaction gtx = cache.getCurrentTransaction(tx);
SamplePojo pojo = new SamplePojo(21, "test");
cache.put("/one/two", "key1", pojo);
//GlobalTransaction gtx = cache.getCurrentTransaction(tx);
TransactionTable table = cache.getTransactionTable();
assertNotNull(mgr.getTransaction());
mgr.commit();
assertNull(mgr.getTransaction());
//assert that the local cache is in the right state
assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());
assertTrue(cache.exists(Fqn.fromString("/one/two")));
assertTrue(cache.exists(Fqn.fromString("/one")));
assertEquals(pojo, cache.get(Fqn.fromString("/one/two"), "key1"));
assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions());
assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions());
assertTrue(cache2.exists(Fqn.fromString("/one/two")));
assertTrue(cache2.exists(Fqn.fromString("/one")));
assertEquals(pojo, cache2.get(Fqn.fromString("/one/two"), "key1"));
destroyCache(cache);
destroyCache(cache2);
}
public void testTwoWayRemoteCacheBroadcast() throws Exception
{
TreeCache cache = createReplicatedCache(TreeCache.REPL_SYNC);
TreeCache cache2 = createReplicatedCache(TreeCache.REPL_SYNC);
DummyTransactionManager mgr = DummyTransactionManager.getInstance();
//start local transaction
mgr.begin();
Transaction tx = mgr.getTransaction();
//this sets
cache.getCurrentTransaction(tx);
SamplePojo pojo = new SamplePojo(21, "test");
cache.put("/one/two", "key1", pojo);
GlobalTransaction gtx = cache.getCurrentTransaction(tx);
TransactionTable table = cache.getTransactionTable();
assertNotNull(mgr.getTransaction());
mgr.commit();
assertNull(mgr.getTransaction());
//assert that the local cache is in the right state
assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());
assertTrue(cache.exists(Fqn.fromString("/one/two")));
assertTrue(cache.exists(Fqn.fromString("/one")));
assertEquals(pojo, cache.get(Fqn.fromString("/one/two"), "key1"));
assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions());
assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions());
assertTrue(cache2.exists(Fqn.fromString("/one/two")));
assertTrue(cache2.exists(Fqn.fromString("/one")));
assertEquals(pojo, cache2.get(Fqn.fromString("/one/two"), "key1"));
destroyCache(cache);
destroyCache(cache2);
}
public void testRemotePessCacheBroadcast() throws Exception
{
TreeCache cache = createPessimisticCache();
TreeCache cache2 = createPessimisticCache();
DummyTransactionManager mgr = DummyTransactionManager.getInstance();
//start local transaction
mgr.begin();
Transaction tx = mgr.getTransaction();
//this sets
cache.getCurrentTransaction(tx);
SamplePojo pojo = new SamplePojo(21, "test");
cache.put("/one/two", "key1", pojo);
mgr.commit();
destroyCache(cache);
destroyCache(cache2);
}
public void testConcurrentNodeRemoval() throws Exception
{
TreeCache cache = createCache();
cache.put(fqn, "key", "value");
// now start a tx to change the value in fqn
TransactionManager mgr = cache.getTransactionManager();
mgr.begin();
cache.put(fqn, "key2", "value2");
Transaction tx = mgr.suspend();
// now remove the original node...
cache.remove(fqn);
mgr.resume(tx);
// now try and commit this - this should fail.
boolean ok = false;
try
{
mgr.commit();
}
catch (RollbackException rbe)
{
ok = true;
}
Assert.assertTrue("Concurrent mod should result in a rollback", ok);
// now assert that the node has in fact been removed.
Assert.assertTrue("The node should have been removed!", !cache.exists(fqn));
}
public void testConcurrentNodeModification() throws Exception
{
TreeCache cache = createCache();
cache.put(fqn, "key", "value");
// now start a tx to change the value in fqn
TransactionManager mgr = cache.getTransactionManager();
mgr.begin();
cache.put(fqn, "key2", "value2");
Transaction tx = mgr.suspend();
// now change the original node...
cache.put(fqn, "key3", "value3");
mgr.resume(tx);
// now try and commit this - this should fail.
boolean ok = false;
try
{
mgr.commit();
}
catch (RollbackException rbe)
{
ok = true;
}
Assert.assertTrue("Concurrent mod should result in a rollback", ok);
}
public void testRemoveAndCreate() throws Exception
{
TreeCache cache = createCache();
TransactionManager tm = cache.getTransactionManager();
Fqn f = Fqn.fromString("/person/test");
Fqn f1 = new Fqn(f, "1");
Fqn f2 = new Fqn(f, "2");
tm.begin();
cache.put(f1, "test", "test");
cache.put(f2, "test", "test");
tm.commit();
assertEquals(2, cache.get(f).getChildren().size());
tm.begin();
cache.remove(f1);
cache.put(f1, "test2", "test2");
tm.commit();
assertEquals("test2", cache.get(f1, "test2"));
assertEquals(2, cache.get(f).getChildren().size());
destroyCache(cache);
}
public void testRemoveChildAfterRemoveParent() throws Exception
{
TreeCache c = createCache();
TransactionManager tm = c.getTransactionManager();
c.put(Fqn.fromString("/a/b"), "k", "v");
tm.begin();
c.remove(Fqn.fromString("/a"));
c.remove(Fqn.fromString("/a/b"));
tm.commit();
destroyCache(c);
}
public void testAddChildAfterRemoveParent() throws Exception
{
TreeCache c = createCache();
TransactionManager tm = c.getTransactionManager();
c.put(Fqn.fromString("/a/b"), "k", "v");
tm.begin();
c.remove(Fqn.fromString("/a"));
c.put(Fqn.fromString("/a/b"), "k", "v");
tm.commit();
destroyCache(c);
}
}