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