/*
*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.transaction;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.TreeCache;
import org.jboss.cache.lock.IsolationLevel;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.UserTransaction;
import java.util.*;
/**
* Unit test for local TreeCache. Use locking and multiple threads to test
* concurrent access to the tree.
*
* @version $Id: ConcurrentTransactionalTest.java 862 2005-11-21 18:03:26Z msurtani $
*/
public class ConcurrentTransactionalTest extends TestCase
{
TreeCache cache;
private Log logger_=LogFactory.getLog(ConcurrentTransactionalTest.class);
static Throwable thread_ex=null;
final int NUM=10000;
static Properties p = null;
String old_factory = null;
final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory";
public ConcurrentTransactionalTest(String name)
{
super(name);
}
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");
}
}
private void createCache(IsolationLevel level) throws Exception
{
cache = new TreeCache();
PropertyConfigurator config = new PropertyConfigurator();
config.configure(cache, "META-INF/local-service.xml");
cache.setCacheMode(TreeCache.LOCAL);
cache.setIsolationLevel(level);
cache.start();
cache.put("/a/b/c", null);
}
private UserTransaction getTransaction() {
UserTransaction tx = null;
try {
tx = (UserTransaction) new InitialContext(p).lookup("UserTransaction");
} catch (NamingException e) {
e.printStackTrace();
}
if(tx == null)
throw new RuntimeException("Tx is null");
return tx;
}
public void tearDown() throws Exception
{
super.tearDown();
cache.stopService();
thread_ex=null;
DummyTransactionManager.destroy();
if (old_factory != null) {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory);
old_factory = null;
}
}
public void testConcurrentAccessWithRWLock() throws Throwable
{
createCache(IsolationLevel.REPEATABLE_READ);
work_();
}
public void testConcurrentAccessWithExclusiveLock() throws Throwable
{
createCache(IsolationLevel.SERIALIZABLE);
work_();
}
private void work_() throws Throwable {
Updater one, two;
try {
one = new Updater("Thread one");
two = new Updater("Thread two");
long current = System.currentTimeMillis();
one.start();
two.start();
one.join();
two.join();
if(thread_ex != null)
throw thread_ex;
long now = System.currentTimeMillis();
log("*** Time elapsed: " + (now-current));
System.out.println("cache content: " + cache.toString());
Set keys = cache.getKeys("/a/b/c");
System.out.println("number of keys=" + keys.size());
if(keys.size() != NUM) {
scanForNullValues(keys);
try {
System.out.println("size=" + keys.size());
List l=new LinkedList(keys);
Collections.sort(l);
System.out.println("keys: " + l);
for(int i=0; i < NUM; i++) {
if(!l.contains(new Integer(i)))
System.out.println("missing: " + i);
}
LinkedList duplicates=new LinkedList();
for(Iterator it=l.iterator(); it.hasNext();) {
Integer integer=(Integer)it.next();
if(duplicates.contains(integer)) {
System.out.println(integer + " is a duplicate");
}
else
duplicates.add(integer);
}
}
catch(Exception e1) {
e1.printStackTrace();
}
}
assertEquals(NUM, keys.size());
log("lock info:\n" + cache.printLockInfo());
} catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
}
private void scanForNullValues(Set keys) {
for(Iterator it=keys.iterator(); it.hasNext();) {
Object o=it.next();
if(o == null)
System.err.println("found a null value in keys");
}
}
void log(String msg)
{
logger_.debug(" [" + Thread.currentThread() + "]: " + msg);
}
class Updater extends Thread {
String val=null;
UserTransaction tx;
Updater(String name) {
this.val=name;
}
public void run() {
try {
log("adding data");
tx=getTransaction();
for(int i=0; i < NUM; i++) {
log("adding data i=" + i);
tx.begin();
cache.put("/a/b/c", new Integer(i), val);
tx.commit();
yield();
}
}
catch(Throwable t) {
t.printStackTrace();
thread_ex=t;
}
}
}
public static Test suite()
{
return new TestSuite(ConcurrentTransactionalTest.class);
}
public static void main(String[] args)
{
junit.textui.TestRunner.run(suite());
}
}