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