/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.test.util.test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import org.jboss.util.threadpool.BasicThreadPool;
import org.jboss.util.threadpool.Task;
import org.jboss.logging.Logger;
import junit.framework.TestCase;
/**
* Tests of thread pool with Tasks added to the pool
*
* @see org.jboss.util.threadpool.ThreadPool
* @author <a href="adrian@jboss.org">Adrian.Brock</a>
* @author Scott.Stark@jboss.org
* @version $Revision: 81036 $
*/
public class ThreadPoolTaskUnitTestCase extends TestCase
{
private static Logger log = Logger.getLogger(ThreadPoolTaskUnitTestCase.class);
/** Basic test */
static final int BASIC = 0;
/** Hold the thread after start */
static final int HOLD_START = 1;
/** The accepted stats */
Stats accepted = new Stats("Accepted");
/** The rejected stats */
Stats rejected = new Stats("Rejected");
/** The started stats */
Stats started = new Stats("Started");
/** The completed stats */
Stats completed = new Stats("Completed");
/** The stopped stats */
Stats stopped = new Stats("Stopped");
/** The thread names */
HashMap threadNames = new HashMap();
/**
* Create a new ThreadPoolTaskUnitTestCase
*
* @param name the test to run
*/
public ThreadPoolTaskUnitTestCase(String name)
{
super(name);
}
protected void setUp() throws Exception
{
log.debug("====> Starting test: " + getName());
}
protected void tearDown() throws Exception
{
log.debug("=====> Stopping test: " + getName());
}
/**
* Basic test
*/
public void testBasic() throws Exception
{
BasicThreadPool pool = new BasicThreadPool();
try
{
pool.runTask(new TestTask(BASIC, "test"));
completed.wait(1);
HashSet expected = makeExpected(new Object[] {"test"});
assertEquals(expected, accepted.tasks);
assertEquals(expected, started.tasks);
assertEquals(expected, completed.tasks);
}
finally
{
pool.stop(true);
}
}
/**
* Multiple Basic test
*/
public void testMultipleBasic() throws Exception
{
BasicThreadPool pool = new BasicThreadPool();
try
{
pool.runTask(new TestTask(BASIC, "test1"));
pool.runTask(new TestTask(BASIC, "test2"));
pool.runTask(new TestTask(BASIC, "test3"));
completed.wait(3);
HashSet expected = makeExpected(new Object[] {"test1", "test2", "test3"});
assertEquals(expected, accepted.tasks);
assertEquals(expected, started.tasks);
assertEquals(expected, completed.tasks);
}
finally
{
pool.stop(true);
}
}
/**
* Test pooling
*/
public void testSimplePooling() throws Exception
{
BasicThreadPool pool = new BasicThreadPool();
pool.setMaximumPoolSize(1);
try
{
pool.runTask(new TestTask(BASIC, "test1"));
completed.wait(1);
pool.runTask(new TestTask(BASIC, "test2"));
completed.wait(2);
assertEquals(threadNames.get("test1"), threadNames.get("test2"));
}
finally
{
pool.stop(true);
}
}
/**
* Test multiple pooling
*/
public void testMultiplePooling() throws Exception
{
BasicThreadPool pool = new BasicThreadPool();
try
{
pool.runTask(new TestTask(HOLD_START, "test1"));
started.wait(1);
pool.runTask(new TestTask(BASIC, "test2"));
completed.wait(1);
started.release("test1");
completed.wait(2);
assertTrue("Shouldn't run on the same thread", threadNames.get("test1").equals(threadNames.get("test2")) == false);
}
finally
{
pool.stop(true);
}
}
/**
* Test maximum pool
*/
public void testMaximumPool() throws Exception
{
BasicThreadPool pool = new BasicThreadPool();
pool.setMaximumPoolSize(1);
try
{
pool.runTask(new TestTask(HOLD_START, "test1"));
started.wait(1);
pool.runTask(new TestTask(BASIC, "test2"));
Thread.sleep(1000);
assertEquals(0, completed.tasks.size());
started.release("test1");
completed.wait(2);
assertEquals(makeExpected(new Object[] {"test1", "test2"}), completed.tasks);
}
finally
{
pool.stop(true);
}
}
/**
* Test maximum cache
*/
public void testMaximumQueue() throws Exception
{
BasicThreadPool pool = new BasicThreadPool();
pool.setMaximumQueueSize(1);
pool.setMaximumPoolSize(1);
try
{
pool.runTask(new TestTask(HOLD_START, "test1"));
started.wait(1);
pool.runTask(new TestTask(BASIC, "test2"));
assertEquals(0, rejected.tasks.size());
pool.runTask(new TestTask(BASIC, "test3"));
assertEquals(makeExpected(new Object[] {"test3"}), rejected.tasks);
started.release("test1");
completed.wait(2);
assertEquals(makeExpected(new Object[] {"test1", "test2"}), completed.tasks);
}
finally
{
pool.stop(true);
}
}
/**
* Test maximum cache
*/
public void testCompleteTimeout() throws Exception
{
BasicThreadPool pool = new BasicThreadPool();
pool.setMaximumQueueSize(1);
pool.setMaximumPoolSize(1);
try
{
/* Test that a task with a timeout that completes within its timeout
works as expected
*/
TestTask task = new TestTask(HOLD_START, "test1", 0, 10*1000, Task.WAIT_NONE);
pool.runTask(task);
started.wait(1);
started.release("test1");
completed.wait(1);
/* Test a task with a timeout that does not complete within its timeout
is stopped
*/
task = new TestTask(HOLD_START, "test2", 0, 10*1000, Task.WAIT_NONE);
task.setRunSleepTime(12*1000);
pool.runTask(task);
started.wait(1);
started.release("test2");
stopped.wait(1);
completed.wait(1);
// Test that another valid task completes as expected
task = new TestTask(HOLD_START, "test3", 0, 0, Task.WAIT_NONE);
pool.runTask(task);
started.wait(1);
started.release("test3");
completed.wait(1);
/* Test a task with a timeout that does not complete within its timeout
is stopped
*/
task = new TestTask(HOLD_START, "test4", 0, 10*1000, Task.WAIT_NONE);
task.setRunSleepTime(12*1000);
pool.runTask(task);
started.wait(1);
started.release("test4");
stopped.wait(1);
completed.wait(1);
}
finally
{
pool.stop(true);
}
}
public void testCompleteTimeoutWithSpinLoop() throws Exception
{
BasicThreadPool pool = new BasicThreadPool();
pool.setMaximumQueueSize(1);
pool.setMaximumPoolSize(1);
try
{
/* Test that a task with a timeout that completes within its timeout
works as expected
*/
TestTask task = new TestTask(HOLD_START, "test1", 0, 10*1000, Task.WAIT_NONE);
task.setRunSleepTime(Long.MAX_VALUE);
pool.runTask(task);
started.wait(1);
started.release("test1");
stopped.wait(1);
completed.wait(1);
}
finally
{
pool.stop(true);
}
}
/**
* Save the thread name
*
* @param data the test data
* @param name the thread name
*/
public synchronized void saveRunnableThreadName(String data, String name)
{
threadNames.put(data, name);
}
/**
* Make the expected result
*
* @param expected the results as an object array
* @return the expected result
*/
public HashSet makeExpected(Object[] expected)
{
return new HashSet(Arrays.asList(expected));
}
/**
* Test task
*/
public class TestTask implements Task
{
/** The test to run */
private int test;
/** The data for the test */
private String data;
/** The start timeout */
private long startTimeout;
/** The completion timeout */
private long completionTimeout;
/** The time to sleep in execute */
private long runSleepTime;
/** The wait type */
private int waitType;
/**
* Create a new TestTask
*
* @param test the test
* @param data the test data
*/
public TestTask(int test, String data)
{
this(test, data, 0, Task.WAIT_NONE);
}
/**
* Create a new TestTask
*
* @param test the test
* @param data the test data
* @param startTimeout the start timeout
* @param waitType the wait type
*/
public TestTask(int test, String data, long startTimeout, int waitType)
{
this(test, data, startTimeout, 0, waitType);
}
public TestTask(int test, String data, long startTimeout,
long completionTimeout, int waitType)
{
this.test = test;
this.data = data;
this.startTimeout = startTimeout;
this.completionTimeout = completionTimeout;
this.waitType = waitType;
}
public void execute()
{
saveThreadName();
log.info("Start execute");
if( runSleepTime > 0 )
{
log.info("Begin spin loop");
if( runSleepTime == Long.MAX_VALUE )
{
while( true )
;
}
else
{
log.info("Begin sleep");
try
{
Thread.sleep(runSleepTime);
}
catch(InterruptedException e)
{
}
}
}
log.info("End execute");
}
public void saveThreadName()
{
saveRunnableThreadName(data, Thread.currentThread().getName());
}
public void accepted(long time)
{
accepted.notify(data, time);
}
public void rejected(long time, Throwable throwable)
{
rejected.notify(data, time, throwable);
}
public void started(long time)
{
started.notify(data, time);
if (test == HOLD_START)
started.waitForRelease(data);
}
public void completed(long time, Throwable throwable)
{
completed.notify(data, time, throwable);
}
public long getCompletionTimeout()
{
return completionTimeout;
}
public int getPriority()
{
return Thread.NORM_PRIORITY;
}
public long getStartTimeout()
{
return startTimeout;
}
public int getWaitType()
{
return waitType;
}
public void stop()
{
stopped.notify(data);
}
public void setRunSleepTime(long runSleepTime)
{
this.runSleepTime = runSleepTime;
}
}
public class Stats
{
/**
* The name
*/
String name;
/** The tasks */
HashSet tasks = new HashSet();
/** The times */
HashMap times = new HashMap();
/** The errors */
HashMap errors = new HashMap();
/** The releases */
HashSet releases = new HashSet();
public Stats(String name)
{
this.name = name;
}
/**
* Wait for expected
*/
public void wait(int target)
throws InterruptedException
{
log.debug(Thread.currentThread().getName() + ": Waiting for " + name + " target=" + target);
synchronized (ThreadPoolTaskUnitTestCase.this)
{
while (tasks.size() < target)
ThreadPoolTaskUnitTestCase.this.wait();
log.debug(Thread.currentThread().getName() + ": Waited for " + name + " target=" + target);
}
}
/**
* Release in waiting
*
* @param data the thread
*/
public void release(String data)
{
log.debug(Thread.currentThread().getName() + ": Releasing " + name + " data=" + data);
synchronized (ThreadPoolTaskUnitTestCase.this)
{
releases.add(data);
ThreadPoolTaskUnitTestCase.this.notifyAll();
log.debug(Thread.currentThread().getName() + ": Released " + name + " data=" + data);
}
}
/**
* Wait for release
*/
public void waitForRelease(String data)
{
log.debug(Thread.currentThread().getName() + ": Waiting for release " + name + " data=" + data);
synchronized (ThreadPoolTaskUnitTestCase.this)
{
try
{
while (releases.contains(data) == false)
ThreadPoolTaskUnitTestCase.this.wait();
}
catch (InterruptedException ignored)
{
}
log.debug(Thread.currentThread().getName() + ": Waited for release " + name + " data=" + data);
}
}
/**
* Notify
*/
public void notify(String data)
{
log.debug(Thread.currentThread().getName() + ": Notifying " + name + " data=" + data);
synchronized (ThreadPoolTaskUnitTestCase.this)
{
tasks.add(data);
ThreadPoolTaskUnitTestCase.this.notifyAll();
log.debug(Thread.currentThread().getName() + ": Notified " + name + " data=" + data);
}
}
/**
* Notify
*/
public void notify(String data, long time)
{
log.debug(Thread.currentThread().getName() + ": Notifying " + name + " data=" + data + " time=" + time);
synchronized (ThreadPoolTaskUnitTestCase.this)
{
tasks.add(data);
times.put(data, new Long(time));
ThreadPoolTaskUnitTestCase.this.notifyAll();
}
log.debug(Thread.currentThread().getName() + ": Notified " + name + " data=" + data + " time=" + time);
}
/**
* Notify
*/
public void notify(String data, long time, Throwable throwable)
{
if (throwable != null)
log.debug(Thread.currentThread().getName() + ": Notifying " + name + " data=" + data + " time=" + time, throwable);
else
log.debug(Thread.currentThread().getName() + ": Notifying " + name + " data=" + data + " time=" + time + " throwable=null");
synchronized (ThreadPoolTaskUnitTestCase.this)
{
tasks.add(data);
times.put(data, new Long(time));
errors.put(data, throwable);
ThreadPoolTaskUnitTestCase.this.notifyAll();
}
if (throwable != null)
log.debug(Thread.currentThread().getName() + ": Notified " + name + " data=" + data + " time=" + time + " throwable=" + throwable.getMessage());
else
log.debug(Thread.currentThread().getName() + ": Notified " + name + " data=" + data + " time=" + time + " throwable=null");
}
/**
* Clear
*/
public void clear()
{
log.debug(Thread.currentThread().getName() + ": Clearing " + name);
synchronized (ThreadPoolTaskUnitTestCase.this)
{
tasks.clear();
log.debug(Thread.currentThread().getName() + ": Cleared " + name);
}
}
}
}