/*
*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
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.aop.test.Address;
import org.jboss.cache.aop.test.Person;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.lock.LockStrategyFactory;
import org.jboss.cache.lock.UpgradeException;
import org.jboss.cache.misc.TestingUtil;
import org.jboss.cache.transaction.DummyTransactionManager;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.transaction.UserTransaction;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Random;
/**
* Local mode stress test for PojoCache. Test getObject and putObject under load
* and concurrency.
*
* @version $Revision: 3157 $
* @author<a href="mailto:bwang@jboss.org">Ben Wang</a> December 2004
*/
public class LocalConcurrentTest extends TestCase
{
static PojoCache cache_;
int cachingMode_ = TreeCache.LOCAL;
Properties p_;
String oldFactory_ = null;
final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory";
static ArrayList nodeList_;
static final int depth_ = 2;
static final int children_ = 2;
static final int MAX_LOOP = 100;
static final int SLEEP_TIME = 50;
static Exception thread_ex=null;
UserTransaction tx_ = null;
public LocalConcurrentTest(String name)
{
super(name);
}
public void setUp() throws Exception
{
super.setUp();
oldFactory_=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");
}
tx_ = (UserTransaction)new InitialContext(p_).lookup("UserTransaction");
initCaches(TreeCache.LOCAL);
nodeList_ = nodeGen(depth_, children_);
log("LocalConcurrentTestCase: cacheMode=TRANSIENT, one cache");
}
public void tearDown() throws Exception
{
super.tearDown();
thread_ex=null;
DummyTransactionManager.destroy();
destroyCaches();
if (oldFactory_ != null) {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, oldFactory_);
oldFactory_ = null;
}
}
void initCaches(int caching_mode) throws Exception
{
cachingMode_ = caching_mode;
cache_ = new PojoCache();
PropertyConfigurator config = new PropertyConfigurator();
config.configure(cache_, "META-INF/local-service.xml"); // read in generic local xml
cache_.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
cache_.createService();
cache_.startService();
}
void destroyCaches() throws Exception
{
cache_.stopService();
cache_ = null;
}
protected void setLevelRW()
{
log("set lock level to RWUpgrade ...");
LockStrategyFactory.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
cache_.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
}
protected void setLevelSerial()
{
log("set lock level to SimpleLock ...");
LockStrategyFactory.setIsolationLevel(IsolationLevel.SERIALIZABLE);
cache_.setIsolationLevel(IsolationLevel.SERIALIZABLE);
}
public void testAll_RWLock() throws Exception
{
try
{
setLevelRW();
all();
} catch (UpgradeException ue)
{
log("Upgrade exception. Can ingore for repeatable read. " +ue);
} catch (Exception ex)
{
log("Exception: " +ex);
throw ex;
}
}
public void XtestAll_SimpleLock() throws Exception
{
setLevelSerial();
all();
}
private void all() throws Exception
{
RunThread t1 = new RunThread(1);
RunThread t2 = new RunThread(2);
RunThread t3 = new RunThread(3);
RunThread t4 = new RunThread(4);
t1.start();
TestingUtil.sleepThread(100);
t2.start();
TestingUtil.sleepThread(100);
t3.start();
TestingUtil.sleepThread(100);
t4.start();
t1.join(60000); // wait for 20 secs
t2.join(60000); // wait for 20 secs
t3.join(60000); // wait for 20 secs
t4.join(60000); // wait for 20 secs
if(thread_ex != null)
throw thread_ex;
}
class RunThread extends Thread
{
final int seed_;
Random random_;
Person person_;
public RunThread(int seed)
{
seed_ = seed;
random_ = new Random(seed);
}
private void createPerson() {
person_ = new Person();
person_.setName("Ben");
person_.setAge(18);
ArrayList lang = new ArrayList();
lang.add("English");
lang.add("French");
lang.add("Mandarin");
person_.setLanguages(lang);
Address addr = new Address();
addr.setZip(95123);
addr.setStreet("Almeria");
addr.setCity("San Jose");
person_.setAddress(addr);
}
public void run() {
try {
_run();
}
catch(Exception e) {
thread_ex=e;
}
}
/**
* Note that this operation needs to run under transaction context as a whole. If not, then different
* threads will have the chance to use the same fqn node. While one thread has finished putObject and the
* other thread can the do the same, it will screw up the reference counting (??).
* @throws Exception
*/
public void _run() throws Exception {
for (int loop = 0; loop < MAX_LOOP; loop++) {
createPerson(); // create a new person instance every loop.
TestingUtil.sleepThread(random_.nextInt(50));
try {
op1();
} catch (CacheException ex)
{
throw ex;
}
}
}
// Operation 1
private void op1() throws CacheException {
int i = random_.nextInt(nodeList_.size() - 1);
if(i==0) return; // it is meaningless to test root
String node = nodeList_.get(i) + "/aop";
cache_.putObject(node, person_);
TestingUtil.sleepThread(random_.nextInt(SLEEP_TIME)); // sleep for max 200 millis
// getObject is not thread safe since there is no way to lock the fqn atomically without triggering
// the replication.
// cache_.getObject(node);
TestingUtil.sleepThread(random_.nextInt(SLEEP_TIME)); // sleep for max 200 millis
cache_.removeObject(node);
}
}
/**
* Generate the tree nodes quasi-exponentially. I.e., depth is the level
* of the hierarchy and children is the number of children under each node.
* This strucutre is used to add, get, and remove for each node.
*/
private ArrayList nodeGen(int depth, int children)
{
ArrayList strList = new ArrayList();
ArrayList oldList = new ArrayList();
ArrayList newList = new ArrayList();
// Skip root node
oldList.add("/");
newList.add("/");
strList.add("/");
while (depth > 0) {
// Trying to produce node name at this depth.
newList = new ArrayList();
for (int i = 0; i < oldList.size(); i++) {
for (int j = 0; j < children; j++) {
String tmp = (String) oldList.get(i);
tmp += Integer.toString(j);
if (depth != 1) {
tmp += "/";
}
newList.add(tmp);
}
}
strList.addAll(newList);
oldList = newList;
depth--;
}
// let's prune out root node
for(int i=0; i < strList.size(); i++) {
if( strList.get(i).equals("/") ) {
strList.remove(i);
break;
}
}
log("Nodes generated: " + strList.size());
return strList;
}
public static Test suite() throws Exception
{
return new TestSuite(LocalConcurrentTest.class);
}
private static void log(String str)
{
System.out.println("Thread: " + Thread.currentThread() + ": " + str);
// System.out.println(str);
}
}