1
2
3
4
5
6
7
8
9
10
11 package org.mule.util.concurrent;
12
13 import org.mule.tck.AbstractMuleTestCase;
14 import org.mule.util.StringUtils;
15
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Iterator;
19 import java.util.LinkedList;
20 import java.util.List;
21 import java.util.Map;
22
23 import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue;
24 import edu.emory.mathcs.backport.java.util.concurrent.RejectedExecutionException;
25 import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor;
26 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
27 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
28 import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
29
30 public class WaitPolicyTestCase extends AbstractMuleTestCase
31 {
32 private ExceptionCollectingThreadGroup threadGroup;
33 private ThreadPoolExecutor executor;
34 private ReentrantLock executorLock;
35
36
37 protected void doSetUp() throws Exception
38 {
39 super.doSetUp();
40
41
42 executor = new ThreadPoolExecutor(1, 1, 10000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(1));
43 executor.prestartAllCoreThreads();
44
45
46
47 executorLock = new ReentrantLock(true);
48
49
50
51 threadGroup = new ExceptionCollectingThreadGroup();
52
53
54 SleepyTask.activeTasks.set(0);
55 }
56
57
58 protected void doTearDown() throws Exception
59 {
60 executor.shutdown();
61 threadGroup.destroy();
62 super.doTearDown();
63 }
64
65
66
67
68
69
70
71
72 protected LinkedList
73 {
74 if (tasks == null || tasks.isEmpty())
75 {
76 throw new IllegalArgumentException("List<Runnable> must not be empty");
77 }
78
79 LinkedList submitters = new LinkedList();
80
81 executorLock.lock();
82
83 for (Iterator i = tasks.iterator(); i.hasNext();)
84 {
85 final Runnable task = (Runnable)i.next();
86
87 Runnable submitterAction = new Runnable()
88 {
89 public void run()
90 {
91
92
93
94 try
95 {
96 executorLock.lock();
97 executor.execute(task);
98 }
99 finally
100 {
101 executorLock.unlock();
102 }
103 }
104 };
105
106 Thread submitter = new Thread(threadGroup, submitterAction);
107 submitter.setDaemon(true);
108 submitters.add(submitter);
109 submitter.start();
110
111
112 while (submitter.isAlive() && !executorLock.hasQueuedThread(submitter))
113 {
114 Thread.sleep(10);
115 }
116 }
117
118 executorLock.unlock();
119 return submitters;
120 }
121
122 public void testWaitPolicyWithShutdownExecutor() throws Exception
123 {
124 assertEquals(0, SleepyTask.activeTasks.get());
125
126
127 executor.setRejectedExecutionHandler(new LastRejectedWaitPolicy());
128 executor.shutdown();
129
130
131 List tasks = new ArrayList();
132 tasks.add(new SleepyTask("rejected", 1000));
133
134
135 LinkedList submitters = this.execute(tasks);
136 assertFalse(submitters.isEmpty());
137
138
139 Thread.sleep(1000);
140
141 LinkedList exceptions = threadGroup.collectedExceptions();
142 assertEquals(1, exceptions.size());
143
144 Map.Entry threadFailure = (Map.Entry)((Map)(exceptions.getFirst())).entrySet().iterator().next();
145 assertEquals(submitters.getFirst(), threadFailure.getKey());
146 assertEquals(RejectedExecutionException.class, threadFailure.getValue().getClass());
147 assertEquals(0, SleepyTask.activeTasks.get());
148 }
149
150 public void testWaitPolicyForever() throws Exception
151 {
152 assertEquals(0, SleepyTask.activeTasks.get());
153
154
155 LastRejectedWaitPolicy policy = new LastRejectedWaitPolicy(-1, TimeUnit.SECONDS);
156 executor.setRejectedExecutionHandler(policy);
157
158
159 List tasks = new ArrayList();
160
161 tasks.add(new SleepyTask("run", 1000));
162
163 tasks.add(new SleepyTask("queued", 1000));
164
165 Runnable waiting = new SleepyTask("waitingForever", 1000);
166 tasks.add(waiting);
167
168
169 LinkedList submitters = this.execute(tasks);
170 assertFalse(submitters.isEmpty());
171
172
173 assertFalse(executor.awaitTermination(4000, TimeUnit.MILLISECONDS));
174 assertSame(waiting, policy.lastRejectedRunnable());
175 assertEquals(0, SleepyTask.activeTasks.get());
176 }
177
178 public void testWaitPolicyWithTimeout() throws Exception
179 {
180 assertEquals(0, SleepyTask.activeTasks.get());
181
182
183 LastRejectedWaitPolicy policy = new LastRejectedWaitPolicy(2500, TimeUnit.MILLISECONDS);
184 executor.setRejectedExecutionHandler(policy);
185
186
187 List tasks = new ArrayList();
188
189 tasks.add(new SleepyTask("run", 1000));
190
191 tasks.add(new SleepyTask("queued", 1000));
192
193 Runnable waiting = new SleepyTask("waiting", 1000);
194 tasks.add(waiting);
195
196
197 LinkedList submitters = this.execute(tasks);
198 assertFalse(submitters.isEmpty());
199
200 assertFalse(executor.awaitTermination(5000, TimeUnit.MILLISECONDS));
201 assertSame(waiting, policy.lastRejectedRunnable());
202 assertEquals(0, SleepyTask.activeTasks.get());
203 }
204
205 public void testWaitPolicyWithTimeoutFailure() throws Exception
206 {
207 assertEquals(0, SleepyTask.activeTasks.get());
208
209
210 long failureInterval = 100L;
211 LastRejectedWaitPolicy policy = new LastRejectedWaitPolicy(failureInterval, TimeUnit.MILLISECONDS);
212 executor.setRejectedExecutionHandler(policy);
213
214
215 List tasks = new ArrayList();
216
217 tasks.add(new SleepyTask("run", 1000));
218
219 tasks.add(new SleepyTask("queued", 1000));
220
221 Runnable failedTask = new SleepyTask("waitAndFail", 1000);
222 tasks.add(failedTask);
223
224
225 LinkedList submitters = this.execute(tasks);
226 assertFalse(submitters.isEmpty());
227
228
229 Thread.sleep(failureInterval * 10);
230
231
232 LinkedList exceptions = threadGroup.collectedExceptions();
233 assertEquals(1, exceptions.size());
234
235
236 Map.Entry threadFailure = (Map.Entry)((Map)(exceptions.getFirst())).entrySet().iterator().next();
237 assertEquals(submitters.getLast(), threadFailure.getKey());
238 assertEquals(RejectedExecutionException.class, threadFailure.getValue().getClass());
239
240 executor.shutdown();
241 assertTrue(executor.awaitTermination(2500, TimeUnit.MILLISECONDS));
242 assertSame(failedTask, policy.lastRejectedRunnable());
243 assertEquals(0, SleepyTask.activeTasks.get());
244 }
245
246 }
247
248 class LastRejectedWaitPolicy extends WaitPolicy
249 {
250
251 private volatile Runnable _rejected;
252
253 public LastRejectedWaitPolicy()
254 {
255 super();
256 }
257
258 public LastRejectedWaitPolicy(long time, TimeUnit timeUnit)
259 {
260 super(time, timeUnit);
261 }
262
263 public Runnable lastRejectedRunnable()
264 {
265 return _rejected;
266 }
267
268
269 public void rejectedExecution(Runnable r, ThreadPoolExecutor e)
270 {
271 _rejected = r;
272 super.rejectedExecution(r, e);
273 }
274 }
275
276
277 class SleepyTask extends Object implements Runnable
278 {
279 public static final AtomicInteger activeTasks = new AtomicInteger(0);
280
281 private final String name;
282 private final long sleepTime;
283
284 public SleepyTask(String name, long sleepTime)
285 {
286 if (StringUtils.isEmpty(name))
287 {
288 throw new IllegalArgumentException("SleepyTask needs a name!");
289 }
290
291 this.name = name;
292 this.sleepTime = sleepTime;
293 }
294
295 public String toString()
296 {
297 return this.getClass().getName() + '{' + name + ", " + sleepTime + '}';
298 }
299
300 public void run()
301 {
302 activeTasks.incrementAndGet();
303
304 try
305 {
306 Thread.sleep(sleepTime);
307 }
308 catch (InterruptedException iex)
309 {
310 Thread.currentThread().interrupt();
311 }
312 finally
313 {
314 activeTasks.decrementAndGet();
315 }
316 }
317
318 }
319
320
321 class ExceptionCollectingThreadGroup extends ThreadGroup
322 {
323 private final LinkedList
324
325 public ExceptionCollectingThreadGroup()
326 {
327 super("ExceptionCollectingThreadGroup");
328 }
329
330
331 public LinkedList collectedExceptions()
332 {
333 return exceptions;
334 }
335
336
337
338 public void uncaughtException(Thread t, Throwable e)
339 {
340 synchronized (exceptions)
341 {
342 exceptions.add(Collections.singletonMap(t, e));
343 }
344 }
345 }