package org.jboss.test.cache.test.local;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.jboss.cache.CacheException;
import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.TreeCache;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.lock.TimeoutException;
import org.jboss.cache.transaction.DummyTransactionManager;
import org.jboss.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.transaction.UserTransaction;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
public class TxConcurrentBankUnitTestCase extends TestCase {
TreeCache cache;
private static Logger logger_ = Logger.getLogger(TxConcurrentBankUnitTestCase.class);
static Properties p = null;
String old_factory = null;
final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory";
final String NODE = "/cachetest";
final int ROLLBACK_CHANCE = 100;
static String customer[] = { "cu1", "cu2", "cu3" };
static final int BOOKINGS = 1000;
static boolean _testFailedinThread = false;
public TxConcurrentBankUnitTestCase(String name)
{
super(name);
}
public void failMain() {
_testFailedinThread=true;
}
public void setUp() throws Exception
{
super.setUp();
old_factory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY);
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY);
DummyTransactionManager.getInstance();
if (p == null) {
p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
}
cache = new TreeCache();
PropertyConfigurator config = new PropertyConfigurator();
config.configure(cache, "META-INF/local-eviction-service.xml");
cache.setIsolationLevel(IsolationLevel.SERIALIZABLE);
cache.createService();
cache.startService();
}
public void tearDown() throws Exception
{
super.tearDown();
cache.stopService();
DummyTransactionManager.destroy();
if (old_factory != null) {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory);
old_factory = null;
}
}
public void testConcurrentBooking()
{
Teller one, two;
try {
if(cache.get(NODE)==null) {
cache.put(NODE, "cu1", new Integer(1000));
cache.put(NODE, "cu2", new Integer(1000));
cache.put(NODE, "cu3", new Integer(1000));
}
one = new Teller("one", cache);
two = new Teller("two", cache);
one.start();
sleep(100);
two.start();
one.join();
two.join();
log("lock info:\n" + cache.printLockInfo()+_testFailedinThread);
if(_testFailedinThread) fail();
} catch (Exception e) {
e.printStackTrace();
fail(e.toString());
} finally {
}
}
void sleep(long timeout)
{
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
}
}
static void log(String msg)
{
logger_.info("-- [" + Thread.currentThread() + "]: " + msg);
}
public static Test suite()
{
return new TestSuite(TxConcurrentBankUnitTestCase.class);
}
public static void main(String[] args)
{
junit.textui.TestRunner.run(suite());
}
class Teller extends Thread
{
TreeCache cache;
public Teller(String str, TreeCache cache)
{
super(str);
this.cache = cache;
}
public void run()
{
int count = customer.length;
UserTransaction tx = null;
try {
tx = (UserTransaction) new InitialContext(p).lookup("UserTransaction");
boolean again = false;
int src = 0;
int dst = 0;
int amo = 0;
int anz =0;
while(anz<BOOKINGS) {
if(!again) {
src = (int) (Math.random()*count);
dst = (int) (Math.random()*(count-1));
amo =1+ (int) (Math.random()*20);
if(dst>=src) dst++;
}
tx.begin();
HashMap accounts = getAccounts(); tx.commit();
int sum = sumAccounts(accounts);
log(anz+": "+accounts+" Summe: "+sum);
if(sum!=3000) {
failMain();
return; }
assertEquals("the sum of all accounts always has to be 3000", 3000, sum);
try {
tx.begin();
deposit(customer[src], customer[dst], amo, tx); tx.commit(); again = false;
}
catch(TimeoutException timeout_ex) {
System.out.println("transaction is rolled back, will try again (ex=" + timeout_ex.getClass() + ")");
tx.rollback();
again = true;
}
catch(Throwable e) {
System.out.println("transaction is rolled back, will try again (ex=" + e.getMessage() + ")");
tx.rollback();
again = true;
}
anz++;
yield();
}
} catch (Throwable t) {
t.printStackTrace();
fail(t.toString());
}
}
public void deposit(String from, String to, int amount, UserTransaction tx) throws Exception {
log("deposit("+from+", "+to+", "+amount+") called.");
int act;
act = ((Integer) cache.get(NODE, from)).intValue();
cache.put(NODE, from, new Integer(act-amount));
log("deposit("+from+", "+to+", "+amount+") debited.");
if((int) (Math.random()*ROLLBACK_CHANCE) == 0) {
log("!!!manually set rollback ("+from+", "+to+", "+amount+").");
tx.setRollbackOnly();
throw new Exception("Manually set rollback!");
}
act = ((Integer) cache.get(NODE, to)).intValue();
cache.put(NODE, to, new Integer(act+amount));
log("deposit("+from+", "+to+", "+amount+") finished.");
}
public HashMap getAccounts() throws CacheException {
log("getAccounts() called.");
HashMap result = new HashMap();
try {
Set set = cache.getKeys(NODE); Iterator iter = set.iterator();
while(iter.hasNext()) {
String name = (String) iter.next();
result.put(name, cache.get(NODE, name));
}
return result;
} catch(CacheException ce) {
throw ce;
}
}
protected int sumAccounts(HashMap map) {
Iterator iter = map.values().iterator();
int result = 0;
while(iter.hasNext()) {
result += ((Integer) iter.next()).intValue();
}
return result;
}
}
}