1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3.concurrent;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertNull;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24 import static org.junit.jupiter.api.Assertions.fail;
25
26 import java.util.Iterator;
27 import java.util.NoSuchElementException;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.TimeUnit;
32
33 import org.apache.commons.lang3.AbstractLangTest;
34 import org.junit.jupiter.api.BeforeEach;
35 import org.junit.jupiter.api.Test;
36
37
38
39
40 public class MultiBackgroundInitializerTest extends AbstractLangTest {
41
42
43
44
45
46
47
48 protected static class AbstractChildBackgroundInitializer extends BackgroundInitializer<CloseableCounter> {
49
50 volatile ExecutorService currentExecutor;
51
52
53 CloseableCounter counter = new CloseableCounter();
54
55
56 volatile int initializeCalls;
57
58
59 Exception ex;
60
61
62 final CountDownLatch latch = new CountDownLatch(1);
63 boolean waitForLatch;
64
65 public void enableLatch() {
66 waitForLatch = true;
67 }
68
69 public CloseableCounter getCloseableCounter() {
70 return counter;
71 }
72
73
74
75
76 protected CloseableCounter initializeInternal() throws Exception {
77 initializeCalls++;
78 currentExecutor = getActiveExecutor();
79
80 if (waitForLatch) {
81 latch.await();
82 }
83
84 if (ex != null) {
85 throw ex;
86 }
87
88 return counter.increment();
89 }
90
91 public void releaseLatch() {
92 latch.countDown();
93 }
94 }
95
96 protected static class CloseableCounter {
97
98 public static CloseableCounter wrapInteger(int i) {
99 return new CloseableCounter().setInitializeCalls(i);
100 }
101
102
103 volatile int initializeCalls;
104
105
106 volatile boolean closed;
107
108 public void close() {
109 closed = true;
110 }
111
112 @Override
113 public boolean equals(final Object other) {
114 if (other instanceof CloseableCounter) {
115 return initializeCalls == ((CloseableCounter) other).getInitializeCalls();
116 }
117 return false;
118 }
119
120 public int getInitializeCalls() {
121 return initializeCalls;
122 }
123
124 public CloseableCounter increment() {
125 initializeCalls++;
126 return this;
127 }
128
129 public boolean isClosed() {
130 return closed;
131 }
132
133 public CloseableCounter setInitializeCalls(int i) {
134 initializeCalls = i;
135 return this;
136 }
137 }
138
139 protected static class MethodChildBackgroundInitializer extends AbstractChildBackgroundInitializer {
140 @Override
141 protected CloseableCounter initialize() throws Exception {
142 return initializeInternal();
143 }
144 }
145
146
147 private static final String CHILD_INIT = "childInitializer";
148
149
150 protected static final long PERIOD_MILLIS = 50;
151
152
153 protected MultiBackgroundInitializer initializer;
154
155
156
157
158
159
160
161
162
163
164 private void checkChild(final BackgroundInitializer<?> child,
165 final ExecutorService expExec) throws ConcurrentException {
166 final AbstractChildBackgroundInitializer cinit = (AbstractChildBackgroundInitializer) child;
167 final Integer result = cinit.get().getInitializeCalls();
168 assertEquals(1, result.intValue(), "Wrong result");
169 assertEquals(1, cinit.initializeCalls, "Wrong number of executions");
170 if (expExec != null) {
171 assertEquals(expExec, cinit.currentExecutor, "Wrong executor service");
172 }
173 }
174
175
176
177
178
179
180
181
182
183 private MultiBackgroundInitializer.MultiBackgroundInitializerResults checkInitialize()
184 throws ConcurrentException {
185 final int count = 5;
186 for (int i = 0; i < count; i++) {
187 initializer.addInitializer(CHILD_INIT + i,
188 createChildBackgroundInitializer());
189 }
190 initializer.start();
191 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
192 .get();
193 assertEquals(count, res.initializerNames().size(), "Wrong number of child initializers");
194 for (int i = 0; i < count; i++) {
195 final String key = CHILD_INIT + i;
196 assertTrue(res.initializerNames().contains(key), "Name not found: " + key);
197 assertEquals(CloseableCounter.wrapInteger(1), res.getResultObject(key), "Wrong result object");
198 assertFalse(res.isException(key), "Exception flag");
199 assertNull(res.getException(key), "Got an exception");
200 checkChild(res.getInitializer(key), initializer.getActiveExecutor());
201 }
202 return res;
203 }
204
205
206
207
208
209
210 protected AbstractChildBackgroundInitializer createChildBackgroundInitializer() {
211 return new MethodChildBackgroundInitializer();
212 }
213
214 @BeforeEach
215 public void setUp() {
216 initializer = new MultiBackgroundInitializer();
217 }
218
219
220
221
222
223
224
225 @Test
226 public void testAddInitializerAfterStart() throws ConcurrentException {
227 initializer.start();
228 assertThrows(
229 IllegalStateException.class,
230 () -> initializer.addInitializer(CHILD_INIT, createChildBackgroundInitializer()),
231 "Could add initializer after start()!");
232 initializer.get();
233 }
234
235
236
237
238
239 @Test
240 public void testAddInitializerNullInit() {
241 assertThrows(NullPointerException.class, () -> initializer.addInitializer(CHILD_INIT, null));
242 }
243
244
245
246
247
248 @Test
249 public void testAddInitializerNullName() {
250 assertThrows(NullPointerException.class, () -> initializer.addInitializer(null, createChildBackgroundInitializer()));
251 }
252
253
254
255
256
257
258
259 @Test
260 public void testInitializeChildWithExecutor() throws ConcurrentException, InterruptedException {
261 final String initExec = "childInitializerWithExecutor";
262 final ExecutorService exec = Executors.newSingleThreadExecutor();
263 try {
264 final AbstractChildBackgroundInitializer c1 = createChildBackgroundInitializer();
265 final AbstractChildBackgroundInitializer c2 = createChildBackgroundInitializer();
266 c2.setExternalExecutor(exec);
267 initializer.addInitializer(CHILD_INIT, c1);
268 initializer.addInitializer(initExec, c2);
269 initializer.start();
270 initializer.get();
271 checkChild(c1, initializer.getActiveExecutor());
272 checkChild(c2, exec);
273 } finally {
274 exec.shutdown();
275 exec.awaitTermination(1, TimeUnit.SECONDS);
276 }
277 }
278
279
280
281
282
283
284
285 @Test
286 public void testInitializeEx() throws ConcurrentException {
287 final AbstractChildBackgroundInitializer child = createChildBackgroundInitializer();
288 child.ex = new Exception();
289 initializer.addInitializer(CHILD_INIT, child);
290 initializer.start();
291 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
292 .get();
293 assertTrue(res.isException(CHILD_INIT), "No exception flag");
294 assertNull(res.getResultObject(CHILD_INIT), "Got a results object");
295 final ConcurrentException cex = res.getException(CHILD_INIT);
296 assertEquals(child.ex, cex.getCause(), "Wrong cause");
297 }
298
299
300
301
302
303
304 @Test
305 public void testInitializeExternalExec() throws ConcurrentException, InterruptedException {
306 final ExecutorService exec = Executors.newCachedThreadPool();
307 try {
308 initializer = new MultiBackgroundInitializer(exec);
309 checkInitialize();
310 assertEquals(exec, initializer.getActiveExecutor(), "Wrong executor");
311 assertFalse(exec.isShutdown(), "Executor was shutdown");
312 } finally {
313 exec.shutdown();
314 exec.awaitTermination(1, TimeUnit.SECONDS);
315 }
316 }
317
318
319
320
321
322
323
324 @Test
325 public void testInitializeNested() throws ConcurrentException {
326 final String nameMulti = "multiChildInitializer";
327 initializer
328 .addInitializer(CHILD_INIT, createChildBackgroundInitializer());
329 final MultiBackgroundInitializer mi2 = new MultiBackgroundInitializer();
330 final int count = 3;
331 for (int i = 0; i < count; i++) {
332 mi2
333 .addInitializer(CHILD_INIT + i,
334 createChildBackgroundInitializer());
335 }
336 initializer.addInitializer(nameMulti, mi2);
337 initializer.start();
338 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
339 .get();
340 final ExecutorService exec = initializer.getActiveExecutor();
341 checkChild(res.getInitializer(CHILD_INIT), exec);
342 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res2 = (MultiBackgroundInitializer.MultiBackgroundInitializerResults) res
343 .getResultObject(nameMulti);
344 assertEquals(count, res2.initializerNames().size(), "Wrong number of initializers");
345 for (int i = 0; i < count; i++) {
346 checkChild(res2.getInitializer(CHILD_INIT + i), exec);
347 }
348 assertTrue(exec.isShutdown(), "Executor not shutdown");
349 }
350
351
352
353
354
355
356 @Test
357 public void testInitializeNoChildren() throws ConcurrentException {
358 assertTrue(initializer.start(), "Wrong result of start()");
359 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
360 .get();
361 assertTrue(res.initializerNames().isEmpty(), "Got child initializers");
362 assertTrue(initializer.getActiveExecutor().isShutdown(), "Executor not shutdown");
363 }
364
365
366
367
368
369
370
371 @Test
372 public void testInitializeResultsIsSuccessfulFalse()
373 throws ConcurrentException {
374 final AbstractChildBackgroundInitializer child = createChildBackgroundInitializer();
375 child.ex = new Exception();
376 initializer.addInitializer(CHILD_INIT, child);
377 initializer.start();
378 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
379 .get();
380 assertFalse(res.isSuccessful(), "Wrong success flag");
381 }
382
383
384
385
386
387
388
389 @Test
390 public void testInitializeResultsIsSuccessfulTrue()
391 throws ConcurrentException {
392 final AbstractChildBackgroundInitializer child = createChildBackgroundInitializer();
393 initializer.addInitializer(CHILD_INIT, child);
394 initializer.start();
395 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
396 .get();
397 assertTrue(res.isSuccessful(), "Wrong success flag");
398 }
399
400
401
402
403
404 @Test
405 public void testInitializeRuntimeEx() {
406 final AbstractChildBackgroundInitializer child = createChildBackgroundInitializer();
407 child.ex = new RuntimeException();
408 initializer.addInitializer(CHILD_INIT, child);
409 initializer.start();
410 final Exception ex = assertThrows(Exception.class, initializer::get);
411 assertEquals(child.ex, ex, "Wrong exception");
412 }
413
414
415
416
417
418
419 @Test
420 public void testInitializeTempExec() throws ConcurrentException {
421 checkInitialize();
422 assertTrue(initializer.getActiveExecutor().isShutdown(), "Executor not shutdown");
423 }
424
425 @Test
426 public void testIsInitialized()
427 throws ConcurrentException, InterruptedException {
428 final AbstractChildBackgroundInitializer childOne = createChildBackgroundInitializer();
429 final AbstractChildBackgroundInitializer childTwo = createChildBackgroundInitializer();
430
431 childOne.enableLatch();
432 childTwo.enableLatch();
433
434 assertFalse(initializer.isInitialized(), "Initalized without having anything to initalize");
435
436 initializer.addInitializer("child one", childOne);
437 initializer.addInitializer("child two", childTwo);
438 initializer.start();
439
440 long startTime = System.currentTimeMillis();
441 long waitTime = 3000;
442 long endTime = startTime + waitTime;
443
444 while (! childOne.isStarted() || ! childTwo.isStarted()) {
445 if (System.currentTimeMillis() > endTime) {
446 fail("children never started");
447 Thread.sleep(PERIOD_MILLIS);
448 }
449 }
450
451 assertFalse(initializer.isInitialized(), "Initalized with two children running");
452
453 childOne.releaseLatch();
454 childOne.get();
455 assertFalse(initializer.isInitialized(), "Initalized with one child running");
456
457 childTwo.releaseLatch();
458 childTwo.get();
459 assertTrue(initializer.isInitialized(), "Not initalized with no children running");
460 }
461
462
463
464
465
466
467
468 @Test
469 public void testResultGetExceptionUnknown() throws ConcurrentException {
470 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize();
471 assertThrows(NoSuchElementException.class, () -> res.getException("unknown"));
472 }
473
474
475
476
477
478
479
480 @Test
481 public void testResultGetInitializerUnknown() throws ConcurrentException {
482 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize();
483 assertThrows(NoSuchElementException.class, () -> res.getInitializer("unknown"));
484 }
485
486
487
488
489
490
491
492 @Test
493 public void testResultGetResultObjectUnknown() throws ConcurrentException {
494 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize();
495 assertThrows(NoSuchElementException.class, () -> res.getResultObject("unknown"));
496 }
497
498
499
500
501
502
503 @Test
504 public void testResultInitializerNamesModify() throws ConcurrentException {
505 checkInitialize();
506 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer
507 .get();
508 final Iterator<String> it = res.initializerNames().iterator();
509 it.next();
510 assertThrows(UnsupportedOperationException.class, it::remove);
511 }
512
513
514
515
516
517
518
519 @Test
520 public void testResultIsExceptionUnknown() throws ConcurrentException {
521 final MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize();
522 assertThrows(NoSuchElementException.class, () -> res.isException("unknown"));
523 }
524 }