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.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertTrue;
23
24 import java.lang.management.ManagementFactory;
25 import java.time.Duration;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.atomic.AtomicInteger;
34
35 import javax.management.MBeanServer;
36 import javax.management.ObjectName;
37
38 import org.apache.commons.pool2.Waiter;
39 import org.apache.commons.pool2.WaiterFactory;
40 import org.apache.commons.pool2.impl.TestGenericObjectPool.SimpleFactory;
41 import org.junit.jupiter.api.AfterEach;
42 import org.junit.jupiter.api.BeforeEach;
43 import org.junit.jupiter.api.Test;
44 import org.junit.jupiter.api.Timeout;
45
46
47
48 class TestBaseGenericObjectPool {
49
50 BaseGenericObjectPool<String> pool;
51 SimpleFactory factory;
52
53 @BeforeEach
54 public void setUp() {
55 factory = new SimpleFactory();
56 pool = new GenericObjectPool<>(factory);
57 }
58
59 @AfterEach
60 public void tearDown() {
61 pool.close();
62 pool = null;
63 factory = null;
64 }
65
66 @Test
67 void testActiveTimeStatistics() {
68 for (int i = 0; i < 99; i++) {
69 pool.updateStatsReturn(Duration.ofMillis(i));
70 }
71 assertEquals(49, pool.getMeanActiveTimeMillis(), Double.MIN_VALUE);
72 }
73
74 @Test
75 void testBorrowWaitStatistics() {
76 final DefaultPooledObject<String> p = (DefaultPooledObject<String>) factory.makeObject();
77 pool.updateStatsBorrow(p, Duration.ofMillis(10));
78 pool.updateStatsBorrow(p, Duration.ofMillis(20));
79 pool.updateStatsBorrow(p, Duration.ofMillis(20));
80 pool.updateStatsBorrow(p, Duration.ofMillis(30));
81 assertEquals(20, pool.getMeanBorrowWaitTimeMillis(), Double.MIN_VALUE);
82 assertEquals(30, pool.getMaxBorrowWaitTimeMillis(), 0);
83 }
84
85 void testBorrowWaitStatisticsMax() {
86 final DefaultPooledObject<String> p = (DefaultPooledObject<String>) factory.makeObject();
87 assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
88 pool.updateStatsBorrow(p, Duration.ZERO);
89 assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
90 pool.updateStatsBorrow(p, Duration.ofMillis(20));
91 assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
92 pool.updateStatsBorrow(p, Duration.ofMillis(20));
93 assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
94 pool.updateStatsBorrow(p, Duration.ofMillis(10));
95 assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
96 }
97
98 @Test
99 void testCollectDetailedStatisticsConfiguration() {
100
101 final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
102 config.setCollectDetailedStatistics(false);
103 try (GenericObjectPool<String> testPool = new GenericObjectPool<>(factory, config)) {
104 assertFalse(testPool.getCollectDetailedStatistics());
105 }
106
107 pool.setCollectDetailedStatistics(false);
108 assertFalse(pool.getCollectDetailedStatistics());
109 pool.setCollectDetailedStatistics(true);
110 assertTrue(pool.getCollectDetailedStatistics());
111 }
112
113 @Test
114 void testCollectDetailedStatisticsDefault() {
115
116 assertTrue(pool.getCollectDetailedStatistics());
117 }
118
119 @Test
120 void testCollectDetailedStatisticsDisabled() throws Exception {
121
122 pool.setCollectDetailedStatistics(false);
123 final DefaultPooledObject<String> pooledObject = (DefaultPooledObject<String>) factory.makeObject();
124
125 final long initialActiveTime = pool.getMeanActiveTimeMillis();
126 final long initialIdleTime = pool.getMeanIdleDuration().toMillis();
127 final long initialWaitTime = pool.getMeanBorrowWaitTimeMillis();
128 final long initialMaxWaitTime = pool.getMaxBorrowWaitTimeMillis();
129
130 pool.updateStatsBorrow(pooledObject, Duration.ofMillis(100));
131 pool.updateStatsReturn(Duration.ofMillis(200));
132
133 assertEquals(1, pool.getBorrowedCount());
134 assertEquals(1, pool.getReturnedCount());
135
136 assertEquals(initialActiveTime, pool.getMeanActiveTimeMillis());
137 assertEquals(initialIdleTime, pool.getMeanIdleDuration().toMillis());
138 assertEquals(initialWaitTime, pool.getMeanBorrowWaitTimeMillis());
139 assertEquals(initialMaxWaitTime, pool.getMaxBorrowWaitTimeMillis());
140 }
141
142 @Test
143 void testCollectDetailedStatisticsEnabled() throws Exception {
144
145 pool.setCollectDetailedStatistics(true);
146 final DefaultPooledObject<String> pooledObject = (DefaultPooledObject<String>) factory.makeObject();
147
148 pool.updateStatsBorrow(pooledObject, Duration.ofMillis(100));
149 pool.updateStatsReturn(Duration.ofMillis(200));
150
151 assertEquals(1, pool.getBorrowedCount());
152 assertEquals(1, pool.getReturnedCount());
153
154 assertEquals(200, pool.getMeanActiveTimeMillis());
155 assertEquals(100, pool.getMeanBorrowWaitTimeMillis());
156 assertEquals(100, pool.getMaxBorrowWaitTimeMillis());
157 }
158
159 @Test
160 void testCollectDetailedStatisticsToggling() throws Exception {
161 final DefaultPooledObject<String> pooledObject = (DefaultPooledObject<String>) factory.makeObject();
162
163 pool.setCollectDetailedStatistics(true);
164 pool.updateStatsBorrow(pooledObject, Duration.ofMillis(50));
165 pool.updateStatsReturn(Duration.ofMillis(100));
166 assertEquals(50, pool.getMeanBorrowWaitTimeMillis());
167 assertEquals(100, pool.getMeanActiveTimeMillis());
168
169 pool.setCollectDetailedStatistics(false);
170 pool.updateStatsBorrow(pooledObject, Duration.ofMillis(200));
171 pool.updateStatsReturn(Duration.ofMillis(300));
172
173 assertEquals(50, pool.getMeanBorrowWaitTimeMillis());
174 assertEquals(100, pool.getMeanActiveTimeMillis());
175
176 assertEquals(2, pool.getBorrowedCount());
177 assertEquals(2, pool.getReturnedCount());
178 }
179
180 @Test
181 void testDetailedStatisticsConfigIntegration() {
182
183 final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
184 config.setCollectDetailedStatistics(false);
185 try (GenericObjectPool<String> testPool = new GenericObjectPool<>(factory, config)) {
186 assertFalse(testPool.getCollectDetailedStatistics(), "Pool should respect collectDetailedStatistics setting from config");
187
188 final String configString = config.toString();
189 assertTrue(configString.contains("collectDetailedStatistics"), "Config toString should include collectDetailedStatistics property");
190 }
191 }
192
193 @Test
194 void testEvictionTimerMultiplePools() throws InterruptedException {
195 final AtomicIntegerFactory factory = new AtomicIntegerFactory();
196 factory.setValidateLatency(50);
197 try (GenericObjectPool<AtomicInteger> evictingPool = new GenericObjectPool<>(factory)) {
198 evictingPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
199 evictingPool.setNumTestsPerEvictionRun(5);
200 evictingPool.setTestWhileIdle(true);
201 evictingPool.setMinEvictableIdleTime(Duration.ofMillis(50));
202 for (int i = 0; i < 10; i++) {
203 try {
204 evictingPool.addObject();
205 } catch (final Exception e) {
206 e.printStackTrace();
207 }
208 }
209
210 for (int i = 0; i < 1000; i++) {
211 try (GenericObjectPool<AtomicInteger> nonEvictingPool = new GenericObjectPool<>(factory)) {
212
213 }
214 }
215
216 Thread.sleep(1000);
217 assertEquals(0, evictingPool.getNumIdle());
218 }
219 }
220
221
222
223
224
225 @SuppressWarnings("resource")
226 @Test
227 @Timeout(value = 10_000, unit = TimeUnit.MILLISECONDS)
228 void testJMXRegistrationLatency() {
229 final int numPools = 1000;
230 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
231 final ArrayList<GenericObjectPool<Waiter>> pools = new ArrayList<>();
232 try {
233
234 for (int i = 0; i < numPools; i++) {
235 pools.add(new GenericObjectPool<>(new WaiterFactory<>(0, 0, 0, 0, 0, 0), new GenericObjectPoolConfig<>()));
236 }
237
238 final ObjectName oname = pools.get(numPools - 1).getJmxName();
239 assertEquals(1, mbs.queryNames(oname, null).size());
240 } finally {
241 pools.forEach(GenericObjectPool::close);
242 }
243 }
244
245 @Test
246 void testStatsStoreCircularBuffer() throws Exception {
247
248 final DefaultPooledObject<String> pooledObject = (DefaultPooledObject<String>) factory.makeObject();
249
250 final int cacheSize = 100;
251 for (int i = 0; i < cacheSize + 50; i++) {
252 pool.updateStatsBorrow(pooledObject, Duration.ofMillis(i));
253 pool.updateStatsReturn(Duration.ofMillis(i * 2));
254 }
255
256 assertTrue(pool.getMeanActiveTimeMillis() > 0);
257 assertTrue(pool.getMeanBorrowWaitTimeMillis() > 0);
258 assertTrue(pool.getMaxBorrowWaitTimeMillis() > 0);
259
260
261 assertTrue(pool.getMeanBorrowWaitTimeMillis() >= 50);
262 }
263
264 @Test
265 void testStatsStoreConcurrentAccess() throws Exception {
266
267 final int numThreads = 10;
268 final int operationsPerThread = 1000;
269 final ExecutorService executor = Executors.newFixedThreadPool(numThreads);
270 final CountDownLatch startLatch = new CountDownLatch(1);
271 final CountDownLatch completeLatch = new CountDownLatch(numThreads);
272 final List<Future<Void>> futures = new ArrayList<>();
273
274 for (int i = 0; i < numThreads; i++) {
275 final int threadId = i;
276 futures.add(executor.submit(() -> {
277 try {
278 final DefaultPooledObject<String> pooledObject = (DefaultPooledObject<String>) factory.makeObject();
279
280 startLatch.await();
281
282 for (int j = 0; j < operationsPerThread; j++) {
283 pool.updateStatsBorrow(pooledObject, Duration.ofMillis(threadId * 10 + j));
284 pool.updateStatsReturn(Duration.ofMillis(threadId * 20 + j));
285 }
286 } catch (final Exception e) {
287 throw new RuntimeException(e);
288 } finally {
289 completeLatch.countDown();
290 }
291 return null;
292 }));
293 }
294
295 startLatch.countDown();
296
297 assertTrue(completeLatch.await(30, TimeUnit.SECONDS), "Concurrent test should complete within 30 seconds");
298
299 for (final Future<Void> future : futures) {
300 future.get();
301 }
302
303 assertEquals(numThreads * operationsPerThread, pool.getBorrowedCount());
304 assertEquals(numThreads * operationsPerThread, pool.getReturnedCount());
305
306 assertTrue(pool.getMeanActiveTimeMillis() >= 0);
307 assertTrue(pool.getMeanBorrowWaitTimeMillis() >= 0);
308 assertTrue(pool.getMaxBorrowWaitTimeMillis() >= 0);
309 executor.shutdown();
310 assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS));
311 }
312 }