001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.test.util.test;
023:
024: import java.util.Arrays;
025: import java.util.HashMap;
026: import java.util.HashSet;
027: import java.util.ArrayList;
028:
029: import org.jboss.util.threadpool.BasicThreadPool;
030: import org.jboss.util.threadpool.ThreadPoolFullException;
031: import org.jboss.util.threadpool.BlockingMode;
032: import org.apache.log4j.Logger;
033: import junit.framework.TestCase;
034:
035: /**
036: * Tests of thread pool with Runnables added to the pool
037: *
038: * @see org.jboss.util.threadpool.ThreadPool
039: * @author <a href="adrian@jboss.org">Adrian.Brock</a>
040: * @author Scott.Stark@jboss.org
041: * @version $Revision: 61634 $
042: */
043: public class ThreadPoolRunnableUnitTestCase extends TestCase {
044: private static Logger log = Logger
045: .getLogger(ThreadPoolRunnableUnitTestCase.class);
046:
047: /** Basic test */
048: static final int BASIC = 0;
049:
050: /** Hold the thread after start */
051: static final int HOLD_START = 1;
052:
053: /** The started runnables */
054: HashSet startedRunnables = new HashSet();
055:
056: /** The started releases */
057: HashSet startedReleases = new HashSet();
058:
059: /** The finished runnables */
060: HashSet finishedRunnables = new HashSet();
061:
062: /** The thread names */
063: HashMap threadNames = new HashMap();
064:
065: /**
066: * Create a new ThreadPoolRunnableUnitTestCase
067: *
068: * @param name the test to run
069: */
070: public ThreadPoolRunnableUnitTestCase(String name) {
071: super (name);
072: }
073:
074: /**
075: * Basic test
076: */
077: public void testBasic() throws Exception {
078: log.debug("testBasic");
079: BasicThreadPool pool = new BasicThreadPool();
080: try {
081: pool.run(new TestRunnable(BASIC, "test"));
082: waitFinished(1);
083: HashSet expected = makeExpected(new Object[] { "test" });
084: assertEquals(expected, finishedRunnables);
085: } finally {
086: pool.stop(true);
087: }
088: }
089:
090: /**
091: * Multiple Basic test
092: */
093: public void testMultipleBasic() throws Exception {
094: log.debug("testMultipleBasic");
095: BasicThreadPool pool = new BasicThreadPool();
096: try {
097: pool.run(new TestRunnable(BASIC, "test1"));
098: pool.run(new TestRunnable(BASIC, "test2"));
099: pool.run(new TestRunnable(BASIC, "test3"));
100: waitFinished(3);
101: HashSet expected = makeExpected(new Object[] { "test1",
102: "test2", "test3" });
103: assertEquals(expected, finishedRunnables);
104: } finally {
105: pool.stop(true);
106: }
107: }
108:
109: /**
110: * Test pooling
111: */
112: public void testSimplePooling() throws Exception {
113: log.debug("testSimplePooling");
114: BasicThreadPool pool = new BasicThreadPool();
115: pool.setMaximumPoolSize(1);
116: try {
117: pool.run(new TestRunnable(BASIC, "test1"));
118: waitFinished(1);
119: pool.run(new TestRunnable(BASIC, "test2"));
120: waitFinished(2);
121: assertEquals(threadNames.get("test1"), threadNames
122: .get("test2"));
123: } finally {
124: pool.stop(true);
125: }
126: }
127:
128: /**
129: * Test multiple pooling
130: */
131: public void testMultiplePooling() throws Exception {
132: log.debug("testMultiplePooling");
133: BasicThreadPool pool = new BasicThreadPool();
134: try {
135: pool.run(new TestRunnable(HOLD_START, "test1"));
136: waitStarted(1);
137: pool.run(new TestRunnable(BASIC, "test2"));
138: waitFinished(1);
139: releaseStarted("test1");
140: waitFinished(2);
141: assertTrue("Shouldn't run on the same thread",
142: threadNames.get("test1").equals(
143: threadNames.get("test2")) == false);
144: } finally {
145: pool.stop(true);
146: }
147: }
148:
149: /**
150: * Test maximum pool
151: */
152: public void testMaximumPool() throws Exception {
153: log.debug("testMaximumPool");
154: BasicThreadPool pool = new BasicThreadPool();
155: pool.setMaximumPoolSize(1);
156: try {
157: pool.run(new TestRunnable(HOLD_START, "test1"));
158: waitStarted(1);
159: pool.run(new TestRunnable(BASIC, "test2"));
160: Thread.sleep(1000);
161: assertEquals(0, finishedRunnables.size());
162: releaseStarted("test1");
163: waitFinished(2);
164: assertEquals(
165: makeExpected(new Object[] { "test1", "test2" }),
166: finishedRunnables);
167: } finally {
168: pool.stop(true);
169: }
170: }
171:
172: /**
173: * Test maximum cache
174: */
175: public void testMaximumQueue() throws Exception {
176: log.debug("testMaximumQueue");
177: BasicThreadPool pool = new BasicThreadPool();
178: pool.setMaximumQueueSize(1);
179: pool.setMaximumPoolSize(1);
180: try {
181: pool.run(new TestRunnable(HOLD_START, "test1"));
182: waitStarted(1);
183: pool.run(new TestRunnable(BASIC, "test2"));
184:
185: boolean caught = false;
186: try {
187: pool.run(new TestRunnable(BASIC, "test3"));
188: } catch (ThreadPoolFullException expected) {
189: caught = true;
190: }
191: assertTrue("Expected ThreadPoolFullException", caught);
192:
193: releaseStarted("test1");
194: waitFinished(2);
195: assertEquals(
196: makeExpected(new Object[] { "test1", "test2" }),
197: finishedRunnables);
198: } finally {
199: pool.stop(true);
200: }
201: }
202:
203: /**
204: * Test runnable timeouts
205: */
206: public void testRunnableTimeout() throws Exception {
207: log.debug("testRunnableTimeout");
208: BasicThreadPool pool = new BasicThreadPool();
209: pool.setMaximumQueueSize(1);
210: pool.setMaximumPoolSize(1);
211: try {
212: TestRunnable test = new TestRunnable(HOLD_START, "test1",
213: 12 * 1000);
214: pool.run(test, 0, 10 * 1000);
215: waitStarted(1);
216: releaseStarted("test1");
217: waitFinished(1);
218: assertEquals(makeExpected(new Object[] { "test1" }),
219: finishedRunnables);
220: } finally {
221: pool.stop(true);
222: }
223: }
224:
225: /**
226: * Test runnable timeouts
227: */
228: public void testRunnableTimeoutWithSpinLoop() throws Exception {
229: log.debug("testRunnableTimeoutWithSpinLoop");
230: BasicThreadPool pool = new BasicThreadPool();
231: pool.setMaximumQueueSize(1);
232: pool.setMaximumPoolSize(1);
233: try {
234: TestRunnable test = new TestRunnable(HOLD_START, "test1",
235: Long.MAX_VALUE);
236: pool.run(test, 0, 8 * 1000);
237: waitStarted(1);
238: releaseStarted("test1");
239: Thread.sleep(12 * 1000);
240: // Run another task to validate the previous thread has been cleared
241: pool.run(new TestRunnable(BASIC, "test2"));
242: waitStarted(1);
243: releaseStarted("test2");
244: waitFinished(1);
245: assertEquals(makeExpected(new Object[] { "test2" }),
246: finishedRunnables);
247: } finally {
248: pool.stop(true);
249: }
250: }
251:
252: /**
253: * Test runnable timeouts
254: */
255: public void testRunnableTimeoutWithSpinLoop2() throws Exception {
256: log.debug("testRunnableTimeoutWithSpinLoop2");
257: BasicThreadPool pool = new BasicThreadPool();
258: pool.setMaximumQueueSize(1);
259: pool.setMaximumPoolSize(1);
260: pool.setBlockingMode(BlockingMode.RUN);
261: try {
262: TestRunnable test = new TestRunnable(BASIC, "testx",
263: Long.MAX_VALUE);
264: pool.run(test, 0, 1 * 1000);
265: // Run another task to validate the previous thread has been cleared
266: ArrayList tmp = new ArrayList();
267: for (int n = 0; n < 10; n++) {
268: String name = "test" + n;
269: pool.run(new TestRunnable(BASIC, name));
270: tmp.add(name);
271: }
272: Thread.sleep(3000);
273: assertEquals(makeExpected(tmp.toArray()), finishedRunnables);
274: } finally {
275: pool.stop(true);
276: }
277: }
278:
279: /**
280: * Save the thread name
281: *
282: * @param data the test data
283: * @param name the thread name
284: */
285: public synchronized void saveRunnableThreadName(String data,
286: String name) {
287: threadNames.put(data, name);
288: }
289:
290: /**
291: * Wait for expected starts
292: */
293: public synchronized void waitStarted(int target)
294: throws InterruptedException {
295: log.info("waitStarted, target=" + target);
296: while (startedRunnables.size() < target)
297: wait();
298: }
299:
300: /**
301: * Release in waiting for start
302: *
303: * @param data the thread to start
304: */
305: public synchronized void releaseStarted(String data) {
306: log.info("releaseStarted, data=" + data);
307: startedReleases.add(data);
308: notifyAll();
309: }
310:
311: /**
312: * Wait for release started
313: */
314: public synchronized void waitForReleaseStarted(String data) {
315: try {
316: log.info("waitForReleaseStarted, data=" + data);
317: while (startedReleases.contains(data) == false)
318: wait();
319: } catch (InterruptedException ignored) {
320: }
321: }
322:
323: /**
324: * Notify started
325: */
326: public synchronized void notifyStarted(String data) {
327: log.info("notifyStarted, data=" + data);
328: startedRunnables.add(data);
329: notifyAll();
330: }
331:
332: /**
333: * Clear started
334: */
335: public synchronized void clearStarted() {
336: log.info("clearStarted");
337: startedRunnables.clear();
338: }
339:
340: /**
341: * Wait for expected finishes
342: */
343: public synchronized void waitFinished(int target)
344: throws InterruptedException {
345: log.info("waitFinished, target=" + target);
346: while (finishedRunnables.size() < target)
347: wait();
348: }
349:
350: /**
351: * Notify finished
352: */
353: public synchronized void notifyFinished(String data) {
354: log.info("notifyFinished, data=" + data);
355: finishedRunnables.add(data);
356: notifyAll();
357: }
358:
359: /**
360: * Clear finished
361: */
362: public synchronized void clearFinished() {
363: log.info("clearFinished");
364: finishedRunnables.clear();
365: }
366:
367: /**
368: * Make the expected result
369: *
370: * @param expected the results as an object array
371: * @return the expected result
372: */
373: public HashSet makeExpected(Object[] expected) {
374: return new HashSet(Arrays.asList(expected));
375: }
376:
377: /**
378: * Test runnable
379: */
380: public class TestRunnable implements Runnable {
381: /** The test to run */
382: private int test;
383: /** The data for the test */
384: private String data;
385: private long runSleepTime;
386:
387: /**
388: * Create a new TestRunnable
389: *
390: * @param test the test
391: * @param data the test data
392: */
393: public TestRunnable(int test, String data) {
394: this (test, data, 0);
395: }
396:
397: public TestRunnable(int test, String data, long runSleepTime) {
398: this .test = test;
399: this .data = data;
400: this .runSleepTime = runSleepTime;
401: }
402:
403: /**
404: * Runnable implementation
405: */
406: public void run() {
407: log.info("Begin run");
408: saveThreadName();
409: started();
410: if (runSleepTime > 0) {
411: log.info("Begin spin loop");
412: if (runSleepTime == Long.MAX_VALUE) {
413: while (true)
414: ;
415: } else {
416: log.info("Begin sleep");
417: try {
418: Thread.sleep(runSleepTime);
419: } catch (InterruptedException e) {
420: }
421: }
422: }
423: finished();
424: log.info("End run");
425: }
426:
427: /**
428: * Save the thread
429: */
430: public void saveThreadName() {
431: saveRunnableThreadName(data, Thread.currentThread()
432: .getName());
433: }
434:
435: /**
436: * The test is finished
437: */
438: public void started() {
439: notifyStarted(data);
440: if (test == HOLD_START)
441: waitForReleaseStarted(data);
442: }
443:
444: /**
445: * The test is finished
446: */
447: public void finished() {
448: notifyFinished(data);
449: }
450: }
451: }
|