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 ThreadPoolExecutor executor;
34 ReentrantLock executorLock;
35
36 @Override
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 @Override
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<Thread> execute(final List<Runnable> tasks) throws InterruptedException
73 {
74 if (tasks == null || tasks.isEmpty())
75 {
76 throw new IllegalArgumentException("List<Runnable> must not be empty");
77 }
78
79 LinkedList<Thread> submitters = new LinkedList<Thread>();
80
81 executorLock.lock();
82
83 for (Iterator<Runnable> i = tasks.iterator(); i.hasNext();)
84 {
85 final Runnable task = 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<Runnable> tasks = new ArrayList<Runnable>();
132 tasks.add(new SleepyTask("rejected", 1000));
133
134
135 LinkedList<Thread> submitters = this.execute(tasks);
136 assertFalse(submitters.isEmpty());
137
138
139 Thread.sleep(1000);
140
141 LinkedList<Map<Thread, Throwable>> exceptions = threadGroup.collectedExceptions();
142 assertEquals(1, exceptions.size());
143
144 Map.Entry<Thread, Throwable> threadFailure = 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<Runnable> tasks = new ArrayList<Runnable>();
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<Thread> 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<Runnable> tasks = new ArrayList<Runnable>();
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<Thread> 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<Runnable> tasks = new ArrayList<Runnable>();
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<Thread> submitters = this.execute(tasks);
226 assertFalse(submitters.isEmpty());
227
228
229 Thread.sleep(failureInterval * 10);
230
231
232 LinkedList<Map<Thread, Throwable>> exceptions = threadGroup.collectedExceptions();
233 assertEquals(1, exceptions.size());
234
235
236 Map.Entry<Thread, Throwable> threadFailure = 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 class LastRejectedWaitPolicy extends WaitPolicy
248 {
249
250 private volatile Runnable _rejected;
251
252 public LastRejectedWaitPolicy()
253 {
254 super();
255 }
256
257 public LastRejectedWaitPolicy(long time, TimeUnit timeUnit)
258 {
259 super(time, timeUnit);
260 }
261
262 public Runnable lastRejectedRunnable()
263 {
264 return _rejected;
265 }
266
267 @Override
268 public void rejectedExecution(Runnable r, ThreadPoolExecutor e)
269 {
270 _rejected = r;
271 super.rejectedExecution(r, e);
272 }
273 }
274
275
276 class SleepyTask extends Object implements Runnable
277 {
278 public static final AtomicInteger activeTasks = new AtomicInteger(0);
279
280 private final String name;
281 private final long sleepTime;
282
283 public SleepyTask(String name, long sleepTime)
284 {
285 if (StringUtils.isEmpty(name))
286 {
287 throw new IllegalArgumentException("SleepyTask needs a name!");
288 }
289
290 this.name = name;
291 this.sleepTime = sleepTime;
292 }
293
294 @Override
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<Map<Thread, Throwable>> exceptions = new LinkedList<Map<Thread, Throwable>>();
324
325 public ExceptionCollectingThreadGroup()
326 {
327 super("ExceptionCollectingThreadGroup");
328 }
329
330
331 public LinkedList<Map<Thread, Throwable>> collectedExceptions()
332 {
333 return exceptions;
334 }
335
336
337 @Override
338 public void uncaughtException(Thread t, Throwable e)
339 {
340 synchronized (exceptions)
341 {
342 exceptions.add(Collections.singletonMap(t, e));
343 }
344 }
345 }