View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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.assertInstanceOf;
22  import static org.junit.jupiter.api.Assertions.assertNotEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNull;
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.io.Serializable;
30  import java.lang.management.ManagementFactory;
31  import java.lang.ref.WeakReference;
32  import java.nio.charset.UnsupportedCharsetException;
33  import java.time.Duration;
34  import java.time.Instant;
35  import java.util.ArrayList;
36  import java.util.Collections;
37  import java.util.HashSet;
38  import java.util.List;
39  import java.util.NoSuchElementException;
40  import java.util.Objects;
41  import java.util.Random;
42  import java.util.Set;
43  import java.util.Timer;
44  import java.util.TimerTask;
45  import java.util.concurrent.CountDownLatch;
46  import java.util.concurrent.ExecutorService;
47  import java.util.concurrent.Executors;
48  import java.util.concurrent.FutureTask;
49  import java.util.concurrent.Semaphore;
50  import java.util.concurrent.TimeUnit;
51  import java.util.concurrent.atomic.AtomicBoolean;
52  import java.util.concurrent.atomic.AtomicInteger;
53  
54  import javax.management.MBeanServer;
55  import javax.management.ObjectName;
56  
57  import org.apache.commons.lang3.ThreadUtils;
58  import org.apache.commons.lang3.function.Suppliers;
59  import org.apache.commons.lang3.time.DurationUtils;
60  import org.apache.commons.pool2.BasePooledObjectFactory;
61  import org.apache.commons.pool2.ObjectPool;
62  import org.apache.commons.pool2.PoolUtils;
63  import org.apache.commons.pool2.PooledObject;
64  import org.apache.commons.pool2.PooledObjectFactory;
65  import org.apache.commons.pool2.SwallowedExceptionListener;
66  import org.apache.commons.pool2.TestBaseObjectPool;
67  import org.apache.commons.pool2.VisitTracker;
68  import org.apache.commons.pool2.VisitTrackerFactory;
69  import org.apache.commons.pool2.Waiter;
70  import org.apache.commons.pool2.WaiterFactory;
71  import org.junit.jupiter.api.AfterEach;
72  import org.junit.jupiter.api.BeforeEach;
73  import org.junit.jupiter.api.Disabled;
74  import org.junit.jupiter.api.Test;
75  import org.junit.jupiter.api.Timeout;
76  
77  /**
78   */
79  class TestGenericObjectPool extends TestBaseObjectPool {
80      private final class ConcurrentBorrowAndEvictThread extends Thread {
81          private final boolean borrow;
82          String obj;
83  
84          private ConcurrentBorrowAndEvictThread(final boolean borrow) {
85              this.borrow = borrow;
86          }
87  
88          @Override
89          public void run() {
90              try {
91                  if (borrow) {
92                      obj = genericObjectPool.borrowObject();
93                  } else {
94                      genericObjectPool.evict();
95                  }
96              } catch (final Exception e) {
97                  // Ignore.
98              }
99          }
100     }
101 
102     private static final class CreateErrorFactory extends BasePooledObjectFactory<String> {
103         private final Semaphore semaphore = new Semaphore(0);
104 
105         @Override
106         public String create() throws InterruptedException {
107             semaphore.acquire();
108             throw new UnknownError("wiggle");
109         }
110 
111         public boolean hasQueuedThreads() {
112             return semaphore.hasQueuedThreads();
113         }
114 
115         public void release() {
116             semaphore.release();
117         }
118 
119         @Override
120         public PooledObject<String> wrap(final String obj) {
121             return new DefaultPooledObject<>(obj);
122         }
123     }
124 
125     private static final class CreateFailFactory extends BasePooledObjectFactory<String> {
126         private final Semaphore semaphore = new Semaphore(0);
127 
128         @Override
129         public String create() throws InterruptedException {
130             semaphore.acquire();
131             throw new UnsupportedCharsetException("wibble");
132         }
133 
134         public boolean hasQueuedThreads() {
135             return semaphore.hasQueuedThreads();
136         }
137 
138         public void release() {
139             semaphore.release();
140         }
141 
142         @Override
143         public PooledObject<String> wrap(final String obj) {
144             return new DefaultPooledObject<>(obj);
145         }
146     }
147 
148     private static final class DummyFactory extends BasePooledObjectFactory<Object> {
149         @Override
150         public Object create() {
151             return null;
152         }
153 
154         @Override
155         public PooledObject<Object> wrap(final Object value) {
156             return new DefaultPooledObject<>(value);
157         }
158     }
159 
160     private static final class EvictionThread<T> extends Thread {
161         private final GenericObjectPool<T> pool;
162 
163         private EvictionThread(final GenericObjectPool<T> pool) {
164             this.pool = pool;
165         }
166 
167         @Override
168         public void run() {
169             try {
170                 pool.evict();
171             } catch (final Exception e) {
172                 // Ignore
173             }
174         }
175     }
176 
177     /**
178      * Factory that creates HashSets. Note that this means 0) All instances are initially equal (not discernible by equals) 1) Instances are mutable and
179      * mutation can cause change in identity / hash code.
180      */
181     private static final class HashSetFactory extends BasePooledObjectFactory<HashSet<String>> {
182         @Override
183         public HashSet<String> create() {
184             return new HashSet<>();
185         }
186 
187         @Override
188         public PooledObject<HashSet<String>> wrap(final HashSet<String> value) {
189             return new DefaultPooledObject<>(value);
190         }
191     }
192 
193     /**
194      * Attempts to invalidate an object, swallowing IllegalStateException.
195      */
196     private static class InvalidateThread implements Runnable {
197         private final String obj;
198         private final ObjectPool<String> pool;
199         private boolean done;
200 
201         private InvalidateThread(final ObjectPool<String> pool, final String obj) {
202             this.obj = obj;
203             this.pool = pool;
204         }
205 
206         private boolean complete() {
207             return done;
208         }
209 
210         @Override
211         public void run() {
212             try {
213                 pool.invalidateObject(obj);
214             } catch (final IllegalStateException ex) {
215                 // Ignore
216             } catch (final Exception ex) {
217                 fail("Unexpected exception " + ex.toString());
218             } finally {
219                 done = true;
220             }
221         }
222     }
223 
224     private static final class InvalidFactory extends BasePooledObjectFactory<Object> {
225         @Override
226         public Object create() {
227             return new Object();
228         }
229 
230         @Override
231         public boolean validateObject(final PooledObject<Object> obj) {
232             Waiter.sleepQuietly(1000);
233             return false;
234         }
235 
236         @Override
237         public PooledObject<Object> wrap(final Object value) {
238             return new DefaultPooledObject<>(value);
239         }
240     }
241 
242     static class SimpleFactory implements PooledObjectFactory<String> {
243         int makeCounter;
244         int activationCounter;
245         int validateCounter;
246         int activeCount;
247         boolean evenValid = true;
248         boolean oddValid = true;
249         boolean exceptionOnPassivate;
250         boolean exceptionOnActivate;
251         boolean exceptionOnDestroy;
252         boolean exceptionOnValidate;
253         boolean enableValidation = true;
254         long destroyLatency;
255         long makeLatency;
256         long validateLatency;
257         int maxTotal = Integer.MAX_VALUE;
258 
259         SimpleFactory() {
260             this(true);
261         }
262 
263         SimpleFactory(final boolean valid) {
264             this(valid, valid);
265         }
266 
267         SimpleFactory(final boolean evalid, final boolean ovalid) {
268             evenValid = evalid;
269             oddValid = ovalid;
270         }
271 
272         @Override
273         public void activateObject(final PooledObject<String> obj) throws Exception {
274             final boolean hurl;
275             final boolean evenTest;
276             final boolean oddTest;
277             final int counter;
278             synchronized (this) {
279                 hurl = exceptionOnActivate;
280                 evenTest = evenValid;
281                 oddTest = oddValid;
282                 counter = activationCounter++;
283             }
284             if (hurl && !(counter % 2 == 0 ? evenTest : oddTest)) {
285                 throw new Exception();
286             }
287         }
288 
289         @Override
290         public void destroyObject(final PooledObject<String> obj) throws Exception {
291             final long waitLatency;
292             final boolean hurl;
293             synchronized (this) {
294                 waitLatency = destroyLatency;
295                 hurl = exceptionOnDestroy;
296             }
297             if (waitLatency > 0) {
298                 doWait(waitLatency);
299             }
300             synchronized (this) {
301                 activeCount--;
302             }
303             if (hurl) {
304                 throw new Exception();
305             }
306         }
307 
308         private void doWait(final long latency) {
309             Waiter.sleepQuietly(latency);
310         }
311 
312         synchronized int getMakeCounter() {
313             return makeCounter;
314         }
315 
316         synchronized boolean isThrowExceptionOnActivate() {
317             return exceptionOnActivate;
318         }
319 
320         synchronized boolean isValidationEnabled() {
321             return enableValidation;
322         }
323 
324         @Override
325         public PooledObject<String> makeObject() {
326             final long waitLatency;
327             synchronized (this) {
328                 activeCount++;
329                 if (activeCount > maxTotal) {
330                     throw new IllegalStateException("Too many active instances: " + activeCount);
331                 }
332                 waitLatency = makeLatency;
333             }
334             if (waitLatency > 0) {
335                 doWait(waitLatency);
336             }
337             final int counter;
338             synchronized (this) {
339                 counter = makeCounter++;
340             }
341             return new DefaultPooledObject<>(String.valueOf(counter));
342         }
343 
344         @Override
345         public void passivateObject(final PooledObject<String> obj) throws Exception {
346             final boolean hurl;
347             synchronized (this) {
348                 hurl = exceptionOnPassivate;
349             }
350             if (hurl) {
351                 throw new Exception();
352             }
353         }
354 
355         synchronized void setDestroyLatency(final long destroyLatency) {
356             this.destroyLatency = destroyLatency;
357         }
358 
359         synchronized void setEvenValid(final boolean valid) {
360             evenValid = valid;
361         }
362 
363         synchronized void setMakeLatency(final long makeLatency) {
364             this.makeLatency = makeLatency;
365         }
366 
367         synchronized void setMaxTotal(final int maxTotal) {
368             this.maxTotal = maxTotal;
369         }
370 
371         synchronized void setOddValid(final boolean valid) {
372             oddValid = valid;
373         }
374 
375         synchronized void setThrowExceptionOnActivate(final boolean b) {
376             exceptionOnActivate = b;
377         }
378 
379         synchronized void setThrowExceptionOnDestroy(final boolean b) {
380             exceptionOnDestroy = b;
381         }
382 
383         synchronized void setThrowExceptionOnPassivate(final boolean bool) {
384             exceptionOnPassivate = bool;
385         }
386 
387         synchronized void setThrowExceptionOnValidate(final boolean bool) {
388             exceptionOnValidate = bool;
389         }
390 
391         synchronized void setValid(final boolean valid) {
392             setEvenValid(valid);
393             setOddValid(valid);
394         }
395 
396         synchronized void setValidateLatency(final long validateLatency) {
397             this.validateLatency = validateLatency;
398         }
399 
400         synchronized void setValidationEnabled(final boolean b) {
401             enableValidation = b;
402         }
403 
404         @Override
405         public boolean validateObject(final PooledObject<String> obj) {
406             final boolean validate;
407             final boolean throwException;
408             final boolean evenTest;
409             final boolean oddTest;
410             final long waitLatency;
411             final int counter;
412             synchronized (this) {
413                 validate = enableValidation;
414                 throwException = exceptionOnValidate;
415                 evenTest = evenValid;
416                 oddTest = oddValid;
417                 counter = validateCounter++;
418                 waitLatency = validateLatency;
419             }
420             if (waitLatency > 0) {
421                 doWait(waitLatency);
422             }
423             if (throwException) {
424                 throw new RuntimeException("validation failed");
425             }
426             if (validate) {
427                 return counter % 2 == 0 ? evenTest : oddTest;
428             }
429             return true;
430         }
431     }
432 
433     /**
434      * Must not be public for the test to instantiate this class through reflection.
435      *
436      * @param <T> the type of objects in the pool.
437      */
438     public static class TestEvictionPolicy<T> implements EvictionPolicy<T> {
439         private final AtomicInteger callCount = new AtomicInteger();
440 
441         @Override
442         public boolean evict(final EvictionConfig config, final PooledObject<T> underTest, final int idleCount) {
443             return callCount.incrementAndGet() > 1500;
444         }
445     }
446 
447     private static class TestThread<T> implements Runnable {
448         /** Source of random delay times */
449         private final Random random;
450         /** Pool to borrow from */
451         private final ObjectPool<T> pool;
452         /** Number of borrow attempts */
453         private final int iter;
454         /** Delay before each borrow attempt */
455         private final int startDelay;
456         /** Time to hold each borrowed object before returning it */
457         private final int holdTime;
458         /** Whether or not start and hold time are randomly generated */
459         private final boolean randomDelay;
460         /** Object expected to be borrowed (fail otherwise) */
461         private final Object expectedObject;
462         private volatile boolean complete;
463         private volatile boolean failed;
464         private volatile Throwable error;
465 
466         private TestThread(final ObjectPool<T> pool) {
467             this(pool, 100, 50, true, null);
468         }
469 
470         private TestThread(final ObjectPool<T> pool, final int iter) {
471             this(pool, iter, 50, true, null);
472         }
473 
474         private TestThread(final ObjectPool<T> pool, final int iter, final int delay) {
475             this(pool, iter, delay, true, null);
476         }
477 
478         private TestThread(final ObjectPool<T> pool, final int iter, final int delay, final boolean randomDelay) {
479             this(pool, iter, delay, randomDelay, null);
480         }
481 
482         private TestThread(final ObjectPool<T> pool, final int iter, final int delay, final boolean randomDelay, final Object obj) {
483             this(pool, iter, delay, delay, randomDelay, obj);
484         }
485 
486         private TestThread(final ObjectPool<T> pool, final int iter, final int startDelay, final int holdTime, final boolean randomDelay, final Object obj) {
487             this.pool = pool;
488             this.iter = iter;
489             this.startDelay = startDelay;
490             this.holdTime = holdTime;
491             this.randomDelay = randomDelay;
492             this.random = this.randomDelay ? new Random() : null;
493             this.expectedObject = obj;
494         }
495 
496         private boolean complete() {
497             return complete;
498         }
499 
500         private boolean failed() {
501             return failed;
502         }
503 
504         @Override
505         public void run() {
506             for (int i = 0; i < iter; i++) {
507                 final long actualStartDelay = randomDelay ? (long) random.nextInt(startDelay) : startDelay;
508                 final long actualHoldTime = randomDelay ? (long) random.nextInt(holdTime) : holdTime;
509                 Waiter.sleepQuietly(actualStartDelay);
510                 T obj = null;
511                 try {
512                     obj = pool.borrowObject();
513                 } catch (final Exception e) {
514                     error = e;
515                     failed = true;
516                     complete = true;
517                     break;
518                 }
519                 if (expectedObject != null && !expectedObject.equals(obj)) {
520                     error = new Throwable("Expected: " + expectedObject + " found: " + obj);
521                     failed = true;
522                     complete = true;
523                     break;
524                 }
525                 Waiter.sleepQuietly(actualHoldTime);
526                 try {
527                     pool.returnObject(obj);
528                 } catch (final Exception e) {
529                     error = e;
530                     failed = true;
531                     complete = true;
532                     break;
533                 }
534             }
535             complete = true;
536         }
537     }
538 
539     /*
540      * Very simple test thread that just tries to borrow an object from the provided pool returns it after a wait
541      */
542     private static class WaitingTestThread extends Thread {
543         private final GenericObjectPool<String> pool;
544         private final long pause;
545         private Throwable thrown;
546         private long preBorrowMillis; // just before borrow
547         private long postBorrowMillis; // borrow returned
548         private long postReturnMillis; // after object was returned
549         private long endedMillis;
550         private String objectId;
551 
552         private WaitingTestThread(final GenericObjectPool<String> pool, final long pause) {
553             this.pool = pool;
554             this.pause = pause;
555             this.thrown = null;
556         }
557 
558         @Override
559         public void run() {
560             try {
561                 preBorrowMillis = System.currentTimeMillis();
562                 final String obj = pool.borrowObject();
563                 objectId = obj;
564                 postBorrowMillis = System.currentTimeMillis();
565                 Thread.sleep(pause);
566                 pool.returnObject(obj);
567                 postReturnMillis = System.currentTimeMillis();
568             } catch (final Throwable e) {
569                 thrown = e;
570             } finally {
571                 endedMillis = System.currentTimeMillis();
572             }
573         }
574     }
575 
576     // To pass this to a Maven test, use:
577     // mvn test -DargLine="-DTestGenericObjectPool.display.thread.details=true"
578     // @see https://issues.apache.org/jira/browse/SUREFIRE-121
579     private static final boolean DISPLAY_THREAD_DETAILS = Boolean.getBoolean("TestGenericObjectPool.display.thread.details");
580     private GenericObjectPool<String> genericObjectPool;
581     private SimpleFactory simpleFactory;
582 
583     @SuppressWarnings("deprecation")
584     private void assertConfiguration(final GenericObjectPoolConfig<?> expected, final GenericObjectPool<?> actual) {
585         assertEquals(Boolean.valueOf(expected.getTestOnCreate()), Boolean.valueOf(actual.getTestOnCreate()), "testOnCreate");
586         assertEquals(Boolean.valueOf(expected.getTestOnBorrow()), Boolean.valueOf(actual.getTestOnBorrow()), "testOnBorrow");
587         assertEquals(Boolean.valueOf(expected.getTestOnReturn()), Boolean.valueOf(actual.getTestOnReturn()), "testOnReturn");
588         assertEquals(Boolean.valueOf(expected.getTestWhileIdle()), Boolean.valueOf(actual.getTestWhileIdle()), "testWhileIdle");
589         assertEquals(Boolean.valueOf(expected.getBlockWhenExhausted()), Boolean.valueOf(actual.getBlockWhenExhausted()), "whenExhaustedAction");
590         assertEquals(expected.getMaxTotal(), actual.getMaxTotal(), "maxTotal");
591         assertEquals(expected.getMaxIdle(), actual.getMaxIdle(), "maxIdle");
592         assertEquals(expected.getMaxWaitMillis(), actual.getMaxWaitMillis(), "maxWaitDuration");
593         assertEquals(expected.getMaxWaitDuration(), actual.getMaxWaitDuration(), "maxWaitDuration");
594         assertEquals(expected.getMinEvictableIdleTimeMillis(), actual.getMinEvictableIdleTimeMillis(), "minEvictableIdleTimeMillis");
595         assertEquals(expected.getMinEvictableIdleTime(), actual.getMinEvictableIdleTime(), "minEvictableIdleTime");
596         assertEquals(expected.getMinEvictableIdleDuration(), actual.getMinEvictableIdleDuration(), "minEvictableIdleDuration");
597         assertEquals(expected.getNumTestsPerEvictionRun(), actual.getNumTestsPerEvictionRun(), "numTestsPerEvictionRun");
598         assertEquals(expected.getEvictorShutdownTimeoutDuration(), actual.getEvictorShutdownTimeoutDuration(), "evictorShutdownTimeoutDuration");
599         assertEquals(expected.getEvictorShutdownTimeoutMillis(), actual.getEvictorShutdownTimeoutMillis(), "evictorShutdownTimeoutMillis");
600         assertEquals(expected.getEvictorShutdownTimeout(), actual.getEvictorShutdownTimeout(), "evictorShutdownTimeout");
601         assertEquals(expected.getTimeBetweenEvictionRunsMillis(), actual.getTimeBetweenEvictionRunsMillis(), "timeBetweenEvictionRunsMillis");
602         assertEquals(expected.getDurationBetweenEvictionRuns(), actual.getTimeBetweenEvictionRuns(), "timeBetweenEvictionRuns");
603         assertEquals(expected.getTimeBetweenEvictionRuns(), actual.getTimeBetweenEvictionRuns(), "timeBetweenEvictionRuns");
604     }
605 
606     private void checkEvict(final boolean lifo) throws Exception {
607         // yea this is hairy but it tests all the code paths in GOP.evict()
608         genericObjectPool.setSoftMinEvictableIdleDuration(Duration.ofMillis(10));
609         genericObjectPool.setSoftMinEvictableIdle(Duration.ofMillis(10));
610         genericObjectPool.setSoftMinEvictableIdleTime(Duration.ofMillis(10));
611         genericObjectPool.setMinIdle(2);
612         genericObjectPool.setTestWhileIdle(true);
613         genericObjectPool.setLifo(lifo);
614         genericObjectPool.addObjects(5);
615         genericObjectPool.evict();
616         simpleFactory.setEvenValid(false);
617         simpleFactory.setOddValid(false);
618         simpleFactory.setThrowExceptionOnActivate(true);
619         genericObjectPool.evict();
620         genericObjectPool.addObjects(5);
621         simpleFactory.setThrowExceptionOnActivate(false);
622         simpleFactory.setThrowExceptionOnPassivate(true);
623         genericObjectPool.evict();
624         simpleFactory.setThrowExceptionOnPassivate(false);
625         simpleFactory.setEvenValid(true);
626         simpleFactory.setOddValid(true);
627         Thread.sleep(125);
628         genericObjectPool.evict();
629         assertEquals(2, genericObjectPool.getNumIdle());
630     }
631 
632     private void checkEvictionOrder(final boolean lifo) throws Exception {
633         checkEvictionOrderPart1(lifo);
634         tearDown();
635         setUp();
636         checkEvictionOrderPart2(lifo);
637     }
638 
639     private void checkEvictionOrderPart1(final boolean lifo) throws Exception {
640         genericObjectPool.setNumTestsPerEvictionRun(2);
641         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
642         genericObjectPool.setLifo(lifo);
643         for (int i = 0; i < 5; i++) {
644             genericObjectPool.addObject();
645             Thread.sleep(100);
646         }
647         // Order, oldest to youngest, is "0", "1", ...,"4"
648         genericObjectPool.evict(); // Should evict "0" and "1"
649         final Object obj = genericObjectPool.borrowObject();
650         assertNotEquals("0", obj, "oldest not evicted");
651         assertNotEquals("1", obj, "second oldest not evicted");
652         // 2 should be next out for FIFO, 4 for LIFO
653         assertEquals(lifo ? "4" : "2", obj, "Wrong instance returned");
654     }
655 
656     private void checkEvictionOrderPart2(final boolean lifo) throws Exception {
657         // Two eviction runs in sequence
658         genericObjectPool.setNumTestsPerEvictionRun(2);
659         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
660         genericObjectPool.setLifo(lifo);
661         for (int i = 0; i < 5; i++) {
662             genericObjectPool.addObject();
663             Thread.sleep(100);
664         }
665         genericObjectPool.evict(); // Should evict "0" and "1"
666         genericObjectPool.evict(); // Should evict "2" and "3"
667         final Object obj = genericObjectPool.borrowObject();
668         assertEquals("4", obj, "Wrong instance remaining in pool");
669     }
670 
671     private void checkEvictorVisiting(final boolean lifo) throws Exception {
672         VisitTracker<Object> obj;
673         VisitTrackerFactory<Object> trackerFactory = new VisitTrackerFactory<>();
674         try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) {
675             trackerPool.setNumTestsPerEvictionRun(2);
676             trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
677             trackerPool.setTestWhileIdle(true);
678             trackerPool.setLifo(lifo);
679             trackerPool.setTestOnReturn(false);
680             trackerPool.setTestOnBorrow(false);
681             for (int i = 0; i < 8; i++) {
682                 trackerPool.addObject();
683             }
684             trackerPool.evict(); // Visit oldest 2 - 0 and 1
685             obj = trackerPool.borrowObject();
686             trackerPool.returnObject(obj);
687             obj = trackerPool.borrowObject();
688             trackerPool.returnObject(obj);
689             // borrow, return, borrow, return
690             // FIFO will move 0 and 1 to end
691             // LIFO, 7 out, then in, then out, then in
692             trackerPool.evict(); // Should visit 2 and 3 in either case
693             for (int i = 0; i < 8; i++) {
694                 final VisitTracker<Object> tracker = trackerPool.borrowObject();
695                 if (tracker.getId() >= 4) {
696                     assertEquals(0, tracker.getValidateCount(), "Unexpected instance visited " + tracker.getId());
697                 } else {
698                     assertEquals(1, tracker.getValidateCount(), "Instance " + tracker.getId() + " visited wrong number of times.");
699                 }
700             }
701         }
702         trackerFactory = new VisitTrackerFactory<>();
703         try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) {
704             trackerPool.setNumTestsPerEvictionRun(3);
705             trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
706             trackerPool.setTestWhileIdle(true);
707             trackerPool.setLifo(lifo);
708             trackerPool.setTestOnReturn(false);
709             trackerPool.setTestOnBorrow(false);
710             for (int i = 0; i < 8; i++) {
711                 trackerPool.addObject();
712             }
713             trackerPool.evict(); // 0, 1, 2
714             trackerPool.evict(); // 3, 4, 5
715             obj = trackerPool.borrowObject();
716             trackerPool.returnObject(obj);
717             obj = trackerPool.borrowObject();
718             trackerPool.returnObject(obj);
719             obj = trackerPool.borrowObject();
720             trackerPool.returnObject(obj);
721             // borrow, return, borrow, return
722             // FIFO 3,4,5,6,7,0,1,2
723             // LIFO 7,6,5,4,3,2,1,0
724             // In either case, pointer should be at 6
725             trackerPool.evict();
726             // Should hit 6,7,0 - 0 for second time
727             for (int i = 0; i < 8; i++) {
728                 final VisitTracker<Object> tracker = trackerPool.borrowObject();
729                 if (tracker.getId() != 0) {
730                     assertEquals(1, tracker.getValidateCount(), "Instance " + tracker.getId() + " visited wrong number of times.");
731                 } else {
732                     assertEquals(2, tracker.getValidateCount(), "Instance " + tracker.getId() + " visited wrong number of times.");
733                 }
734             }
735         }
736         // Randomly generate a pools with random numTests
737         // and make sure evictor cycles through elements appropriately
738         final int[] smallPrimes = { 2, 3, 5, 7 };
739         final Random random = new Random();
740         random.setSeed(System.currentTimeMillis());
741         for (int i = 0; i < 4; i++) {
742             for (int j = 0; j < 5; j++) {
743                 try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) {
744                     trackerPool.setNumTestsPerEvictionRun(smallPrimes[i]);
745                     trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
746                     trackerPool.setTestWhileIdle(true);
747                     trackerPool.setLifo(lifo);
748                     trackerPool.setTestOnReturn(false);
749                     trackerPool.setTestOnBorrow(false);
750                     trackerPool.setMaxIdle(-1);
751                     final int instanceCount = 10 + random.nextInt(20);
752                     trackerPool.setMaxTotal(instanceCount);
753                     trackerPool.setMaxIdle(instanceCount);
754                     for (int k = 0; k < instanceCount; k++) {
755                         trackerPool.addObject();
756                     }
757                     // Execute a random number of evictor runs
758                     final int runs = 10 + random.nextInt(50);
759                     for (int k = 0; k < runs; k++) {
760                         trackerPool.evict();
761                     }
762                     // Number of times evictor should have cycled through the pool
763                     final int cycleCount = runs * trackerPool.getNumTestsPerEvictionRun() / instanceCount;
764                     // Look at elements and make sure they are visited cycleCount
765                     // or cycleCount + 1 times
766                     VisitTracker<Object> tracker = null;
767                     int visitCount = 0;
768                     for (int k = 0; k < instanceCount; k++) {
769                         tracker = trackerPool.borrowObject();
770                         assertTrue(trackerPool.getNumActive() <= trackerPool.getMaxTotal());
771                         visitCount = tracker.getValidateCount();
772                         assertTrue(visitCount >= cycleCount && visitCount <= cycleCount + 1);
773                     }
774                 }
775             }
776         }
777     }
778 
779     private BasePooledObjectFactory<String> createDefaultPooledObjectFactory() {
780         return new BasePooledObjectFactory<String>() {
781             @Override
782             public String create() {
783                 // fake
784                 return null;
785             }
786 
787             @Override
788             public PooledObject<String> wrap(final String obj) {
789                 // fake
790                 return new DefaultPooledObject<>(obj);
791             }
792         };
793     }
794 
795     private BasePooledObjectFactory<String> createNullPooledObjectFactory() {
796         return new BasePooledObjectFactory<String>() {
797             @Override
798             public String create() {
799                 // fake
800                 return null;
801             }
802 
803             @Override
804             public PooledObject<String> wrap(final String obj) {
805                 // fake
806                 return null;
807             }
808         };
809     }
810 
811     private BasePooledObjectFactory<Object> createPooledObjectFactory() {
812         return new BasePooledObjectFactory<Object>() {
813             @Override
814             public Object create() {
815                 return new Object();
816             }
817 
818             @Override
819             public PooledObject<Object> wrap(final Object obj) {
820                 return new DefaultPooledObject<>(obj);
821             }
822         };
823     }
824 
825     private BasePooledObjectFactory<String> createSlowObjectFactory(final Duration sleepDuration) {
826         return new BasePooledObjectFactory<String>() {
827             @Override
828             public String create() throws InterruptedException {
829                 ThreadUtils.sleep(sleepDuration);
830                 return "created";
831             }
832 
833             @Override
834             public PooledObject<String> wrap(final String obj) {
835                 // fake
836                 return new DefaultPooledObject<>(obj);
837             }
838         };
839     }
840 
841     @Override
842     protected Object getNthObject(final int n) {
843         return String.valueOf(n);
844     }
845 
846     @Override
847     protected boolean isFifo() {
848         return false;
849     }
850 
851     @Override
852     protected boolean isLifo() {
853         return true;
854     }
855 
856     @Override
857     protected ObjectPool<String> makeEmptyPool(final int minCap) {
858         final GenericObjectPool<String> mtPool = new GenericObjectPool<>(new SimpleFactory());
859         mtPool.setMaxTotal(minCap);
860         mtPool.setMaxIdle(minCap);
861         return mtPool;
862     }
863 
864     @Override
865     protected <E extends Exception> ObjectPool<Object> makeEmptyPool(final PooledObjectFactory<Object> fac) {
866         return new GenericObjectPool<>(fac);
867     }
868 
869     /*
870      * Kicks off numThreads test threads, each of which will go through iterations borrow-return cycles with random delay times <= delay in between.
871      */
872     private <T> void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericObjectPool<T> testPool) {
873         final TestThread<T>[] threads = new TestThread[numThreads];
874         for (int i = 0; i < numThreads; i++) {
875             threads[i] = new TestThread<>(testPool, iterations, delay);
876             final Thread t = new Thread(threads[i]);
877             t.start();
878         }
879         for (int i = 0; i < numThreads; i++) {
880             while (!threads[i].complete()) {
881                 Waiter.sleepQuietly(500L);
882             }
883             if (threads[i].failed()) {
884                 fail("Thread " + i + " failed: " + threads[i].error.toString());
885             }
886         }
887     }
888 
889     @BeforeEach
890     public void setUp() {
891         simpleFactory = new SimpleFactory();
892         genericObjectPool = new GenericObjectPool<>(simpleFactory);
893     }
894 
895     @AfterEach
896     public void tearDown() throws Exception {
897         final ObjectName jmxName = genericObjectPool.getJmxName();
898         final String poolName = Objects.toString(jmxName, null);
899         genericObjectPool.clear();
900         genericObjectPool.close();
901         genericObjectPool = null;
902         simpleFactory = null;
903         final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
904         final Set<ObjectName> result = mbs.queryNames(new ObjectName("org.apache.commoms.pool2:type=GenericObjectPool,*"), null);
905         // There should be no registered pools at this point
906         final int registeredPoolCount = result.size();
907         final StringBuilder msg = new StringBuilder("Current pool is: ");
908         msg.append(poolName);
909         msg.append("  Still open pools are: ");
910         for (final ObjectName name : result) {
911             // Clean these up ready for the next test
912             msg.append(name.toString());
913             msg.append(" created via\n");
914             msg.append(mbs.getAttribute(name, "CreationStackTrace"));
915             msg.append('\n');
916             mbs.unregisterMBean(name);
917         }
918         assertEquals(0, registeredPoolCount, msg.toString());
919         // Make sure that EvictionTimer executor is shut down.
920         Thread.yield();
921         if (EvictionTimer.getExecutor() != null) {
922             Thread.sleep(1000);
923         }
924         assertNull(EvictionTimer.getExecutor(), "EvictionTimer.getExecutor()");
925     }
926 
927     /**
928      * Check that a pool that starts an evictor, but is never closed does not leave EvictionTimer executor running. Confirmation check is in
929      * {@link #tearDown()}.
930      *
931      * @throws TestException        Custom exception
932      * @throws InterruptedException if any thread has interrupted the current thread. The <em>interrupted status</em> of the current thread is cleared when this
933      *                              exception is thrown.
934      */
935     @SuppressWarnings("deprecation")
936     @Test
937     void testAbandonedPool() throws Exception {
938         final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
939         config.setJmxEnabled(false);
940 
941         // Need to create at least 2 pools to test EvictorTimer Reaper for ConcurrentModificationException
942         // Note: If the test fails, the ConcurrentModificationException isn't visible without modifications to the
943         //       Reaper class.
944         GenericObjectPool<String> abandoned1 = new GenericObjectPool<>(simpleFactory, config);
945         abandoned1.setDurationBetweenEvictionRuns(Duration.ofMillis(100)); // Starts evictor
946         GenericObjectPool<String> abandoned2 = new GenericObjectPool<>(simpleFactory, config);
947         abandoned2.setDurationBetweenEvictionRuns(Duration.ofMillis(100)); // Starts evictor
948 
949         // This is ugly, but forces GC to hit the pools
950         final WeakReference<GenericObjectPool<String>> ref1 = new WeakReference<>(abandoned1);
951         abandoned1 = null;
952         while (ref1.get() != null) {
953             System.gc();
954             Thread.sleep(100);
955         }
956         final WeakReference<GenericObjectPool<String>> ref2 = new WeakReference<>(abandoned2);
957         abandoned2 = null;
958         while (ref2.get() != null) {
959             System.gc();
960             Thread.sleep(100);
961         }
962     }
963 
964     @Test
965     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
966     void testAddObject() throws Exception {
967         assertEquals(0, genericObjectPool.getNumIdle(), "should be zero idle");
968         genericObjectPool.addObject();
969         assertEquals(1, genericObjectPool.getNumIdle(), "should be one idle");
970         assertEquals(0, genericObjectPool.getNumActive(), "should be zero active");
971         final String obj = genericObjectPool.borrowObject();
972         assertEquals(0, genericObjectPool.getNumIdle(), "should be zero idle");
973         assertEquals(1, genericObjectPool.getNumActive(), "should be one active");
974         genericObjectPool.returnObject(obj);
975         assertEquals(1, genericObjectPool.getNumIdle(), "should be one idle");
976         assertEquals(0, genericObjectPool.getNumActive(), "should be zero active");
977     }
978 
979     @Test
980     void testAddObjectCanAddToMaxIdle() throws Exception {
981         genericObjectPool.setMaxTotal(5);
982         genericObjectPool.borrowObject();
983         genericObjectPool.borrowObject();
984         genericObjectPool.setMaxIdle(3);
985         for (int i = 0; i < 3; i++) {
986             genericObjectPool.addObject();
987         }
988         assertEquals(3, genericObjectPool.getNumIdle());
989     }
990 
991     @Test
992     @Timeout(value = 400, unit = TimeUnit.MILLISECONDS)
993     void testAddObjectFastReturn() throws Exception {
994         final SimpleFactory simpleFactory = new SimpleFactory();
995         simpleFactory.makeLatency = 500;
996         try (GenericObjectPool<String> pool = new GenericObjectPool<>(simpleFactory)) {
997             pool.setMaxTotal(1);
998             pool.setBlockWhenExhausted(true);
999             pool.setMaxWait(Duration.ofMillis(1000));
1000             // Start a test thread. The thread will trigger a create, which will take 500 ms to complete
1001             final TestThread<String> thread = new TestThread<>(pool);
1002             final Thread t = new Thread(thread);
1003             t.start();
1004             Thread.sleep(50); // Wait for the thread to start
1005             pool.addObject(); // Should return immediately
1006         }
1007     }
1008 
1009     @Test
1010     void testAddObjectRespectsMaxIdle() throws Exception {
1011         genericObjectPool.setMaxIdle(1);
1012         genericObjectPool.addObject();
1013         genericObjectPool.addObject(); // should be no-op
1014         assertEquals(1, genericObjectPool.getNumIdle());
1015     }
1016 
1017     @Test
1018     void testAddObjectRespectsMaxTotal() throws Exception {
1019         genericObjectPool.setMaxTotal(1);
1020         genericObjectPool.addObject();
1021         genericObjectPool.addObject(); // should be no-op
1022         assertEquals(1, genericObjectPool.getNumIdle());
1023     }
1024 
1025     @Test
1026     void testAppendStats() {
1027         assertFalse(genericObjectPool.getMessageStatistics());
1028         assertEquals("foo", genericObjectPool.appendStats("foo"));
1029         try (GenericObjectPool<?> pool = new GenericObjectPool<>(new SimpleFactory())) {
1030             pool.setMessagesStatistics(true);
1031             assertNotEquals("foo", pool.appendStats("foo"));
1032             pool.setMessagesStatistics(false);
1033             assertEquals("foo", pool.appendStats("foo"));
1034         }
1035     }
1036 
1037     /*
1038      * Note: This test relies on timing for correct execution. There *should* be enough margin for this to work correctly on most (all?) systems but be aware of
1039      * this if you see a failure of this test.
1040      */
1041     @SuppressWarnings({ "rawtypes", "unchecked" })
1042     @Test
1043     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1044     void testBorrowObjectFairness() throws Exception {
1045         final int numThreads = 40;
1046         final int maxTotal = 40;
1047         final GenericObjectPoolConfig config = new GenericObjectPoolConfig();
1048         config.setMaxTotal(maxTotal);
1049         config.setMaxIdle(maxTotal);
1050         config.setFairness(true);
1051         config.setLifo(false);
1052         genericObjectPool = new GenericObjectPool(simpleFactory, config);
1053         // Exhaust the pool
1054         final String[] objects = new String[maxTotal];
1055         for (int i = 0; i < maxTotal; i++) {
1056             objects[i] = genericObjectPool.borrowObject();
1057         }
1058         // Start and park threads waiting to borrow objects
1059         final TestThread[] threads = new TestThread[numThreads];
1060         for (int i = 0; i < numThreads; i++) {
1061             threads[i] = new TestThread(genericObjectPool, 1, 0, 2000, false, String.valueOf(i % maxTotal));
1062             final Thread t = new Thread(threads[i]);
1063             t.start();
1064             // Short delay to ensure threads start in correct order
1065             try {
1066                 Thread.sleep(10);
1067             } catch (final InterruptedException e) {
1068                 fail(e.toString());
1069             }
1070         }
1071         // Return objects, other threads should get served in order
1072         for (int i = 0; i < maxTotal; i++) {
1073             genericObjectPool.returnObject(objects[i]);
1074         }
1075         // Wait for threads to finish
1076         for (int i = 0; i < numThreads; i++) {
1077             while (!threads[i].complete()) {
1078                 Waiter.sleepQuietly(500L);
1079             }
1080             if (threads[i].failed()) {
1081                 fail("Thread " + i + " failed: " + threads[i].error.toString());
1082             }
1083         }
1084     }
1085 
1086     @Test
1087     @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
1088     void testBorrowObjectOverrideMaxWaitLarge() throws Exception {
1089         try (GenericObjectPool<String> pool = new GenericObjectPool<>(createSlowObjectFactory(Duration.ofSeconds(60)))) {
1090             pool.setMaxTotal(1);
1091             pool.setMaxWait(Duration.ofMillis(1_000)); // large
1092             pool.setBlockWhenExhausted(false);
1093             // thread1 tries creating a slow object to make pool full.
1094             final WaitingTestThread thread1 = new WaitingTestThread(pool, 0);
1095             thread1.start();
1096             // Wait for thread1's reaching to create().
1097             Thread.sleep(100);
1098             // The test needs to make sure a wait happens in create().
1099             final Duration d = DurationUtils.of(() -> assertThrows(NoSuchElementException.class, () -> pool.borrowObject(Duration.ofMillis(1)),
1100                     "borrowObject must fail quickly due to timeout parameter"));
1101             final long millis = d.toMillis();
1102             final long nanos = d.toNanos();
1103             assertTrue(nanos >= 0, () -> "borrowObject(Duration) argument not respected: " + nanos);
1104             assertTrue(millis >= 0, () -> "borrowObject(Duration) argument not respected: " + millis); // not > 0 to account for spurious waits
1105             assertTrue(millis < 50, () -> "borrowObject(Duration) argument not respected: " + millis);
1106         }
1107     }
1108 
1109     @Test
1110     @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
1111     void testBorrowObjectOverrideMaxWaitSmall() throws Exception {
1112         try (GenericObjectPool<String> pool = new GenericObjectPool<>(createSlowObjectFactory(Duration.ofSeconds(60)))) {
1113             pool.setMaxTotal(1);
1114             pool.setMaxWait(Duration.ofMillis(1)); // small
1115             pool.setBlockWhenExhausted(false);
1116             // thread1 tries creating a slow object to make pool full.
1117             final WaitingTestThread thread1 = new WaitingTestThread(pool, 0);
1118             thread1.start();
1119             // Wait for thread1's reaching to create().
1120             Thread.sleep(100);
1121             // The test needs to make sure a wait happens in create().
1122             final Duration d = DurationUtils.of(() -> assertThrows(NoSuchElementException.class, () -> pool.borrowObject(Duration.ofMillis(500)),
1123                     "borrowObject must fail slowly due to timeout parameter"));
1124             final long millis = d.toMillis();
1125             final long nanos = d.toNanos();
1126             assertTrue(nanos >= 0, () -> "borrowObject(Duration) argument not respected: " + nanos);
1127             assertTrue(millis >= 0, () -> "borrowObject(Duration) argument not respected: " + millis); // not > 0 to account for spurious waits
1128             assertTrue(millis < 600, () -> "borrowObject(Duration) argument not respected: " + millis);
1129             assertTrue(millis > 490, () -> "borrowObject(Duration) argument not respected: " + millis);
1130         }
1131     }
1132 
1133     @Test
1134     void testBorrowTimings() throws Exception {
1135         // Borrow
1136         final String object = genericObjectPool.borrowObject();
1137         final PooledObject<String> po = genericObjectPool.getPooledObject(object);
1138         // In the initial state, all instants are the creation instant: last borrow, last use, last return.
1139         // In the initial state, the active duration is the time between "now" and the creation time.
1140         // In the initial state, the idle duration is the time between "now" and the last return, which is the creation time.
1141         // But... this PO might have already been used in other tests in this class.
1142         final Instant lastBorrowInstant1 = po.getLastBorrowInstant();
1143         final Instant lastReturnInstant1 = po.getLastReturnInstant();
1144         final Instant lastUsedInstant1 = po.getLastUsedInstant();
1145         assertTrue(po.getCreateInstant().compareTo(lastBorrowInstant1) <= 0);
1146         assertTrue(po.getCreateInstant().compareTo(lastReturnInstant1) <= 0);
1147         assertTrue(po.getCreateInstant().compareTo(lastUsedInstant1) <= 0);
1148         // Deprecated
1149         assertTrue(po.getCreateTime() <= lastBorrowInstant1.toEpochMilli());
1150         assertTrue(po.getCreateTime() <= lastReturnInstant1.toEpochMilli());
1151         assertTrue(po.getCreateTime() <= lastUsedInstant1.toEpochMilli());
1152         // Sleep MUST be "long enough" to detect that more than 0 milliseconds have elapsed.
1153         // Need an API in Java 8 to get the clock granularity.
1154         Thread.sleep(200);
1155         assertFalse(po.getActiveDuration().isNegative());
1156         assertFalse(po.getActiveDuration().isZero());
1157         // We use greaterThanOrEqualTo instead of equal because "now" many be different when each argument is evaluated.
1158         assertTrue(Duration.ZERO.compareTo(Duration.ZERO.plusNanos(1)) <= 0); // sanity check
1159         assertTrue(po.getActiveDuration().compareTo(po.getIdleDuration()) <= 0);
1160         // Deprecated
1161         assertTrue(po.getActiveDuration().toMillis() <= po.getActiveTimeMillis());
1162         assertTrue(po.getActiveDuration().compareTo(po.getIdleDuration()) <= 0);
1163         //
1164         // TODO How to compare ID with AD since other tests may have touched the PO?
1165         assertTrue(po.getActiveDuration().compareTo(po.getIdleTime()) <= 0);
1166         assertTrue(po.getActiveDuration().toMillis() <= po.getIdleTimeMillis());
1167         //
1168         assertTrue(po.getCreateInstant().compareTo(po.getLastBorrowInstant()) <= 0);
1169         assertTrue(po.getCreateInstant().compareTo(po.getLastReturnInstant()) <= 0);
1170         assertTrue(po.getCreateInstant().compareTo(po.getLastUsedInstant()) <= 0);
1171         assertTrue(lastBorrowInstant1.compareTo(po.getLastBorrowInstant()) <= 0);
1172         assertTrue(lastReturnInstant1.compareTo(po.getLastReturnInstant()) <= 0);
1173         assertTrue(lastUsedInstant1.compareTo(po.getLastUsedInstant()) <= 0);
1174         genericObjectPool.returnObject(object);
1175         assertFalse(po.getActiveDuration().isNegative());
1176         assertFalse(po.getActiveDuration().isZero());
1177         assertTrue(po.getActiveDuration().toMillis() <= po.getActiveTimeMillis());
1178         assertTrue(po.getActiveDuration().compareTo(po.getActiveTime()) <= 0);
1179         assertTrue(lastBorrowInstant1.compareTo(po.getLastBorrowInstant()) <= 0);
1180         assertTrue(lastReturnInstant1.compareTo(po.getLastReturnInstant()) <= 0);
1181         assertTrue(lastUsedInstant1.compareTo(po.getLastUsedInstant()) <= 0);
1182     }
1183 
1184     /**
1185      * On first borrow, first object fails validation, second object is OK. Subsequent borrows are OK. This was POOL-152.
1186      *
1187      * @throws Exception
1188      */
1189     @Test
1190     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1191     void testBrokenFactoryShouldNotBlockPool() throws Exception {
1192         final int maxTotal = 1;
1193         simpleFactory.setMaxTotal(maxTotal);
1194         genericObjectPool.setMaxTotal(maxTotal);
1195         genericObjectPool.setBlockWhenExhausted(true);
1196         genericObjectPool.setTestOnBorrow(true);
1197         // First borrow object will need to create a new object which will fail
1198         // validation.
1199         String obj = null;
1200         Exception ex = null;
1201         simpleFactory.setValid(false);
1202         try {
1203             obj = genericObjectPool.borrowObject();
1204         } catch (final Exception e) {
1205             ex = e;
1206         }
1207         // Failure expected
1208         assertNotNull(ex);
1209         assertInstanceOf(NoSuchElementException.class, ex);
1210         assertNull(obj);
1211         // Configure factory to create valid objects so subsequent borrows work
1212         simpleFactory.setValid(true);
1213         // Subsequent borrows should be OK
1214         obj = genericObjectPool.borrowObject();
1215         assertNotNull(obj);
1216         genericObjectPool.returnObject(obj);
1217     }
1218 
1219     // POOL-259
1220     @Test
1221     void testClientWaitStats() throws Exception {
1222         final SimpleFactory factory = new SimpleFactory();
1223         // Give makeObject a little latency
1224         factory.setMakeLatency(200);
1225         try (GenericObjectPool<String> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) {
1226             final String s = pool.borrowObject();
1227             // First borrow waits on create, so wait time should be at least 200 ms
1228             // Allow 100ms error in clock times
1229             assertTrue(pool.getMaxBorrowWaitTimeMillis() >= 100);
1230             assertTrue(pool.getMeanBorrowWaitTimeMillis() >= 100);
1231             pool.returnObject(s);
1232             pool.borrowObject();
1233             // Second borrow does not have to wait on create, average should be about 100
1234             assertTrue(pool.getMaxBorrowWaitTimeMillis() > 100);
1235             assertTrue(pool.getMeanBorrowWaitTimeMillis() < 200);
1236             assertTrue(pool.getMeanBorrowWaitTimeMillis() > 20);
1237         }
1238     }
1239 
1240     @Test
1241     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1242     void testCloseMultiplePools1() {
1243         try (GenericObjectPool<String> genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) {
1244             genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
1245             genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
1246         }
1247         genericObjectPool.close();
1248     }
1249 
1250     @Test
1251     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1252     void testCloseMultiplePools2() throws Exception {
1253         try (GenericObjectPool<String> genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) {
1254             // Ensure eviction takes a long time, during which time EvictionTimer.executor's queue is empty
1255             simpleFactory.setDestroyLatency(1000L);
1256             // Ensure there is an object to evict, so that above latency takes effect
1257             genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
1258             genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
1259             genericObjectPool.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND_DURATION);
1260             genericObjectPool2.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND_DURATION);
1261             genericObjectPool.addObject();
1262             genericObjectPool2.addObject();
1263             // Close both pools
1264         }
1265         genericObjectPool.close();
1266     }
1267 
1268     @Test
1269     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1270     void testConcurrentBorrowAndEvict() throws Exception {
1271         genericObjectPool.setMaxTotal(1);
1272         genericObjectPool.addObject();
1273         for (int i = 0; i < 5000; i++) {
1274             final ConcurrentBorrowAndEvictThread one = new ConcurrentBorrowAndEvictThread(true);
1275             final ConcurrentBorrowAndEvictThread two = new ConcurrentBorrowAndEvictThread(false);
1276             one.start();
1277             two.start();
1278             one.join();
1279             two.join();
1280             genericObjectPool.returnObject(one.obj);
1281             /*
1282              * Uncomment this for a progress indication if (i % 10 == 0) { System.out.println(i/10); }
1283              */
1284         }
1285     }
1286 
1287     /**
1288      * POOL-231 - verify that concurrent invalidates of the same object do not corrupt pool destroyCount.
1289      *
1290      * @throws Exception May occur in some failure modes
1291      */
1292     @Test
1293     void testConcurrentInvalidate() throws Exception {
1294         // Get allObjects and idleObjects loaded with some instances
1295         final int nObjects = 1000;
1296         genericObjectPool.setMaxTotal(nObjects);
1297         genericObjectPool.setMaxIdle(nObjects);
1298         final String[] obj = new String[nObjects];
1299         for (int i = 0; i < nObjects; i++) {
1300             obj[i] = genericObjectPool.borrowObject();
1301         }
1302         for (int i = 0; i < nObjects; i++) {
1303             if (i % 2 == 0) {
1304                 genericObjectPool.returnObject(obj[i]);
1305             }
1306         }
1307         final int nThreads = 20;
1308         final int nIterations = 60;
1309         final InvalidateThread[] threads = new InvalidateThread[nThreads];
1310         // Randomly generated list of distinct invalidation targets
1311         final ArrayList<Integer> targets = new ArrayList<>();
1312         final Random random = new Random();
1313         for (int j = 0; j < nIterations; j++) {
1314             // Get a random invalidation target
1315             Integer targ = Integer.valueOf(random.nextInt(nObjects));
1316             while (targets.contains(targ)) {
1317                 targ = Integer.valueOf(random.nextInt(nObjects));
1318             }
1319             targets.add(targ);
1320             // Launch nThreads threads all trying to invalidate the target
1321             for (int i = 0; i < nThreads; i++) {
1322                 threads[i] = new InvalidateThread(genericObjectPool, obj[targ.intValue()]);
1323             }
1324             for (int i = 0; i < nThreads; i++) {
1325                 new Thread(threads[i]).start();
1326             }
1327             boolean done = false;
1328             while (!done) {
1329                 done = true;
1330                 for (int i = 0; i < nThreads; i++) {
1331                     done = done && threads[i].complete();
1332                 }
1333                 Thread.sleep(100);
1334             }
1335         }
1336         assertEquals(nIterations, genericObjectPool.getDestroyedCount());
1337     }
1338 
1339     @Test
1340     void testConstructorNullFactory() {
1341         // add dummy assert (won't be invoked because of IAE) to avoid "unused" warning
1342         assertThrows(IllegalArgumentException.class, () -> new GenericObjectPool<>(null));
1343     }
1344 
1345     @Test
1346     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1347     void testConstructors() {
1348         // Make constructor arguments all different from defaults
1349         final int minIdle = 2;
1350         final Duration maxWaitDuration = Duration.ofMillis(3);
1351         final long maxWaitMillis = maxWaitDuration.toMillis();
1352         final int maxIdle = 4;
1353         final int maxTotal = 5;
1354         final Duration minEvictableIdleDuration = Duration.ofMillis(6);
1355         final long minEvictableIdleMillis = minEvictableIdleDuration.toMillis();
1356         final int numTestsPerEvictionRun = 7;
1357         final boolean testOnBorrow = true;
1358         final boolean testOnReturn = true;
1359         final boolean testWhileIdle = true;
1360         final long timeBetweenEvictionRunsMillis = 8;
1361         final boolean blockWhenExhausted = false;
1362         final boolean lifo = false;
1363         final PooledObjectFactory<Object> dummyFactory = new DummyFactory();
1364         try (GenericObjectPool<Object> dummyPool = new GenericObjectPool<>(dummyFactory)) {
1365             assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_IDLE, dummyPool.getMaxIdle());
1366             assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, dummyPool.getMaxWaitMillis());
1367             assertEquals(GenericObjectPoolConfig.DEFAULT_MIN_IDLE, dummyPool.getMinIdle());
1368             assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL, dummyPool.getMaxTotal());
1369             assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, dummyPool.getMinEvictableIdleTimeMillis());
1370             assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME, dummyPool.getMinEvictableIdleTime());
1371             assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME, dummyPool.getMinEvictableIdleDuration());
1372             assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN, dummyPool.getNumTestsPerEvictionRun());
1373             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW), Boolean.valueOf(dummyPool.getTestOnBorrow()));
1374             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN), Boolean.valueOf(dummyPool.getTestOnReturn()));
1375             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE), Boolean.valueOf(dummyPool.getTestWhileIdle()));
1376             assertEquals(BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS, dummyPool.getDurationBetweenEvictionRuns());
1377             assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, dummyPool.getTimeBetweenEvictionRunsMillis());
1378             assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS, dummyPool.getTimeBetweenEvictionRuns());
1379             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED), Boolean.valueOf(dummyPool.getBlockWhenExhausted()));
1380             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(dummyPool.getLifo()));
1381         }
1382         final GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
1383         config.setLifo(lifo);
1384         config.setMaxIdle(maxIdle);
1385         config.setMinIdle(minIdle);
1386         config.setMaxTotal(maxTotal);
1387         config.setMaxWait(maxWaitDuration);
1388         config.setMinEvictableIdleTimeMillis(minEvictableIdleMillis);
1389         assertEquals(minEvictableIdleMillis, config.getMinEvictableIdleTime().toMillis());
1390         config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
1391         config.setTestOnBorrow(testOnBorrow);
1392         config.setTestOnReturn(testOnReturn);
1393         config.setTestWhileIdle(testWhileIdle);
1394         config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
1395         assertEquals(timeBetweenEvictionRunsMillis, config.getTimeBetweenEvictionRuns().toMillis());
1396         config.setBlockWhenExhausted(blockWhenExhausted);
1397         try (GenericObjectPool<Object> dummyPool = new GenericObjectPool<>(dummyFactory, config)) {
1398             assertEquals(maxIdle, dummyPool.getMaxIdle());
1399             assertEquals(maxWaitDuration, dummyPool.getMaxWaitDuration());
1400             assertEquals(maxWaitMillis, dummyPool.getMaxWaitMillis());
1401             assertEquals(minIdle, dummyPool.getMinIdle());
1402             assertEquals(maxTotal, dummyPool.getMaxTotal());
1403             assertEquals(minEvictableIdleMillis, dummyPool.getMinEvictableIdleTimeMillis());
1404             assertEquals(numTestsPerEvictionRun, dummyPool.getNumTestsPerEvictionRun());
1405             assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(dummyPool.getTestOnBorrow()));
1406             assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(dummyPool.getTestOnReturn()));
1407             assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(dummyPool.getTestWhileIdle()));
1408             assertEquals(timeBetweenEvictionRunsMillis, dummyPool.getTimeBetweenEvictionRunsMillis());
1409             assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(dummyPool.getBlockWhenExhausted()));
1410             assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(dummyPool.getLifo()));
1411         }
1412     }
1413 
1414     @Test
1415     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1416     void testDefaultConfiguration() {
1417         assertConfiguration(new GenericObjectPoolConfig<>(), genericObjectPool);
1418     }
1419 
1420     /**
1421      * Verifies that when a factory's makeObject produces instances that are not discernible by equals, the pool can handle them.
1422      *
1423      * JIRA: POOL-283
1424      */
1425     @Test
1426     void testEqualsIndiscernible() throws Exception {
1427         final HashSetFactory factory = new HashSetFactory();
1428         try (GenericObjectPool<HashSet<String>> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) {
1429             final HashSet<String> s1 = pool.borrowObject();
1430             final HashSet<String> s2 = pool.borrowObject();
1431             pool.returnObject(s1);
1432             pool.returnObject(s2);
1433         }
1434     }
1435 
1436     @Test
1437     void testErrorFactoryDoesNotBlockThreads() throws Exception {
1438         final CreateErrorFactory factory = new CreateErrorFactory();
1439         try (GenericObjectPool<String> createFailFactoryPool = new GenericObjectPool<>(factory)) {
1440             createFailFactoryPool.setMaxTotal(1);
1441             // Try and borrow the first object from the pool
1442             final WaitingTestThread thread1 = new WaitingTestThread(createFailFactoryPool, 0);
1443             thread1.start();
1444             // Wait for thread to reach semaphore
1445             while (!factory.hasQueuedThreads()) {
1446                 Thread.sleep(200);
1447             }
1448             // Try and borrow the second object from the pool
1449             final WaitingTestThread thread2 = new WaitingTestThread(createFailFactoryPool, 0);
1450             thread2.start();
1451             // Pool will not call factory since maximum number of object creations
1452             // are already queued.
1453             // Thread 2 will wait on an object being returned to the pool
1454             // Give thread 2 a chance to reach this state
1455             Thread.sleep(1000);
1456             // Release thread1
1457             factory.release();
1458             // Pre-release thread2
1459             factory.release();
1460             // Both threads should now complete.
1461             boolean threadRunning = true;
1462             int count = 0;
1463             while (threadRunning && count < 15) {
1464                 threadRunning = thread1.isAlive();
1465                 threadRunning = thread2.isAlive();
1466                 Thread.sleep(200);
1467                 count++;
1468             }
1469             assertFalse(thread1.isAlive());
1470             assertFalse(thread2.isAlive());
1471             assertTrue(thread1.thrown instanceof UnknownError);
1472             assertTrue(thread2.thrown instanceof UnknownError);
1473         }
1474     }
1475 
1476     /**
1477      * Tests addObject contention between ensureMinIdle triggered by the Evictor with minIdle &gt; 0 and borrowObject.
1478      *
1479      * @throws Exception May occur in some failure modes
1480      */
1481     @Test
1482     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1483     void testEvictAddObjects() throws Exception {
1484         simpleFactory.setMakeLatency(300);
1485         simpleFactory.setMaxTotal(2);
1486         genericObjectPool.setMaxTotal(2);
1487         genericObjectPool.setMinIdle(1);
1488         genericObjectPool.borrowObject(); // numActive = 1, numIdle = 0
1489         // Create a test thread that will run once and try a borrow after
1490         // 150ms fixed delay
1491         final TestThread<String> borrower = new TestThread<>(genericObjectPool, 1, 150, false);
1492         final Thread borrowerThread = new Thread(borrower);
1493         // Set evictor to run in 100 ms - will create idle instance
1494         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
1495         borrowerThread.start(); // Off to the races
1496         borrowerThread.join();
1497         assertFalse(borrower.failed());
1498     }
1499 
1500     @Test
1501     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1502     void testEvictFIFO() throws Exception {
1503         checkEvict(false);
1504     }
1505 
1506     @Test
1507     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1508     void testEviction() throws Exception {
1509         genericObjectPool.setMaxIdle(500);
1510         genericObjectPool.setMaxTotal(500);
1511         genericObjectPool.setNumTestsPerEvictionRun(100);
1512         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
1513         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
1514         genericObjectPool.setTestWhileIdle(true);
1515         final String[] active = new String[500];
1516         for (int i = 0; i < 500; i++) {
1517             active[i] = genericObjectPool.borrowObject();
1518         }
1519         for (int i = 0; i < 500; i++) {
1520             genericObjectPool.returnObject(active[i]);
1521         }
1522         Waiter.sleepQuietly(1000L);
1523         assertTrue(genericObjectPool.getNumIdle() < 500, "Should be less than 500 idle, found " + genericObjectPool.getNumIdle());
1524         Waiter.sleepQuietly(600L);
1525         assertTrue(genericObjectPool.getNumIdle() < 400, "Should be less than 400 idle, found " + genericObjectPool.getNumIdle());
1526         Waiter.sleepQuietly(600L);
1527         assertTrue(genericObjectPool.getNumIdle() < 300, "Should be less than 300 idle, found " + genericObjectPool.getNumIdle());
1528         Waiter.sleepQuietly(600L);
1529         assertTrue(genericObjectPool.getNumIdle() < 200, "Should be less than 200 idle, found " + genericObjectPool.getNumIdle());
1530         Waiter.sleepQuietly(600L);
1531         assertTrue(genericObjectPool.getNumIdle() < 100, "Should be less than 100 idle, found " + genericObjectPool.getNumIdle());
1532         Waiter.sleepQuietly(600L);
1533         assertEquals(0, genericObjectPool.getNumIdle(), "Should be zero idle, found " + genericObjectPool.getNumIdle());
1534         for (int i = 0; i < 500; i++) {
1535             active[i] = genericObjectPool.borrowObject();
1536         }
1537         for (int i = 0; i < 500; i++) {
1538             genericObjectPool.returnObject(active[i]);
1539         }
1540         Waiter.sleepQuietly(1000L);
1541         assertTrue(genericObjectPool.getNumIdle() < 500, "Should be less than 500 idle, found " + genericObjectPool.getNumIdle());
1542         Waiter.sleepQuietly(600L);
1543         assertTrue(genericObjectPool.getNumIdle() < 400, "Should be less than 400 idle, found " + genericObjectPool.getNumIdle());
1544         Waiter.sleepQuietly(600L);
1545         assertTrue(genericObjectPool.getNumIdle() < 300, "Should be less than 300 idle, found " + genericObjectPool.getNumIdle());
1546         Waiter.sleepQuietly(600L);
1547         assertTrue(genericObjectPool.getNumIdle() < 200, "Should be less than 200 idle, found " + genericObjectPool.getNumIdle());
1548         Waiter.sleepQuietly(600L);
1549         assertTrue(genericObjectPool.getNumIdle() < 100, "Should be less than 100 idle, found " + genericObjectPool.getNumIdle());
1550         Waiter.sleepQuietly(600L);
1551         assertEquals(0, genericObjectPool.getNumIdle(), "Should be zero idle, found " + genericObjectPool.getNumIdle());
1552     }
1553 
1554     @Test
1555     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1556     void testEvictionInvalid() throws Exception {
1557         try (GenericObjectPool<Object> invalidFactoryPool = new GenericObjectPool<>(new InvalidFactory())) {
1558             invalidFactoryPool.setMaxIdle(1);
1559             invalidFactoryPool.setMaxTotal(1);
1560             invalidFactoryPool.setTestOnBorrow(false);
1561             invalidFactoryPool.setTestOnReturn(false);
1562             invalidFactoryPool.setTestWhileIdle(true);
1563             invalidFactoryPool.setMinEvictableIdleTime(Duration.ofSeconds(100));
1564             invalidFactoryPool.setNumTestsPerEvictionRun(1);
1565             final Object p = invalidFactoryPool.borrowObject();
1566             invalidFactoryPool.returnObject(p);
1567             // Run eviction in a separate thread
1568             final Thread t = new EvictionThread<>(invalidFactoryPool);
1569             t.start();
1570             // Sleep to make sure evictor has started
1571             Thread.sleep(300);
1572             try {
1573                 invalidFactoryPool.borrowObject(1);
1574             } catch (final NoSuchElementException nsee) {
1575                 // Ignore
1576             }
1577             // Make sure evictor has finished
1578             Thread.sleep(1000);
1579             // Should have an empty pool
1580             assertEquals(0, invalidFactoryPool.getNumIdle(), "Idle count different than expected.");
1581             assertEquals(0, invalidFactoryPool.getNumActive(), "Total count different than expected.");
1582         }
1583     }
1584 
1585     /**
1586      * Test to make sure evictor visits least recently used objects first, regardless of FIFO/LIFO.
1587      *
1588      * JIRA: POOL-86
1589      *
1590      * @throws Exception May occur in some failure modes
1591      */
1592     @Test
1593     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1594     void testEvictionOrder() throws Exception {
1595         checkEvictionOrder(false);
1596         tearDown();
1597         setUp();
1598         checkEvictionOrder(true);
1599     }
1600 
1601     @Test
1602     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1603     void testEvictionPolicy() throws Exception {
1604         genericObjectPool.setMaxIdle(500);
1605         genericObjectPool.setMaxTotal(500);
1606         genericObjectPool.setNumTestsPerEvictionRun(500);
1607         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
1608         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
1609         genericObjectPool.setTestWhileIdle(true);
1610         // ClassNotFoundException
1611         assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(Long.toString(System.currentTimeMillis())),
1612                 "setEvictionPolicyClassName must throw an error if the class name is invalid.");
1613         // InstantiationException
1614         assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(Serializable.class.getName()),
1615                 "setEvictionPolicyClassName must throw an error if the class name is invalid.");
1616         // IllegalAccessException
1617         assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(Collections.class.getName()),
1618                 "setEvictionPolicyClassName must throw an error if the class name is invalid.");
1619         assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(String.class.getName()),
1620                 () -> "setEvictionPolicyClassName must throw an error if a class that does not implement EvictionPolicy is specified.");
1621         genericObjectPool.setEvictionPolicy(new TestEvictionPolicy<>());
1622         assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName());
1623         genericObjectPool.setEvictionPolicyClassName(TestEvictionPolicy.class.getName());
1624         assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName());
1625         final String[] active = new String[500];
1626         for (int i = 0; i < 500; i++) {
1627             active[i] = genericObjectPool.borrowObject();
1628         }
1629         for (int i = 0; i < 500; i++) {
1630             genericObjectPool.returnObject(active[i]);
1631         }
1632         // Eviction policy ignores first 1500 attempts to evict and then always
1633         // evicts. After 1s, there should have been two runs of 500 tests so no
1634         // evictions
1635         Waiter.sleepQuietly(1000L);
1636         assertEquals(500, genericObjectPool.getNumIdle(), "Should be 500 idle");
1637         // A further 1s wasn't enough so allow 2s for the evictor to clear out
1638         // all of the idle objects.
1639         Waiter.sleepQuietly(2000L);
1640         assertEquals(0, genericObjectPool.getNumIdle(), "Should be 0 idle");
1641     }
1642 
1643     @Test
1644     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1645     void testEvictionSoftMinIdle() throws Exception {
1646         final class TimeTest extends BasePooledObjectFactory<TimeTest> {
1647             private final long createTimeMillis;
1648 
1649             TimeTest() {
1650                 createTimeMillis = System.currentTimeMillis();
1651             }
1652 
1653             @Override
1654             public TimeTest create() {
1655                 return new TimeTest();
1656             }
1657 
1658             public long getCreateTimeMillis() {
1659                 return createTimeMillis;
1660             }
1661 
1662             @Override
1663             public PooledObject<TimeTest> wrap(final TimeTest value) {
1664                 return new DefaultPooledObject<>(value);
1665             }
1666         }
1667         try (GenericObjectPool<TimeTest> timePool = new GenericObjectPool<>(new TimeTest())) {
1668             timePool.setMaxIdle(5);
1669             timePool.setMaxTotal(5);
1670             timePool.setNumTestsPerEvictionRun(5);
1671             timePool.setMinEvictableIdleDuration(Duration.ofSeconds(3));
1672             timePool.setMinEvictableIdle(Duration.ofSeconds(3));
1673             timePool.setMinEvictableIdleTime(Duration.ofSeconds(3));
1674             timePool.setSoftMinEvictableIdleTime(TestConstants.ONE_SECOND_DURATION);
1675             timePool.setMinIdle(2);
1676             final TimeTest[] active = new TimeTest[5];
1677             final Long[] creationTime = new Long[5];
1678             for (int i = 0; i < 5; i++) {
1679                 active[i] = timePool.borrowObject();
1680                 creationTime[i] = Long.valueOf(active[i].getCreateTimeMillis());
1681             }
1682             for (int i = 0; i < 5; i++) {
1683                 timePool.returnObject(active[i]);
1684             }
1685             // Soft evict all but minIdle(2)
1686             Thread.sleep(1500L);
1687             timePool.evict();
1688             assertEquals(2, timePool.getNumIdle(), "Idle count different than expected.");
1689             // Hard evict the rest.
1690             Thread.sleep(2000L);
1691             timePool.evict();
1692             assertEquals(0, timePool.getNumIdle(), "Idle count different than expected.");
1693         }
1694     }
1695 
1696     @Test
1697     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1698     void testEvictionWithNegativeNumTests() throws Exception {
1699         // when numTestsPerEvictionRun is negative, it represents a fraction of the idle objects to test
1700         genericObjectPool.setMaxIdle(6);
1701         genericObjectPool.setMaxTotal(6);
1702         genericObjectPool.setNumTestsPerEvictionRun(-2);
1703         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
1704         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
1705         final String[] active = new String[6];
1706         for (int i = 0; i < 6; i++) {
1707             active[i] = genericObjectPool.borrowObject();
1708         }
1709         for (int i = 0; i < 6; i++) {
1710             genericObjectPool.returnObject(active[i]);
1711         }
1712         Waiter.sleepQuietly(100L);
1713         assertTrue(genericObjectPool.getNumIdle() <= 6, "Should at most 6 idle, found " + genericObjectPool.getNumIdle());
1714         Waiter.sleepQuietly(100L);
1715         assertTrue(genericObjectPool.getNumIdle() <= 3, "Should at most 3 idle, found " + genericObjectPool.getNumIdle());
1716         Waiter.sleepQuietly(100L);
1717         assertTrue(genericObjectPool.getNumIdle() <= 2, "Should be at most 2 idle, found " + genericObjectPool.getNumIdle());
1718         Waiter.sleepQuietly(100L);
1719         assertEquals(0, genericObjectPool.getNumIdle(), "Should be zero idle, found " + genericObjectPool.getNumIdle());
1720     }
1721 
1722     @Test
1723     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1724     void testEvictLIFO() throws Exception {
1725         checkEvict(true);
1726     }
1727 
1728     /**
1729      * Verifies that the evictor visits objects in expected order and frequency.
1730      *
1731      * @throws Exception May occur in some failure modes
1732      */
1733     @Test
1734     void testEvictorVisiting() throws Exception {
1735         checkEvictorVisiting(true);
1736         checkEvictorVisiting(false);
1737     }
1738 
1739     @Test
1740     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1741     void testEvictWhileEmpty() throws Exception {
1742         genericObjectPool.evict();
1743         genericObjectPool.evict();
1744         genericObjectPool.close();
1745     }
1746 
1747     @Test
1748     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1749     void testExceptionInValidationDuringEviction() throws Exception {
1750         genericObjectPool.setMaxIdle(1);
1751         genericObjectPool.setMinEvictableIdleTime(Duration.ZERO);
1752         genericObjectPool.setTestWhileIdle(true);
1753         final String active = genericObjectPool.borrowObject();
1754         genericObjectPool.returnObject(active);
1755         simpleFactory.setThrowExceptionOnValidate(true);
1756         assertThrows(RuntimeException.class, () -> genericObjectPool.evict());
1757         assertEquals(0, genericObjectPool.getNumActive());
1758         assertEquals(0, genericObjectPool.getNumIdle());
1759     }
1760 
1761     @Test
1762     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1763     void testExceptionOnActivateDuringBorrow() throws Exception {
1764         final String obj1 = genericObjectPool.borrowObject();
1765         final String obj2 = genericObjectPool.borrowObject();
1766         genericObjectPool.returnObject(obj1);
1767         genericObjectPool.returnObject(obj2);
1768         simpleFactory.setThrowExceptionOnActivate(true);
1769         simpleFactory.setEvenValid(false);
1770         // Activation will now throw every other time
1771         // First attempt throws, but loop continues and second succeeds
1772         final String obj = genericObjectPool.borrowObject();
1773         assertEquals(1, genericObjectPool.getNumActive());
1774         assertEquals(0, genericObjectPool.getNumIdle());
1775         genericObjectPool.returnObject(obj);
1776         simpleFactory.setValid(false);
1777         // Validation will now fail on activation when borrowObject returns
1778         // an idle instance, and then when attempting to create a new instance
1779         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
1780         assertEquals(0, genericObjectPool.getNumActive());
1781         assertEquals(0, genericObjectPool.getNumIdle());
1782     }
1783 
1784     @Test
1785     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1786     void testExceptionOnDestroyDuringBorrow() throws Exception {
1787         simpleFactory.setThrowExceptionOnDestroy(true);
1788         genericObjectPool.setTestOnBorrow(true);
1789         genericObjectPool.borrowObject();
1790         simpleFactory.setValid(false); // Make validation fail on next borrow attempt
1791         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
1792         assertEquals(1, genericObjectPool.getNumActive());
1793         assertEquals(0, genericObjectPool.getNumIdle());
1794     }
1795 
1796     @Test
1797     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1798     void testExceptionOnDestroyDuringReturn() throws Exception {
1799         simpleFactory.setThrowExceptionOnDestroy(true);
1800         genericObjectPool.setTestOnReturn(true);
1801         final String obj1 = genericObjectPool.borrowObject();
1802         genericObjectPool.borrowObject();
1803         simpleFactory.setValid(false); // Make validation fail
1804         genericObjectPool.returnObject(obj1);
1805         assertEquals(1, genericObjectPool.getNumActive());
1806         assertEquals(0, genericObjectPool.getNumIdle());
1807     }
1808 
1809     @Test
1810     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1811     void testExceptionOnPassivateDuringReturn() throws Exception {
1812         final String obj = genericObjectPool.borrowObject();
1813         simpleFactory.setThrowExceptionOnPassivate(true);
1814         genericObjectPool.returnObject(obj);
1815         assertEquals(0, genericObjectPool.getNumIdle());
1816     }
1817 
1818     @Test
1819     void testFailingFactoryDoesNotBlockThreads() throws Exception {
1820         final CreateFailFactory factory = new CreateFailFactory();
1821         try (GenericObjectPool<String> createFailFactoryPool = new GenericObjectPool<>(factory)) {
1822             createFailFactoryPool.setMaxTotal(1);
1823             // Try and borrow the first object from the pool
1824             final WaitingTestThread thread1 = new WaitingTestThread(createFailFactoryPool, 0);
1825             thread1.start();
1826             // Wait for thread to reach semaphore
1827             while (!factory.hasQueuedThreads()) {
1828                 Thread.sleep(200);
1829             }
1830             // Try and borrow the second object from the pool
1831             final WaitingTestThread thread2 = new WaitingTestThread(createFailFactoryPool, 0);
1832             thread2.start();
1833             // Pool will not call factory since maximum number of object creations
1834             // are already queued.
1835             // Thread 2 will wait on an object being returned to the pool
1836             // Give thread 2 a chance to reach this state
1837             Thread.sleep(1000);
1838             // Release thread1
1839             factory.release();
1840             // Pre-release thread2
1841             factory.release();
1842             // Both threads should now complete.
1843             boolean threadRunning = true;
1844             int count = 0;
1845             while (threadRunning && count < 15) {
1846                 threadRunning = thread1.isAlive();
1847                 threadRunning = thread2.isAlive();
1848                 Thread.sleep(200);
1849                 count++;
1850             }
1851             assertFalse(thread1.isAlive());
1852             assertFalse(thread2.isAlive());
1853             assertTrue(thread1.thrown instanceof UnsupportedCharsetException);
1854             assertTrue(thread2.thrown instanceof UnsupportedCharsetException);
1855         }
1856     }
1857 
1858     @Test
1859     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1860     void testFIFO() throws Exception {
1861         genericObjectPool.setLifo(false);
1862         genericObjectPool.addObject(); // "0"
1863         genericObjectPool.addObject(); // "1"
1864         genericObjectPool.addObject(); // "2"
1865         assertEquals("0", genericObjectPool.borrowObject(), "Oldest");
1866         assertEquals("1", genericObjectPool.borrowObject(), "Middle");
1867         assertEquals("2", genericObjectPool.borrowObject(), "Youngest");
1868         final String o = genericObjectPool.borrowObject();
1869         assertEquals("3", o, "new-3");
1870         genericObjectPool.returnObject(o);
1871         assertEquals(o, genericObjectPool.borrowObject(), "returned-3");
1872         assertEquals("4", genericObjectPool.borrowObject(), "new-4");
1873     }
1874 
1875     @Test
1876     void testGetFactoryType_DefaultPooledObjectFactory() {
1877         try (GenericObjectPool<String> pool = new GenericObjectPool<>(createDefaultPooledObjectFactory())) {
1878             assertNotNull(pool.getFactoryType());
1879         }
1880     }
1881 
1882     @Test
1883     void testGetFactoryType_NullPooledObjectFactory() {
1884         try (GenericObjectPool<String> pool = new GenericObjectPool<>(createNullPooledObjectFactory())) {
1885             assertNotNull(pool.getFactoryType());
1886         }
1887     }
1888 
1889     @Test
1890     void testGetFactoryType_PoolUtilsSynchronizedDefaultPooledFactory() {
1891         try (GenericObjectPool<String> pool = new GenericObjectPool<>(PoolUtils.synchronizedPooledFactory(createDefaultPooledObjectFactory()))) {
1892             assertNotNull(pool.getFactoryType());
1893         }
1894     }
1895 
1896     @Test
1897     void testGetFactoryType_PoolUtilsSynchronizedNullPooledFactory() {
1898         try (GenericObjectPool<String> pool = new GenericObjectPool<>(PoolUtils.synchronizedPooledFactory(createNullPooledObjectFactory()))) {
1899             assertNotNull(pool.getFactoryType());
1900         }
1901     }
1902 
1903     @Test
1904     void testGetFactoryType_SynchronizedDefaultPooledObjectFactory() {
1905         try (GenericObjectPool<String> pool = new GenericObjectPool<>(new TestSynchronizedPooledObjectFactory<>(createDefaultPooledObjectFactory()))) {
1906             assertNotNull(pool.getFactoryType());
1907         }
1908     }
1909 
1910     @Test
1911     void testGetFactoryType_SynchronizedNullPooledObjectFactory() {
1912         try (GenericObjectPool<String> pool = new GenericObjectPool<>(new TestSynchronizedPooledObjectFactory<>(createNullPooledObjectFactory()))) {
1913             assertNotNull(pool.getFactoryType());
1914         }
1915     }
1916 
1917     @Test
1918     void testGetStatsString() {
1919         try (GenericObjectPool<String> pool = new GenericObjectPool<>(new TestSynchronizedPooledObjectFactory<>(createNullPooledObjectFactory()))) {
1920             assertNotNull(pool.getStatsString());
1921         }
1922     }
1923 
1924     /**
1925      * Verify that threads waiting on a depleted pool get served when a checked out object is invalidated.
1926      *
1927      * JIRA: POOL-240
1928      *
1929      * @throws Exception May occur in some failure modes
1930      */
1931     @Test
1932     void testInvalidateFreesCapacity() throws Exception {
1933         final SimpleFactory factory = new SimpleFactory();
1934         try (GenericObjectPool<String> pool = new GenericObjectPool<>(factory)) {
1935             pool.setMaxTotal(2);
1936             pool.setMaxWaitMillis(500);
1937             // Borrow an instance and hold if for 5 seconds
1938             final WaitingTestThread thread1 = new WaitingTestThread(pool, 5000);
1939             thread1.start();
1940             // Borrow another instance
1941             final String obj = pool.borrowObject();
1942             // Launch another thread - will block, but fail in 500 ms
1943             final WaitingTestThread thread2 = new WaitingTestThread(pool, 100);
1944             thread2.start();
1945             // Invalidate the object borrowed by this thread - should allow thread2 to create
1946             Thread.sleep(20);
1947             pool.invalidateObject(obj);
1948             Thread.sleep(600); // Wait for thread2 to timeout
1949             if (thread2.thrown != null) {
1950                 fail(thread2.thrown.toString());
1951             }
1952         }
1953     }
1954 
1955     /**
1956      * Ensure the pool is registered.
1957      */
1958     @Test
1959     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1960     void testJmxRegistration() {
1961         final ObjectName oname = genericObjectPool.getJmxName();
1962         final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
1963         final Set<ObjectName> result = mbs.queryNames(oname, null);
1964         assertEquals(1, result.size());
1965         genericObjectPool.jmxUnregister();
1966         final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
1967         config.setJmxEnabled(false);
1968         try (GenericObjectPool<String> poolWithoutJmx = new GenericObjectPool<>(simpleFactory, config)) {
1969             assertNull(poolWithoutJmx.getJmxName());
1970             config.setJmxEnabled(true);
1971             poolWithoutJmx.jmxUnregister();
1972         }
1973         config.setJmxNameBase(null);
1974         try (GenericObjectPool<String> poolWithDefaultJmxNameBase = new GenericObjectPool<>(simpleFactory, config)) {
1975             assertNotNull(poolWithDefaultJmxNameBase.getJmxName());
1976         }
1977     }
1978 
1979     @Test
1980     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1981     void testLIFO() throws Exception {
1982         final String o;
1983         genericObjectPool.setLifo(true);
1984         genericObjectPool.addObject(); // "0"
1985         genericObjectPool.addObject(); // "1"
1986         genericObjectPool.addObject(); // "2"
1987         assertEquals("2", genericObjectPool.borrowObject(), "Youngest");
1988         assertEquals("1", genericObjectPool.borrowObject(), "Middle");
1989         assertEquals("0", genericObjectPool.borrowObject(), "Oldest");
1990         o = genericObjectPool.borrowObject();
1991         assertEquals("3", o, "new-3");
1992         genericObjectPool.returnObject(o);
1993         assertEquals(o, genericObjectPool.borrowObject(), "returned-3");
1994         assertEquals("4", genericObjectPool.borrowObject(), "new-4");
1995     }
1996 
1997     /**
1998      * Simplest example of recovery from factory outage. A thread gets into parked wait on the deque when there is capacity to create, but creates are failing
1999      * due to factory outage. Verify that the borrower is served once the factory is back online.
2000      */
2001     @Test
2002     @Disabled
2003     @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
2004     void testLivenessOnTransientFactoryFailure() throws Exception {
2005         final DisconnectingWaiterFactory<String> factory = new DisconnectingWaiterFactory<>(DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_CREATE_ACTION,
2006                 DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_LIFECYCLE_ACTION, obj -> false // all instances fail validation
2007         );
2008         final AtomicBoolean failed = new AtomicBoolean();
2009         try (GenericObjectPool<Waiter> pool = new GenericObjectPool<>(factory)) {
2010             pool.setMaxWait(Duration.ofMillis(100));
2011             pool.setTestOnReturn(true);
2012             pool.setMaxTotal(1);
2013             final Waiter w = pool.borrowObject();
2014             final Thread t = new Thread(() -> {
2015                 try {
2016                     pool.borrowObject();
2017                 } catch (final Exception e) {
2018                     failed.set(true);
2019                 }
2020             });
2021             Thread.sleep(10);
2022             t.start();
2023             // t is blocked waiting on the deque
2024             Thread.sleep(10);
2025             factory.disconnect();
2026             pool.returnObject(w); // validation fails, so no return
2027             Thread.sleep(10);
2028             factory.connect();
2029             // Borrower should be able to be served now
2030             t.join();
2031         }
2032         if (failed.get()) {
2033             fail("Borrower timed out waiting for an instance");
2034         }
2035     }
2036 
2037     /**
2038      * Test the following scenario: Thread 1 borrows an instance Thread 2 starts to borrow another instance before thread 1 returns its instance Thread 1
2039      * returns its instance while thread 2 is validating its newly created instance The test verifies that the instance created by Thread 2 is not leaked.
2040      *
2041      * @throws Exception May occur in some failure modes
2042      */
2043     @Test
2044     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2045     void testMakeConcurrentWithReturn() throws Exception {
2046         genericObjectPool.setTestOnBorrow(true);
2047         simpleFactory.setValid(true);
2048         // Borrow and return an instance, with a short wait
2049         final WaitingTestThread thread1 = new WaitingTestThread(genericObjectPool, 200);
2050         thread1.start();
2051         Thread.sleep(50); // wait for validation to succeed
2052         // Slow down validation and borrow an instance
2053         simpleFactory.setValidateLatency(400);
2054         final String instance = genericObjectPool.borrowObject();
2055         // Now make sure that we have not leaked an instance
2056         assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle() + 1);
2057         genericObjectPool.returnObject(instance);
2058         assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle());
2059     }
2060 
2061     @Test
2062     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2063     void testMaxIdle() throws Exception {
2064         genericObjectPool.setMaxTotal(100);
2065         genericObjectPool.setMaxIdle(8);
2066         final String[] active = new String[100];
2067         for (int i = 0; i < 100; i++) {
2068             active[i] = genericObjectPool.borrowObject();
2069         }
2070         assertEquals(100, genericObjectPool.getNumActive());
2071         assertEquals(0, genericObjectPool.getNumIdle());
2072         for (int i = 0; i < 100; i++) {
2073             genericObjectPool.returnObject(active[i]);
2074             assertEquals(99 - i, genericObjectPool.getNumActive());
2075             assertEquals(i < 8 ? i + 1 : 8, genericObjectPool.getNumIdle());
2076         }
2077     }
2078 
2079     @Test
2080     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2081     void testMaxIdleZero() throws Exception {
2082         genericObjectPool.setMaxTotal(100);
2083         genericObjectPool.setMaxIdle(0);
2084         final String[] active = new String[100];
2085         for (int i = 0; i < 100; i++) {
2086             active[i] = genericObjectPool.borrowObject();
2087         }
2088         assertEquals(100, genericObjectPool.getNumActive());
2089         assertEquals(0, genericObjectPool.getNumIdle());
2090         for (int i = 0; i < 100; i++) {
2091             genericObjectPool.returnObject(active[i]);
2092             assertEquals(99 - i, genericObjectPool.getNumActive());
2093             assertEquals(0, genericObjectPool.getNumIdle());
2094         }
2095     }
2096 
2097     /**
2098      * Showcasing a possible deadlock situation as reported in POOL-356
2099      */
2100     @Test
2101     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2102     @SuppressWarnings("rawtypes")
2103     void testMaxIdleZeroUnderLoad() {
2104         // Config
2105         final int numThreads = 199; // And main thread makes a round 200.
2106         final int numIter = 20;
2107         final int delay = 25;
2108         final int maxTotal = 10;
2109         simpleFactory.setMaxTotal(maxTotal);
2110         genericObjectPool.setMaxTotal(maxTotal);
2111         genericObjectPool.setBlockWhenExhausted(true);
2112         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
2113         // this is important to trigger POOL-356
2114         genericObjectPool.setMaxIdle(0);
2115         // Start threads to borrow objects
2116         final TestThread[] threads = new TestThread[numThreads];
2117         for (int i = 0; i < numThreads; i++) {
2118             // Factor of 2 on iterations so main thread does work whilst other
2119             // threads are running. Factor of 2 on delay so average delay for
2120             // other threads == actual delay for main thread
2121             threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2);
2122             final Thread t = new Thread(threads[i]);
2123             t.start();
2124         }
2125         // Give the threads a chance to start doing some work
2126         Waiter.sleepQuietly(100L);
2127         for (int i = 0; i < numIter; i++) {
2128             String obj = null;
2129             try {
2130                 Waiter.sleepQuietly(delay);
2131                 obj = genericObjectPool.borrowObject();
2132                 // Under load, observed numActive > maxTotal
2133                 if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) {
2134                     throw new IllegalStateException("Too many active objects");
2135                 }
2136                 Waiter.sleepQuietly(delay);
2137             } catch (final Exception e) {
2138                 // Shouldn't happen
2139                 e.printStackTrace();
2140                 fail("Exception on borrow");
2141             } finally {
2142                 if (obj != null) {
2143                     try {
2144                         genericObjectPool.returnObject(obj);
2145                     } catch (final Exception e) {
2146                         // Ignore
2147                     }
2148                 }
2149             }
2150         }
2151         for (int i = 0; i < numThreads; i++) {
2152             while (!threads[i].complete()) {
2153                 Waiter.sleepQuietly(500L);
2154             }
2155             if (threads[i].failed()) {
2156                 threads[i].error.printStackTrace();
2157                 fail("Thread " + i + " failed: " + threads[i].error.toString());
2158             }
2159         }
2160     }
2161 
2162     @Test
2163     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2164     void testMaxTotal() throws Exception {
2165         genericObjectPool.setMaxTotal(3);
2166         genericObjectPool.setBlockWhenExhausted(false);
2167         genericObjectPool.borrowObject();
2168         genericObjectPool.borrowObject();
2169         genericObjectPool.borrowObject();
2170         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
2171     }
2172 
2173     /**
2174      * Verifies that maxTotal is not exceeded when factory destroyObject has high latency, testOnReturn is set and there is high incidence of validation
2175      * failures.
2176      */
2177     @Test
2178     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2179     void testMaxTotalInvariant() {
2180         final int maxTotal = 15;
2181         simpleFactory.setEvenValid(false); // Every other validation fails
2182         simpleFactory.setDestroyLatency(100); // Destroy takes 100 ms
2183         simpleFactory.setMaxTotal(maxTotal); // (makes - destroys) bound
2184         simpleFactory.setValidationEnabled(true);
2185         genericObjectPool.setMaxTotal(maxTotal);
2186         genericObjectPool.setMaxIdle(-1);
2187         genericObjectPool.setTestOnReturn(true);
2188         genericObjectPool.setMaxWaitMillis(1000L);
2189         runTestThreads(5, 10, 50, genericObjectPool);
2190     }
2191 
2192     @Test
2193     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2194     @SuppressWarnings("rawtypes")
2195     void testMaxTotalUnderLoad() {
2196         // Config
2197         final int numThreads = 199; // And main thread makes a round 200.
2198         final int numIter = 20;
2199         final int delay = 25;
2200         final int maxTotal = 10;
2201         simpleFactory.setMaxTotal(maxTotal);
2202         genericObjectPool.setMaxTotal(maxTotal);
2203         genericObjectPool.setBlockWhenExhausted(true);
2204         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
2205         // Start threads to borrow objects
2206         final TestThread[] threads = new TestThread[numThreads];
2207         for (int i = 0; i < numThreads; i++) {
2208             // Factor of 2 on iterations so main thread does work whilst other
2209             // threads are running. Factor of 2 on delay so average delay for
2210             // other threads == actual delay for main thread
2211             threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2);
2212             final Thread t = new Thread(threads[i]);
2213             t.start();
2214         }
2215         // Give the threads a chance to start doing some work
2216         Waiter.sleepQuietly(5000);
2217         for (int i = 0; i < numIter; i++) {
2218             String obj = null;
2219             try {
2220                 Waiter.sleepQuietly(delay);
2221                 obj = genericObjectPool.borrowObject();
2222                 // Under load, observed numActive > maxTotal
2223                 if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) {
2224                     throw new IllegalStateException("Too many active objects");
2225                 }
2226                 Waiter.sleepQuietly(delay);
2227             } catch (final Exception e) {
2228                 // Shouldn't happen
2229                 e.printStackTrace();
2230                 fail("Exception on borrow");
2231             } finally {
2232                 if (obj != null) {
2233                     try {
2234                         genericObjectPool.returnObject(obj);
2235                     } catch (final Exception e) {
2236                         // Ignore
2237                     }
2238                 }
2239             }
2240         }
2241         for (int i = 0; i < numThreads; i++) {
2242             while (!threads[i].complete()) {
2243                 Waiter.sleepQuietly(500L);
2244             }
2245             if (threads[i].failed()) {
2246                 fail("Thread " + i + " failed: " + threads[i].error.toString());
2247             }
2248         }
2249     }
2250 
2251     @Test
2252     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2253     void testMaxTotalZero() throws Exception {
2254         genericObjectPool.setMaxTotal(0);
2255         genericObjectPool.setBlockWhenExhausted(false);
2256         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
2257     }
2258 
2259     /*
2260      * Test multi-threaded pool access. Multiple threads, but maxTotal only allows half the threads to succeed.
2261      *
2262      * This test was prompted by Continuum build failures in the Commons DBCP test case: TestPerUserPoolDataSource.testMultipleThreads2() Let's see if the this
2263      * fails on Continuum too!
2264      */
2265     @Test
2266     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2267     void testMaxWaitMultiThreaded() throws Exception {
2268         final long maxWait = 500; // wait for connection
2269         final long holdTime = 2 * maxWait; // how long to hold connection
2270         final int threads = 10; // number of threads to grab the object initially
2271         genericObjectPool.setBlockWhenExhausted(true);
2272         genericObjectPool.setMaxWaitMillis(maxWait);
2273         genericObjectPool.setMaxTotal(threads);
2274         // Create enough threads so half the threads will have to wait
2275         final WaitingTestThread[] wtt = new WaitingTestThread[threads * 2];
2276         for (int i = 0; i < wtt.length; i++) {
2277             wtt[i] = new WaitingTestThread(genericObjectPool, holdTime);
2278         }
2279         final long originMillis = System.currentTimeMillis() - 1000;
2280         for (final WaitingTestThread element : wtt) {
2281             element.start();
2282         }
2283         int failed = 0;
2284         for (final WaitingTestThread element : wtt) {
2285             element.join();
2286             if (element.thrown != null) {
2287                 failed++;
2288             }
2289         }
2290         if (DISPLAY_THREAD_DETAILS || wtt.length / 2 != failed) {
2291             System.out.println("MaxWait: " + maxWait + " HoldTime: " + holdTime + " MaxTotal: " + threads + " Threads: " + wtt.length + " Failed: " + failed);
2292             for (final WaitingTestThread wt : wtt) {
2293                 System.out.println("PreBorrow: " + (wt.preBorrowMillis - originMillis) + " PostBorrow: "
2294                         + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - originMillis : -1) + " BorrowTime: "
2295                         + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - wt.preBorrowMillis : -1) + " PostReturn: "
2296                         + (wt.postReturnMillis != 0 ? wt.postReturnMillis - originMillis : -1) + " Ended: " + (wt.endedMillis - originMillis) + " ObjId: "
2297                         + wt.objectId);
2298             }
2299         }
2300         assertEquals(wtt.length / 2, failed, "Expected half the threads to fail");
2301     }
2302 
2303     @Test
2304     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2305     void testMinIdle() throws Exception {
2306         genericObjectPool.setMaxIdle(500);
2307         genericObjectPool.setMinIdle(5);
2308         genericObjectPool.setMaxTotal(10);
2309         genericObjectPool.setNumTestsPerEvictionRun(0);
2310         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
2311         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
2312         genericObjectPool.setTestWhileIdle(true);
2313         Waiter.sleepQuietly(150L);
2314         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2315         final String[] active = new String[5];
2316         active[0] = genericObjectPool.borrowObject();
2317         Waiter.sleepQuietly(150L);
2318         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2319         for (int i = 1; i < 5; i++) {
2320             active[i] = genericObjectPool.borrowObject();
2321         }
2322         Waiter.sleepQuietly(150L);
2323         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2324         for (int i = 0; i < 5; i++) {
2325             genericObjectPool.returnObject(active[i]);
2326         }
2327         Waiter.sleepQuietly(150L);
2328         assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
2329     }
2330 
2331     @Test
2332     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2333     void testMinIdleMaxTotal() throws Exception {
2334         genericObjectPool.setMaxIdle(500);
2335         genericObjectPool.setMinIdle(5);
2336         genericObjectPool.setMaxTotal(10);
2337         genericObjectPool.setNumTestsPerEvictionRun(0);
2338         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
2339         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
2340         genericObjectPool.setTestWhileIdle(true);
2341         Waiter.sleepQuietly(150L);
2342         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2343         final String[] active = new String[10];
2344         Waiter.sleepQuietly(150L);
2345         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2346         for (int i = 0; i < 5; i++) {
2347             active[i] = genericObjectPool.borrowObject();
2348         }
2349         Waiter.sleepQuietly(150L);
2350         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2351         for (int i = 0; i < 5; i++) {
2352             genericObjectPool.returnObject(active[i]);
2353         }
2354         Waiter.sleepQuietly(150L);
2355         assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
2356         for (int i = 0; i < 10; i++) {
2357             active[i] = genericObjectPool.borrowObject();
2358         }
2359         Waiter.sleepQuietly(150L);
2360         assertEquals(0, genericObjectPool.getNumIdle(), "Should be 0 idle, found " + genericObjectPool.getNumIdle());
2361         for (int i = 0; i < 10; i++) {
2362             genericObjectPool.returnObject(active[i]);
2363         }
2364         Waiter.sleepQuietly(150L);
2365         assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
2366     }
2367 
2368     /**
2369      * Verifies that returning an object twice (without borrow in between) causes ISE but does not re-validate or re-passivate the instance.
2370      *
2371      * JIRA: POOL-285
2372      */
2373     @Test
2374     void testMultipleReturn() throws Exception {
2375         final WaiterFactory<String> factory = new WaiterFactory<>(0, 0, 0, 0, 0, 0);
2376         try (GenericObjectPool<Waiter> pool = new GenericObjectPool<>(factory)) {
2377             pool.setTestOnReturn(true);
2378             final Waiter waiter = pool.borrowObject();
2379             pool.returnObject(waiter);
2380             assertEquals(1, waiter.getValidationCount());
2381             assertEquals(1, waiter.getPassivationCount());
2382             try {
2383                 pool.returnObject(waiter);
2384                 fail("Expecting IllegalStateException from multiple return");
2385             } catch (final IllegalStateException ex) {
2386                 // Exception is expected, now check no repeat validation/passivation
2387                 assertEquals(1, waiter.getValidationCount());
2388                 assertEquals(1, waiter.getPassivationCount());
2389             }
2390         }
2391     }
2392 
2393     // POOL-248
2394     @Test
2395     void testMultipleReturnOfSameObject() throws Exception {
2396         try (GenericObjectPool<String> pool = new GenericObjectPool<>(simpleFactory, new GenericObjectPoolConfig<>())) {
2397             assertEquals(0, pool.getNumActive());
2398             assertEquals(0, pool.getNumIdle());
2399             final String obj = pool.borrowObject();
2400             assertEquals(1, pool.getNumActive());
2401             assertEquals(0, pool.getNumIdle());
2402             pool.returnObject(obj);
2403             assertEquals(0, pool.getNumActive());
2404             assertEquals(1, pool.getNumIdle());
2405             assertThrows(IllegalStateException.class, () -> pool.returnObject(obj));
2406             assertEquals(0, pool.getNumActive());
2407             assertEquals(1, pool.getNumIdle());
2408         }
2409     }
2410 
2411     /**
2412      * Verifies that when a borrowed object is mutated in a way that does not preserve equality and hash code, the pool can recognized it on return.
2413      *
2414      * JIRA: POOL-284
2415      */
2416     @Test
2417     void testMutable() throws Exception {
2418         final HashSetFactory factory = new HashSetFactory();
2419         try (GenericObjectPool<HashSet<String>> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) {
2420             final HashSet<String> s1 = pool.borrowObject();
2421             final HashSet<String> s2 = pool.borrowObject();
2422             s1.add("One");
2423             s2.add("One");
2424             pool.returnObject(s1);
2425             pool.returnObject(s2);
2426         }
2427     }
2428 
2429     @Test
2430     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2431     void testNegativeMaxTotal() throws Exception {
2432         genericObjectPool.setMaxTotal(-1);
2433         genericObjectPool.setBlockWhenExhausted(false);
2434         final String obj = genericObjectPool.borrowObject();
2435         assertEquals(getNthObject(0), obj);
2436         genericObjectPool.returnObject(obj);
2437     }
2438 
2439     /**
2440      * Verifies that concurrent threads never "share" instances
2441      */
2442     @Test
2443     void testNoInstanceOverlap() {
2444         final int maxTotal = 5;
2445         final int numThreads = 100;
2446         final int delay = 1;
2447         final int iterations = 1000;
2448         final AtomicIntegerFactory factory = new AtomicIntegerFactory();
2449         try (GenericObjectPool<AtomicInteger> pool = new GenericObjectPool<>(factory)) {
2450             pool.setMaxTotal(maxTotal);
2451             pool.setMaxIdle(maxTotal);
2452             pool.setTestOnBorrow(true);
2453             pool.setBlockWhenExhausted(true);
2454             pool.setMaxWaitMillis(-1);
2455             runTestThreads(numThreads, iterations, delay, pool);
2456             assertEquals(0, pool.getDestroyedByBorrowValidationCount());
2457         }
2458     }
2459 
2460     /**
2461      * POOL-376
2462      */
2463     @Test
2464     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2465     void testNoInvalidateNPE() throws Exception {
2466         genericObjectPool.setMaxTotal(1);
2467         genericObjectPool.setTestOnCreate(true);
2468         genericObjectPool.setMaxWaitMillis(-1);
2469         final String obj = genericObjectPool.borrowObject();
2470         // Make validation fail - this will cause create() to return null
2471         simpleFactory.setValid(false);
2472         // Create a take waiter
2473         final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200);
2474         wtt.start();
2475         // Give wtt time to start
2476         Thread.sleep(200);
2477         genericObjectPool.invalidateObject(obj);
2478         // Now allow create to succeed so waiter can be served
2479         simpleFactory.setValid(true);
2480     }
2481 
2482     /**
2483      * Verify that when a factory returns a null object, pool methods throw NPE.
2484      *
2485      * @throws InterruptedException
2486      */
2487     @Test
2488     @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
2489     void testNPEOnFactoryNull() throws Exception {
2490         // Override default to always return null from makeObject
2491         final DisconnectingWaiterFactory<String> factory = new DisconnectingWaiterFactory<>(Suppliers.nul(),
2492                 DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_LIFECYCLE_ACTION, DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_VALIDATION_ACTION);
2493         try (GenericObjectPool<Waiter> pool = new GenericObjectPool<>(factory)) {
2494             pool.setTestOnBorrow(true);
2495             pool.setMaxTotal(-1);
2496             pool.setMinIdle(1);
2497             // Disconnect the factory - will always return null in this state
2498             factory.disconnect();
2499             assertThrows(NullPointerException.class, pool::borrowObject);
2500             assertThrows(NullPointerException.class, pool::addObject);
2501             assertThrows(NullPointerException.class, pool::ensureMinIdle);
2502         }
2503     }
2504 
2505     /*
2506      * Test for POOL-419.
2507      * https://issues.apache.org/jira/browse/POOL-419
2508      */
2509     @Test
2510     void testPool419() throws Exception {
2511 
2512         final ExecutorService executor = Executors.newFixedThreadPool(100);
2513 
2514         final GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
2515 
2516         final int maxConnections = 10000;
2517 
2518         config.setMaxTotal(maxConnections);
2519         config.setMaxIdle(maxConnections);
2520         config.setMinIdle(1);
2521 
2522         final BasePooledObjectFactory<Object> pof = createPooledObjectFactory();
2523 
2524         try (ObjectPool<Object> connectionPool = new GenericObjectPool<>(pof, config)) {
2525             assertNotNull(connectionPool);
2526 
2527             final CountDownLatch startLatch = new CountDownLatch(1);
2528 
2529             final List<Object> poolObjects = new ArrayList<>();
2530 
2531             final List<FutureTask<Boolean>> tasks = new ArrayList<>();
2532 
2533             for (int i = 0; i < maxConnections; i++) {
2534                 poolObjects.add(connectionPool.borrowObject());
2535             }
2536 
2537             for (final Object poolObject : poolObjects) {
2538 
2539                 tasks.add(new FutureTask<>(() -> {
2540                     startLatch.await();
2541                     connectionPool.invalidateObject(poolObject);
2542                     return true;
2543                 }));
2544 
2545                 tasks.add(new FutureTask<>(() -> {
2546                     startLatch.await();
2547                     connectionPool.returnObject(poolObject);
2548                     return true;
2549                 }));
2550             }
2551 
2552             tasks.forEach(executor::submit);
2553 
2554             startLatch.countDown(); // Start all tasks simultaneously
2555 
2556             executor.shutdown();
2557             assertTrue(executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS));
2558 
2559             assertEquals(0, connectionPool.getNumActive(), "getNumActive() must not return a negative value");
2560 
2561         }
2562     }
2563 
2564     @Test
2565     void testPreparePool() throws Exception {
2566         genericObjectPool.setMinIdle(1);
2567         genericObjectPool.setMaxTotal(1);
2568         genericObjectPool.preparePool();
2569         assertEquals(1, genericObjectPool.getNumIdle());
2570         final String obj = genericObjectPool.borrowObject();
2571         genericObjectPool.preparePool();
2572         assertEquals(0, genericObjectPool.getNumIdle());
2573         genericObjectPool.setMinIdle(0);
2574         genericObjectPool.returnObject(obj);
2575         genericObjectPool.preparePool();
2576         assertEquals(1, genericObjectPool.getNumIdle());
2577     }
2578 
2579     /**
2580      * Verify that when thread A borrows two objects from a pool with maxTotal=2,
2581      * then two more threads try to borrow, returning one object and invalidating
2582      * the other will serve both of the waiting threads.
2583      */
2584     @Test
2585     @Timeout(value = 10_000, unit = TimeUnit.MILLISECONDS)
2586     void testReturnAndInvalidateServesWaiters() throws Exception {
2587         genericObjectPool.setMaxTotal(2);
2588         genericObjectPool.setBlockWhenExhausted(true);
2589         genericObjectPool.setMaxWait(Duration.ofMillis(100));
2590 
2591         // Thread A borrows two objects
2592         final String obj1 = genericObjectPool.borrowObject();
2593         final String obj2 = genericObjectPool.borrowObject();
2594 
2595         // Track successful borrows
2596         final AtomicInteger successfulBorrows = new AtomicInteger(0);
2597 
2598         // Create two borrowing threads that will block
2599         final Thread borrower1 = new Thread(() -> {
2600             try {
2601                 genericObjectPool.borrowObject();
2602                 successfulBorrows.incrementAndGet();
2603             } catch (final Exception e) {
2604                 // Borrow failed
2605             }
2606         });
2607 
2608         final Thread borrower2 = new Thread(() -> {
2609             try {
2610                 genericObjectPool.borrowObject();
2611                 successfulBorrows.incrementAndGet();
2612             } catch (final Exception e) {
2613                 // Borrow failed
2614             }
2615         });
2616 
2617         // Start the borrowing threads - they will block
2618         borrower1.start();
2619         borrower2.start();
2620         Thread.sleep(50); // Give threads time to start and block
2621 
2622         // Thread A returns one object and invalidates the other with no delay
2623         genericObjectPool.invalidateObject(obj1);
2624         genericObjectPool.invalidateObject(obj2);
2625 
2626         // Wait for threads to complete
2627         borrower1.join();
2628         borrower2.join();
2629 
2630         // Both threads should have been served
2631         assertEquals(2, successfulBorrows.get(),
2632             "Both waiting threads should have been served, but only " + successfulBorrows.get() + " were served");
2633     }
2634 
2635     @Test /* maxWaitMillis x2 + padding */
2636     @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
2637     void testReturnBorrowObjectWithingMaxWaitDuration() throws Exception {
2638         final Duration maxWaitDuration = Duration.ofMillis(500);
2639         try (GenericObjectPool<String> createSlowObjectFactoryPool = new GenericObjectPool<>(createSlowObjectFactory(Duration.ofSeconds(60)))) {
2640             createSlowObjectFactoryPool.setMaxTotal(1);
2641             createSlowObjectFactoryPool.setMaxWait(maxWaitDuration);
2642             // thread1 tries creating a slow object to make pool full.
2643             final WaitingTestThread thread1 = new WaitingTestThread(createSlowObjectFactoryPool, 0);
2644             thread1.start();
2645             // Wait for thread1's reaching to create().
2646             Thread.sleep(100);
2647             // another one tries borrowObject. It should return within maxWaitMillis.
2648             assertThrows(NoSuchElementException.class, () -> createSlowObjectFactoryPool.borrowObject(maxWaitDuration),
2649                     "borrowObject must fail due to timeout by maxWait");
2650             assertTrue(thread1.isAlive());
2651         }
2652     }
2653 
2654     @Test /* maxWaitMillis x2 + padding */
2655     @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
2656     void testReturnBorrowObjectWithingMaxWaitMillis() throws Exception {
2657         final long maxWaitMillis = 500;
2658         try (GenericObjectPool<String> createSlowObjectFactoryPool = new GenericObjectPool<>(createSlowObjectFactory(Duration.ofSeconds(60)))) {
2659             createSlowObjectFactoryPool.setMaxTotal(1);
2660             createSlowObjectFactoryPool.setMaxWaitMillis(maxWaitMillis);
2661             // thread1 tries creating a slow object to make pool full.
2662             final WaitingTestThread thread1 = new WaitingTestThread(createSlowObjectFactoryPool, 0);
2663             thread1.start();
2664             // Wait for thread1's reaching to create().
2665             Thread.sleep(100);
2666             // another one tries borrowObject. It should return within maxWaitMillis.
2667             assertThrows(NoSuchElementException.class, () -> createSlowObjectFactoryPool.borrowObject(maxWaitMillis),
2668                     "borrowObject must fail due to timeout by maxWait");
2669             assertTrue(thread1.isAlive());
2670         }
2671     }
2672 
2673     /**
2674      * This is the test case for POOL-263. It is disabled since it will always pass without artificial delay being injected into GOP.returnObject() and a way to
2675      * this hasn't currently been found that doesn't involve polluting the GOP implementation. The artificial delay needs to be inserted just before the final
2676      * call to isLifo() in the returnObject() method.
2677      */
2678     // @Test
2679     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2680     void testReturnObject() throws Exception {
2681         genericObjectPool.setMaxTotal(1);
2682         genericObjectPool.setMaxIdle(-1);
2683         final String active = genericObjectPool.borrowObject();
2684         assertEquals(1, genericObjectPool.getNumActive());
2685         assertEquals(0, genericObjectPool.getNumIdle());
2686         final Thread t = new Thread(() -> genericObjectPool.close());
2687         t.start();
2688         genericObjectPool.returnObject(active);
2689         // Wait for the close() thread to complete
2690         while (t.isAlive()) {
2691             Thread.sleep(50);
2692         }
2693         assertEquals(0, genericObjectPool.getNumIdle());
2694     }
2695 
2696     @Test
2697     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2698     void testSetConfig() throws Exception {
2699         final GenericObjectPoolConfig<String> expected = new GenericObjectPoolConfig<>();
2700         assertConfiguration(expected, genericObjectPool);
2701         expected.setMaxTotal(2);
2702         expected.setMaxIdle(3);
2703         expected.setMaxWait(Duration.ofMillis(5));
2704         expected.setMinEvictableIdleDuration(Duration.ofMillis(7L));
2705         expected.setNumTestsPerEvictionRun(9);
2706         expected.setTestOnCreate(true);
2707         expected.setTestOnBorrow(true);
2708         expected.setTestOnReturn(true);
2709         expected.setTestWhileIdle(true);
2710         expected.setTimeBetweenEvictionRuns(Duration.ofMillis(11L));
2711         expected.setBlockWhenExhausted(false);
2712         genericObjectPool.setConfig(expected);
2713         assertConfiguration(expected, genericObjectPool);
2714     }
2715 
2716     @Test
2717     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2718     void testSettersAndGetters() throws Exception {
2719         {
2720             // The object receives an Exception during its creation to prevent
2721             // memory leaks. See BaseGenericObjectPool constructor for more details.
2722             assertNotEquals("", genericObjectPool.getCreationStackTrace());
2723         }
2724         {
2725             assertEquals(0, genericObjectPool.getBorrowedCount());
2726         }
2727         {
2728             assertEquals(0, genericObjectPool.getReturnedCount());
2729         }
2730         {
2731             assertEquals(0, genericObjectPool.getCreatedCount());
2732         }
2733         {
2734             assertEquals(0, genericObjectPool.getDestroyedCount());
2735         }
2736         {
2737             assertEquals(0, genericObjectPool.getDestroyedByEvictorCount());
2738         }
2739         {
2740             assertEquals(0, genericObjectPool.getDestroyedByBorrowValidationCount());
2741         }
2742         {
2743             assertEquals(0, genericObjectPool.getMeanActiveTimeMillis());
2744         }
2745         {
2746             assertEquals(0, genericObjectPool.getMeanIdleTimeMillis());
2747         }
2748         {
2749             assertEquals(0, genericObjectPool.getMeanBorrowWaitTimeMillis());
2750         }
2751         {
2752             assertEquals(0, genericObjectPool.getMaxBorrowWaitTimeMillis());
2753         }
2754         {
2755             assertEquals(0, genericObjectPool.getNumIdle());
2756         }
2757         {
2758             genericObjectPool.setMaxTotal(123);
2759             assertEquals(123, genericObjectPool.getMaxTotal());
2760         }
2761         {
2762             genericObjectPool.setMaxIdle(12);
2763             assertEquals(12, genericObjectPool.getMaxIdle());
2764         }
2765         {
2766             genericObjectPool.setMaxWaitMillis(1234L);
2767             assertEquals(1234L, genericObjectPool.getMaxWaitMillis());
2768         }
2769         {
2770             genericObjectPool.setMinEvictableIdleTimeMillis(12345L);
2771             assertEquals(12345L, genericObjectPool.getMinEvictableIdleDuration().toMillis());
2772             assertEquals(12345L, genericObjectPool.getMinEvictableIdleTimeMillis());
2773             assertEquals(12345L, genericObjectPool.getMinEvictableIdleTime().toMillis());
2774         }
2775         {
2776             genericObjectPool.setNumTestsPerEvictionRun(11);
2777             assertEquals(11, genericObjectPool.getNumTestsPerEvictionRun());
2778         }
2779         {
2780             genericObjectPool.setTestOnBorrow(true);
2781             assertTrue(genericObjectPool.getTestOnBorrow());
2782             genericObjectPool.setTestOnBorrow(false);
2783             assertFalse(genericObjectPool.getTestOnBorrow());
2784         }
2785         {
2786             genericObjectPool.setTestOnReturn(true);
2787             assertTrue(genericObjectPool.getTestOnReturn());
2788             genericObjectPool.setTestOnReturn(false);
2789             assertFalse(genericObjectPool.getTestOnReturn());
2790         }
2791         {
2792             genericObjectPool.setTestWhileIdle(true);
2793             assertTrue(genericObjectPool.getTestWhileIdle());
2794             genericObjectPool.setTestWhileIdle(false);
2795             assertFalse(genericObjectPool.getTestWhileIdle());
2796         }
2797         {
2798             genericObjectPool.setTimeBetweenEvictionRunsMillis(11235L);
2799             assertEquals(11235L, genericObjectPool.getDurationBetweenEvictionRuns().toMillis());
2800             assertEquals(11235L, genericObjectPool.getTimeBetweenEvictionRunsMillis());
2801             assertEquals(11235L, genericObjectPool.getTimeBetweenEvictionRuns().toMillis());
2802         }
2803         {
2804             genericObjectPool.setSoftMinEvictableIdleTimeMillis(12135L);
2805             assertEquals(12135L, genericObjectPool.getSoftMinEvictableIdleDuration().toMillis());
2806             assertEquals(12135L, genericObjectPool.getSoftMinEvictableIdleTimeMillis());
2807             assertEquals(12135L, genericObjectPool.getSoftMinEvictableIdleTime().toMillis());
2808         }
2809         {
2810             genericObjectPool.setBlockWhenExhausted(true);
2811             assertTrue(genericObjectPool.getBlockWhenExhausted());
2812             genericObjectPool.setBlockWhenExhausted(false);
2813             assertFalse(genericObjectPool.getBlockWhenExhausted());
2814         }
2815     }
2816 
2817     @Test
2818     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2819     void testStartAndStopEvictor() throws Exception {
2820         // set up pool without evictor
2821         genericObjectPool.setMaxIdle(6);
2822         genericObjectPool.setMaxTotal(6);
2823         genericObjectPool.setNumTestsPerEvictionRun(6);
2824         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
2825         for (int j = 0; j < 2; j++) {
2826             // populate the pool
2827             {
2828                 final String[] active = new String[6];
2829                 for (int i = 0; i < 6; i++) {
2830                     active[i] = genericObjectPool.borrowObject();
2831                 }
2832                 for (int i = 0; i < 6; i++) {
2833                     genericObjectPool.returnObject(active[i]);
2834                 }
2835             }
2836             // note that it stays populated
2837             assertEquals(6, genericObjectPool.getNumIdle(), "Should have 6 idle");
2838             // start the evictor
2839             genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(50));
2840             // wait a second (well, .2 seconds)
2841             Waiter.sleepQuietly(200L);
2842             // assert that the evictor has cleared out the pool
2843             assertEquals(0, genericObjectPool.getNumIdle(), "Should have 0 idle");
2844             // stop the evictor
2845             genericObjectPool.startEvictor(Duration.ZERO);
2846         }
2847     }
2848 
2849     @Test
2850     void testSwallowedExceptionListener() {
2851         genericObjectPool.setSwallowedExceptionListener(null); // must simply return
2852         final List<Exception> swallowedExceptions = new ArrayList<>();
2853         /*
2854          * A simple listener, that will throw a OOM on 3rd exception.
2855          */
2856         final SwallowedExceptionListener listener = e -> {
2857             if (swallowedExceptions.size() == 2) {
2858                 throw new OutOfMemoryError();
2859             }
2860             swallowedExceptions.add(e);
2861         };
2862         genericObjectPool.setSwallowedExceptionListener(listener);
2863         final Exception e1 = new Exception();
2864         final Exception e2 = new ArrayIndexOutOfBoundsException();
2865         genericObjectPool.swallowException(e1);
2866         genericObjectPool.swallowException(e2);
2867         assertThrows(OutOfMemoryError.class, () -> genericObjectPool.swallowException(e1));
2868         assertEquals(2, swallowedExceptions.size());
2869     }
2870 
2871     @Test
2872     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2873     void testThreaded1() throws Exception {
2874         genericObjectPool.setMaxTotal(15);
2875         genericObjectPool.setMaxIdle(15);
2876         genericObjectPool.setMaxWaitMillis(1000L);
2877         runTestThreads(20, 100, 50, genericObjectPool);
2878     }
2879 
2880     @Test
2881     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2882     void testTimeoutNoLeak() throws Exception {
2883         genericObjectPool.setMaxTotal(2);
2884         genericObjectPool.setMaxWaitMillis(10);
2885         genericObjectPool.setBlockWhenExhausted(true);
2886         final String obj = genericObjectPool.borrowObject();
2887         final String obj2 = genericObjectPool.borrowObject();
2888         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
2889         genericObjectPool.returnObject(obj2);
2890         genericObjectPool.returnObject(obj);
2891         genericObjectPool.borrowObject();
2892         genericObjectPool.borrowObject();
2893     }
2894 
2895     /**
2896      * Tests POOL-361
2897      */
2898     @Test
2899     void testValidateOnCreate() throws Exception {
2900         genericObjectPool.setTestOnCreate(true);
2901         genericObjectPool.addObject();
2902         assertEquals(1, simpleFactory.validateCounter);
2903     }
2904 
2905     /**
2906      * Tests POOL-361
2907      */
2908     @Test
2909     void testValidateOnCreateFailure() throws Exception {
2910         genericObjectPool.setTestOnCreate(true);
2911         genericObjectPool.setTestOnBorrow(false);
2912         genericObjectPool.setMaxTotal(2);
2913         simpleFactory.setValid(false);
2914         // Make sure failed validations do not leak capacity
2915         genericObjectPool.addObject();
2916         genericObjectPool.addObject();
2917         assertEquals(0, genericObjectPool.getNumIdle());
2918         assertEquals(0, genericObjectPool.getNumActive());
2919         simpleFactory.setValid(true);
2920         final String obj = genericObjectPool.borrowObject();
2921         assertNotNull(obj);
2922         genericObjectPool.addObject();
2923         // Should have one idle, one out now
2924         assertEquals(1, genericObjectPool.getNumIdle());
2925         assertEquals(1, genericObjectPool.getNumActive());
2926     }
2927 
2928     /**
2929      * Verify that threads waiting on a depleted pool get served when a returning object fails validation.
2930      *
2931      * JIRA: POOL-240
2932      *
2933      * @throws Exception May occur in some failure modes
2934      */
2935     @Test
2936     void testValidationFailureOnReturnFreesCapacity() throws Exception {
2937         final SimpleFactory factory = new SimpleFactory();
2938         factory.setValid(false); // Validate will always fail
2939         factory.setValidationEnabled(true);
2940         try (GenericObjectPool<String> pool = new GenericObjectPool<>(factory)) {
2941             pool.setMaxTotal(2);
2942             pool.setMaxWaitMillis(1500);
2943             pool.setTestOnReturn(true);
2944             pool.setTestOnBorrow(false);
2945             // Borrow an instance and hold if for 5 seconds
2946             final WaitingTestThread thread1 = new WaitingTestThread(pool, 5000);
2947             thread1.start();
2948             // Borrow another instance and return it after 500 ms (validation will fail)
2949             final WaitingTestThread thread2 = new WaitingTestThread(pool, 500);
2950             thread2.start();
2951             Thread.sleep(50);
2952             // Try to borrow an object
2953             final String obj = pool.borrowObject();
2954             pool.returnObject(obj);
2955         }
2956     }
2957 
2958 
2959     // POOL-276
2960     @Test
2961     void testValidationOnCreateOnly() throws Exception {
2962         genericObjectPool.setMaxTotal(1);
2963         genericObjectPool.setTestOnCreate(true);
2964         genericObjectPool.setTestOnBorrow(false);
2965         genericObjectPool.setTestOnReturn(false);
2966         genericObjectPool.setTestWhileIdle(false);
2967         final String o1 = genericObjectPool.borrowObject();
2968         assertEquals("0", o1);
2969         final Timer t = new Timer();
2970         t.schedule(new TimerTask() {
2971             @Override
2972             public void run() {
2973                 genericObjectPool.returnObject(o1);
2974             }
2975         }, 3000);
2976         final String o2 = genericObjectPool.borrowObject();
2977         assertEquals("0", o2);
2978         assertEquals(1, simpleFactory.validateCounter);
2979     }
2980 
2981     @Test
2982     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2983     void testWhenExhaustedBlock() throws Exception {
2984         genericObjectPool.setMaxTotal(1);
2985         genericObjectPool.setBlockWhenExhausted(true);
2986         genericObjectPool.setMaxWaitMillis(10L);
2987         final String obj1 = genericObjectPool.borrowObject();
2988         assertNotNull(obj1);
2989         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
2990         genericObjectPool.returnObject(obj1);
2991         genericObjectPool.close();
2992     }
2993 
2994     /**
2995      * POOL-189
2996      *
2997      * @throws Exception May occur in some failure modes
2998      */
2999     @Test
3000     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
3001     void testWhenExhaustedBlockClosePool() throws Exception {
3002         genericObjectPool.setMaxTotal(1);
3003         genericObjectPool.setBlockWhenExhausted(true);
3004         genericObjectPool.setMaxWaitMillis(-1);
3005         final Object obj1 = genericObjectPool.borrowObject();
3006         // Make sure an object was obtained
3007         assertNotNull(obj1);
3008         // Create a separate thread to try and borrow another object
3009         final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200);
3010         wtt.start();
3011         // Give wtt time to start
3012         Thread.sleep(200);
3013         // close the pool (Bug POOL-189)
3014         genericObjectPool.close();
3015         // Give interrupt time to take effect
3016         Thread.sleep(200);
3017         // Check thread was interrupted
3018         assertTrue(wtt.thrown instanceof InterruptedException);
3019     }
3020 
3021     @Test
3022     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
3023     void testWhenExhaustedBlockInterrupt() throws Exception {
3024         genericObjectPool.setMaxTotal(1);
3025         genericObjectPool.setBlockWhenExhausted(true);
3026         genericObjectPool.setMaxWaitMillis(-1);
3027         final String obj1 = genericObjectPool.borrowObject();
3028         // Make sure on object was obtained
3029         assertNotNull(obj1);
3030         // Create a separate thread to try and borrow another object
3031         final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200000);
3032         wtt.start();
3033         // Give wtt time to start
3034         Thread.sleep(200);
3035         wtt.interrupt();
3036         // Give interrupt time to take effect
3037         Thread.sleep(200);
3038         // Check thread was interrupted
3039         assertTrue(wtt.thrown instanceof InterruptedException);
3040         // Return object to the pool
3041         genericObjectPool.returnObject(obj1);
3042         // Bug POOL-162 - check there is now an object in the pool
3043         genericObjectPool.setMaxWaitMillis(10L);
3044         String obj2 = null;
3045         try {
3046             obj2 = genericObjectPool.borrowObject();
3047             assertNotNull(obj2);
3048         } catch (final NoSuchElementException e) {
3049             // Not expected
3050             fail("NoSuchElementException not expected");
3051         }
3052         genericObjectPool.returnObject(obj2);
3053         genericObjectPool.close();
3054     }
3055 
3056     @Test
3057     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
3058     void testWhenExhaustedFail() throws Exception {
3059         genericObjectPool.setMaxTotal(1);
3060         genericObjectPool.setBlockWhenExhausted(false);
3061         final String obj1 = genericObjectPool.borrowObject();
3062         assertNotNull(obj1);
3063         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
3064         genericObjectPool.returnObject(obj1);
3065         assertEquals(1, genericObjectPool.getNumIdle());
3066         genericObjectPool.close();
3067     }
3068 }