1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.pool2.impl;
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.assertNotEquals;
22 import static org.junit.jupiter.api.Assertions.assertNotNull;
23 import static org.junit.jupiter.api.Assertions.assertNotSame;
24 import static org.junit.jupiter.api.Assertions.assertSame;
25 import static org.junit.jupiter.api.Assertions.assertThrows;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27 import static org.junit.jupiter.api.Assertions.fail;
28
29 import java.lang.management.ManagementFactory;
30 import java.time.Duration;
31 import java.time.Instant;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.NoSuchElementException;
37 import java.util.Objects;
38 import java.util.Random;
39 import java.util.Set;
40 import java.util.Timer;
41 import java.util.TimerTask;
42 import java.util.concurrent.ConcurrentHashMap;
43 import java.util.concurrent.ExecutionException;
44 import java.util.concurrent.ExecutorService;
45 import java.util.concurrent.Executors;
46 import java.util.concurrent.Future;
47 import java.util.concurrent.Semaphore;
48 import java.util.concurrent.ThreadFactory;
49 import java.util.concurrent.TimeUnit;
50 import java.util.concurrent.atomic.AtomicInteger;
51
52 import javax.management.MBeanServer;
53 import javax.management.ObjectName;
54
55 import org.apache.commons.lang3.exception.ExceptionUtils;
56 import org.apache.commons.lang3.function.Suppliers;
57 import org.apache.commons.pool2.AbstractTestKeyedObjectPool;
58 import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
59 import org.apache.commons.pool2.DestroyMode;
60 import org.apache.commons.pool2.KeyedObjectPool;
61 import org.apache.commons.pool2.KeyedPooledObjectFactory;
62 import org.apache.commons.pool2.PooledObject;
63 import org.apache.commons.pool2.VisitTracker;
64 import org.apache.commons.pool2.VisitTrackerFactory;
65 import org.apache.commons.pool2.Waiter;
66 import org.apache.commons.pool2.WaiterFactory;
67 import org.junit.jupiter.api.AfterEach;
68 import org.junit.jupiter.api.BeforeEach;
69 import org.junit.jupiter.api.Test;
70 import org.junit.jupiter.api.Timeout;
71 import org.junit.jupiter.params.ParameterizedTest;
72 import org.junit.jupiter.params.provider.EnumSource;
73
74
75
76 public class TestGenericKeyedObjectPool extends AbstractTestKeyedObjectPool {
77 private static final class DaemonThreadFactory implements ThreadFactory {
78 @Override
79 public Thread newThread(final Runnable r) {
80 final Thread t = new Thread(r);
81 t.setDaemon(true);
82 return t;
83 }
84 }
85
86 private static final class DummyFactory extends BaseKeyedPooledObjectFactory<Object, Object> {
87 @Override
88 public Object create(final Object key) {
89 return null;
90 }
91
92 @Override
93 public PooledObject<Object> wrap(final Object value) {
94 return new DefaultPooledObject<>(value);
95 }
96 }
97
98
99
100
101
102 private static final class HashSetFactory extends BaseKeyedPooledObjectFactory<String, HashSet<String>> {
103 @Override
104 public HashSet<String> create(final String key) {
105 return new HashSet<>();
106 }
107
108 @Override
109 public PooledObject<HashSet<String>> wrap(final HashSet<String> value) {
110 return new DefaultPooledObject<>(value);
111 }
112 }
113
114
115
116
117 private static class InvalidateThread implements Runnable {
118 private final String obj;
119 private final KeyedObjectPool<String, String> pool;
120 private final String key;
121 private boolean done;
122
123 private InvalidateThread(final KeyedObjectPool<String, String> pool, final String key, final String obj) {
124 this.obj = obj;
125 this.pool = pool;
126 this.key = key;
127 }
128
129 private boolean complete() {
130 return done;
131 }
132
133 @Override
134 public void run() {
135 try {
136 pool.invalidateObject(key, obj);
137 } catch (final IllegalStateException ex) {
138
139 } catch (final Exception ex) {
140 fail("Unexpected exception " + ex.toString());
141 } finally {
142 done = true;
143 }
144 }
145 }
146
147 private static final class ObjectFactory extends BaseKeyedPooledObjectFactory<Integer, Object> {
148 @Override
149 public Object create(final Integer key) {
150 return new Object();
151 }
152
153 @Override
154 public PooledObject<Object> wrap(final Object value) {
155 return new DefaultPooledObject<>(value);
156 }
157 }
158
159 public static class SimpleFactory<K> implements KeyedPooledObjectFactory<K, String> {
160 volatile int counter;
161 final boolean valid;
162 int activeCount;
163 int validateCounter;
164 boolean evenValid = true;
165 boolean oddValid = true;
166 boolean enableValidation;
167 long destroyLatency;
168 long makeLatency;
169 long validateLatency;
170 volatile int maxTotalPerKey = Integer.MAX_VALUE;
171 boolean exceptionOnPassivate;
172 boolean exceptionOnActivate;
173 boolean exceptionOnDestroy;
174 boolean exceptionOnValidate;
175 boolean exceptionOnCreate;
176
177 public SimpleFactory() {
178 this(true);
179 }
180
181 public SimpleFactory(final boolean valid) {
182 this.valid = valid;
183 }
184
185 @Override
186 public void activateObject(final K key, final PooledObject<String> obj) throws Exception {
187 if (exceptionOnActivate && !(validateCounter++ % 2 == 0 ? evenValid : oddValid)) {
188 throw new Exception();
189 }
190 }
191
192 @Override
193 public void destroyObject(final K key, final PooledObject<String> obj) throws Exception {
194 doWait(destroyLatency);
195 synchronized (this) {
196 activeCount--;
197 }
198 if (exceptionOnDestroy) {
199 throw new Exception();
200 }
201 }
202
203 private void doWait(final long latency) {
204 Waiter.sleepQuietly(latency);
205 }
206
207 @Override
208 public PooledObject<String> makeObject(final K key) throws Exception {
209 if (exceptionOnCreate) {
210 throw new Exception();
211 }
212 doWait(makeLatency);
213 String out = null;
214 synchronized (this) {
215 activeCount++;
216 if (activeCount > maxTotalPerKey) {
217 throw new IllegalStateException("Too many active instances: " + activeCount);
218 }
219 out = String.valueOf(key) + String.valueOf(counter++);
220 }
221 return new DefaultPooledObject<>(out);
222 }
223
224 @Override
225 public void passivateObject(final K key, final PooledObject<String> obj) throws Exception {
226 if (exceptionOnPassivate) {
227 throw new Exception();
228 }
229 }
230
231 public void setDestroyLatency(final long destroyLatency) {
232 this.destroyLatency = destroyLatency;
233 }
234
235 void setEvenValid(final boolean valid) {
236 evenValid = valid;
237 }
238
239 public void setMakeLatency(final long makeLatency) {
240 this.makeLatency = makeLatency;
241 }
242
243 public void setMaxTotalPerKey(final int maxTotalPerKey) {
244 this.maxTotalPerKey = maxTotalPerKey;
245 }
246
247 public void setThrowExceptionOnActivate(final boolean b) {
248 exceptionOnActivate = b;
249 }
250
251 public void setThrowExceptionOnDestroy(final boolean b) {
252 exceptionOnDestroy = b;
253 }
254
255 public void setThrowExceptionOnPassivate(final boolean b) {
256 exceptionOnPassivate = b;
257 }
258
259 public void setThrowExceptionOnValidate(final boolean b) {
260 exceptionOnValidate = b;
261 }
262
263 void setValid(final boolean valid) {
264 evenValid = valid;
265 oddValid = valid;
266 }
267
268 public void setValidateLatency(final long validateLatency) {
269 this.validateLatency = validateLatency;
270 }
271
272 public void setValidationEnabled(final boolean b) {
273 enableValidation = b;
274 }
275
276 @Override
277 public boolean validateObject(final K key, final PooledObject<String> obj) {
278 doWait(validateLatency);
279 if (exceptionOnValidate) {
280 throw new RuntimeException("validation failed");
281 }
282 if (enableValidation) {
283 return validateCounter++ % 2 == 0 ? evenValid : oddValid;
284 }
285 return valid;
286 }
287 }
288
289 private static final class SimplePerKeyFactory extends BaseKeyedPooledObjectFactory<Object, Object> {
290 final ConcurrentHashMap<Object, AtomicInteger> map = new ConcurrentHashMap<>();
291
292 @Override
293 public Object create(final Object key) {
294 final int counter = map.computeIfAbsent(key, k -> new AtomicInteger(-1)).incrementAndGet();
295 return String.valueOf(key) + String.valueOf(counter);
296 }
297
298 @Override
299 public PooledObject<Object> wrap(final Object value) {
300 return new DefaultPooledObject<>(value);
301 }
302 }
303
304
305
306
307
308
309 private static class SimpleTestThread<T> implements Runnable {
310 private final KeyedObjectPool<String, T> pool;
311 private final String key;
312
313 SimpleTestThread(final KeyedObjectPool<String, T> pool, final String key) {
314 this.pool = pool;
315 this.key = key;
316 }
317
318 @Override
319 public void run() {
320 try {
321 pool.returnObject(key, pool.borrowObject(key));
322 } catch (final Exception e) {
323
324 }
325 }
326 }
327
328
329
330
331
332
333 private static final class SlowEvictionPolicy<T> extends DefaultEvictionPolicy<T> {
334 private final long delay;
335
336
337
338
339
340
341 SlowEvictionPolicy(final long delay) {
342 this.delay = delay;
343 }
344
345 @Override
346 public boolean evict(final EvictionConfig config, final PooledObject<T> underTest, final int idleCount) {
347 Waiter.sleepQuietly(delay);
348 return super.evict(config, underTest, idleCount);
349 }
350 }
351
352 private static class TestThread<T> implements Runnable {
353 private final Random random = new Random();
354
355 private final KeyedObjectPool<String, T> pool;
356
357 private final int iter;
358
359 private final int startDelay;
360
361 private final int holdTime;
362
363 private final boolean randomDelay;
364
365 private final T expectedObject;
366
367 private final String key;
368 private volatile boolean complete;
369 private volatile boolean failed;
370 private volatile Exception exception;
371
372 private TestThread(final KeyedObjectPool<String, T> pool) {
373 this(pool, 100, 50, 50, true, null, null);
374 }
375
376 private TestThread(final KeyedObjectPool<String, T> pool, final int iter) {
377 this(pool, iter, 50, 50, true, null, null);
378 }
379
380 private TestThread(final KeyedObjectPool<String, T> pool, final int iter, final int delay) {
381 this(pool, iter, delay, delay, true, null, null);
382 }
383
384 private TestThread(final KeyedObjectPool<String, T> pool, final int iter, final int startDelay, final int holdTime, final boolean randomDelay,
385 final T expectedObject, final String key) {
386 this.pool = pool;
387 this.iter = iter;
388 this.startDelay = startDelay;
389 this.holdTime = holdTime;
390 this.randomDelay = randomDelay;
391 this.expectedObject = expectedObject;
392 this.key = key;
393 }
394
395 private boolean complete() {
396 return complete;
397 }
398
399 private boolean failed() {
400 return failed;
401 }
402
403 @Override
404 public void run() {
405 for (int i = 0; i < iter; i++) {
406 final String actualKey = key == null ? String.valueOf(random.nextInt(3)) : key;
407 Waiter.sleepQuietly(randomDelay ? random.nextInt(startDelay) : startDelay);
408 T obj = null;
409 try {
410 obj = pool.borrowObject(actualKey);
411 } catch (final Exception e) {
412 exception = e;
413 failed = true;
414 complete = true;
415 break;
416 }
417 if (expectedObject != null && !expectedObject.equals(obj)) {
418 exception = new Exception("Expected: " + expectedObject + " found: " + obj);
419 failed = true;
420 complete = true;
421 break;
422 }
423 Waiter.sleepQuietly(randomDelay ? random.nextInt(holdTime) : holdTime);
424 try {
425 pool.returnObject(actualKey, obj);
426 } catch (final Exception e) {
427 exception = e;
428 failed = true;
429 complete = true;
430 break;
431 }
432 }
433 complete = true;
434 }
435 }
436
437
438
439
440 private static class WaitingTestThread extends Thread {
441 private final KeyedObjectPool<String, String> pool;
442 private final String key;
443 private final long pause;
444 private Throwable thrown;
445 private long preBorrowMillis;
446 private long postBorrowMillis;
447 private long postReturnMillis;
448 private long endedMillis;
449 private String objectId;
450
451 private WaitingTestThread(final KeyedObjectPool<String, String> pool, final String key, final long pause) {
452 this.pool = pool;
453 this.key = key;
454 this.pause = pause;
455 this.thrown = null;
456 }
457
458 @Override
459 public void run() {
460 try {
461 preBorrowMillis = System.currentTimeMillis();
462 final String obj = pool.borrowObject(key);
463 objectId = obj;
464 postBorrowMillis = System.currentTimeMillis();
465 Thread.sleep(pause);
466 pool.returnObject(key, obj);
467 postReturnMillis = System.currentTimeMillis();
468 } catch (final Exception e) {
469 thrown = e;
470 } finally {
471 endedMillis = System.currentTimeMillis();
472 }
473 }
474 }
475
476 private static final Integer KEY_ZERO = Integer.valueOf(0);
477 private static final Integer KEY_ONE = Integer.valueOf(1);
478 private static final Integer KEY_TWO = Integer.valueOf(2);
479 private static final boolean DISPLAY_THREAD_DETAILS = Boolean.getBoolean("TestGenericKeyedObjectPool.display.thread.details");
480
481
482
483
484 private GenericKeyedObjectPool<String, String> gkoPool;
485
486 private SimpleFactory<String> simpleFactory;
487
488 private void checkEvictionOrder(final boolean lifo) throws Exception {
489 final SimpleFactory<Integer> intFactory = new SimpleFactory<>();
490 try (GenericKeyedObjectPool<Integer, String> intPool = new GenericKeyedObjectPool<>(intFactory)) {
491 intPool.setNumTestsPerEvictionRun(2);
492 intPool.setMinEvictableIdleTime(Duration.ofMillis(100));
493 intPool.setLifo(lifo);
494 for (int i = 0; i < 3; i++) {
495 final Integer key = Integer.valueOf(i);
496 for (int j = 0; j < 5; j++) {
497 intPool.addObject(key);
498 }
499 }
500
501 Thread.sleep(200);
502
503
504
505
506
507 intPool.evict();
508 assertEquals(3, intPool.getNumIdle(KEY_ZERO));
509 final String objZeroA = intPool.borrowObject(KEY_ZERO);
510 assertTrue(lifo ? objZeroA.equals("04") : objZeroA.equals("02"));
511 assertEquals(2, intPool.getNumIdle(KEY_ZERO));
512 final String objZeroB = intPool.borrowObject(KEY_ZERO);
513 assertEquals("03", objZeroB);
514 assertEquals(1, intPool.getNumIdle(KEY_ZERO));
515 intPool.evict();
516 assertEquals(0, intPool.getNumIdle(KEY_ZERO));
517 assertEquals(4, intPool.getNumIdle(KEY_ONE));
518 final String objOneA = intPool.borrowObject(KEY_ONE);
519 assertTrue(lifo ? objOneA.equals("19") : objOneA.equals("16"));
520 assertEquals(3, intPool.getNumIdle(KEY_ONE));
521 final String objOneB = intPool.borrowObject(KEY_ONE);
522 assertTrue(lifo ? objOneB.equals("18") : objOneB.equals("17"));
523 assertEquals(2, intPool.getNumIdle(KEY_ONE));
524 intPool.evict();
525 assertEquals(0, intPool.getNumIdle(KEY_ONE));
526 intPool.evict();
527 assertEquals(3, intPool.getNumIdle(KEY_TWO));
528 final String objTwoA = intPool.borrowObject(KEY_TWO);
529 assertTrue(lifo ? objTwoA.equals("214") : objTwoA.equals("212"));
530 assertEquals(2, intPool.getNumIdle(KEY_TWO));
531 intPool.evict();
532 assertEquals(0, intPool.getNumIdle(KEY_TWO));
533 intPool.evict();
534
535 intPool.returnObject(KEY_ZERO, objZeroA);
536 intPool.returnObject(KEY_ZERO, objZeroB);
537 intPool.returnObject(KEY_ONE, objOneA);
538 intPool.returnObject(KEY_ONE, objOneB);
539 intPool.returnObject(KEY_TWO, objTwoA);
540
541 intPool.clear();
542
543 intPool.setMinEvictableIdleTime(Duration.ofMillis(500));
544 intFactory.counter = 0;
545 for (int i = 0; i < 3; i++) {
546 final Integer key = Integer.valueOf(i);
547 for (int j = 0; j < 5; j++) {
548 intPool.addObject(key);
549 }
550 Thread.sleep(200);
551 }
552
553 intPool.evict();
554 assertEquals(3, intPool.getNumIdle(KEY_ZERO));
555 intPool.evict();
556 assertEquals(1, intPool.getNumIdle(KEY_ZERO));
557 intPool.evict();
558 assertEquals(0, intPool.getNumIdle(KEY_ZERO));
559 assertEquals(5, intPool.getNumIdle(KEY_ONE));
560 assertEquals(5, intPool.getNumIdle(KEY_TWO));
561 intPool.evict();
562 assertEquals(5, intPool.getNumIdle(KEY_ONE));
563 assertEquals(5, intPool.getNumIdle(KEY_TWO));
564 intPool.evict();
565 assertEquals(5, intPool.getNumIdle(KEY_ONE));
566 assertEquals(5, intPool.getNumIdle(KEY_TWO));
567 intPool.evict();
568 assertEquals(5, intPool.getNumIdle(KEY_ONE));
569 assertEquals(5, intPool.getNumIdle(KEY_TWO));
570 intPool.evict();
571 assertEquals(5, intPool.getNumIdle(KEY_ONE));
572 assertEquals(5, intPool.getNumIdle(KEY_TWO));
573 intPool.evict();
574 assertEquals(5, intPool.getNumIdle(KEY_ONE));
575 assertEquals(5, intPool.getNumIdle(KEY_TWO));
576 Thread.sleep(200);
577 intPool.evict();
578 assertEquals(3, intPool.getNumIdle(KEY_ONE));
579 assertEquals(5, intPool.getNumIdle(KEY_TWO));
580 final String obj = intPool.borrowObject(KEY_ONE);
581 if (lifo) {
582 assertEquals("19", obj);
583 } else {
584 assertEquals("15", obj);
585 }
586 }
587 }
588
589 private void checkEvictorVisiting(final boolean lifo) throws Exception {
590 VisitTrackerFactory<Integer> trackerFactory = new VisitTrackerFactory<>();
591 try (GenericKeyedObjectPool<Integer, VisitTracker<Integer>> intPool = new GenericKeyedObjectPool<>(trackerFactory)) {
592 intPool.setNumTestsPerEvictionRun(2);
593 intPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
594 intPool.setTestWhileIdle(true);
595 intPool.setLifo(lifo);
596 intPool.setTestOnReturn(false);
597 intPool.setTestOnBorrow(false);
598 for (int i = 0; i < 3; i++) {
599 trackerFactory.resetId();
600 final Integer key = Integer.valueOf(i);
601 for (int j = 0; j < 8; j++) {
602 intPool.addObject(key);
603 }
604 }
605 intPool.evict();
606 VisitTracker<Integer> obj = intPool.borrowObject(KEY_ZERO);
607 intPool.returnObject(KEY_ZERO, obj);
608 obj = intPool.borrowObject(KEY_ZERO);
609 intPool.returnObject(KEY_ZERO, obj);
610
611
612
613 intPool.evict();
614 for (int i = 0; i < 8; i++) {
615 final VisitTracker<Integer> tracker = intPool.borrowObject(KEY_ZERO);
616 if (tracker.getId() >= 4) {
617 assertEquals(0, tracker.getValidateCount(), "Unexpected instance visited " + tracker.getId());
618 } else {
619 assertEquals(1, tracker.getValidateCount(), "Instance " + tracker.getId() + " visited wrong number of times.");
620 }
621 }
622
623 intPool.setNumTestsPerEvictionRun(3);
624 intPool.evict();
625 intPool.evict();
626 obj = intPool.borrowObject(KEY_ONE);
627 intPool.returnObject(KEY_ONE, obj);
628 obj = intPool.borrowObject(KEY_ONE);
629 intPool.returnObject(KEY_ONE, obj);
630 obj = intPool.borrowObject(KEY_ONE);
631 intPool.returnObject(KEY_ONE, obj);
632
633
634
635
636 intPool.evict();
637
638
639 intPool.evict();
640
641
642 intPool.evict();
643
644
645 intPool.evict();
646
647
648 for (int i = 0; i < 8; i++) {
649 final VisitTracker<Integer> tracker = intPool.borrowObject(KEY_ONE);
650 if (lifo && tracker.getId() > 1 || !lifo && tracker.getId() > 2) {
651 assertEquals(1, tracker.getValidateCount(), "Instance " + tracker.getId() + " visited wrong number of times.");
652 } else {
653 assertEquals(2, tracker.getValidateCount(), "Instance " + tracker.getId() + " visited wrong number of times.");
654 }
655 }
656 }
657
658
659 final int[] smallPrimes = { 2, 3, 5, 7 };
660 final Random random = new Random();
661 random.setSeed(System.currentTimeMillis());
662 for (int i = 0; i < smallPrimes.length; i++) {
663 for (int j = 0; j < 5; j++) {
664
665
666 trackerFactory = new VisitTrackerFactory<>();
667 try (GenericKeyedObjectPool<Integer, VisitTracker<Integer>> intPool = new GenericKeyedObjectPool<>(trackerFactory)) {
668 intPool.setMaxIdlePerKey(-1);
669 intPool.setMaxTotalPerKey(-1);
670 intPool.setNumTestsPerEvictionRun(smallPrimes[i]);
671 intPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
672 intPool.setTestWhileIdle(true);
673 intPool.setLifo(lifo);
674 intPool.setTestOnReturn(false);
675 intPool.setTestOnBorrow(false);
676 final int zeroLength = 10 + random.nextInt(20);
677 for (int k = 0; k < zeroLength; k++) {
678 intPool.addObject(KEY_ZERO);
679 }
680 final int oneLength = 10 + random.nextInt(20);
681 for (int k = 0; k < oneLength; k++) {
682 intPool.addObject(KEY_ONE);
683 }
684 final int twoLength = 10 + random.nextInt(20);
685 for (int k = 0; k < twoLength; k++) {
686 intPool.addObject(KEY_TWO);
687 }
688
689 final int runs = 10 + random.nextInt(50);
690 for (int k = 0; k < runs; k++) {
691 intPool.evict();
692 }
693
694 final int totalInstances = zeroLength + oneLength + twoLength;
695
696 final int cycleCount = runs * intPool.getNumTestsPerEvictionRun() / totalInstances;
697
698
699 VisitTracker<Integer> tracker = null;
700 int visitCount = 0;
701 for (int k = 0; k < zeroLength; k++) {
702 tracker = intPool.borrowObject(KEY_ZERO);
703 visitCount = tracker.getValidateCount();
704 if (visitCount < cycleCount || visitCount > cycleCount + 1) {
705 fail(formatSettings("ZERO", "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k, "visitCount", visitCount, "cycleCount", cycleCount,
706 "totalInstances", totalInstances, zeroLength, oneLength, twoLength));
707 }
708 }
709 for (int k = 0; k < oneLength; k++) {
710 tracker = intPool.borrowObject(KEY_ONE);
711 visitCount = tracker.getValidateCount();
712 if (visitCount < cycleCount || visitCount > cycleCount + 1) {
713 fail(formatSettings("ONE", "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k, "visitCount", visitCount, "cycleCount", cycleCount,
714 "totalInstances", totalInstances, zeroLength, oneLength, twoLength));
715 }
716 }
717 final int[] visits = new int[twoLength];
718 for (int k = 0; k < twoLength; k++) {
719 tracker = intPool.borrowObject(KEY_TWO);
720 visitCount = tracker.getValidateCount();
721 visits[k] = visitCount;
722 if (visitCount < cycleCount || visitCount > cycleCount + 1) {
723 final StringBuilder sb = new StringBuilder("Visits:");
724 for (int l = 0; l <= k; l++) {
725 sb.append(visits[l]).append(' ');
726 }
727 fail(formatSettings("TWO " + sb.toString(), "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k, "visitCount", visitCount,
728 "cycleCount", cycleCount, "totalInstances", totalInstances, zeroLength, oneLength, twoLength));
729 }
730 }
731 }
732 }
733 }
734 }
735
736 private String formatSettings(final String title, final String s, final int i, final String s0, final boolean b0, final String s1, final int i1,
737 final String s2, final int i2, final String s3, final int i3, final String s4, final int i4, final String s5, final int i5, final String s6,
738 final int i6, final int zeroLength, final int oneLength, final int twoLength) {
739 final StringBuilder sb = new StringBuilder(80);
740 sb.append(title).append(' ');
741 sb.append(s).append('=').append(i).append(' ');
742 sb.append(s0).append('=').append(b0).append(' ');
743 sb.append(s1).append('=').append(i1).append(' ');
744 sb.append(s2).append('=').append(i2).append(' ');
745 sb.append(s3).append('=').append(i3).append(' ');
746 sb.append(s4).append('=').append(i4).append(' ');
747 sb.append(s5).append('=').append(i5).append(' ');
748 sb.append(s6).append('=').append(i6).append(' ');
749 sb.append("Lengths=").append(zeroLength).append(',').append(oneLength).append(',').append(twoLength).append(' ');
750 return sb.toString();
751 }
752
753 @Override
754 protected Object getNthObject(final Object key, final int n) {
755 return String.valueOf(key) + String.valueOf(n);
756 }
757
758 @Override
759 protected boolean isFifo() {
760 return false;
761 }
762
763 @Override
764 protected boolean isLifo() {
765 return true;
766 }
767
768 @SuppressWarnings("unchecked")
769 @Override
770 protected <E extends Exception> KeyedObjectPool<Object, Object> makeEmptyPool(final int minCapacity) {
771 final KeyedPooledObjectFactory<Object, Object> perKeyFactory = new SimplePerKeyFactory();
772 final GenericKeyedObjectPool<Object, Object> perKeyPool = new GenericKeyedObjectPool<>(perKeyFactory);
773 perKeyPool.setMaxTotalPerKey(minCapacity);
774 perKeyPool.setMaxIdlePerKey(minCapacity);
775 return perKeyPool;
776 }
777
778 @Override
779 protected <E extends Exception> KeyedObjectPool<Object, Object> makeEmptyPool(final KeyedPooledObjectFactory<Object, Object> fac) {
780 return new GenericKeyedObjectPool<>(fac);
781 }
782
783 @Override
784 protected Object makeKey(final int n) {
785 return String.valueOf(n);
786 }
787
788
789
790
791
792
793
794
795
796
797
798 public <T> void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericKeyedObjectPool<String, T> gkopPool) {
799 final ArrayList<TestThread<T>> threads = new ArrayList<>();
800 for (int i = 0; i < numThreads; i++) {
801 final TestThread<T> testThread = new TestThread<>(gkopPool, iterations, delay);
802 threads.add(testThread);
803 final Thread t = new Thread(testThread);
804 t.start();
805 }
806 for (final TestThread<T> testThread : threads) {
807 while (!testThread.complete()) {
808 Waiter.sleepQuietly(500L);
809 }
810 if (testThread.failed()) {
811 fail("Thread failed: " + threads.indexOf(testThread) + "\n" + ExceptionUtils.getStackTrace(testThread.exception));
812 }
813 }
814 }
815
816 @BeforeEach
817 public void setUp() {
818 simpleFactory = new SimpleFactory<>();
819 gkoPool = new GenericKeyedObjectPool<>(simpleFactory);
820 }
821
822 @AfterEach
823 public void tearDownJmx() throws Exception {
824 super.tearDown();
825 final ObjectName jmxName = gkoPool.getJmxName();
826 final String poolName = Objects.toString(jmxName, null);
827 gkoPool.clear();
828 gkoPool.close();
829 gkoPool = null;
830 simpleFactory = null;
831 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
832 final Set<ObjectName> result = mbs.queryNames(new ObjectName("org.apache.commoms.pool2:type=GenericKeyedObjectPool,*"), null);
833
834 final int registeredPoolCount = result.size();
835 final StringBuilder msg = new StringBuilder("Current pool is: ");
836 msg.append(poolName);
837 msg.append(" Still open pools are: ");
838 for (final ObjectName name : result) {
839
840 msg.append(name.toString());
841 msg.append(" created via\n");
842 msg.append(mbs.getAttribute(name, "CreationStackTrace"));
843 msg.append('\n');
844 mbs.unregisterMBean(name);
845 }
846 assertEquals(0, registeredPoolCount, msg.toString());
847 }
848
849 @Test
850 void testAppendStats() {
851 assertFalse(gkoPool.getMessageStatistics());
852 assertEquals("foo", gkoPool.appendStats("foo"));
853 try (GenericKeyedObjectPool<?, ?> pool = new GenericKeyedObjectPool<>(new SimpleFactory<>())) {
854 pool.setMessagesStatistics(true);
855 assertNotEquals("foo", pool.appendStats("foo"));
856 pool.setMessagesStatistics(false);
857 assertEquals("foo", pool.appendStats("foo"));
858 }
859 }
860
861 @Test
862 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
863 void testBlockedKeyDoesNotBlockPool() throws Exception {
864 gkoPool.setBlockWhenExhausted(true);
865 gkoPool.setMaxWaitMillis(5000);
866 gkoPool.setMaxTotalPerKey(1);
867 gkoPool.setMaxTotal(-1);
868 gkoPool.borrowObject("one");
869 final long startMillis = System.currentTimeMillis();
870
871 final Runnable simple = new SimpleTestThread<>(gkoPool, "one");
872 new Thread(simple).start();
873
874
875
876 Thread.sleep(1000);
877 gkoPool.borrowObject("two");
878 final long endMillis = System.currentTimeMillis();
879
880
881
882
883 assertTrue(endMillis - startMillis < 4000, "Elapsed time: " + (endMillis - startMillis) + " should be less than 4000");
884 }
885
886
887
888
889
890 @SuppressWarnings("rawtypes")
891 @Test
892 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
893 void testBorrowObjectFairness() throws Exception {
894 final int numThreads = 40;
895 final int maxTotal = 40;
896 final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
897 config.setMaxTotalPerKey(maxTotal);
898 config.setFairness(true);
899 config.setLifo(false);
900 config.setMaxIdlePerKey(maxTotal);
901 gkoPool = new GenericKeyedObjectPool<>(simpleFactory, config);
902
903 final String[] objects = new String[maxTotal];
904 for (int i = 0; i < maxTotal; i++) {
905 objects[i] = gkoPool.borrowObject("0");
906 }
907
908 final TestThread[] threads = new TestThread[numThreads];
909 for (int i = 0; i < numThreads; i++) {
910 threads[i] = new TestThread<>(gkoPool, 1, 0, 2000, false, "0" + String.valueOf(i % maxTotal), "0");
911 final Thread t = new Thread(threads[i]);
912 t.start();
913
914 try {
915 Thread.sleep(10);
916 } catch (final InterruptedException e) {
917 fail(e.toString());
918 }
919 }
920
921 for (int i = 0; i < maxTotal; i++) {
922 gkoPool.returnObject("0", objects[i]);
923 }
924
925 for (int i = 0; i < numThreads; i++) {
926 while (!threads[i].complete()) {
927 Waiter.sleepQuietly(500L);
928 }
929 if (threads[i].failed()) {
930 fail("Thread " + i + " failed: " + ExceptionUtils.getStackTrace(threads[i].exception));
931 }
932 }
933 }
934
935
936
937
938
939
940 @Test
941 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
942 void testClear() throws Exception {
943 gkoPool.setMaxTotal(2);
944 gkoPool.setMaxTotalPerKey(2);
945 gkoPool.setBlockWhenExhausted(false);
946 gkoPool.addObject("one");
947 gkoPool.addObject("one");
948 assertEquals(2, gkoPool.getNumIdle());
949 gkoPool.clear("one");
950 assertEquals(0, gkoPool.getNumIdle());
951 assertEquals(0, gkoPool.getNumIdle("one"));
952 final String obj1 = gkoPool.borrowObject("one");
953 final String obj2 = gkoPool.borrowObject("one");
954 gkoPool.returnObject("one", obj1);
955 gkoPool.returnObject("one", obj2);
956 gkoPool.clear();
957 assertEquals(0, gkoPool.getNumIdle());
958 assertEquals(0, gkoPool.getNumIdle("one"));
959 gkoPool.borrowObject("one");
960 gkoPool.borrowObject("one");
961 gkoPool.close();
962 }
963
964
965
966
967
968
969 @Test
970 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
971 void testClearOldest() throws Exception {
972
973 final WaiterFactory<String> waiterFactory = new WaiterFactory<>(0, 20, 0, 0, 0, 0, 50, 5, 0);
974 try (GenericKeyedObjectPool<String, Waiter> waiterPool = new GenericKeyedObjectPool<>(waiterFactory)) {
975 waiterPool.setMaxTotalPerKey(5);
976 waiterPool.setMaxTotal(50);
977 waiterPool.setLifo(false);
978
979 for (int i = 0; i < 10; i++) {
980 final String key = Integer.toString(i);
981 for (int j = 0; j < 5; j++) {
982 waiterPool.addObject(key);
983 }
984
985 Thread.sleep(20);
986 }
987
988
989
990 final SimpleTestThread<Waiter> t2 = new SimpleTestThread<>(waiterPool, "51");
991 final Thread thread2 = new Thread(t2);
992 thread2.start();
993 Thread.sleep(50);
994 final Waiter waiter = waiterPool.borrowObject("1");
995 Thread.sleep(200);
996 waiterPool.returnObject("1", waiter);
997 }
998 }
999
1000
1001
1002
1003
1004
1005 @Test
1006 void testClearReuseCapacity() throws Exception {
1007 gkoPool.setMaxTotalPerKey(6);
1008 gkoPool.setMaxTotal(6);
1009 gkoPool.setMaxWait(Duration.ofSeconds(5));
1010
1011 final ArrayList<Thread> testThreads = new ArrayList<>();
1012 testThreads.add(new Thread(new SimpleTestThread<>(gkoPool, "one")));
1013 testThreads.add(new Thread(new SimpleTestThread<>(gkoPool, "two")));
1014 testThreads.add(new Thread(new SimpleTestThread<>(gkoPool, "two")));
1015 testThreads.add(new Thread(new SimpleTestThread<>(gkoPool, "three")));
1016 testThreads.add(new Thread(new SimpleTestThread<>(gkoPool, "three")));
1017 testThreads.add(new Thread(new SimpleTestThread<>(gkoPool, "three")));
1018
1019 final String four = gkoPool.borrowObject("four");
1020 final String four2 = gkoPool.borrowObject("four");
1021 final String five = gkoPool.borrowObject("five");
1022 final String five2 = gkoPool.borrowObject("five");
1023 final String six = gkoPool.borrowObject("six");
1024 final String six2 = gkoPool.borrowObject("six");
1025 Thread.sleep(100);
1026
1027 for (final Thread t : testThreads) {
1028 t.start();
1029 }
1030 Thread.sleep(100);
1031
1032
1033 gkoPool.returnObject("four", four);
1034 gkoPool.returnObject("four", four2);
1035 gkoPool.clear("four");
1036 Thread.sleep(20);
1037 assertTrue(!testThreads.get(3).isAlive() || !testThreads.get(4).isAlive() || !testThreads.get(5).isAlive());
1038
1039 gkoPool.returnObject("five", five);
1040 gkoPool.returnObject("five", five2);
1041 gkoPool.clear("five");
1042 Thread.sleep(20);
1043
1044 gkoPool.returnObject("six", six);
1045 gkoPool.returnObject("six", six2);
1046 gkoPool.clear("six");
1047 Thread.sleep(20);
1048 for (final Thread t : testThreads) {
1049 assertFalse(t.isAlive());
1050 }
1051 }
1052
1053
1054
1055
1056 @Test
1057 @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS)
1058 void testClearUnblocksWaiters() throws Exception {
1059 final GenericKeyedObjectPoolConfig<Integer> config = new GenericKeyedObjectPoolConfig<>();
1060 config.setMaxTotalPerKey(1);
1061 config.setMaxTotal(1);
1062 config.setMaxWait(Duration.ofMillis(500));
1063 try (GenericKeyedObjectPool<Integer, Integer> testPool = new GenericKeyedObjectPool<>(new KeyedPooledObjectFactory<Integer, Integer>() {
1064
1065 @Override
1066 public void activateObject(final Integer key, final PooledObject<Integer> p) {
1067
1068 }
1069
1070 @Override
1071 public void destroyObject(final Integer key, final PooledObject<Integer> p) throws InterruptedException {
1072 Thread.sleep(10);
1073 }
1074
1075 @Override
1076 public PooledObject<Integer> makeObject(final Integer key) {
1077 return new DefaultPooledObject<>(10);
1078 }
1079
1080 @Override
1081 public void passivateObject(final Integer key, final PooledObject<Integer> p) {
1082
1083 }
1084
1085 @Override
1086 public boolean validateObject(final Integer key, final PooledObject<Integer> p) {
1087 return true;
1088 }
1089 }, config)) {
1090 final Integer borrowKey = 10;
1091 final int iterations = 100;
1092 final ExecutorService executor = Executors.newFixedThreadPool(2);
1093 final Thread t = new Thread(() -> {
1094 try {
1095 for (int i = 0; i < iterations; i++) {
1096 final Integer integer = testPool.borrowObject(borrowKey);
1097 testPool.returnObject(borrowKey, integer);
1098 Thread.sleep(10);
1099 }
1100 } catch (final Exception e) {
1101 fail(e);
1102 }
1103 });
1104 final Thread t2 = new Thread(() -> {
1105 try {
1106 for (int i = 0; i < iterations; i++) {
1107 testPool.clear(borrowKey);
1108 Thread.sleep(10);
1109 }
1110 } catch (final Exception e) {
1111 fail(e);
1112 }
1113 });
1114 final Future<?> f1 = executor.submit(t);
1115 final Future<?> f2 = executor.submit(t2);
1116 f2.get();
1117 f1.get();
1118 }
1119 }
1120
1121
1122 @Test
1123 void testClientWaitStats() throws Exception {
1124 final SimpleFactory<String> factory = new SimpleFactory<>();
1125
1126 factory.setMakeLatency(200);
1127 try (GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory, new GenericKeyedObjectPoolConfig<>())) {
1128 final String s = pool.borrowObject("one");
1129
1130
1131 assertTrue(pool.getMaxBorrowWaitTimeMillis() >= 100);
1132 assertTrue(pool.getMeanBorrowWaitTimeMillis() >= 100);
1133 pool.returnObject("one", s);
1134 pool.borrowObject("one");
1135
1136 assertTrue(pool.getMaxBorrowWaitTimeMillis() > 100);
1137 assertTrue(pool.getMeanBorrowWaitTimeMillis() < 200);
1138 assertTrue(pool.getMeanBorrowWaitTimeMillis() > 20);
1139 }
1140 }
1141
1142
1143
1144
1145
1146
1147 @Test
1148 void testConcurrentBorrowAndClear() throws Exception {
1149 final int threadCount = 64;
1150 final int taskCount = 64;
1151 final int addCount = 1;
1152 final int borrowCycles = 1024;
1153 final int clearCycles = 1024;
1154 final boolean useYield = true;
1155 testConcurrentBorrowAndClear(threadCount, taskCount, addCount, borrowCycles, clearCycles, useYield);
1156 }
1157
1158
1159
1160
1161
1162
1163 private void testConcurrentBorrowAndClear(final int threadCount, final int taskCount, final int addCount, final int borrowCycles, final int clearCycles,
1164 final boolean useYield) throws Exception {
1165 final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
1166 final int maxTotalPerKey = borrowCycles + 1;
1167 config.setMaxTotalPerKey(threadCount);
1168 config.setMaxIdlePerKey(threadCount);
1169 config.setMaxTotal(maxTotalPerKey * threadCount);
1170 config.setBlockWhenExhausted(false);
1171 gkoPool = new GenericKeyedObjectPool<>(simpleFactory, config);
1172 final String key = "0";
1173 gkoPool.addObjects(Arrays.asList(key), threadCount);
1174
1175 final ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
1176 final List<Future<?>> futures = new ArrayList<>();
1177 try {
1178 for (int t = 0; t < taskCount; t++) {
1179 futures.add(threadPool.submit(() -> {
1180 for (int i = 0; i < clearCycles; i++) {
1181 if (useYield) {
1182 Thread.yield();
1183 }
1184 gkoPool.clear(key, true);
1185 try {
1186 gkoPool.addObjects(Arrays.asList(key), addCount);
1187 } catch (final Exception e) {
1188 fail(e);
1189 }
1190 }
1191 }));
1192 futures.add(threadPool.submit(() -> {
1193 try {
1194 for (int i = 0; i < borrowCycles; i++) {
1195 if (useYield) {
1196 Thread.yield();
1197 }
1198 final String pooled = gkoPool.borrowObject(key);
1199 gkoPool.returnObject(key, pooled);
1200 }
1201 } catch (final Exception e) {
1202 fail(e);
1203 }
1204 }));
1205 }
1206 futures.forEach(f -> {
1207 try {
1208 f.get();
1209 } catch (InterruptedException | ExecutionException e) {
1210 fail(e);
1211 }
1212 });
1213 } finally {
1214 threadPool.shutdownNow();
1215 }
1216 }
1217
1218
1219
1220
1221
1222
1223
1224 @Test
1225 void testConcurrentBorrowAndClear_JiraComment17741156() throws Exception {
1226 final int threadCount = 2;
1227 final int taskCount = 2;
1228 final int addCount = 1;
1229 final int borrowCycles = 5_000;
1230 final int clearCycles = 5_000;
1231 final boolean useYield = false;
1232 testConcurrentBorrowAndClear(threadCount, taskCount, addCount, borrowCycles, clearCycles, useYield);
1233 }
1234
1235
1236
1237
1238
1239
1240 @Test
1241 void testConcurrentInvalidate() throws Exception {
1242
1243 final int nObjects = 1000;
1244 final String key = "one";
1245 gkoPool.setMaxTotal(nObjects);
1246 gkoPool.setMaxTotalPerKey(nObjects);
1247 gkoPool.setMaxIdlePerKey(nObjects);
1248 final String[] obj = new String[nObjects];
1249 for (int i = 0; i < nObjects; i++) {
1250 obj[i] = gkoPool.borrowObject(key);
1251 }
1252 for (int i = 0; i < nObjects; i++) {
1253 if (i % 2 == 0) {
1254 gkoPool.returnObject(key, obj[i]);
1255 }
1256 }
1257 final int nThreads = 20;
1258 final int nIterations = 60;
1259 final InvalidateThread[] threads = new InvalidateThread[nThreads];
1260
1261 final ArrayList<Integer> targets = new ArrayList<>();
1262 final Random random = new Random();
1263 for (int j = 0; j < nIterations; j++) {
1264
1265 Integer targ = Integer.valueOf(random.nextInt(nObjects));
1266 while (targets.contains(targ)) {
1267 targ = Integer.valueOf(random.nextInt(nObjects));
1268 }
1269 targets.add(targ);
1270
1271 for (int i = 0; i < nThreads; i++) {
1272 threads[i] = new InvalidateThread(gkoPool, key, obj[targ.intValue()]);
1273 }
1274 for (int i = 0; i < nThreads; i++) {
1275 new Thread(threads[i]).start();
1276 }
1277 boolean done = false;
1278 while (!done) {
1279 done = true;
1280 for (int i = 0; i < nThreads; i++) {
1281 done = done && threads[i].complete();
1282 }
1283 Thread.sleep(100);
1284 }
1285 }
1286 assertEquals(nIterations, gkoPool.getDestroyedCount());
1287 }
1288
1289 @Test
1290 void testConstructorNullFactory() {
1291
1292 assertThrows(IllegalArgumentException.class, () -> new GenericKeyedObjectPool<>(null));
1293 }
1294
1295 @SuppressWarnings("deprecation")
1296 @Test
1297 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1298 void testConstructors() {
1299
1300 final int maxTotalPerKey = 1;
1301 final int minIdle = 2;
1302 final Duration maxWaitDuration = Duration.ofMillis(3);
1303 final long maxWaitMillis = maxWaitDuration.toMillis();
1304 final int maxIdle = 4;
1305 final int maxTotal = 5;
1306 final long minEvictableIdleTimeMillis = 6;
1307 final int numTestsPerEvictionRun = 7;
1308 final boolean testOnBorrow = true;
1309 final boolean testOnReturn = true;
1310 final boolean testWhileIdle = true;
1311 final long timeBetweenEvictionRunsMillis = 8;
1312 final boolean blockWhenExhausted = false;
1313 final boolean lifo = false;
1314 final KeyedPooledObjectFactory<Object, Object> dummyFactory = new DummyFactory();
1315 try (GenericKeyedObjectPool<Object, Object> objPool = new GenericKeyedObjectPool<>(dummyFactory)) {
1316 assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY, objPool.getMaxTotalPerKey());
1317 assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY, objPool.getMaxIdlePerKey());
1318 assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, objPool.getMaxWaitMillis());
1319 assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY, objPool.getMinIdlePerKey());
1320 assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL, objPool.getMaxTotal());
1321
1322 assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, objPool.getMinEvictableIdleTimeMillis());
1323 assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME, objPool.getMinEvictableIdleTime());
1324 assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME, objPool.getMinEvictableIdleDuration());
1325
1326 assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN, objPool.getNumTestsPerEvictionRun());
1327 assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW), Boolean.valueOf(objPool.getTestOnBorrow()));
1328 assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN), Boolean.valueOf(objPool.getTestOnReturn()));
1329 assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE), Boolean.valueOf(objPool.getTestWhileIdle()));
1330
1331 assertEquals(BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS, objPool.getDurationBetweenEvictionRuns());
1332 assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, objPool.getTimeBetweenEvictionRunsMillis());
1333 assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS, objPool.getTimeBetweenEvictionRuns());
1334
1335 assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED), Boolean.valueOf(objPool.getBlockWhenExhausted()));
1336 assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(objPool.getLifo()));
1337 }
1338 final GenericKeyedObjectPoolConfig<Object> config = new GenericKeyedObjectPoolConfig<>();
1339 config.setLifo(lifo);
1340 config.setMaxTotalPerKey(maxTotalPerKey);
1341 config.setMaxIdlePerKey(maxIdle);
1342 config.setMinIdlePerKey(minIdle);
1343 config.setMaxTotal(maxTotal);
1344 config.setMaxWait(maxWaitDuration);
1345 config.setMinEvictableIdleDuration(Duration.ofMillis(minEvictableIdleTimeMillis));
1346 config.setMinEvictableIdleTime(Duration.ofMillis(minEvictableIdleTimeMillis));
1347 config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
1348 config.setTestOnBorrow(testOnBorrow);
1349 config.setTestOnReturn(testOnReturn);
1350 config.setTestWhileIdle(testWhileIdle);
1351 config.setTimeBetweenEvictionRuns(Duration.ofMillis(timeBetweenEvictionRunsMillis));
1352 config.setBlockWhenExhausted(blockWhenExhausted);
1353 try (GenericKeyedObjectPool<Object, Object> objPool = new GenericKeyedObjectPool<>(dummyFactory, config)) {
1354 assertEquals(maxTotalPerKey, objPool.getMaxTotalPerKey());
1355 assertEquals(maxIdle, objPool.getMaxIdlePerKey());
1356 assertEquals(maxWaitDuration, objPool.getMaxWaitDuration());
1357 assertEquals(maxWaitMillis, objPool.getMaxWaitMillis());
1358 assertEquals(minIdle, objPool.getMinIdlePerKey());
1359 assertEquals(maxTotal, objPool.getMaxTotal());
1360 assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleDuration().toMillis());
1361 assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTimeMillis());
1362 assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTime().toMillis());
1363 assertEquals(numTestsPerEvictionRun, objPool.getNumTestsPerEvictionRun());
1364 assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(objPool.getTestOnBorrow()));
1365 assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(objPool.getTestOnReturn()));
1366 assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(objPool.getTestWhileIdle()));
1367 assertEquals(timeBetweenEvictionRunsMillis, objPool.getDurationBetweenEvictionRuns().toMillis());
1368 assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRunsMillis());
1369 assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRuns().toMillis());
1370 assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(objPool.getBlockWhenExhausted()));
1371 assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(objPool.getLifo()));
1372 }
1373 }
1374
1375
1376
1377
1378 @Test
1379 void testContructorEvictionConfig() throws Exception {
1380 final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
1381 config.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
1382 config.setMinEvictableIdleDuration(Duration.ofMillis(50));
1383 config.setNumTestsPerEvictionRun(5);
1384 try (GenericKeyedObjectPool<String, String> p = new GenericKeyedObjectPool<>(simpleFactory, config)) {
1385 for (int i = 0; i < 5; i++) {
1386 p.addObject("one");
1387 }
1388 Waiter.sleepQuietly(100);
1389 assertEquals(5, p.getNumIdle("one"));
1390 Waiter.sleepQuietly(500);
1391 assertEquals(0, p.getNumIdle("one"));
1392 }
1393 }
1394
1395
1396
1397
1398
1399
1400 @Test
1401 void testEqualsIndiscernible() throws Exception {
1402 final HashSetFactory factory = new HashSetFactory();
1403 try (GenericKeyedObjectPool<String, HashSet<String>> pool = new GenericKeyedObjectPool<>(factory, new GenericKeyedObjectPoolConfig<>())) {
1404 final HashSet<String> s1 = pool.borrowObject("a");
1405 final HashSet<String> s2 = pool.borrowObject("a");
1406 pool.returnObject("a", s1);
1407 pool.returnObject("a", s2);
1408 }
1409 }
1410
1411 @Test
1412 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1413 void testEviction() throws Exception {
1414 gkoPool.setMaxIdlePerKey(500);
1415 gkoPool.setMaxTotalPerKey(500);
1416 gkoPool.setNumTestsPerEvictionRun(100);
1417 gkoPool.setMinEvictableIdleTime(Duration.ofMillis(250));
1418 gkoPool.setDurationBetweenEvictionRuns(Duration.ofMillis(500));
1419 final String[] active = new String[500];
1420 for (int i = 0; i < 500; i++) {
1421 active[i] = gkoPool.borrowObject("");
1422 }
1423 for (int i = 0; i < 500; i++) {
1424 gkoPool.returnObject("", active[i]);
1425 }
1426 Waiter.sleepQuietly(1000L);
1427 assertTrue(gkoPool.getNumIdle("") < 500, "Should be less than 500 idle, found " + gkoPool.getNumIdle(""));
1428 Waiter.sleepQuietly(600L);
1429 assertTrue(gkoPool.getNumIdle("") < 400, "Should be less than 400 idle, found " + gkoPool.getNumIdle(""));
1430 Waiter.sleepQuietly(600L);
1431 assertTrue(gkoPool.getNumIdle("") < 300, "Should be less than 300 idle, found " + gkoPool.getNumIdle(""));
1432 Waiter.sleepQuietly(600L);
1433 assertTrue(gkoPool.getNumIdle("") < 200, "Should be less than 200 idle, found " + gkoPool.getNumIdle(""));
1434 Waiter.sleepQuietly(600L);
1435 assertTrue(gkoPool.getNumIdle("") < 100, "Should be less than 100 idle, found " + gkoPool.getNumIdle(""));
1436 Waiter.sleepQuietly(600L);
1437 assertEquals(0, gkoPool.getNumIdle(""), "Should be zero idle, found " + gkoPool.getNumIdle(""));
1438 for (int i = 0; i < 500; i++) {
1439 active[i] = gkoPool.borrowObject("");
1440 }
1441 for (int i = 0; i < 500; i++) {
1442 gkoPool.returnObject("", active[i]);
1443 }
1444 Waiter.sleepQuietly(1000L);
1445 assertTrue(gkoPool.getNumIdle("") < 500, "Should be less than 500 idle, found " + gkoPool.getNumIdle(""));
1446 Waiter.sleepQuietly(600L);
1447 assertTrue(gkoPool.getNumIdle("") < 400, "Should be less than 400 idle, found " + gkoPool.getNumIdle(""));
1448 Waiter.sleepQuietly(600L);
1449 assertTrue(gkoPool.getNumIdle("") < 300, "Should be less than 300 idle, found " + gkoPool.getNumIdle(""));
1450 Waiter.sleepQuietly(600L);
1451 assertTrue(gkoPool.getNumIdle("") < 200, "Should be less than 200 idle, found " + gkoPool.getNumIdle(""));
1452 Waiter.sleepQuietly(600L);
1453 assertTrue(gkoPool.getNumIdle("") < 100, "Should be less than 100 idle, found " + gkoPool.getNumIdle(""));
1454 Waiter.sleepQuietly(600L);
1455 assertEquals(0, gkoPool.getNumIdle(""), "Should be zero idle, found " + gkoPool.getNumIdle(""));
1456 }
1457
1458 @Test
1459 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1460 void testEviction2() throws Exception {
1461 gkoPool.setMaxIdlePerKey(500);
1462 gkoPool.setMaxTotalPerKey(500);
1463 gkoPool.setNumTestsPerEvictionRun(100);
1464 gkoPool.setMinEvictableIdleTime(Duration.ofMillis(500));
1465 gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
1466 final String[] active = new String[500];
1467 final String[] active2 = new String[500];
1468 for (int i = 0; i < 500; i++) {
1469 active[i] = gkoPool.borrowObject("");
1470 active2[i] = gkoPool.borrowObject("2");
1471 }
1472 for (int i = 0; i < 500; i++) {
1473 gkoPool.returnObject("", active[i]);
1474 gkoPool.returnObject("2", active2[i]);
1475 }
1476 Waiter.sleepQuietly(1100L);
1477 assertTrue(gkoPool.getNumIdle() < 1000, "Should be less than 1000 idle, found " + gkoPool.getNumIdle());
1478 final long sleepMillisPart2 = 600L;
1479 Waiter.sleepQuietly(sleepMillisPart2);
1480 assertTrue(gkoPool.getNumIdle() < 900, "Should be less than 900 idle, found " + gkoPool.getNumIdle());
1481 Waiter.sleepQuietly(sleepMillisPart2);
1482 assertTrue(gkoPool.getNumIdle() < 800, "Should be less than 800 idle, found " + gkoPool.getNumIdle());
1483 Waiter.sleepQuietly(sleepMillisPart2);
1484 assertTrue(gkoPool.getNumIdle() < 700, "Should be less than 700 idle, found " + gkoPool.getNumIdle());
1485 Waiter.sleepQuietly(sleepMillisPart2);
1486 assertTrue(gkoPool.getNumIdle() < 600, "Should be less than 600 idle, found " + gkoPool.getNumIdle());
1487 Waiter.sleepQuietly(sleepMillisPart2);
1488 assertTrue(gkoPool.getNumIdle() < 500, "Should be less than 500 idle, found " + gkoPool.getNumIdle());
1489 Waiter.sleepQuietly(sleepMillisPart2);
1490 assertTrue(gkoPool.getNumIdle() < 400, "Should be less than 400 idle, found " + gkoPool.getNumIdle());
1491 Waiter.sleepQuietly(sleepMillisPart2);
1492 assertTrue(gkoPool.getNumIdle() < 300, "Should be less than 300 idle, found " + gkoPool.getNumIdle());
1493 Waiter.sleepQuietly(sleepMillisPart2);
1494 assertTrue(gkoPool.getNumIdle() < 200, "Should be less than 200 idle, found " + gkoPool.getNumIdle());
1495 Waiter.sleepQuietly(sleepMillisPart2);
1496 assertTrue(gkoPool.getNumIdle() < 100, "Should be less than 100 idle, found " + gkoPool.getNumIdle());
1497 Waiter.sleepQuietly(sleepMillisPart2);
1498 assertEquals(0, gkoPool.getNumIdle(), "Should be zero idle, found " + gkoPool.getNumIdle());
1499 }
1500
1501
1502
1503
1504
1505
1506
1507
1508 @Test
1509 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1510 void testEvictionOrder() throws Exception {
1511 checkEvictionOrder(false);
1512 checkEvictionOrder(true);
1513 }
1514
1515
1516 @Test
1517 void testEvictorClearOldestRace() throws Exception {
1518 gkoPool.setMinEvictableIdleTime(Duration.ofMillis(100));
1519 gkoPool.setNumTestsPerEvictionRun(1);
1520
1521
1522 gkoPool.setEvictionPolicy(new SlowEvictionPolicy<>(1000));
1523
1524 final String val = gkoPool.borrowObject("foo");
1525
1526 gkoPool.addObject("foo");
1527
1528 Thread.sleep(1000);
1529
1530 gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(10));
1531
1532 Thread.sleep(100);
1533 gkoPool.clearOldest();
1534
1535 Thread.sleep(1500);
1536
1537 gkoPool.returnObject("foo", val);
1538 }
1539
1540
1541
1542
1543
1544
1545 @Test
1546 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1547 void testEvictorVisiting() throws Exception {
1548 checkEvictorVisiting(true);
1549 checkEvictorVisiting(false);
1550 }
1551
1552 @Test
1553 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1554 void testExceptionInValidationDuringEviction() throws Exception {
1555 gkoPool.setMaxIdlePerKey(1);
1556 gkoPool.setMinEvictableIdleTime(Duration.ZERO);
1557 gkoPool.setTestWhileIdle(true);
1558 final String obj = gkoPool.borrowObject("one");
1559 gkoPool.returnObject("one", obj);
1560 simpleFactory.setThrowExceptionOnValidate(true);
1561 assertThrows(RuntimeException.class, gkoPool::evict);
1562 assertEquals(0, gkoPool.getNumActive());
1563 assertEquals(0, gkoPool.getNumIdle());
1564 }
1565
1566 @Test
1567 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1568 void testExceptionOnActivateDuringBorrow() throws Exception {
1569 final String obj1 = gkoPool.borrowObject("one");
1570 final String obj2 = gkoPool.borrowObject("one");
1571 gkoPool.returnObject("one", obj1);
1572 gkoPool.returnObject("one", obj2);
1573 simpleFactory.setThrowExceptionOnActivate(true);
1574 simpleFactory.setEvenValid(false);
1575
1576
1577 final String obj = gkoPool.borrowObject("one");
1578 assertEquals(1, gkoPool.getNumActive("one"));
1579 assertEquals(0, gkoPool.getNumIdle("one"));
1580 assertEquals(1, gkoPool.getNumActive());
1581 assertEquals(0, gkoPool.getNumIdle());
1582 gkoPool.returnObject("one", obj);
1583 simpleFactory.setValid(false);
1584
1585
1586 assertThrows(NoSuchElementException.class, () -> gkoPool.borrowObject("one"));
1587 assertEquals(0, gkoPool.getNumActive("one"));
1588 assertEquals(0, gkoPool.getNumIdle("one"));
1589 assertEquals(0, gkoPool.getNumActive());
1590 assertEquals(0, gkoPool.getNumIdle());
1591 }
1592
1593 @Test
1594 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1595 void testExceptionOnDestroyDuringBorrow() throws Exception {
1596 simpleFactory.setThrowExceptionOnDestroy(true);
1597 simpleFactory.setValidationEnabled(true);
1598 gkoPool.setTestOnBorrow(true);
1599 gkoPool.borrowObject("one");
1600 simpleFactory.setValid(false);
1601 assertThrows(NoSuchElementException.class, () -> gkoPool.borrowObject("one"));
1602 assertEquals(1, gkoPool.getNumActive("one"));
1603 assertEquals(0, gkoPool.getNumIdle("one"));
1604 assertEquals(1, gkoPool.getNumActive());
1605 assertEquals(0, gkoPool.getNumIdle());
1606 }
1607
1608 @Test
1609 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1610 void testExceptionOnDestroyDuringReturn() throws Exception {
1611 simpleFactory.setThrowExceptionOnDestroy(true);
1612 simpleFactory.setValidationEnabled(true);
1613 gkoPool.setTestOnReturn(true);
1614 final String obj1 = gkoPool.borrowObject("one");
1615 gkoPool.borrowObject("one");
1616 simpleFactory.setValid(false);
1617 gkoPool.returnObject("one", obj1);
1618 assertEquals(1, gkoPool.getNumActive("one"));
1619 assertEquals(0, gkoPool.getNumIdle("one"));
1620 assertEquals(1, gkoPool.getNumActive());
1621 assertEquals(0, gkoPool.getNumIdle());
1622 }
1623
1624 @Test
1625 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1626 void testExceptionOnPassivateDuringReturn() throws Exception {
1627 final String obj = gkoPool.borrowObject("one");
1628 simpleFactory.setThrowExceptionOnPassivate(true);
1629 gkoPool.returnObject("one", obj);
1630 assertEquals(0, gkoPool.getNumIdle());
1631 gkoPool.close();
1632 }
1633
1634 @Test
1635 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1636 void testFIFO() throws Exception {
1637 gkoPool.setLifo(false);
1638 final String key = "key";
1639 gkoPool.addObject(key);
1640 gkoPool.addObject(key);
1641 gkoPool.addObject(key);
1642 assertEquals("key0", gkoPool.borrowObject(key), "Oldest");
1643 assertEquals("key1", gkoPool.borrowObject(key), "Middle");
1644 assertEquals("key2", gkoPool.borrowObject(key), "Youngest");
1645 final String s = gkoPool.borrowObject(key);
1646 assertEquals("key3", s, "new-3");
1647 gkoPool.returnObject(key, s);
1648 assertEquals(s, gkoPool.borrowObject(key), "returned");
1649 assertEquals("key4", gkoPool.borrowObject(key), "new-4");
1650 }
1651
1652 @Test
1653 @Timeout(value = 60, unit = TimeUnit.SECONDS)
1654 void testGetKeys() throws Exception {
1655 gkoPool.addObject("one");
1656 assertEquals(1, gkoPool.getKeys().size());
1657 gkoPool.addObject("two");
1658 assertEquals(2, gkoPool.getKeys().size());
1659 gkoPool.clear("one");
1660 assertEquals(1, gkoPool.getKeys().size());
1661 assertEquals("two", gkoPool.getKeys().get(0));
1662 gkoPool.clear();
1663 }
1664
1665 @Test
1666 void testGetStatsString() {
1667 assertNotNull(gkoPool.getStatsString());
1668 }
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678 @Test
1679 void testInvalidateFreesCapacity() throws Exception {
1680 final SimpleFactory<String> factory = new SimpleFactory<>();
1681 try (GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
1682 pool.setMaxTotalPerKey(2);
1683 pool.setMaxWaitMillis(500);
1684
1685 final WaitingTestThread thread1 = new WaitingTestThread(pool, "one", 5000);
1686 thread1.start();
1687
1688 final String obj = pool.borrowObject("one");
1689
1690 final WaitingTestThread thread2 = new WaitingTestThread(pool, "one", 100);
1691 thread2.start();
1692
1693 Thread.sleep(20);
1694 pool.invalidateObject("one", obj);
1695 Thread.sleep(600);
1696 if (thread2.thrown != null) {
1697 fail(thread2.thrown.toString());
1698 }
1699 }
1700 }
1701
1702 @Test
1703 void testInvalidateFreesCapacityForOtherKeys() throws Exception {
1704 gkoPool.setMaxTotal(1);
1705 gkoPool.setMaxWait(Duration.ofMillis(500));
1706 final Thread borrower = new Thread(new SimpleTestThread<>(gkoPool, "two"));
1707 final String obj = gkoPool.borrowObject("one");
1708 borrower.start();
1709 Thread.sleep(100);
1710 gkoPool.invalidateObject("one", obj);
1711 Thread.sleep(20);
1712 assertFalse(borrower.isAlive());
1713 }
1714
1715 @Test
1716 void testInvalidateMissingKey() throws Exception {
1717 assertThrows(IllegalStateException.class, () -> gkoPool.invalidateObject("UnknownKey", "Ignored"));
1718 }
1719
1720 @ParameterizedTest
1721 @EnumSource(DestroyMode.class)
1722 void testInvalidateMissingKeyForDestroyMode(final DestroyMode destroyMode) throws Exception {
1723 assertThrows(IllegalStateException.class, () -> gkoPool.invalidateObject("UnknownKey", "Ignored", destroyMode));
1724 }
1725
1726
1727
1728
1729
1730
1731
1732
1733 @Test
1734 void testInvalidateWaiting() throws Exception {
1735 final GenericKeyedObjectPoolConfig<Object> config = new GenericKeyedObjectPoolConfig<>();
1736 config.setMaxTotal(2);
1737 config.setBlockWhenExhausted(true);
1738 config.setMinIdlePerKey(0);
1739 config.setMaxWait(Duration.ofMillis(-1));
1740 config.setNumTestsPerEvictionRun(Integer.MAX_VALUE);
1741 config.setTestOnBorrow(true);
1742 config.setTestOnReturn(false);
1743 config.setTestWhileIdle(true);
1744 config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
1745 try (GenericKeyedObjectPool<Integer, Object> pool = new GenericKeyedObjectPool<>(new ObjectFactory(), config)) {
1746
1747 pool.borrowObject(Integer.valueOf(1));
1748 final Object object2 = pool.borrowObject(Integer.valueOf(1));
1749
1750 final ExecutorService executorService = Executors.newSingleThreadExecutor(new DaemonThreadFactory());
1751 final Semaphore signal = new Semaphore(0);
1752 final Future<Exception> result = executorService.submit(() -> {
1753 try {
1754 signal.release();
1755 final Object object3 = pool.borrowObject(Integer.valueOf(1));
1756 pool.returnObject(Integer.valueOf(1), object3);
1757 signal.release();
1758 } catch (final Exception e1) {
1759 return e1;
1760 } catch (final Throwable e2) {
1761 return new Exception(e2);
1762 }
1763 return null;
1764 });
1765
1766 assertTrue(signal.tryAcquire(5, TimeUnit.SECONDS));
1767
1768 Thread.sleep(500);
1769 pool.invalidateObject(Integer.valueOf(1), object2);
1770 assertTrue(signal.tryAcquire(2, TimeUnit.SECONDS), "Call to invalidateObject did not unblock pool waiters.");
1771 if (result.get() != null) {
1772 throw new AssertionError(result.get());
1773 }
1774 }
1775 }
1776
1777
1778
1779
1780 @Test
1781 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1782 void testJmxRegistration() {
1783 final ObjectName oname = gkoPool.getJmxName();
1784 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
1785 final Set<ObjectName> result = mbs.queryNames(oname, null);
1786 assertEquals(1, result.size());
1787 }
1788
1789 @Test
1790 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1791 void testLIFO() throws Exception {
1792 gkoPool.setLifo(true);
1793 final String key = "key";
1794 gkoPool.addObject(key);
1795 gkoPool.addObject(key);
1796 gkoPool.addObject(key);
1797 assertEquals("key2", gkoPool.borrowObject(key), "Youngest");
1798 assertEquals("key1", gkoPool.borrowObject(key), "Middle");
1799 assertEquals("key0", gkoPool.borrowObject(key), "Oldest");
1800 final String s = gkoPool.borrowObject(key);
1801 assertEquals("key3", s, "new-3");
1802 gkoPool.returnObject(key, s);
1803 assertEquals(s, gkoPool.borrowObject(key), "returned");
1804 assertEquals("key4", gkoPool.borrowObject(key), "new-4");
1805 }
1806
1807
1808
1809
1810
1811
1812 @Test
1813 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1814 void testLivenessPerKey() throws Exception {
1815 gkoPool.setMaxIdlePerKey(3);
1816 gkoPool.setMaxTotal(3);
1817 gkoPool.setMaxTotalPerKey(3);
1818 gkoPool.setMaxWaitMillis(3000);
1819
1820 final WaitingTestThread t1 = new WaitingTestThread(gkoPool, "1", 100);
1821 final WaitingTestThread t2 = new WaitingTestThread(gkoPool, "1", 100);
1822 final WaitingTestThread t3 = new WaitingTestThread(gkoPool, "1", 100);
1823 t1.start();
1824 t2.start();
1825 t3.start();
1826
1827
1828 gkoPool.borrowObject("2");
1829 }
1830
1831
1832
1833
1834
1835
1836
1837
1838 @Test
1839 void testMakeObjectException() throws Exception {
1840 final SimpleFactory<String> factory = new SimpleFactory<>();
1841 try (GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
1842 pool.setMaxTotalPerKey(1);
1843 pool.setBlockWhenExhausted(false);
1844 factory.exceptionOnCreate = true;
1845 assertThrows(Exception.class, () -> pool.borrowObject("One"));
1846 factory.exceptionOnCreate = false;
1847 pool.borrowObject("One");
1848 }
1849 }
1850
1851
1852
1853
1854 @Test
1855 @Timeout(value = 200000, unit = TimeUnit.MILLISECONDS)
1856 void testMaxActivePerKeyExceeded() {
1857 final WaiterFactory<String> waiterFactory = new WaiterFactory<>(0, 20, 0, 0, 0, 0, 8, 5, 0);
1858
1859
1860 try (GenericKeyedObjectPool<String, Waiter> waiterPool = new GenericKeyedObjectPool<>(waiterFactory)) {
1861 waiterPool.setMaxTotalPerKey(5);
1862 waiterPool.setMaxTotal(8);
1863 waiterPool.setTestOnBorrow(true);
1864 waiterPool.setMaxIdlePerKey(5);
1865 waiterPool.setMaxWaitMillis(-1);
1866 runTestThreads(20, 300, 250, waiterPool);
1867 }
1868 }
1869
1870 @Test
1871 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1872 void testMaxIdle() throws Exception {
1873 gkoPool.setMaxTotalPerKey(100);
1874 gkoPool.setMaxIdlePerKey(8);
1875 final String[] active = new String[100];
1876 for (int i = 0; i < 100; i++) {
1877 active[i] = gkoPool.borrowObject("");
1878 }
1879 assertEquals(100, gkoPool.getNumActive(""));
1880 assertEquals(0, gkoPool.getNumIdle(""));
1881 for (int i = 0; i < 100; i++) {
1882 gkoPool.returnObject("", active[i]);
1883 assertEquals(99 - i, gkoPool.getNumActive(""));
1884 assertEquals(i < 8 ? i + 1 : 8, gkoPool.getNumIdle(""));
1885 }
1886 for (int i = 0; i < 100; i++) {
1887 active[i] = gkoPool.borrowObject("a");
1888 }
1889 assertEquals(100, gkoPool.getNumActive("a"));
1890 assertEquals(0, gkoPool.getNumIdle("a"));
1891 for (int i = 0; i < 100; i++) {
1892 gkoPool.returnObject("a", active[i]);
1893 assertEquals(99 - i, gkoPool.getNumActive("a"));
1894 assertEquals(i < 8 ? i + 1 : 8, gkoPool.getNumIdle("a"));
1895 }
1896
1897 assertEquals(16, gkoPool.getNumIdle());
1898
1899 assertEquals(8, gkoPool.getNumIdle(""));
1900 assertEquals(8, gkoPool.getNumIdle("a"));
1901 }
1902
1903 @Test
1904 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1905 void testMaxTotal() throws Exception {
1906 gkoPool.setMaxTotalPerKey(2);
1907 gkoPool.setMaxTotal(3);
1908 gkoPool.setBlockWhenExhausted(false);
1909 final String o1 = gkoPool.borrowObject("a");
1910 assertNotNull(o1);
1911 final String o2 = gkoPool.borrowObject("a");
1912 assertNotNull(o2);
1913 final String o3 = gkoPool.borrowObject("b");
1914 assertNotNull(o3);
1915 assertThrows(NoSuchElementException.class, () -> gkoPool.borrowObject("c"));
1916 assertEquals(0, gkoPool.getNumIdle());
1917 gkoPool.returnObject("b", o3);
1918 assertEquals(1, gkoPool.getNumIdle());
1919 assertEquals(1, gkoPool.getNumIdle("b"));
1920 final Object o4 = gkoPool.borrowObject("b");
1921 assertNotNull(o4);
1922 assertEquals(0, gkoPool.getNumIdle());
1923 assertEquals(0, gkoPool.getNumIdle("b"));
1924 gkoPool.setMaxTotal(4);
1925 final Object o5 = gkoPool.borrowObject("b");
1926 assertNotNull(o5);
1927 assertEquals(2, gkoPool.getNumActive("a"));
1928 assertEquals(2, gkoPool.getNumActive("b"));
1929 assertEquals(gkoPool.getMaxTotal(), gkoPool.getNumActive("b") + gkoPool.getNumActive("b"));
1930 assertEquals(gkoPool.getNumActive(), gkoPool.getMaxTotal());
1931 }
1932
1933
1934
1935
1936
1937 @Test
1938 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1939 void testMaxTotalInvariant() {
1940 final int maxTotal = 15;
1941 simpleFactory.setEvenValid(false);
1942 simpleFactory.setDestroyLatency(100);
1943 simpleFactory.setMaxTotalPerKey(maxTotal);
1944 simpleFactory.setValidationEnabled(true);
1945 gkoPool.setMaxTotal(maxTotal);
1946 gkoPool.setMaxIdlePerKey(-1);
1947 gkoPool.setTestOnReturn(true);
1948 gkoPool.setMaxWaitMillis(10000L);
1949 runTestThreads(5, 10, 50, gkoPool);
1950 }
1951
1952 @Test
1953 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1954 void testMaxTotalLRU() throws Exception {
1955 gkoPool.setMaxTotalPerKey(2);
1956 gkoPool.setMaxTotal(3);
1957 final String o1 = gkoPool.borrowObject("a");
1958 assertNotNull(o1);
1959 gkoPool.returnObject("a", o1);
1960 Thread.sleep(25);
1961 final String o2 = gkoPool.borrowObject("b");
1962 assertNotNull(o2);
1963 gkoPool.returnObject("b", o2);
1964 Thread.sleep(25);
1965 final String o3 = gkoPool.borrowObject("c");
1966 assertNotNull(o3);
1967 gkoPool.returnObject("c", o3);
1968 Thread.sleep(25);
1969 final String o4 = gkoPool.borrowObject("a");
1970 assertNotNull(o4);
1971 gkoPool.returnObject("a", o4);
1972 Thread.sleep(25);
1973 assertSame(o1, o4);
1974
1975 final String o5 = gkoPool.borrowObject("d");
1976 assertNotNull(o5);
1977 gkoPool.returnObject("d", o5);
1978 Thread.sleep(25);
1979
1980
1981 final String o6 = gkoPool.borrowObject("b");
1982 assertNotNull(o6);
1983 gkoPool.returnObject("b", o6);
1984 assertNotSame(o1, o6);
1985 assertNotSame(o2, o6);
1986
1987 final String o7 = gkoPool.borrowObject("a");
1988 assertNotNull(o7);
1989 gkoPool.returnObject("a", o7);
1990 assertSame(o4, o7);
1991 }
1992
1993 @Test
1994 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
1995 void testMaxTotalPerKey() throws Exception {
1996 gkoPool.setMaxTotalPerKey(3);
1997 gkoPool.setBlockWhenExhausted(false);
1998 gkoPool.borrowObject("");
1999 gkoPool.borrowObject("");
2000 gkoPool.borrowObject("");
2001 assertThrows(NoSuchElementException.class, () -> gkoPool.borrowObject(""));
2002 }
2003
2004 @Test
2005 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2006 void testMaxTotalPerKeyZero() {
2007 gkoPool.setMaxTotalPerKey(0);
2008 gkoPool.setBlockWhenExhausted(false);
2009 assertThrows(NoSuchElementException.class, () -> gkoPool.borrowObject("a"));
2010 }
2011
2012
2013
2014
2015
2016
2017 @Test
2018 void testMaxTotalWithThreads() throws Exception {
2019 gkoPool.setMaxTotalPerKey(2);
2020 gkoPool.setMaxTotal(1);
2021 final int holdTime = 2000;
2022 final TestThread<String> testA = new TestThread<>(gkoPool, 1, 0, holdTime, false, null, "a");
2023 final TestThread<String> testB = new TestThread<>(gkoPool, 1, 0, holdTime, false, null, "b");
2024 final Thread threadA = new Thread(testA);
2025 final Thread threadB = new Thread(testB);
2026 threadA.start();
2027 threadB.start();
2028 Thread.sleep(holdTime * 2);
2029
2030 boolean threadRunning = true;
2031 int count = 0;
2032 while (threadRunning && count < 15) {
2033 threadRunning = threadA.isAlive();
2034 threadRunning = threadB.isAlive();
2035 Thread.sleep(200);
2036 count++;
2037 }
2038 assertFalse(threadA.isAlive());
2039 assertFalse(threadB.isAlive());
2040 assertFalse(testA.failed);
2041 assertFalse(testB.failed);
2042 }
2043
2044 @Test
2045 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2046 void testMaxTotalZero() {
2047 gkoPool.setMaxTotal(0);
2048 gkoPool.setBlockWhenExhausted(false);
2049 assertThrows(NoSuchElementException.class, () -> gkoPool.borrowObject("a"));
2050 }
2051
2052
2053
2054
2055
2056
2057
2058 @Test
2059 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2060 void testMaxWaitMultiThreaded() throws Exception {
2061 final long maxWait = 500;
2062 final long holdTime = 4 * maxWait;
2063 final int keyCount = 4;
2064 final int threadsPerKey = 5;
2065 gkoPool.setBlockWhenExhausted(true);
2066 gkoPool.setMaxWaitMillis(maxWait);
2067 gkoPool.setMaxTotalPerKey(threadsPerKey);
2068
2069 final WaitingTestThread[] wtt = new WaitingTestThread[keyCount * threadsPerKey * 2];
2070 for (int i = 0; i < wtt.length; i++) {
2071 wtt[i] = new WaitingTestThread(gkoPool, Integer.toString(i % keyCount), holdTime);
2072 }
2073 final long originMillis = System.currentTimeMillis() - 1000;
2074 for (final WaitingTestThread element : wtt) {
2075 element.start();
2076 }
2077 int failed = 0;
2078 for (final WaitingTestThread element : wtt) {
2079 element.join();
2080 if (element.thrown != null) {
2081 failed++;
2082 }
2083 }
2084 if (DISPLAY_THREAD_DETAILS || wtt.length / 2 != failed) {
2085 System.out.println("MaxWait: " + maxWait + " HoldTime: " + holdTime + " KeyCount: " + keyCount + " MaxActive: " + threadsPerKey + " Threads: "
2086 + wtt.length + " Failed: " + failed);
2087 for (final WaitingTestThread wt : wtt) {
2088 System.out.println("Preborrow: " + (wt.preBorrowMillis - originMillis) + " Postborrow: "
2089 + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - originMillis : -1) + " BorrowTime: "
2090 + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - wt.preBorrowMillis : -1) + " PostReturn: "
2091 + (wt.postReturnMillis != 0 ? wt.postReturnMillis - originMillis : -1) + " Ended: " + (wt.endedMillis - originMillis) + " Key: "
2092 + wt.key + " ObjId: " + wt.objectId);
2093 }
2094 }
2095 assertEquals(wtt.length / 2, failed, "Expected half the threads to fail");
2096 }
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108 @Test
2109 void testMaxWaitTimeOutOnTime() throws Exception {
2110 final Duration maxWaitDuration = Duration.ofSeconds(1);
2111 final SimpleFactory<String> factory = new SimpleFactory<>();
2112 factory.setMakeLatency(500);
2113 factory.setValidationEnabled(true);
2114 factory.setValid(false);
2115 try (GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
2116 pool.setBlockWhenExhausted(true);
2117 pool.setMaxWait(maxWaitDuration);
2118 pool.setMaxTotalPerKey(1);
2119 pool.setMaxTotal(1);
2120 pool.setTestOnCreate(true);
2121 final Instant startTime = Instant.now();
2122
2123
2124 try {
2125 pool.borrowObject("a");
2126 } catch (final NoSuchElementException ex) {
2127
2128 }
2129
2130 final Duration duration = Duration.between(startTime, Instant.now());
2131 final long clockGranularityMillis = 20;
2132 assertTrue(duration.toMillis() < maxWaitDuration.toMillis() + clockGranularityMillis,
2133 "Thread A should have timed out after " + maxWaitDuration.toMillis() + " ms, but took " + duration.toMillis() + " ms");
2134 }
2135 }
2136
2137 @Test
2138 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2139 void testMinIdle() throws Exception {
2140 gkoPool.setMaxIdlePerKey(500);
2141 gkoPool.setMinIdlePerKey(5);
2142 gkoPool.setMaxTotalPerKey(10);
2143 gkoPool.setNumTestsPerEvictionRun(0);
2144 gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
2145 gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
2146 gkoPool.setTestWhileIdle(true);
2147
2148 final String key = "A";
2149 gkoPool.preparePool(key);
2150 Waiter.sleepQuietly(150L);
2151 assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle());
2152 final String[] active = new String[5];
2153 active[0] = gkoPool.borrowObject(key);
2154 Waiter.sleepQuietly(150L);
2155 assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle());
2156 for (int i = 1; i < 5; i++) {
2157 active[i] = gkoPool.borrowObject(key);
2158 }
2159 Waiter.sleepQuietly(150L);
2160 assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle());
2161 for (int i = 0; i < 5; i++) {
2162 gkoPool.returnObject(key, active[i]);
2163 }
2164 Waiter.sleepQuietly(150L);
2165 assertEquals(10, gkoPool.getNumIdle(), "Should be 10 idle, found " + gkoPool.getNumIdle());
2166 }
2167
2168 @Test
2169 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2170 void testMinIdleMaxTotalPerKey() throws Exception {
2171 gkoPool.setMaxIdlePerKey(500);
2172 gkoPool.setMinIdlePerKey(5);
2173 gkoPool.setMaxTotalPerKey(10);
2174 gkoPool.setNumTestsPerEvictionRun(0);
2175 gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
2176 gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
2177 gkoPool.setTestWhileIdle(true);
2178 final String key = "A";
2179 gkoPool.preparePool(key);
2180 assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle());
2181 Waiter.sleepQuietly(150L);
2182 assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle());
2183 final String[] active = new String[10];
2184 Waiter.sleepQuietly(150L);
2185 assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle());
2186 for (int i = 0; i < 5; i++) {
2187 active[i] = gkoPool.borrowObject(key);
2188 }
2189 Waiter.sleepQuietly(150L);
2190 assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle());
2191 for (int i = 0; i < 5; i++) {
2192 gkoPool.returnObject(key, active[i]);
2193 }
2194 Waiter.sleepQuietly(150L);
2195 assertEquals(10, gkoPool.getNumIdle(), "Should be 10 idle, found " + gkoPool.getNumIdle());
2196 for (int i = 0; i < 10; i++) {
2197 active[i] = gkoPool.borrowObject(key);
2198 }
2199 Waiter.sleepQuietly(150L);
2200 assertEquals(0, gkoPool.getNumIdle(), "Should be 0 idle, found " + gkoPool.getNumIdle());
2201 for (int i = 0; i < 10; i++) {
2202 gkoPool.returnObject(key, active[i]);
2203 }
2204 Waiter.sleepQuietly(150L);
2205 assertEquals(10, gkoPool.getNumIdle(), "Should be 10 idle, found " + gkoPool.getNumIdle());
2206 }
2207
2208 @Test
2209 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2210 void testMinIdleNoPreparePool() throws Exception {
2211 gkoPool.setMaxIdlePerKey(500);
2212 gkoPool.setMinIdlePerKey(5);
2213 gkoPool.setMaxTotalPerKey(10);
2214 gkoPool.setNumTestsPerEvictionRun(0);
2215 gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
2216 gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
2217 gkoPool.setTestWhileIdle(true);
2218
2219 final String key = "A";
2220 Waiter.sleepQuietly(150L);
2221 assertEquals(0, gkoPool.getNumIdle(), "Should be 0 idle, found " + gkoPool.getNumIdle());
2222 final Object active = gkoPool.borrowObject(key);
2223 assertNotNull(active);
2224 Waiter.sleepQuietly(150L);
2225 assertEquals(5, gkoPool.getNumIdle(), "Should be 5 idle, found " + gkoPool.getNumIdle());
2226 }
2227
2228
2229
2230
2231
2232
2233 @Test
2234 void testMultipleReturn() throws Exception {
2235 final WaiterFactory<String> factory = new WaiterFactory<>(0, 0, 0, 0, 0, 0);
2236 try (GenericKeyedObjectPool<String, Waiter> pool = new GenericKeyedObjectPool<>(factory)) {
2237 pool.setTestOnReturn(true);
2238 final Waiter waiter = pool.borrowObject("a");
2239 pool.returnObject("a", waiter);
2240 assertEquals(1, waiter.getValidationCount());
2241 assertEquals(1, waiter.getPassivationCount());
2242 try {
2243 pool.returnObject("a", waiter);
2244 fail("Expecting IllegalStateException from multiple return");
2245 } catch (final IllegalStateException ex) {
2246
2247 assertEquals(1, waiter.getValidationCount());
2248 assertEquals(1, waiter.getPassivationCount());
2249 }
2250 }
2251 }
2252
2253
2254
2255
2256
2257
2258 @Test
2259 void testMutable() throws Exception {
2260 final HashSetFactory factory = new HashSetFactory();
2261 try (GenericKeyedObjectPool<String, HashSet<String>> pool = new GenericKeyedObjectPool<>(factory, new GenericKeyedObjectPoolConfig<>())) {
2262 final HashSet<String> s1 = pool.borrowObject("a");
2263 final HashSet<String> s2 = pool.borrowObject("a");
2264 s1.add("One");
2265 s2.add("One");
2266 pool.returnObject("a", s1);
2267 pool.returnObject("a", s2);
2268 }
2269 }
2270
2271 @Test
2272 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2273 void testNegativeMaxTotalPerKey() throws Exception {
2274 gkoPool.setMaxTotalPerKey(-1);
2275 gkoPool.setBlockWhenExhausted(false);
2276 final String obj = gkoPool.borrowObject("");
2277 assertEquals("0", obj);
2278 gkoPool.returnObject("", obj);
2279 }
2280
2281
2282
2283
2284 @Test
2285 @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
2286 void testNPEOnFactoryNull() {
2287
2288 final DisconnectingWaiterFactory<String> factory = new DisconnectingWaiterFactory<>(
2289 Suppliers.nul(),
2290 DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_LIFECYCLE_ACTION,
2291 DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_VALIDATION_ACTION
2292 );
2293
2294 try (GenericKeyedObjectPool<String, Waiter> pool = new GenericKeyedObjectPool<>(factory)) {
2295 final String key = "one";
2296 pool.setTestOnBorrow(true);
2297 pool.setMaxTotal(-1);
2298 pool.setMinIdlePerKey(1);
2299
2300 factory.disconnect();
2301 assertThrows(NullPointerException.class, () -> pool.borrowObject(key));
2302 assertThrows(NullPointerException.class, () -> pool.addObject(key));
2303 assertThrows(NullPointerException.class, pool::ensureMinIdle);
2304 }
2305 }
2306
2307 @Test
2308 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2309 void testNumActiveNumIdle2() throws Exception {
2310 assertEquals(0, gkoPool.getNumActive());
2311 assertEquals(0, gkoPool.getNumIdle());
2312 assertEquals(0, gkoPool.getNumActive("A"));
2313 assertEquals(0, gkoPool.getNumIdle("A"));
2314 assertEquals(0, gkoPool.getNumActive("B"));
2315 assertEquals(0, gkoPool.getNumIdle("B"));
2316 final String objA0 = gkoPool.borrowObject("A");
2317 final String objB0 = gkoPool.borrowObject("B");
2318 assertEquals(2, gkoPool.getNumActive());
2319 assertEquals(0, gkoPool.getNumIdle());
2320 assertEquals(1, gkoPool.getNumActive("A"));
2321 assertEquals(0, gkoPool.getNumIdle("A"));
2322 assertEquals(1, gkoPool.getNumActive("B"));
2323 assertEquals(0, gkoPool.getNumIdle("B"));
2324 final String objA1 = gkoPool.borrowObject("A");
2325 final String objB1 = gkoPool.borrowObject("B");
2326 assertEquals(4, gkoPool.getNumActive());
2327 assertEquals(0, gkoPool.getNumIdle());
2328 assertEquals(2, gkoPool.getNumActive("A"));
2329 assertEquals(0, gkoPool.getNumIdle("A"));
2330 assertEquals(2, gkoPool.getNumActive("B"));
2331 assertEquals(0, gkoPool.getNumIdle("B"));
2332 gkoPool.returnObject("A", objA0);
2333 gkoPool.returnObject("B", objB0);
2334 assertEquals(2, gkoPool.getNumActive());
2335 assertEquals(2, gkoPool.getNumIdle());
2336 assertEquals(1, gkoPool.getNumActive("A"));
2337 assertEquals(1, gkoPool.getNumIdle("A"));
2338 assertEquals(1, gkoPool.getNumActive("B"));
2339 assertEquals(1, gkoPool.getNumIdle("B"));
2340 gkoPool.returnObject("A", objA1);
2341 gkoPool.returnObject("B", objB1);
2342 assertEquals(0, gkoPool.getNumActive());
2343 assertEquals(4, gkoPool.getNumIdle());
2344 assertEquals(0, gkoPool.getNumActive("A"));
2345 assertEquals(2, gkoPool.getNumIdle("A"));
2346 assertEquals(0, gkoPool.getNumActive("B"));
2347 assertEquals(2, gkoPool.getNumIdle("B"));
2348 }
2349
2350 @Test
2351 void testReturnObjectThrowsIllegalStateException() {
2352 try (GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(new SimpleFactory<>())) {
2353 assertThrows(IllegalStateException.class, () -> pool.returnObject("Foo", "Bar"));
2354 }
2355 }
2356
2357 @Test
2358 void testReturnObjectWithBlockWhenExhausted() throws Exception {
2359 gkoPool.setBlockWhenExhausted(true);
2360 gkoPool.setMaxTotal(1);
2361
2362 final String obj = gkoPool.borrowObject("0");
2363 gkoPool.returnObject("0", obj);
2364
2365 final TestThread<String> testA = new TestThread<>(gkoPool, 1, 0, 500, false, null, "0");
2366 final TestThread<String> testB = new TestThread<>(gkoPool, 1, 0, 0, false, null, "1");
2367 final Thread threadA = new Thread(testA);
2368 final Thread threadB = new Thread(testB);
2369 threadA.start();
2370 threadB.start();
2371 threadA.join();
2372 threadB.join();
2373 }
2374
2375 @Test
2376 void testReturnObjectWithoutBlockWhenExhausted() throws Exception {
2377 gkoPool.setBlockWhenExhausted(false);
2378
2379 final String obj = gkoPool.borrowObject("0");
2380 gkoPool.returnObject("0", obj);
2381 }
2382
2383
2384
2385
2386
2387
2388
2389
2390 @Test
2391 void testReturnToHead() throws Exception {
2392 final SimpleFactory<String> factory = new SimpleFactory<>();
2393 factory.setValidateLatency(100);
2394 factory.setValid(true);
2395 try (GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
2396 pool.setMaxWaitMillis(1000);
2397 pool.setTestWhileIdle(true);
2398 pool.setMaxTotalPerKey(2);
2399 pool.setNumTestsPerEvictionRun(1);
2400 pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
2401
2402 pool.addObject("one");
2403 pool.addObject("one");
2404
2405
2406 Thread.sleep(800);
2407
2408
2409 Thread.sleep(250);
2410 final String o1 = pool.borrowObject("one");
2411 final String o2 = pool.borrowObject("one");
2412 pool.returnObject("one", o1);
2413 pool.returnObject("one", o2);
2414 }
2415 }
2416
2417
2418
2419
2420
2421
2422 @Test
2423 @Timeout(value = 10_000, unit = TimeUnit.MILLISECONDS)
2424 void testReuseCapacityOnMaintenanceBehavior() throws Exception {
2425 gkoPool.setMaxTotalPerKey(2);
2426 gkoPool.setMaxTotal(4);
2427 gkoPool.setBlockWhenExhausted(true);
2428 gkoPool.setMaxWait(Duration.ofSeconds(5));
2429 gkoPool.setReuseCapacityOnReturn(false);
2430 gkoPool.setReuseCapacityOnMaintenance(true);
2431
2432
2433 final Thread waiter = new Thread(new SimpleTestThread<>(gkoPool, "key1"));
2434
2435
2436 final String obj1 = gkoPool.borrowObject("key2");
2437 final String obj2 = gkoPool.borrowObject("key2");
2438 final String obj3 = gkoPool.borrowObject("key3");
2439 final String obj4 = gkoPool.borrowObject("key3");
2440
2441 Thread.sleep(100);
2442
2443
2444 waiter.start();
2445 Thread.sleep(100);
2446
2447
2448
2449 gkoPool.returnObject("key2", obj1);
2450
2451
2452
2453 Thread.sleep(100);
2454 assertTrue(waiter.isAlive());
2455
2456
2457 gkoPool.evict();
2458 Thread.sleep(100);
2459
2460
2461 assertFalse(waiter.isAlive());
2462
2463
2464 gkoPool.returnObject("key2", obj2);
2465 gkoPool.returnObject("key3", obj3);
2466 gkoPool.returnObject("key3", obj4);
2467 }
2468
2469 @Test
2470 void testReuseCapacityOnMaintenanceConfig() throws Exception {
2471
2472 assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_REUSE_CAPACITY_ON_MAINTENANCE, gkoPool.getReuseCapacityOnMaintenance());
2473 assertFalse(gkoPool.getReuseCapacityOnMaintenance());
2474
2475
2476 final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
2477 config.setReuseCapacityOnMaintenance(true);
2478 assertEquals(true, config.getReuseCapacityOnMaintenance());
2479
2480 try (GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(simpleFactory, config)) {
2481 assertEquals(true, pool.getReuseCapacityOnMaintenance());
2482 }
2483
2484
2485 gkoPool.setReuseCapacityOnMaintenance(true);
2486 assertEquals(true, gkoPool.getReuseCapacityOnMaintenance());
2487
2488 gkoPool.setReuseCapacityOnMaintenance(false);
2489 assertEquals(false, gkoPool.getReuseCapacityOnMaintenance());
2490 }
2491
2492 @Test
2493 void testReuseCapacityOnReturnConfig() throws Exception {
2494
2495 assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_REUSE_CAPACITY_ON_RETURN, gkoPool.getReuseCapacityOnReturn());
2496 assertTrue(gkoPool.getReuseCapacityOnReturn());
2497
2498
2499 final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
2500 config.setReuseCapacityOnReturn(false);
2501 assertEquals(false, config.getReuseCapacityOnReturn());
2502
2503 try (GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(simpleFactory, config)) {
2504 assertEquals(false, pool.getReuseCapacityOnReturn());
2505 }
2506
2507
2508 gkoPool.setReuseCapacityOnReturn(false);
2509 assertEquals(false, gkoPool.getReuseCapacityOnReturn());
2510
2511 gkoPool.setReuseCapacityOnReturn(true);
2512 assertEquals(true, gkoPool.getReuseCapacityOnReturn());
2513 }
2514
2515 @Test
2516 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2517 void testSettersAndGetters() {
2518 {
2519 gkoPool.setMaxTotalPerKey(123);
2520 assertEquals(123, gkoPool.getMaxTotalPerKey());
2521 }
2522 {
2523 gkoPool.setMaxIdlePerKey(12);
2524 assertEquals(12, gkoPool.getMaxIdlePerKey());
2525 }
2526 {
2527 gkoPool.setMaxWaitMillis(1234L);
2528 assertEquals(1234L, gkoPool.getMaxWaitMillis());
2529 }
2530 {
2531 gkoPool.setMinEvictableIdleTimeMillis(12345L);
2532 assertEquals(12345L, gkoPool.getMinEvictableIdleTimeMillis());
2533 }
2534 {
2535 gkoPool.setMinEvictableIdleTime(Duration.ofMillis(12345L));
2536 assertEquals(12345L, gkoPool.getMinEvictableIdleTime().toMillis());
2537 }
2538 {
2539 gkoPool.setMinEvictableIdleTime(Duration.ofMillis(12345L));
2540 assertEquals(12345L, gkoPool.getMinEvictableIdleDuration().toMillis());
2541 }
2542 {
2543 gkoPool.setNumTestsPerEvictionRun(11);
2544 assertEquals(11, gkoPool.getNumTestsPerEvictionRun());
2545 }
2546 {
2547 gkoPool.setTestOnBorrow(true);
2548 assertTrue(gkoPool.getTestOnBorrow());
2549 gkoPool.setTestOnBorrow(false);
2550 assertFalse(gkoPool.getTestOnBorrow());
2551 }
2552 {
2553 gkoPool.setTestOnReturn(true);
2554 assertTrue(gkoPool.getTestOnReturn());
2555 gkoPool.setTestOnReturn(false);
2556 assertFalse(gkoPool.getTestOnReturn());
2557 }
2558 {
2559 gkoPool.setTestWhileIdle(true);
2560 assertTrue(gkoPool.getTestWhileIdle());
2561 gkoPool.setTestWhileIdle(false);
2562 assertFalse(gkoPool.getTestWhileIdle());
2563 }
2564 {
2565 gkoPool.setTimeBetweenEvictionRunsMillis(11235L);
2566 assertEquals(11235L, gkoPool.getDurationBetweenEvictionRuns().toMillis());
2567 assertEquals(11235L, gkoPool.getTimeBetweenEvictionRuns().toMillis());
2568 assertEquals(11235L, gkoPool.getTimeBetweenEvictionRunsMillis());
2569 }
2570 {
2571 gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(11235L));
2572 assertEquals(11235L, gkoPool.getDurationBetweenEvictionRuns().toMillis());
2573 assertEquals(11235L, gkoPool.getTimeBetweenEvictionRuns().toMillis());
2574 assertEquals(11235L, gkoPool.getTimeBetweenEvictionRunsMillis());
2575 }
2576 {
2577 gkoPool.setBlockWhenExhausted(true);
2578 assertTrue(gkoPool.getBlockWhenExhausted());
2579 gkoPool.setBlockWhenExhausted(false);
2580 assertFalse(gkoPool.getBlockWhenExhausted());
2581 }
2582 }
2583
2584 @Test
2585 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2586 void testThreaded1() {
2587 gkoPool.setMaxTotalPerKey(15);
2588 gkoPool.setMaxIdlePerKey(15);
2589 gkoPool.setMaxWaitMillis(1000L);
2590 runTestThreads(20, 100, 50, gkoPool);
2591 }
2592
2593
2594 @Test
2595 void testValidateOnCreate() throws Exception {
2596 gkoPool.setTestOnCreate(true);
2597 simpleFactory.setValidationEnabled(true);
2598 gkoPool.addObject("one");
2599 assertEquals(1, simpleFactory.validateCounter);
2600 }
2601
2602 @Test
2603 void testValidateOnCreateFailure() throws Exception {
2604 gkoPool.setTestOnCreate(true);
2605 gkoPool.setTestOnBorrow(false);
2606 gkoPool.setMaxTotal(2);
2607 simpleFactory.setValidationEnabled(true);
2608 simpleFactory.setValid(false);
2609
2610 gkoPool.addObject("one");
2611 gkoPool.addObject("one");
2612 assertEquals(0, gkoPool.getNumIdle());
2613 assertEquals(0, gkoPool.getNumActive());
2614 simpleFactory.setValid(true);
2615 final String obj = gkoPool.borrowObject("one");
2616 assertNotNull(obj);
2617 gkoPool.addObject("one");
2618
2619 assertEquals(1, gkoPool.getNumIdle());
2620 assertEquals(1, gkoPool.getNumActive());
2621 }
2622
2623
2624
2625
2626
2627
2628
2629
2630 @Test
2631 void testValidationFailureOnReturnFreesCapacity() throws Exception {
2632 final SimpleFactory<String> factory = new SimpleFactory<>();
2633 factory.setValid(false);
2634 factory.setValidationEnabled(true);
2635 try (GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
2636 pool.setMaxTotalPerKey(2);
2637 pool.setMaxWaitMillis(1500);
2638 pool.setTestOnReturn(true);
2639 pool.setTestOnBorrow(false);
2640
2641 final WaitingTestThread thread1 = new WaitingTestThread(pool, "one", 5000);
2642 thread1.start();
2643
2644 final WaitingTestThread thread2 = new WaitingTestThread(pool, "one", 500);
2645 thread2.start();
2646 Thread.sleep(50);
2647
2648 final String obj = pool.borrowObject("one");
2649 pool.returnObject("one", obj);
2650 }
2651 }
2652
2653
2654
2655 @Test
2656 void testValidationOnCreateOnly() throws Exception {
2657 simpleFactory.enableValidation = true;
2658 gkoPool.setMaxTotal(1);
2659 gkoPool.setTestOnCreate(true);
2660 gkoPool.setTestOnBorrow(false);
2661 gkoPool.setTestOnReturn(false);
2662 gkoPool.setTestWhileIdle(false);
2663 final String o1 = gkoPool.borrowObject("KEY");
2664 assertEquals("KEY0", o1);
2665 final Timer t = new Timer();
2666 t.schedule(new TimerTask() {
2667 @Override
2668 public void run() {
2669 gkoPool.returnObject("KEY", o1);
2670 }
2671 }, 3000);
2672 final String o2 = gkoPool.borrowObject("KEY");
2673 assertEquals("KEY0", o2);
2674 assertEquals(1, simpleFactory.validateCounter);
2675 }
2676
2677 @Test
2678 void testWaiterCounts() throws Exception {
2679 final String[] keys = { "one", "two", "three" };
2680 final String[] objects = new String[keys.length];
2681 gkoPool.setMaxTotalPerKey(1);
2682 gkoPool.setBlockWhenExhausted(true);
2683
2684 assertTrue(gkoPool.getNumWaitersByKey().isEmpty());
2685 assertEquals(0, gkoPool.getNumWaiters());
2686
2687 for (int i = 0; i < keys.length; i++) {
2688 objects[i] = gkoPool.borrowObject(keys[i]);
2689 }
2690
2691
2692 final WaitingTestThread[] waiters = new WaitingTestThread[6];
2693 for (int i = 0; i < 3; i++) {
2694 for (int j = 0; j < i + 1; j++) {
2695 waiters[i + j] = new WaitingTestThread(gkoPool, keys[i], 100);
2696 waiters[i + j].start();
2697 }
2698 }
2699 Thread.sleep(10);
2700
2701 assertEquals(6, gkoPool.getNumWaiters());
2702 for (int i = 0; i < 3; i++) {
2703 assertEquals(i + 1, gkoPool.getNumWaitersByKey().get(keys[i]).intValue());
2704 }
2705
2706 for (int i = 0; i < 3; i++) {
2707 gkoPool.returnObject(keys[i], objects[i]);
2708 }
2709
2710 Thread.sleep(10);
2711
2712 assertEquals(3, gkoPool.getNumWaiters());
2713 for (int i = 0; i < 3; i++) {
2714 assertEquals(i, gkoPool.getNumWaitersByKey().get(keys[i]).intValue());
2715 }
2716
2717 for (int i = 0; i < 5; i++) {
2718 waiters[i].join();
2719 }
2720
2721 assertEquals(0, gkoPool.getNumWaiters());
2722 for (int i = 0; i < 3; i++) {
2723 assertEquals(0, gkoPool.getNumWaitersByKey().get(keys[i]).intValue());
2724 }
2725 assertEquals(3, gkoPool.getNumIdle());
2726 for (final String key : keys) {
2727 assertEquals(1, gkoPool.getNumIdle(key));
2728 }
2729
2730 gkoPool.clear();
2731 assertTrue(gkoPool.getNumWaitersByKey().isEmpty());
2732 assertEquals(0, gkoPool.getNumWaiters());
2733 gkoPool.close();
2734 }
2735
2736
2737
2738
2739
2740
2741 @Test
2742 @Timeout(value = 60_000, unit = TimeUnit.MILLISECONDS)
2743 void testWhenExhaustedBlockClosePool() throws Exception {
2744 gkoPool.setMaxTotalPerKey(1);
2745 gkoPool.setBlockWhenExhausted(true);
2746 gkoPool.setMaxWaitMillis(-1);
2747 final String obj1 = gkoPool.borrowObject("a");
2748
2749 assertNotNull(obj1);
2750
2751 final WaitingTestThread wtt = new WaitingTestThread(gkoPool, "a", 200);
2752 wtt.start();
2753
2754 Thread.sleep(200);
2755
2756 gkoPool.close();
2757
2758 Thread.sleep(200);
2759
2760 assertTrue(wtt.thrown instanceof InterruptedException);
2761 }
2762 }
2763