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.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertNull;
23 import static org.junit.jupiter.api.Assertions.assertSame;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26
27 import java.time.Duration;
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 import java.util.concurrent.atomic.AtomicBoolean;
33 import java.util.concurrent.atomic.AtomicInteger;
34 import java.util.concurrent.atomic.AtomicReference;
35
36 import org.apache.commons.lang3.AbstractLangTest;
37 import org.apache.commons.lang3.ThreadUtils;
38 import org.junit.jupiter.api.Test;
39
40 public class BackgroundInitializerTest extends AbstractLangTest {
41
42
43
44
45 protected static class AbstractBackgroundInitializerTestImpl extends
46 BackgroundInitializer<CloseableCounter> {
47
48 Exception ex;
49
50
51 boolean shouldSleep;
52
53
54 final CountDownLatch latch = new CountDownLatch(1);
55 boolean waitForLatch;
56
57
58 CloseableCounter counter = new CloseableCounter();
59
60 AbstractBackgroundInitializerTestImpl() {
61 }
62
63 AbstractBackgroundInitializerTestImpl(final ExecutorService exec) {
64 super(exec);
65 }
66
67 public void enableLatch() {
68 waitForLatch = true;
69 }
70
71 public CloseableCounter getCloseableCounter() {
72 return counter;
73 }
74
75
76
77
78
79
80
81 protected CloseableCounter initializeInternal() throws Exception {
82 if (ex != null) {
83 throw ex;
84 }
85 if (shouldSleep) {
86 ThreadUtils.sleep(Duration.ofMinutes(1));
87 }
88 if (waitForLatch) {
89 latch.await();
90 }
91 return counter.increment();
92 }
93
94 public void releaseLatch() {
95 latch.countDown();
96 }
97 }
98
99 protected static class CloseableCounter {
100
101 AtomicInteger initializeCalls = new AtomicInteger();
102
103
104 AtomicBoolean closed = new AtomicBoolean();
105
106 public void close() {
107 closed.set(true);
108 }
109
110 public int getInitializeCalls() {
111 return initializeCalls.get();
112 }
113
114 public CloseableCounter increment() {
115 initializeCalls.incrementAndGet();
116 return this;
117 }
118
119 public boolean isClosed() {
120 return closed.get();
121 }
122 }
123
124 protected static class MethodBackgroundInitializerTestImpl extends AbstractBackgroundInitializerTestImpl {
125
126 MethodBackgroundInitializerTestImpl() {
127 }
128
129 MethodBackgroundInitializerTestImpl(final ExecutorService exec) {
130 super(exec);
131 }
132
133 @Override
134 protected CloseableCounter initialize() throws Exception {
135 return initializeInternal();
136 }
137 }
138
139
140
141
142
143
144
145 private void checkInitialize(final AbstractBackgroundInitializerTestImpl init) throws ConcurrentException {
146 final Integer result = init.get().getInitializeCalls();
147 assertEquals(1, result.intValue(), "Wrong result");
148 assertEquals(1, init.getCloseableCounter().getInitializeCalls(), "Wrong number of invocations");
149 assertNotNull(init.getFuture(), "No future");
150 }
151
152 protected AbstractBackgroundInitializerTestImpl getBackgroundInitializerTestImpl() {
153 return new MethodBackgroundInitializerTestImpl();
154 }
155
156 protected AbstractBackgroundInitializerTestImpl getBackgroundInitializerTestImpl(final ExecutorService exec) {
157 return new MethodBackgroundInitializerTestImpl(exec);
158 }
159
160
161
162
163
164 @Test
165 public void testGetActiveExecutorBeforeStart() {
166 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
167 assertNull(init.getActiveExecutor(), "Got an executor");
168 }
169
170
171
172
173 @Test
174 public void testGetActiveExecutorExternal() throws InterruptedException, ConcurrentException {
175 final ExecutorService exec = Executors.newSingleThreadExecutor();
176 try {
177 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl(
178 exec);
179 init.start();
180 assertSame(exec, init.getActiveExecutor(), "Wrong executor");
181 checkInitialize(init);
182 } finally {
183 exec.shutdown();
184 exec.awaitTermination(1, TimeUnit.SECONDS);
185 }
186 }
187
188
189
190
191 @Test
192 public void testGetActiveExecutorTemp() throws ConcurrentException {
193 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
194 init.start();
195 assertNotNull(init.getActiveExecutor(), "No active executor");
196 checkInitialize(init);
197 }
198
199
200
201
202 @Test
203 public void testGetBeforeStart() {
204 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
205 assertThrows(IllegalStateException.class, init::get);
206 }
207
208
209
210
211
212 @Test
213 public void testGetCheckedException() {
214 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
215 final Exception ex = new Exception();
216 init.ex = ex;
217 init.start();
218 final ConcurrentException cex = assertThrows(ConcurrentException.class, init::get);
219 assertEquals(ex, cex.getCause(), "Exception not thrown");
220 }
221
222
223
224
225
226
227 @Test
228 public void testGetInterruptedException() throws InterruptedException {
229 final ExecutorService exec = Executors.newSingleThreadExecutor();
230 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl(
231 exec);
232 final CountDownLatch latch1 = new CountDownLatch(1);
233 init.shouldSleep = true;
234 init.start();
235 final AtomicReference<InterruptedException> iex = new AtomicReference<>();
236 final Thread getThread = new Thread() {
237 @Override
238 public void run() {
239 try {
240 init.get();
241 } catch (final ConcurrentException cex) {
242 if (cex.getCause() instanceof InterruptedException) {
243 iex.set((InterruptedException) cex.getCause());
244 }
245 } finally {
246 assertTrue(isInterrupted(), "Thread not interrupted");
247 latch1.countDown();
248 }
249 }
250 };
251 getThread.start();
252 getThread.interrupt();
253 latch1.await();
254 exec.shutdownNow();
255 exec.awaitTermination(1, TimeUnit.SECONDS);
256 assertNotNull(iex.get(), "No interrupted exception");
257 }
258
259
260
261
262
263 @Test
264 public void testGetRuntimeException() {
265 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
266 final RuntimeException rex = new RuntimeException();
267 init.ex = rex;
268 init.start();
269 final Exception ex = assertThrows(Exception.class, init::get);
270 assertEquals(rex, ex, "Runtime exception not thrown");
271 }
272
273
274
275
276 @Test
277 public void testInitialize() throws ConcurrentException {
278 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
279 init.start();
280 checkInitialize(init);
281 }
282
283
284
285
286
287 @Test
288 public void testInitializeTempExecutor() throws ConcurrentException {
289 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
290 assertTrue(init.start(), "Wrong result of start()");
291 checkInitialize(init);
292 assertTrue(init.getActiveExecutor().isShutdown(), "Executor not shutdown");
293 }
294
295
296
297
298 @Test
299 public void testIsInitialized() throws ConcurrentException {
300 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
301 init.enableLatch();
302 init.start();
303 assertTrue(init.isStarted(), "Not started");
304 assertFalse(init.isInitialized(), "Initalized before releasing latch");
305 init.releaseLatch();
306 init.get();
307 assertTrue(init.isInitialized(), "Not initalized after releasing latch");
308 }
309
310
311
312
313 @Test
314 public void testIsStartedAfterGet() throws ConcurrentException {
315 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
316 init.start();
317 checkInitialize(init);
318 assertTrue(init.isStarted(), "Not started");
319 }
320
321
322
323
324 @Test
325 public void testIsStartedFalse() {
326 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
327 assertFalse(init.isStarted(), "Already started");
328 }
329
330
331
332
333 @Test
334 public void testIsStartedTrue() {
335 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
336 init.start();
337 assertTrue(init.isStarted(), "Not started");
338 }
339
340
341
342
343
344 @Test
345 public void testSetExternalExecutor() throws ConcurrentException {
346 final ExecutorService exec = Executors.newCachedThreadPool();
347 try {
348 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
349 init.setExternalExecutor(exec);
350 assertEquals(exec, init.getExternalExecutor(), "Wrong executor service");
351 assertTrue(init.start(), "Wrong result of start()");
352 assertSame(exec, init.getActiveExecutor(), "Wrong active executor");
353 checkInitialize(init);
354 assertFalse(exec.isShutdown(), "Executor was shutdown");
355 } finally {
356 exec.shutdown();
357 }
358 }
359
360
361
362
363
364
365 @Test
366 public void testSetExternalExecutorAfterStart() throws ConcurrentException, InterruptedException {
367 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
368 init.start();
369 final ExecutorService exec = Executors.newSingleThreadExecutor();
370 try {
371 assertThrows(IllegalStateException.class, () -> init.setExternalExecutor(exec));
372 init.get();
373 } finally {
374 exec.shutdown();
375 exec.awaitTermination(1, TimeUnit.SECONDS);
376 }
377 }
378
379
380
381
382
383 @Test
384 public void testStartMultipleTimes() throws ConcurrentException {
385 final AbstractBackgroundInitializerTestImpl init = getBackgroundInitializerTestImpl();
386 assertTrue(init.start(), "Wrong result for start()");
387 for (int i = 0; i < 10; i++) {
388 assertFalse(init.start(), "Could start again");
389 }
390 checkInitialize(init);
391 }
392 }