1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.pool2.impl;
19
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21
22 import java.time.Duration;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.concurrent.TimeUnit;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27 import org.apache.commons.pool2.BasePooledObjectFactory;
28 import org.apache.commons.pool2.PooledObject;
29 import org.apache.commons.pool2.Waiter;
30 import org.junit.jupiter.api.Test;
31 import org.junit.jupiter.api.Timeout;
32
33
34
35
36 public class TestGenericObjectPoolFactoryCreateFailure {
37
38 private static class SingleObjectFactory extends BasePooledObjectFactory<Object> {
39 private final AtomicBoolean created = new AtomicBoolean();
40
41 @Override
42 public Object create() throws Exception {
43 if (!created.getAndSet(true)) {
44 return new Object();
45 }
46 throw new Exception("Already created");
47 }
48
49 @Override
50 public boolean validateObject(final PooledObject<Object> p) {
51 return true;
52 }
53
54 @Override
55 public PooledObject<Object> wrap(final Object obj) {
56 return new DefaultPooledObject<>(new Object());
57 }
58 }
59
60 private static class WinnerRunnable implements Runnable {
61 private final CountDownLatch barrier;
62 private final AtomicBoolean failed;
63 private final GenericObjectPool<Object> pool;
64 private WinnerRunnable(final GenericObjectPool<Object> pool, final CountDownLatch barrier, final AtomicBoolean failed) {
65 this.pool = pool;
66 this.failed = failed;
67 this.barrier = barrier;
68 }
69 @Override
70 public void run() {
71 try {
72 println("start borrowing in parallel thread");
73 final Object obj = pool.borrowObject();
74
75
76 if (!barrier.await(5, TimeUnit.SECONDS)) {
77 println("Timeout waiting");
78 failed.set(true);
79 } else {
80
81 Waiter.sleepQuietly(1000);
82 }
83
84 pool.returnObject(obj);
85 println("ended borrowing in parallel thread");
86 } catch (final Exception e) {
87 failed.set(true);
88 e.printStackTrace();
89 }
90 }
91 }
92
93 private static final Duration NEG_ONE_DURATION = Duration.ofMillis(-1);
94
95 private static void println(final String msg) {
96
97 }
98
99 @Test
100 @Timeout(value = 10_000, unit = TimeUnit.MILLISECONDS)
101 public void testBorrowObjectStuck() {
102 final SingleObjectFactory factory = new SingleObjectFactory();
103 final GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
104 config.setMaxIdle(1);
105 config.setMaxTotal(1);
106 config.setBlockWhenExhausted(true);
107 config.setMinIdle(0);
108 config.setTestOnBorrow(true);
109 config.setTestOnReturn(true);
110 config.setTestWhileIdle(false);
111 config.setTimeBetweenEvictionRuns(NEG_ONE_DURATION);
112 config.setMinEvictableIdleTime(NEG_ONE_DURATION);
113 config.setMinEvictableIdleDuration(NEG_ONE_DURATION);
114 config.setSoftMinEvictableIdleDuration(NEG_ONE_DURATION);
115
116 config.setMaxWait(NEG_ONE_DURATION);
117 try (GenericObjectPool<Object> pool = new GenericObjectPool<>(factory, config)) {
118
119 final AtomicBoolean failed = new AtomicBoolean();
120 final CountDownLatch barrier = new CountDownLatch(1);
121 final Thread thread1 = new Thread(new WinnerRunnable(pool, barrier, failed));
122 thread1.start();
123
124
125 while (!factory.created.get()) {
126 Waiter.sleepQuietly(5);
127 }
128
129
130 barrier.countDown();
131 try {
132 println("try borrow in main thread");
133
134 final Object o = pool.borrowObject();
135 println("Success borrow in main thread " + o);
136 } catch (final Exception e) {
137 e.printStackTrace();
138 }
139
140 assertFalse(failed.get());
141 }
142
143 }
144 }