/*
* 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 org.jboss.cache.TreeCache;
import org.jboss.cache.interceptors.OrderedSynchronizationHandler;
import org.jboss.cache.misc.TestingUtil;
import org.jgroups.JChannel;
import org.jgroups.blocks.RpcDispatcher;
import javax.transaction.RollbackException;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
/**
* @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
*/
public class AbortionTest extends TestCase
{
private MyTC cache1, cache2, cache3;
protected void setUp() throws Exception
{
super.setUp();
System.out.println("********* START: SET UP *************");
cache1 = initCache(false);
TestingUtil.sleepThread(1500); // to ensure cache1 is the coordinator
cache2 = initCache(false);
cache3 = initCache(true);
System.out.println("********* END: SET UP *************");
}
protected void tearDown() throws Exception
{
System.out.println("********* START: TEAR DOWN *************");
destroyCache(cache3);
destroyCache(cache2);
destroyCache(cache1);
cache1 = null;
cache2 = null;
cache3 = null;
super.tearDown();
System.out.println("********* END: TEAR DOWN *************");
}
private MyTC initCache(boolean notifying) throws Exception
{
MyTC c = new MyTC();
c.setCacheMode("REPL_SYNC");
c.setClusterProperties( getJGroupsStack() );
c.setFetchStateOnStartup(false);
c.setFetchInMemoryState(false);
if (!notifying)
{
c.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
}
else
{
c.setTransactionManagerLookupClass("org.jboss.cache.transaction.NotifyingTransactionManager");
}
c.startService();
return c;
}
// we need a 'special' stack that does not attempt redelivery since we kill a channel midway during a tx in this test.
private String getJGroupsStack()
{
// return "UDP(mcast_addr=224.0.0.36;mcast_port=55566;ip_ttl=32;" +
// "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" +
// "PING(timeout=10;num_initial_members=1):" +
// "pbcast.NAKACK(gc_lag=50;max_xmit_size=8192;retransmit_timeout=10):" +
// "UNICAST(timeout=600):" +
// "FRAG(frag_size=8192;down_thread=false;up_thread=false):" +
// "pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;" +
// "shun=false;print_local_addr=true):" +
// "pbcast.STATE_TRANSFER";
return JChannel.DEFAULT_PROTOCOL_STACK;
}
private void destroyCache(MyTC c)
{
if (c != null)
{
c.stopService();
c.destroyService();
}
}
public void testSyncCaches() throws Exception
{
performTest(false, false);
}
public void testSyncCachesSyncCommitRollback() throws Exception
{
performTest(true, false);
}
/**
* Note that this tests a *remote* beforeCompletion abort - which is a part of the calling instance's afterCompletion.
*
* @throws Exception
*/
public void testAbortBeforeCompletion() throws Exception
{
performTest(true, true);
}
private void performTest(boolean syncCommitRollback, boolean abortBeforeCompletion) throws Exception
{
cache1.setSyncCommitPhase(syncCommitRollback);
cache1.setSyncRollbackPhase(syncCommitRollback);
cache2.setSyncCommitPhase(syncCommitRollback);
cache2.setSyncRollbackPhase(syncCommitRollback);
cache3.setSyncCommitPhase(syncCommitRollback);
cache3.setSyncRollbackPhase(syncCommitRollback);
TransactionManager mgr1 = cache1.getTransactionManager();
TransactionManager mgr2 = cache2.getTransactionManager();
NotifyingTransactionManager mgr3 = (NotifyingTransactionManager) cache3.getTransactionManager();
assertSame(mgr1, mgr2);
assertNotSame(mgr1, mgr3);
assertNotSame(mgr2, mgr3);
assertTrue(mgr1 instanceof DummyTransactionManager);
assertTrue(mgr2 instanceof DummyTransactionManager);
assertTrue(mgr3 instanceof NotifyingTransactionManager);
cache1.put("/test", "key", "value");
assertEquals("value", cache1.get("/test", "key"));
assertEquals("value", cache2.get("/test", "key"));
assertEquals("value", cache3.get("/test", "key"));
// replicates
final boolean fAbortBeforeCompletion = abortBeforeCompletion;
mgr3.notification = new NotifyingTransactionManager.Notification()
{
public void notify(Transaction tx) throws SystemException, RollbackException
{
final Transaction finalTx = tx;
System.out.println("Notify called.");
// add an aborting sync handler.
Synchronization abort = new Synchronization()
{
Transaction t = finalTx;
public void beforeCompletion()
{
if (fAbortBeforeCompletion)
{
cache3.myChannel.close();
System.out.println("Returning from abort.beforeCompletion");
try
{
finalTx.setRollbackOnly();
}
catch (SystemException e)
{
throw new RuntimeException("Unable to set rollback", e);
}
throw new RuntimeException("Dummy exception");
}
}
public void afterCompletion(int i)
{
if (!fAbortBeforeCompletion)
{
cache3.myChannel.close();
System.out.println("Returning from abort.afterCompletion");
throw new RuntimeException("Dummy exception");
}
}
};
OrderedSynchronizationHandler osh = OrderedSynchronizationHandler.getInstance(tx);
osh.registerAtHead(abort);
System.out.println("Added sync handler.");
}
};
mgr1.begin();
Transaction tx = mgr1.getTransaction();
cache1.put("/test", "key", "value2");
tx.commit();
TestingUtil.sleepThread(5000);
// only test cache1 and cache2. Assume cache3 has crashed out.
assertEquals(0, cache1.getNumberOfLocksHeld());
assertEquals(0, cache2.getNumberOfLocksHeld());
assertEquals("put in transaction should NOT have been rolled back", "value2", cache1.get("/test", "key"));
assertEquals("put in transaction should NOT have been rolled back", "value2", cache2.get("/test", "key"));
}
public static class MyTC extends TreeCache
{
JChannel myChannel;
RpcDispatcher myDispatcher;
public MyTC() throws Exception
{
super();
}
public void startService() throws Exception
{
super.startService();
myChannel = channel;
myDispatcher = disp;
}
}
}