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     private List<Runnable> getRunnables(final int numThreads,
847                                         final CountDownLatch startLatch,
848                                         final CountDownLatch doneLatch) {
849         final List<Runnable> tasks = new ArrayList<>();
850 
851         for (int i = 0; i < numThreads; i++) {
852             tasks.add(() -> {
853                 try {
854                     startLatch.await(); // Wait for all threads to be ready
855                     genericObjectPool.addObject();
856                 } catch (final Exception e) {
857                     Thread.currentThread().interrupt(); // Restore interrupt status
858                 } finally {
859                     doneLatch.countDown();
860                 }
861             });
862         }
863         return tasks;
864     }
865 
866     @Override
867     protected boolean isFifo() {
868         return false;
869     }
870 
871     @Override
872     protected boolean isLifo() {
873         return true;
874     }
875 
876     @Override
877     protected ObjectPool<String> makeEmptyPool(final int minCap) {
878         final GenericObjectPool<String> mtPool = new GenericObjectPool<>(new SimpleFactory());
879         mtPool.setMaxTotal(minCap);
880         mtPool.setMaxIdle(minCap);
881         return mtPool;
882     }
883 
884     @Override
885     protected <E extends Exception> ObjectPool<Object> makeEmptyPool(final PooledObjectFactory<Object> fac) {
886         return new GenericObjectPool<>(fac);
887     }
888 
889     /*
890      * Kicks off numThreads test threads, each of which will go through iterations borrow-return cycles with random delay times <= delay in between.
891      */
892     private <T> void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericObjectPool<T> testPool) {
893         final TestThread<T>[] threads = new TestThread[numThreads];
894         for (int i = 0; i < numThreads; i++) {
895             threads[i] = new TestThread<>(testPool, iterations, delay);
896             final Thread t = new Thread(threads[i]);
897             t.start();
898         }
899         for (int i = 0; i < numThreads; i++) {
900             while (!threads[i].complete()) {
901                 Waiter.sleepQuietly(500L);
902             }
903             if (threads[i].failed()) {
904                 fail("Thread " + i + " failed: " + threads[i].error.toString());
905             }
906         }
907     }
908 
909     @BeforeEach
910     public void setUp() {
911         simpleFactory = new SimpleFactory();
912         genericObjectPool = new GenericObjectPool<>(simpleFactory);
913     }
914 
915     @AfterEach
916     public void tearDown() throws Exception {
917         final ObjectName jmxName = genericObjectPool.getJmxName();
918         final String poolName = Objects.toString(jmxName, null);
919         genericObjectPool.clear();
920         genericObjectPool.close();
921         genericObjectPool = null;
922         simpleFactory = null;
923         final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
924         final Set<ObjectName> result = mbs.queryNames(new ObjectName("org.apache.commoms.pool2:type=GenericObjectPool,*"), null);
925         // There should be no registered pools at this point
926         final int registeredPoolCount = result.size();
927         final StringBuilder msg = new StringBuilder("Current pool is: ");
928         msg.append(poolName);
929         msg.append("  Still open pools are: ");
930         for (final ObjectName name : result) {
931             // Clean these up ready for the next test
932             msg.append(name.toString());
933             msg.append(" created via\n");
934             msg.append(mbs.getAttribute(name, "CreationStackTrace"));
935             msg.append('\n');
936             mbs.unregisterMBean(name);
937         }
938         assertEquals(0, registeredPoolCount, msg.toString());
939         // Make sure that EvictionTimer executor is shut down.
940         Thread.yield();
941         if (EvictionTimer.getExecutor() != null) {
942             Thread.sleep(1000);
943         }
944         assertNull(EvictionTimer.getExecutor(), "EvictionTimer.getExecutor()");
945     }
946 
947     /**
948      * Check that a pool that starts an evictor, but is never closed does not leave EvictionTimer executor running. Confirmation check is in
949      * {@link #tearDown()}.
950      *
951      * @throws TestException        Custom exception
952      * @throws InterruptedException if any thread has interrupted the current thread. The <em>interrupted status</em> of the current thread is cleared when this
953      *                              exception is thrown.
954      */
955     @SuppressWarnings("deprecation")
956     @Test
957     void testAbandonedPool() throws Exception {
958         final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
959         config.setJmxEnabled(false);
960 
961         // Need to create at least 2 pools to test EvictorTimer Reaper for ConcurrentModificationException
962         // Note: If the test fails, the ConcurrentModificationException isn't visible without modifications to the
963         //       Reaper class.
964         GenericObjectPool<String> abandoned1 = new GenericObjectPool<>(simpleFactory, config);
965         abandoned1.setDurationBetweenEvictionRuns(Duration.ofMillis(100)); // Starts evictor
966         GenericObjectPool<String> abandoned2 = new GenericObjectPool<>(simpleFactory, config);
967         abandoned2.setDurationBetweenEvictionRuns(Duration.ofMillis(100)); // Starts evictor
968 
969         // This is ugly, but forces GC to hit the pools
970         final WeakReference<GenericObjectPool<String>> ref1 = new WeakReference<>(abandoned1);
971         abandoned1 = null;
972         while (ref1.get() != null) {
973             System.gc();
974             Thread.sleep(100);
975         }
976         final WeakReference<GenericObjectPool<String>> ref2 = new WeakReference<>(abandoned2);
977         abandoned2 = null;
978         while (ref2.get() != null) {
979             System.gc();
980             Thread.sleep(100);
981         }
982     }
983 
984     @Test
985     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
986     void testAddObject() throws Exception {
987         assertEquals(0, genericObjectPool.getNumIdle(), "should be zero idle");
988         genericObjectPool.addObject();
989         assertEquals(1, genericObjectPool.getNumIdle(), "should be one idle");
990         assertEquals(0, genericObjectPool.getNumActive(), "should be zero active");
991         final String obj = genericObjectPool.borrowObject();
992         assertEquals(0, genericObjectPool.getNumIdle(), "should be zero idle");
993         assertEquals(1, genericObjectPool.getNumActive(), "should be one active");
994         genericObjectPool.returnObject(obj);
995         assertEquals(1, genericObjectPool.getNumIdle(), "should be one idle");
996         assertEquals(0, genericObjectPool.getNumActive(), "should be zero active");
997     }
998 
999     @Test
1000     void testAddObjectCanAddToMaxIdle() throws Exception {
1001         genericObjectPool.setMaxTotal(5);
1002         genericObjectPool.borrowObject();
1003         genericObjectPool.borrowObject();
1004         genericObjectPool.setMaxIdle(3);
1005         for (int i = 0; i < 3; i++) {
1006             genericObjectPool.addObject();
1007         }
1008         assertEquals(3, genericObjectPool.getNumIdle());
1009     }
1010 
1011     @Disabled("WIP for https://issues.apache.org/jira/browse/POOL-413")
1012     @Test
1013     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1014     void testAddObjectConcurrentCallsRespectsMaxIdle() throws Exception {
1015         final int maxIdleLimit = 5;
1016         final int numThreads = 10;
1017         genericObjectPool.setMaxIdle(maxIdleLimit);
1018 
1019         final CountDownLatch startLatch = new CountDownLatch(1);
1020         final CountDownLatch doneLatch = new CountDownLatch(numThreads);
1021 
1022         final List<Runnable> tasks = getRunnables(numThreads, startLatch, doneLatch);
1023 
1024         final ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
1025         tasks.forEach(executorService::submit);
1026         try {
1027             startLatch.countDown(); // Start all threads simultaneously
1028             doneLatch.await(); // Wait for all threads to complete
1029         } finally {
1030             executorService.shutdown();
1031             assertTrue(executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS));
1032         }
1033 
1034         assertTrue(genericObjectPool.getNumIdle() <= maxIdleLimit,
1035             "Concurrent addObject() calls should not exceed maxIdle limit of " + maxIdleLimit +
1036                 ", but found " + genericObjectPool.getNumIdle() + " idle objects");
1037     }
1038 
1039     @Test
1040     @Timeout(value = 400, unit = TimeUnit.MILLISECONDS)
1041     void testAddObjectFastReturn() throws Exception {
1042         final SimpleFactory simpleFactory = new SimpleFactory();
1043         simpleFactory.makeLatency = 500;
1044         try (GenericObjectPool<String> pool = new GenericObjectPool<>(simpleFactory)) {
1045             pool.setMaxTotal(1);
1046             pool.setBlockWhenExhausted(true);
1047             pool.setMaxWait(Duration.ofMillis(1000));
1048             // Start a test thread. The thread will trigger a create, which will take 500 ms to complete
1049             final TestThread<String> thread = new TestThread<>(pool);
1050             final Thread t = new Thread(thread);
1051             t.start();
1052             Thread.sleep(50); // Wait for the thread to start
1053             pool.addObject(); // Should return immediately
1054         }
1055     }
1056 
1057     @Test
1058     void testAddObjectNegativeMaxIdle() throws Exception {
1059         genericObjectPool.setMaxIdle(-1);
1060         genericObjectPool.addObject();
1061         assertEquals(1, genericObjectPool.getNumIdle());
1062     }
1063 
1064     @Test
1065     void testAddObjectNegativeMaxTotal() throws Exception {
1066         genericObjectPool.setMaxTotal(-1);
1067         genericObjectPool.addObject();
1068         assertEquals(1, genericObjectPool.getNumIdle());
1069     }
1070 
1071     @Test
1072     void testAddObjectRespectsMaxIdle() throws Exception {
1073         genericObjectPool.setMaxIdle(1);
1074         genericObjectPool.addObject();
1075         genericObjectPool.addObject(); // should be no-op
1076         assertEquals(1, genericObjectPool.getNumIdle());
1077     }
1078 
1079     @Test
1080     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1081     void testAddObjectRespectsMaxIdleLimit() throws Exception {
1082         genericObjectPool.setMaxIdle(1);
1083         genericObjectPool.addObject();
1084         genericObjectPool.addObject();
1085         assertEquals(1, genericObjectPool.getNumIdle(), "should be one idle");
1086 
1087         genericObjectPool.setMaxIdle(-1);
1088         genericObjectPool.addObject();
1089         genericObjectPool.addObject();
1090         genericObjectPool.addObject();
1091         assertEquals(4, genericObjectPool.getNumIdle(), "should be four idle");
1092     }
1093 
1094     @Test
1095     void testAddObjectRespectsMaxTotal() throws Exception {
1096         genericObjectPool.setMaxTotal(1);
1097         genericObjectPool.addObject();
1098         genericObjectPool.addObject(); // should be no-op
1099         assertEquals(1, genericObjectPool.getNumIdle());
1100     }
1101 
1102     @Test
1103     void testAddObjectZeroMaxIdle() throws Exception {
1104         genericObjectPool.setMaxIdle(0);
1105         genericObjectPool.addObject();
1106         assertEquals(0, genericObjectPool.getNumIdle());
1107     }
1108 
1109     @Test
1110     void testAppendStats() {
1111         assertFalse(genericObjectPool.getMessageStatistics());
1112         assertEquals("foo", genericObjectPool.appendStats("foo"));
1113         try (GenericObjectPool<?> pool = new GenericObjectPool<>(new SimpleFactory())) {
1114             pool.setMessagesStatistics(true);
1115             assertNotEquals("foo", pool.appendStats("foo"));
1116             pool.setMessagesStatistics(false);
1117             assertEquals("foo", pool.appendStats("foo"));
1118         }
1119     }
1120 
1121     /*
1122      * 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
1123      * this if you see a failure of this test.
1124      */
1125     @SuppressWarnings({ "rawtypes", "unchecked" })
1126     @Test
1127     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1128     void testBorrowObjectFairness() throws Exception {
1129         final int numThreads = 40;
1130         final int maxTotal = 40;
1131         final GenericObjectPoolConfig config = new GenericObjectPoolConfig();
1132         config.setMaxTotal(maxTotal);
1133         config.setMaxIdle(maxTotal);
1134         config.setFairness(true);
1135         config.setLifo(false);
1136         genericObjectPool = new GenericObjectPool(simpleFactory, config);
1137         // Exhaust the pool
1138         final String[] objects = new String[maxTotal];
1139         for (int i = 0; i < maxTotal; i++) {
1140             objects[i] = genericObjectPool.borrowObject();
1141         }
1142         // Start and park threads waiting to borrow objects
1143         final TestThread[] threads = new TestThread[numThreads];
1144         for (int i = 0; i < numThreads; i++) {
1145             threads[i] = new TestThread(genericObjectPool, 1, 0, 2000, false, String.valueOf(i % maxTotal));
1146             final Thread t = new Thread(threads[i]);
1147             t.start();
1148             // Short delay to ensure threads start in correct order
1149             try {
1150                 Thread.sleep(10);
1151             } catch (final InterruptedException e) {
1152                 fail(e.toString());
1153             }
1154         }
1155         // Return objects, other threads should get served in order
1156         for (int i = 0; i < maxTotal; i++) {
1157             genericObjectPool.returnObject(objects[i]);
1158         }
1159         // Wait for threads to finish
1160         for (int i = 0; i < numThreads; i++) {
1161             while (!threads[i].complete()) {
1162                 Waiter.sleepQuietly(500L);
1163             }
1164             if (threads[i].failed()) {
1165                 fail("Thread " + i + " failed: " + threads[i].error.toString());
1166             }
1167         }
1168     }
1169 
1170     @Test
1171     @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
1172     void testBorrowObjectOverrideMaxWaitLarge() throws Exception {
1173         try (GenericObjectPool<String> pool = new GenericObjectPool<>(createSlowObjectFactory(Duration.ofSeconds(60)))) {
1174             pool.setMaxTotal(1);
1175             pool.setMaxWait(Duration.ofMillis(1_000)); // large
1176             pool.setBlockWhenExhausted(false);
1177             // thread1 tries creating a slow object to make pool full.
1178             final WaitingTestThread thread1 = new WaitingTestThread(pool, 0);
1179             thread1.start();
1180             // Wait for thread1's reaching to create().
1181             Thread.sleep(100);
1182             // The test needs to make sure a wait happens in create().
1183             final Duration d = DurationUtils.of(() -> assertThrows(NoSuchElementException.class, () -> pool.borrowObject(Duration.ofMillis(1)),
1184                     "borrowObject must fail quickly due to timeout parameter"));
1185             final long millis = d.toMillis();
1186             final long nanos = d.toNanos();
1187             assertTrue(nanos >= 0, () -> "borrowObject(Duration) argument not respected: " + nanos);
1188             assertTrue(millis >= 0, () -> "borrowObject(Duration) argument not respected: " + millis); // not > 0 to account for spurious waits
1189             assertTrue(millis < 50, () -> "borrowObject(Duration) argument not respected: " + millis);
1190         }
1191     }
1192 
1193     @Test
1194     @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
1195     void testBorrowObjectOverrideMaxWaitSmall() throws Exception {
1196         try (GenericObjectPool<String> pool = new GenericObjectPool<>(createSlowObjectFactory(Duration.ofSeconds(60)))) {
1197             pool.setMaxTotal(1);
1198             pool.setMaxWait(Duration.ofMillis(1)); // small
1199             pool.setBlockWhenExhausted(false);
1200             // thread1 tries creating a slow object to make pool full.
1201             final WaitingTestThread thread1 = new WaitingTestThread(pool, 0);
1202             thread1.start();
1203             // Wait for thread1's reaching to create().
1204             Thread.sleep(100);
1205             // The test needs to make sure a wait happens in create().
1206             final Duration d = DurationUtils.of(() -> assertThrows(NoSuchElementException.class, () -> pool.borrowObject(Duration.ofMillis(500)),
1207                     "borrowObject must fail slowly due to timeout parameter"));
1208             final long millis = d.toMillis();
1209             final long nanos = d.toNanos();
1210             assertTrue(nanos >= 0, () -> "borrowObject(Duration) argument not respected: " + nanos);
1211             assertTrue(millis >= 0, () -> "borrowObject(Duration) argument not respected: " + millis); // not > 0 to account for spurious waits
1212             assertTrue(millis < 600, () -> "borrowObject(Duration) argument not respected: " + millis);
1213             assertTrue(millis > 490, () -> "borrowObject(Duration) argument not respected: " + millis);
1214         }
1215     }
1216 
1217     @Test
1218     void testBorrowTimings() throws Exception {
1219         // Borrow
1220         final String object = genericObjectPool.borrowObject();
1221         final PooledObject<String> po = genericObjectPool.getPooledObject(object);
1222         // In the initial state, all instants are the creation instant: last borrow, last use, last return.
1223         // In the initial state, the active duration is the time between "now" and the creation time.
1224         // In the initial state, the idle duration is the time between "now" and the last return, which is the creation time.
1225         // But... this PO might have already been used in other tests in this class.
1226         final Instant lastBorrowInstant1 = po.getLastBorrowInstant();
1227         final Instant lastReturnInstant1 = po.getLastReturnInstant();
1228         final Instant lastUsedInstant1 = po.getLastUsedInstant();
1229         assertTrue(po.getCreateInstant().compareTo(lastBorrowInstant1) <= 0);
1230         assertTrue(po.getCreateInstant().compareTo(lastReturnInstant1) <= 0);
1231         assertTrue(po.getCreateInstant().compareTo(lastUsedInstant1) <= 0);
1232         // Deprecated
1233         assertTrue(po.getCreateTime() <= lastBorrowInstant1.toEpochMilli());
1234         assertTrue(po.getCreateTime() <= lastReturnInstant1.toEpochMilli());
1235         assertTrue(po.getCreateTime() <= lastUsedInstant1.toEpochMilli());
1236         // Sleep MUST be "long enough" to detect that more than 0 milliseconds have elapsed.
1237         // Need an API in Java 8 to get the clock granularity.
1238         Thread.sleep(200);
1239         assertFalse(po.getActiveDuration().isNegative());
1240         assertFalse(po.getActiveDuration().isZero());
1241         // We use greaterThanOrEqualTo instead of equal because "now" many be different when each argument is evaluated.
1242         assertTrue(Duration.ZERO.compareTo(Duration.ZERO.plusNanos(1)) <= 0); // sanity check
1243         assertTrue(po.getActiveDuration().compareTo(po.getIdleDuration()) <= 0);
1244         // Deprecated
1245         assertTrue(po.getActiveDuration().toMillis() <= po.getActiveTimeMillis());
1246         assertTrue(po.getActiveDuration().compareTo(po.getIdleDuration()) <= 0);
1247         //
1248         // TODO How to compare ID with AD since other tests may have touched the PO?
1249         assertTrue(po.getActiveDuration().compareTo(po.getIdleTime()) <= 0);
1250         assertTrue(po.getActiveDuration().toMillis() <= po.getIdleTimeMillis());
1251         //
1252         assertTrue(po.getCreateInstant().compareTo(po.getLastBorrowInstant()) <= 0);
1253         assertTrue(po.getCreateInstant().compareTo(po.getLastReturnInstant()) <= 0);
1254         assertTrue(po.getCreateInstant().compareTo(po.getLastUsedInstant()) <= 0);
1255         assertTrue(lastBorrowInstant1.compareTo(po.getLastBorrowInstant()) <= 0);
1256         assertTrue(lastReturnInstant1.compareTo(po.getLastReturnInstant()) <= 0);
1257         assertTrue(lastUsedInstant1.compareTo(po.getLastUsedInstant()) <= 0);
1258         genericObjectPool.returnObject(object);
1259         assertFalse(po.getActiveDuration().isNegative());
1260         assertFalse(po.getActiveDuration().isZero());
1261         assertTrue(po.getActiveDuration().toMillis() <= po.getActiveTimeMillis());
1262         assertTrue(po.getActiveDuration().compareTo(po.getActiveTime()) <= 0);
1263         assertTrue(lastBorrowInstant1.compareTo(po.getLastBorrowInstant()) <= 0);
1264         assertTrue(lastReturnInstant1.compareTo(po.getLastReturnInstant()) <= 0);
1265         assertTrue(lastUsedInstant1.compareTo(po.getLastUsedInstant()) <= 0);
1266     }
1267 
1268     /**
1269      * On first borrow, first object fails validation, second object is OK. Subsequent borrows are OK. This was POOL-152.
1270      *
1271      * @throws Exception
1272      */
1273     @Test
1274     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1275     void testBrokenFactoryShouldNotBlockPool() throws Exception {
1276         final int maxTotal = 1;
1277         simpleFactory.setMaxTotal(maxTotal);
1278         genericObjectPool.setMaxTotal(maxTotal);
1279         genericObjectPool.setBlockWhenExhausted(true);
1280         genericObjectPool.setTestOnBorrow(true);
1281         // First borrow object will need to create a new object which will fail
1282         // validation.
1283         String obj = null;
1284         Exception ex = null;
1285         simpleFactory.setValid(false);
1286         try {
1287             obj = genericObjectPool.borrowObject();
1288         } catch (final Exception e) {
1289             ex = e;
1290         }
1291         // Failure expected
1292         assertNotNull(ex);
1293         assertInstanceOf(NoSuchElementException.class, ex);
1294         assertNull(obj);
1295         // Configure factory to create valid objects so subsequent borrows work
1296         simpleFactory.setValid(true);
1297         // Subsequent borrows should be OK
1298         obj = genericObjectPool.borrowObject();
1299         assertNotNull(obj);
1300         genericObjectPool.returnObject(obj);
1301     }
1302 
1303     // POOL-259
1304     @Test
1305     void testClientWaitStats() throws Exception {
1306         final SimpleFactory factory = new SimpleFactory();
1307         // Give makeObject a little latency
1308         factory.setMakeLatency(200);
1309         try (GenericObjectPool<String> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) {
1310             final String s = pool.borrowObject();
1311             // First borrow waits on create, so wait time should be at least 200 ms
1312             // Allow 100ms error in clock times
1313             assertTrue(pool.getMaxBorrowWaitTimeMillis() >= 100);
1314             assertTrue(pool.getMeanBorrowWaitTimeMillis() >= 100);
1315             pool.returnObject(s);
1316             pool.borrowObject();
1317             // Second borrow does not have to wait on create, average should be about 100
1318             assertTrue(pool.getMaxBorrowWaitTimeMillis() > 100);
1319             assertTrue(pool.getMeanBorrowWaitTimeMillis() < 200);
1320             assertTrue(pool.getMeanBorrowWaitTimeMillis() > 20);
1321         }
1322     }
1323 
1324     @Test
1325     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1326     void testCloseMultiplePools1() {
1327         try (GenericObjectPool<String> genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) {
1328             genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
1329             genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
1330         }
1331         genericObjectPool.close();
1332     }
1333 
1334     @Test
1335     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1336     void testCloseMultiplePools2() throws Exception {
1337         try (GenericObjectPool<String> genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) {
1338             // Ensure eviction takes a long time, during which time EvictionTimer.executor's queue is empty
1339             simpleFactory.setDestroyLatency(1000L);
1340             // Ensure there is an object to evict, so that above latency takes effect
1341             genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
1342             genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
1343             genericObjectPool.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND_DURATION);
1344             genericObjectPool2.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND_DURATION);
1345             genericObjectPool.addObject();
1346             genericObjectPool2.addObject();
1347             // Close both pools
1348         }
1349         genericObjectPool.close();
1350     }
1351 
1352     @Test
1353     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1354     void testConcurrentBorrowAndEvict() throws Exception {
1355         genericObjectPool.setMaxTotal(1);
1356         genericObjectPool.addObject();
1357         for (int i = 0; i < 5000; i++) {
1358             final ConcurrentBorrowAndEvictThread one = new ConcurrentBorrowAndEvictThread(true);
1359             final ConcurrentBorrowAndEvictThread two = new ConcurrentBorrowAndEvictThread(false);
1360             one.start();
1361             two.start();
1362             one.join();
1363             two.join();
1364             genericObjectPool.returnObject(one.obj);
1365             /*
1366              * Uncomment this for a progress indication if (i % 10 == 0) { System.out.println(i/10); }
1367              */
1368         }
1369     }
1370 
1371     /**
1372      * POOL-231 - verify that concurrent invalidates of the same object do not corrupt pool destroyCount.
1373      *
1374      * @throws Exception May occur in some failure modes
1375      */
1376     @Test
1377     void testConcurrentInvalidate() throws Exception {
1378         // Get allObjects and idleObjects loaded with some instances
1379         final int nObjects = 1000;
1380         genericObjectPool.setMaxTotal(nObjects);
1381         genericObjectPool.setMaxIdle(nObjects);
1382         final String[] obj = new String[nObjects];
1383         for (int i = 0; i < nObjects; i++) {
1384             obj[i] = genericObjectPool.borrowObject();
1385         }
1386         for (int i = 0; i < nObjects; i++) {
1387             if (i % 2 == 0) {
1388                 genericObjectPool.returnObject(obj[i]);
1389             }
1390         }
1391         final int nThreads = 20;
1392         final int nIterations = 60;
1393         final InvalidateThread[] threads = new InvalidateThread[nThreads];
1394         // Randomly generated list of distinct invalidation targets
1395         final ArrayList<Integer> targets = new ArrayList<>();
1396         final Random random = new Random();
1397         for (int j = 0; j < nIterations; j++) {
1398             // Get a random invalidation target
1399             Integer targ = Integer.valueOf(random.nextInt(nObjects));
1400             while (targets.contains(targ)) {
1401                 targ = Integer.valueOf(random.nextInt(nObjects));
1402             }
1403             targets.add(targ);
1404             // Launch nThreads threads all trying to invalidate the target
1405             for (int i = 0; i < nThreads; i++) {
1406                 threads[i] = new InvalidateThread(genericObjectPool, obj[targ.intValue()]);
1407             }
1408             for (int i = 0; i < nThreads; i++) {
1409                 new Thread(threads[i]).start();
1410             }
1411             boolean done = false;
1412             while (!done) {
1413                 done = true;
1414                 for (int i = 0; i < nThreads; i++) {
1415                     done = done && threads[i].complete();
1416                 }
1417                 Thread.sleep(100);
1418             }
1419         }
1420         assertEquals(nIterations, genericObjectPool.getDestroyedCount());
1421     }
1422 
1423     @Test
1424     void testConstructorNullFactory() {
1425         // add dummy assert (won't be invoked because of IAE) to avoid "unused" warning
1426         assertThrows(IllegalArgumentException.class, () -> new GenericObjectPool<>(null));
1427     }
1428 
1429     @Test
1430     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1431     void testConstructors() {
1432         // Make constructor arguments all different from defaults
1433         final int minIdle = 2;
1434         final Duration maxWaitDuration = Duration.ofMillis(3);
1435         final long maxWaitMillis = maxWaitDuration.toMillis();
1436         final int maxIdle = 4;
1437         final int maxTotal = 5;
1438         final Duration minEvictableIdleDuration = Duration.ofMillis(6);
1439         final long minEvictableIdleMillis = minEvictableIdleDuration.toMillis();
1440         final int numTestsPerEvictionRun = 7;
1441         final boolean testOnBorrow = true;
1442         final boolean testOnReturn = true;
1443         final boolean testWhileIdle = true;
1444         final long timeBetweenEvictionRunsMillis = 8;
1445         final boolean blockWhenExhausted = false;
1446         final boolean lifo = false;
1447         final PooledObjectFactory<Object> dummyFactory = new DummyFactory();
1448         try (GenericObjectPool<Object> dummyPool = new GenericObjectPool<>(dummyFactory)) {
1449             assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_IDLE, dummyPool.getMaxIdle());
1450             assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, dummyPool.getMaxWaitMillis());
1451             assertEquals(GenericObjectPoolConfig.DEFAULT_MIN_IDLE, dummyPool.getMinIdle());
1452             assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL, dummyPool.getMaxTotal());
1453             assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, dummyPool.getMinEvictableIdleTimeMillis());
1454             assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME, dummyPool.getMinEvictableIdleTime());
1455             assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME, dummyPool.getMinEvictableIdleDuration());
1456             assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN, dummyPool.getNumTestsPerEvictionRun());
1457             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW), Boolean.valueOf(dummyPool.getTestOnBorrow()));
1458             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN), Boolean.valueOf(dummyPool.getTestOnReturn()));
1459             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE), Boolean.valueOf(dummyPool.getTestWhileIdle()));
1460             assertEquals(BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS, dummyPool.getDurationBetweenEvictionRuns());
1461             assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, dummyPool.getTimeBetweenEvictionRunsMillis());
1462             assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS, dummyPool.getTimeBetweenEvictionRuns());
1463             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED), Boolean.valueOf(dummyPool.getBlockWhenExhausted()));
1464             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(dummyPool.getLifo()));
1465         }
1466         final GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
1467         config.setLifo(lifo);
1468         config.setMaxIdle(maxIdle);
1469         config.setMinIdle(minIdle);
1470         config.setMaxTotal(maxTotal);
1471         config.setMaxWait(maxWaitDuration);
1472         config.setMinEvictableIdleTimeMillis(minEvictableIdleMillis);
1473         assertEquals(minEvictableIdleMillis, config.getMinEvictableIdleTime().toMillis());
1474         config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
1475         config.setTestOnBorrow(testOnBorrow);
1476         config.setTestOnReturn(testOnReturn);
1477         config.setTestWhileIdle(testWhileIdle);
1478         config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
1479         assertEquals(timeBetweenEvictionRunsMillis, config.getTimeBetweenEvictionRuns().toMillis());
1480         config.setBlockWhenExhausted(blockWhenExhausted);
1481         try (GenericObjectPool<Object> dummyPool = new GenericObjectPool<>(dummyFactory, config)) {
1482             assertEquals(maxIdle, dummyPool.getMaxIdle());
1483             assertEquals(maxWaitDuration, dummyPool.getMaxWaitDuration());
1484             assertEquals(maxWaitMillis, dummyPool.getMaxWaitMillis());
1485             assertEquals(minIdle, dummyPool.getMinIdle());
1486             assertEquals(maxTotal, dummyPool.getMaxTotal());
1487             assertEquals(minEvictableIdleMillis, dummyPool.getMinEvictableIdleTimeMillis());
1488             assertEquals(numTestsPerEvictionRun, dummyPool.getNumTestsPerEvictionRun());
1489             assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(dummyPool.getTestOnBorrow()));
1490             assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(dummyPool.getTestOnReturn()));
1491             assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(dummyPool.getTestWhileIdle()));
1492             assertEquals(timeBetweenEvictionRunsMillis, dummyPool.getTimeBetweenEvictionRunsMillis());
1493             assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(dummyPool.getBlockWhenExhausted()));
1494             assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(dummyPool.getLifo()));
1495         }
1496     }
1497 
1498     @Test
1499     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1500     void testDefaultConfiguration() {
1501         assertConfiguration(new GenericObjectPoolConfig<>(), genericObjectPool);
1502     }
1503 
1504     /**
1505      * Verifies that when a factory's makeObject produces instances that are not discernible by equals, the pool can handle them.
1506      *
1507      * JIRA: POOL-283
1508      */
1509     @Test
1510     void testEqualsIndiscernible() throws Exception {
1511         final HashSetFactory factory = new HashSetFactory();
1512         try (GenericObjectPool<HashSet<String>> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) {
1513             final HashSet<String> s1 = pool.borrowObject();
1514             final HashSet<String> s2 = pool.borrowObject();
1515             pool.returnObject(s1);
1516             pool.returnObject(s2);
1517         }
1518     }
1519 
1520     @Test
1521     void testErrorFactoryDoesNotBlockThreads() throws Exception {
1522         final CreateErrorFactory factory = new CreateErrorFactory();
1523         try (GenericObjectPool<String> createFailFactoryPool = new GenericObjectPool<>(factory)) {
1524             createFailFactoryPool.setMaxTotal(1);
1525             // Try and borrow the first object from the pool
1526             final WaitingTestThread thread1 = new WaitingTestThread(createFailFactoryPool, 0);
1527             thread1.start();
1528             // Wait for thread to reach semaphore
1529             while (!factory.hasQueuedThreads()) {
1530                 Thread.sleep(200);
1531             }
1532             // Try and borrow the second object from the pool
1533             final WaitingTestThread thread2 = new WaitingTestThread(createFailFactoryPool, 0);
1534             thread2.start();
1535             // Pool will not call factory since maximum number of object creations
1536             // are already queued.
1537             // Thread 2 will wait on an object being returned to the pool
1538             // Give thread 2 a chance to reach this state
1539             Thread.sleep(1000);
1540             // Release thread1
1541             factory.release();
1542             // Pre-release thread2
1543             factory.release();
1544             // Both threads should now complete.
1545             boolean threadRunning = true;
1546             int count = 0;
1547             while (threadRunning && count < 15) {
1548                 threadRunning = thread1.isAlive();
1549                 threadRunning = thread2.isAlive();
1550                 Thread.sleep(200);
1551                 count++;
1552             }
1553             assertFalse(thread1.isAlive());
1554             assertFalse(thread2.isAlive());
1555             assertTrue(thread1.thrown instanceof UnknownError);
1556             assertTrue(thread2.thrown instanceof UnknownError);
1557         }
1558     }
1559 
1560     /**
1561      * Tests addObject contention between ensureMinIdle triggered by the Evictor with minIdle &gt; 0 and borrowObject.
1562      *
1563      * @throws Exception May occur in some failure modes
1564      */
1565     @Test
1566     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1567     void testEvictAddObjects() throws Exception {
1568         simpleFactory.setMakeLatency(300);
1569         simpleFactory.setMaxTotal(2);
1570         genericObjectPool.setMaxTotal(2);
1571         genericObjectPool.setMinIdle(1);
1572         genericObjectPool.borrowObject(); // numActive = 1, numIdle = 0
1573         // Create a test thread that will run once and try a borrow after
1574         // 150ms fixed delay
1575         final TestThread<String> borrower = new TestThread<>(genericObjectPool, 1, 150, false);
1576         final Thread borrowerThread = new Thread(borrower);
1577         // Set evictor to run in 100 ms - will create idle instance
1578         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
1579         borrowerThread.start(); // Off to the races
1580         borrowerThread.join();
1581         assertFalse(borrower.failed());
1582     }
1583 
1584     @Test
1585     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1586     void testEvictFIFO() throws Exception {
1587         checkEvict(false);
1588     }
1589 
1590     @Test
1591     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1592     void testEviction() throws Exception {
1593         genericObjectPool.setMaxIdle(500);
1594         genericObjectPool.setMaxTotal(500);
1595         genericObjectPool.setNumTestsPerEvictionRun(100);
1596         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
1597         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
1598         genericObjectPool.setTestWhileIdle(true);
1599         final String[] active = new String[500];
1600         for (int i = 0; i < 500; i++) {
1601             active[i] = genericObjectPool.borrowObject();
1602         }
1603         for (int i = 0; i < 500; i++) {
1604             genericObjectPool.returnObject(active[i]);
1605         }
1606         Waiter.sleepQuietly(1000L);
1607         assertTrue(genericObjectPool.getNumIdle() < 500, "Should be less than 500 idle, found " + genericObjectPool.getNumIdle());
1608         Waiter.sleepQuietly(600L);
1609         assertTrue(genericObjectPool.getNumIdle() < 400, "Should be less than 400 idle, found " + genericObjectPool.getNumIdle());
1610         Waiter.sleepQuietly(600L);
1611         assertTrue(genericObjectPool.getNumIdle() < 300, "Should be less than 300 idle, found " + genericObjectPool.getNumIdle());
1612         Waiter.sleepQuietly(600L);
1613         assertTrue(genericObjectPool.getNumIdle() < 200, "Should be less than 200 idle, found " + genericObjectPool.getNumIdle());
1614         Waiter.sleepQuietly(600L);
1615         assertTrue(genericObjectPool.getNumIdle() < 100, "Should be less than 100 idle, found " + genericObjectPool.getNumIdle());
1616         Waiter.sleepQuietly(600L);
1617         assertEquals(0, genericObjectPool.getNumIdle(), "Should be zero idle, found " + genericObjectPool.getNumIdle());
1618         for (int i = 0; i < 500; i++) {
1619             active[i] = genericObjectPool.borrowObject();
1620         }
1621         for (int i = 0; i < 500; i++) {
1622             genericObjectPool.returnObject(active[i]);
1623         }
1624         Waiter.sleepQuietly(1000L);
1625         assertTrue(genericObjectPool.getNumIdle() < 500, "Should be less than 500 idle, found " + genericObjectPool.getNumIdle());
1626         Waiter.sleepQuietly(600L);
1627         assertTrue(genericObjectPool.getNumIdle() < 400, "Should be less than 400 idle, found " + genericObjectPool.getNumIdle());
1628         Waiter.sleepQuietly(600L);
1629         assertTrue(genericObjectPool.getNumIdle() < 300, "Should be less than 300 idle, found " + genericObjectPool.getNumIdle());
1630         Waiter.sleepQuietly(600L);
1631         assertTrue(genericObjectPool.getNumIdle() < 200, "Should be less than 200 idle, found " + genericObjectPool.getNumIdle());
1632         Waiter.sleepQuietly(600L);
1633         assertTrue(genericObjectPool.getNumIdle() < 100, "Should be less than 100 idle, found " + genericObjectPool.getNumIdle());
1634         Waiter.sleepQuietly(600L);
1635         assertEquals(0, genericObjectPool.getNumIdle(), "Should be zero idle, found " + genericObjectPool.getNumIdle());
1636     }
1637 
1638     @Test
1639     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1640     void testEvictionInvalid() throws Exception {
1641         try (GenericObjectPool<Object> invalidFactoryPool = new GenericObjectPool<>(new InvalidFactory())) {
1642             invalidFactoryPool.setMaxIdle(1);
1643             invalidFactoryPool.setMaxTotal(1);
1644             invalidFactoryPool.setTestOnBorrow(false);
1645             invalidFactoryPool.setTestOnReturn(false);
1646             invalidFactoryPool.setTestWhileIdle(true);
1647             invalidFactoryPool.setMinEvictableIdleTime(Duration.ofSeconds(100));
1648             invalidFactoryPool.setNumTestsPerEvictionRun(1);
1649             final Object p = invalidFactoryPool.borrowObject();
1650             invalidFactoryPool.returnObject(p);
1651             // Run eviction in a separate thread
1652             final Thread t = new EvictionThread<>(invalidFactoryPool);
1653             t.start();
1654             // Sleep to make sure evictor has started
1655             Thread.sleep(300);
1656             try {
1657                 invalidFactoryPool.borrowObject(1);
1658             } catch (final NoSuchElementException nsee) {
1659                 // Ignore
1660             }
1661             // Make sure evictor has finished
1662             Thread.sleep(1000);
1663             // Should have an empty pool
1664             assertEquals(0, invalidFactoryPool.getNumIdle(), "Idle count different than expected.");
1665             assertEquals(0, invalidFactoryPool.getNumActive(), "Total count different than expected.");
1666         }
1667     }
1668 
1669     /**
1670      * Test to make sure evictor visits least recently used objects first, regardless of FIFO/LIFO.
1671      *
1672      * JIRA: POOL-86
1673      *
1674      * @throws Exception May occur in some failure modes
1675      */
1676     @Test
1677     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1678     void testEvictionOrder() throws Exception {
1679         checkEvictionOrder(false);
1680         tearDown();
1681         setUp();
1682         checkEvictionOrder(true);
1683     }
1684 
1685     @Test
1686     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1687     void testEvictionPolicy() throws Exception {
1688         genericObjectPool.setMaxIdle(500);
1689         genericObjectPool.setMaxTotal(500);
1690         genericObjectPool.setNumTestsPerEvictionRun(500);
1691         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
1692         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
1693         genericObjectPool.setTestWhileIdle(true);
1694         // ClassNotFoundException
1695         assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(Long.toString(System.currentTimeMillis())),
1696                 "setEvictionPolicyClassName must throw an error if the class name is invalid.");
1697         // InstantiationException
1698         assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(Serializable.class.getName()),
1699                 "setEvictionPolicyClassName must throw an error if the class name is invalid.");
1700         // IllegalAccessException
1701         assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(Collections.class.getName()),
1702                 "setEvictionPolicyClassName must throw an error if the class name is invalid.");
1703         assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(String.class.getName()),
1704                 () -> "setEvictionPolicyClassName must throw an error if a class that does not implement EvictionPolicy is specified.");
1705         genericObjectPool.setEvictionPolicy(new TestEvictionPolicy<>());
1706         assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName());
1707         genericObjectPool.setEvictionPolicyClassName(TestEvictionPolicy.class.getName());
1708         assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName());
1709         final String[] active = new String[500];
1710         for (int i = 0; i < 500; i++) {
1711             active[i] = genericObjectPool.borrowObject();
1712         }
1713         for (int i = 0; i < 500; i++) {
1714             genericObjectPool.returnObject(active[i]);
1715         }
1716         // Eviction policy ignores first 1500 attempts to evict and then always
1717         // evicts. After 1s, there should have been two runs of 500 tests so no
1718         // evictions
1719         Waiter.sleepQuietly(1000L);
1720         assertEquals(500, genericObjectPool.getNumIdle(), "Should be 500 idle");
1721         // A further 1s wasn't enough so allow 2s for the evictor to clear out
1722         // all of the idle objects.
1723         Waiter.sleepQuietly(2000L);
1724         assertEquals(0, genericObjectPool.getNumIdle(), "Should be 0 idle");
1725     }
1726 
1727     @Test
1728     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1729     void testEvictionSoftMinIdle() throws Exception {
1730         final class TimeTest extends BasePooledObjectFactory<TimeTest> {
1731             private final long createTimeMillis;
1732 
1733             TimeTest() {
1734                 createTimeMillis = System.currentTimeMillis();
1735             }
1736 
1737             @Override
1738             public TimeTest create() {
1739                 return new TimeTest();
1740             }
1741 
1742             public long getCreateTimeMillis() {
1743                 return createTimeMillis;
1744             }
1745 
1746             @Override
1747             public PooledObject<TimeTest> wrap(final TimeTest value) {
1748                 return new DefaultPooledObject<>(value);
1749             }
1750         }
1751         try (GenericObjectPool<TimeTest> timePool = new GenericObjectPool<>(new TimeTest())) {
1752             timePool.setMaxIdle(5);
1753             timePool.setMaxTotal(5);
1754             timePool.setNumTestsPerEvictionRun(5);
1755             timePool.setMinEvictableIdleDuration(Duration.ofSeconds(3));
1756             timePool.setMinEvictableIdle(Duration.ofSeconds(3));
1757             timePool.setMinEvictableIdleTime(Duration.ofSeconds(3));
1758             timePool.setSoftMinEvictableIdleTime(TestConstants.ONE_SECOND_DURATION);
1759             timePool.setMinIdle(2);
1760             final TimeTest[] active = new TimeTest[5];
1761             final Long[] creationTime = new Long[5];
1762             for (int i = 0; i < 5; i++) {
1763                 active[i] = timePool.borrowObject();
1764                 creationTime[i] = Long.valueOf(active[i].getCreateTimeMillis());
1765             }
1766             for (int i = 0; i < 5; i++) {
1767                 timePool.returnObject(active[i]);
1768             }
1769             // Soft evict all but minIdle(2)
1770             Thread.sleep(1500L);
1771             timePool.evict();
1772             assertEquals(2, timePool.getNumIdle(), "Idle count different than expected.");
1773             // Hard evict the rest.
1774             Thread.sleep(2000L);
1775             timePool.evict();
1776             assertEquals(0, timePool.getNumIdle(), "Idle count different than expected.");
1777         }
1778     }
1779 
1780     @Test
1781     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1782     void testEvictionWithNegativeNumTests() throws Exception {
1783         // when numTestsPerEvictionRun is negative, it represents a fraction of the idle objects to test
1784         genericObjectPool.setMaxIdle(6);
1785         genericObjectPool.setMaxTotal(6);
1786         genericObjectPool.setNumTestsPerEvictionRun(-2);
1787         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
1788         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
1789         final String[] active = new String[6];
1790         for (int i = 0; i < 6; i++) {
1791             active[i] = genericObjectPool.borrowObject();
1792         }
1793         for (int i = 0; i < 6; i++) {
1794             genericObjectPool.returnObject(active[i]);
1795         }
1796         Waiter.sleepQuietly(100L);
1797         assertTrue(genericObjectPool.getNumIdle() <= 6, "Should at most 6 idle, found " + genericObjectPool.getNumIdle());
1798         Waiter.sleepQuietly(100L);
1799         assertTrue(genericObjectPool.getNumIdle() <= 3, "Should at most 3 idle, found " + genericObjectPool.getNumIdle());
1800         Waiter.sleepQuietly(100L);
1801         assertTrue(genericObjectPool.getNumIdle() <= 2, "Should be at most 2 idle, found " + genericObjectPool.getNumIdle());
1802         Waiter.sleepQuietly(100L);
1803         assertEquals(0, genericObjectPool.getNumIdle(), "Should be zero idle, found " + genericObjectPool.getNumIdle());
1804     }
1805 
1806     @Test
1807     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1808     void testEvictLIFO() throws Exception {
1809         checkEvict(true);
1810     }
1811 
1812     /**
1813      * Verifies that the evictor visits objects in expected order and frequency.
1814      *
1815      * @throws Exception May occur in some failure modes
1816      */
1817     @Test
1818     void testEvictorVisiting() throws Exception {
1819         checkEvictorVisiting(true);
1820         checkEvictorVisiting(false);
1821     }
1822 
1823     @Test
1824     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1825     void testEvictWhileEmpty() throws Exception {
1826         genericObjectPool.evict();
1827         genericObjectPool.evict();
1828         genericObjectPool.close();
1829     }
1830 
1831     @Test
1832     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1833     void testExceptionInValidationDuringEviction() throws Exception {
1834         genericObjectPool.setMaxIdle(1);
1835         genericObjectPool.setMinEvictableIdleTime(Duration.ZERO);
1836         genericObjectPool.setTestWhileIdle(true);
1837         final String active = genericObjectPool.borrowObject();
1838         genericObjectPool.returnObject(active);
1839         simpleFactory.setThrowExceptionOnValidate(true);
1840         assertThrows(RuntimeException.class, () -> genericObjectPool.evict());
1841         assertEquals(0, genericObjectPool.getNumActive());
1842         assertEquals(0, genericObjectPool.getNumIdle());
1843     }
1844 
1845     @Test
1846     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1847     void testExceptionOnActivateDuringBorrow() throws Exception {
1848         final String obj1 = genericObjectPool.borrowObject();
1849         final String obj2 = genericObjectPool.borrowObject();
1850         genericObjectPool.returnObject(obj1);
1851         genericObjectPool.returnObject(obj2);
1852         simpleFactory.setThrowExceptionOnActivate(true);
1853         simpleFactory.setEvenValid(false);
1854         // Activation will now throw every other time
1855         // First attempt throws, but loop continues and second succeeds
1856         final String obj = genericObjectPool.borrowObject();
1857         assertEquals(1, genericObjectPool.getNumActive());
1858         assertEquals(0, genericObjectPool.getNumIdle());
1859         genericObjectPool.returnObject(obj);
1860         simpleFactory.setValid(false);
1861         // Validation will now fail on activation when borrowObject returns
1862         // an idle instance, and then when attempting to create a new instance
1863         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
1864         assertEquals(0, genericObjectPool.getNumActive());
1865         assertEquals(0, genericObjectPool.getNumIdle());
1866     }
1867 
1868     @Test
1869     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1870     void testExceptionOnDestroyDuringBorrow() throws Exception {
1871         simpleFactory.setThrowExceptionOnDestroy(true);
1872         genericObjectPool.setTestOnBorrow(true);
1873         genericObjectPool.borrowObject();
1874         simpleFactory.setValid(false); // Make validation fail on next borrow attempt
1875         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
1876         assertEquals(1, genericObjectPool.getNumActive());
1877         assertEquals(0, genericObjectPool.getNumIdle());
1878     }
1879 
1880     @Test
1881     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1882     void testExceptionOnDestroyDuringReturn() throws Exception {
1883         simpleFactory.setThrowExceptionOnDestroy(true);
1884         genericObjectPool.setTestOnReturn(true);
1885         final String obj1 = genericObjectPool.borrowObject();
1886         genericObjectPool.borrowObject();
1887         simpleFactory.setValid(false); // Make validation fail
1888         genericObjectPool.returnObject(obj1);
1889         assertEquals(1, genericObjectPool.getNumActive());
1890         assertEquals(0, genericObjectPool.getNumIdle());
1891     }
1892 
1893     @Test
1894     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1895     void testExceptionOnPassivateDuringReturn() throws Exception {
1896         final String obj = genericObjectPool.borrowObject();
1897         simpleFactory.setThrowExceptionOnPassivate(true);
1898         genericObjectPool.returnObject(obj);
1899         assertEquals(0, genericObjectPool.getNumIdle());
1900     }
1901 
1902     @Test
1903     void testFailingFactoryDoesNotBlockThreads() throws Exception {
1904         final CreateFailFactory factory = new CreateFailFactory();
1905         try (GenericObjectPool<String> createFailFactoryPool = new GenericObjectPool<>(factory)) {
1906             createFailFactoryPool.setMaxTotal(1);
1907             // Try and borrow the first object from the pool
1908             final WaitingTestThread thread1 = new WaitingTestThread(createFailFactoryPool, 0);
1909             thread1.start();
1910             // Wait for thread to reach semaphore
1911             while (!factory.hasQueuedThreads()) {
1912                 Thread.sleep(200);
1913             }
1914             // Try and borrow the second object from the pool
1915             final WaitingTestThread thread2 = new WaitingTestThread(createFailFactoryPool, 0);
1916             thread2.start();
1917             // Pool will not call factory since maximum number of object creations
1918             // are already queued.
1919             // Thread 2 will wait on an object being returned to the pool
1920             // Give thread 2 a chance to reach this state
1921             Thread.sleep(1000);
1922             // Release thread1
1923             factory.release();
1924             // Pre-release thread2
1925             factory.release();
1926             // Both threads should now complete.
1927             boolean threadRunning = true;
1928             int count = 0;
1929             while (threadRunning && count < 15) {
1930                 threadRunning = thread1.isAlive();
1931                 threadRunning = thread2.isAlive();
1932                 Thread.sleep(200);
1933                 count++;
1934             }
1935             assertFalse(thread1.isAlive());
1936             assertFalse(thread2.isAlive());
1937             assertTrue(thread1.thrown instanceof UnsupportedCharsetException);
1938             assertTrue(thread2.thrown instanceof UnsupportedCharsetException);
1939         }
1940     }
1941 
1942     @Test
1943     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
1944     void testFIFO() throws Exception {
1945         genericObjectPool.setLifo(false);
1946         genericObjectPool.addObject(); // "0"
1947         genericObjectPool.addObject(); // "1"
1948         genericObjectPool.addObject(); // "2"
1949         assertEquals("0", genericObjectPool.borrowObject(), "Oldest");
1950         assertEquals("1", genericObjectPool.borrowObject(), "Middle");
1951         assertEquals("2", genericObjectPool.borrowObject(), "Youngest");
1952         final String o = genericObjectPool.borrowObject();
1953         assertEquals("3", o, "new-3");
1954         genericObjectPool.returnObject(o);
1955         assertEquals(o, genericObjectPool.borrowObject(), "returned-3");
1956         assertEquals("4", genericObjectPool.borrowObject(), "new-4");
1957     }
1958 
1959     @Test
1960     void testGetFactoryType_DefaultPooledObjectFactory() {
1961         try (GenericObjectPool<String> pool = new GenericObjectPool<>(createDefaultPooledObjectFactory())) {
1962             assertNotNull(pool.getFactoryType());
1963         }
1964     }
1965 
1966     @Test
1967     void testGetFactoryType_NullPooledObjectFactory() {
1968         try (GenericObjectPool<String> pool = new GenericObjectPool<>(createNullPooledObjectFactory())) {
1969             assertNotNull(pool.getFactoryType());
1970         }
1971     }
1972 
1973     @Test
1974     void testGetFactoryType_PoolUtilsSynchronizedDefaultPooledFactory() {
1975         try (GenericObjectPool<String> pool = new GenericObjectPool<>(PoolUtils.synchronizedPooledFactory(createDefaultPooledObjectFactory()))) {
1976             assertNotNull(pool.getFactoryType());
1977         }
1978     }
1979 
1980     @Test
1981     void testGetFactoryType_PoolUtilsSynchronizedNullPooledFactory() {
1982         try (GenericObjectPool<String> pool = new GenericObjectPool<>(PoolUtils.synchronizedPooledFactory(createNullPooledObjectFactory()))) {
1983             assertNotNull(pool.getFactoryType());
1984         }
1985     }
1986 
1987     @Test
1988     void testGetFactoryType_SynchronizedDefaultPooledObjectFactory() {
1989         try (GenericObjectPool<String> pool = new GenericObjectPool<>(new TestSynchronizedPooledObjectFactory<>(createDefaultPooledObjectFactory()))) {
1990             assertNotNull(pool.getFactoryType());
1991         }
1992     }
1993 
1994     @Test
1995     void testGetFactoryType_SynchronizedNullPooledObjectFactory() {
1996         try (GenericObjectPool<String> pool = new GenericObjectPool<>(new TestSynchronizedPooledObjectFactory<>(createNullPooledObjectFactory()))) {
1997             assertNotNull(pool.getFactoryType());
1998         }
1999     }
2000 
2001     @Test
2002     void testGetStatsString() {
2003         try (GenericObjectPool<String> pool = new GenericObjectPool<>(new TestSynchronizedPooledObjectFactory<>(createNullPooledObjectFactory()))) {
2004             assertNotNull(pool.getStatsString());
2005         }
2006     }
2007 
2008     /**
2009      * Verify that threads waiting on a depleted pool get served when a checked out object is invalidated.
2010      *
2011      * JIRA: POOL-240
2012      *
2013      * @throws Exception May occur in some failure modes
2014      */
2015     @Test
2016     void testInvalidateFreesCapacity() throws Exception {
2017         final SimpleFactory factory = new SimpleFactory();
2018         try (GenericObjectPool<String> pool = new GenericObjectPool<>(factory)) {
2019             pool.setMaxTotal(2);
2020             pool.setMaxWaitMillis(500);
2021             // Borrow an instance and hold if for 5 seconds
2022             final WaitingTestThread thread1 = new WaitingTestThread(pool, 5000);
2023             thread1.start();
2024             // Borrow another instance
2025             final String obj = pool.borrowObject();
2026             // Launch another thread - will block, but fail in 500 ms
2027             final WaitingTestThread thread2 = new WaitingTestThread(pool, 100);
2028             thread2.start();
2029             // Invalidate the object borrowed by this thread - should allow thread2 to create
2030             Thread.sleep(20);
2031             pool.invalidateObject(obj);
2032             Thread.sleep(600); // Wait for thread2 to timeout
2033             if (thread2.thrown != null) {
2034                 fail(thread2.thrown.toString());
2035             }
2036         }
2037     }
2038 
2039     /**
2040      * Ensure the pool is registered.
2041      */
2042     @Test
2043     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2044     void testJmxRegistration() {
2045         final ObjectName oname = genericObjectPool.getJmxName();
2046         final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
2047         final Set<ObjectName> result = mbs.queryNames(oname, null);
2048         assertEquals(1, result.size());
2049         genericObjectPool.jmxUnregister();
2050         final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
2051         config.setJmxEnabled(false);
2052         try (GenericObjectPool<String> poolWithoutJmx = new GenericObjectPool<>(simpleFactory, config)) {
2053             assertNull(poolWithoutJmx.getJmxName());
2054             config.setJmxEnabled(true);
2055             poolWithoutJmx.jmxUnregister();
2056         }
2057         config.setJmxNameBase(null);
2058         try (GenericObjectPool<String> poolWithDefaultJmxNameBase = new GenericObjectPool<>(simpleFactory, config)) {
2059             assertNotNull(poolWithDefaultJmxNameBase.getJmxName());
2060         }
2061     }
2062 
2063     @Test
2064     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2065     void testLIFO() throws Exception {
2066         final String o;
2067         genericObjectPool.setLifo(true);
2068         genericObjectPool.addObject(); // "0"
2069         genericObjectPool.addObject(); // "1"
2070         genericObjectPool.addObject(); // "2"
2071         assertEquals("2", genericObjectPool.borrowObject(), "Youngest");
2072         assertEquals("1", genericObjectPool.borrowObject(), "Middle");
2073         assertEquals("0", genericObjectPool.borrowObject(), "Oldest");
2074         o = genericObjectPool.borrowObject();
2075         assertEquals("3", o, "new-3");
2076         genericObjectPool.returnObject(o);
2077         assertEquals(o, genericObjectPool.borrowObject(), "returned-3");
2078         assertEquals("4", genericObjectPool.borrowObject(), "new-4");
2079     }
2080 
2081     /**
2082      * 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
2083      * due to factory outage. Verify that the borrower is served once the factory is back online.
2084      */
2085     @Test
2086     @Disabled
2087     @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
2088     void testLivenessOnTransientFactoryFailure() throws Exception {
2089         final DisconnectingWaiterFactory<String> factory = new DisconnectingWaiterFactory<>(DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_CREATE_ACTION,
2090                 DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_LIFECYCLE_ACTION, obj -> false // all instances fail validation
2091         );
2092         final AtomicBoolean failed = new AtomicBoolean();
2093         try (GenericObjectPool<Waiter> pool = new GenericObjectPool<>(factory)) {
2094             pool.setMaxWait(Duration.ofMillis(100));
2095             pool.setTestOnReturn(true);
2096             pool.setMaxTotal(1);
2097             final Waiter w = pool.borrowObject();
2098             final Thread t = new Thread(() -> {
2099                 try {
2100                     pool.borrowObject();
2101                 } catch (final Exception e) {
2102                     failed.set(true);
2103                 }
2104             });
2105             Thread.sleep(10);
2106             t.start();
2107             // t is blocked waiting on the deque
2108             Thread.sleep(10);
2109             factory.disconnect();
2110             pool.returnObject(w); // validation fails, so no return
2111             Thread.sleep(10);
2112             factory.connect();
2113             // Borrower should be able to be served now
2114             t.join();
2115         }
2116         if (failed.get()) {
2117             fail("Borrower timed out waiting for an instance");
2118         }
2119     }
2120 
2121     /**
2122      * Test the following scenario: Thread 1 borrows an instance Thread 2 starts to borrow another instance before thread 1 returns its instance Thread 1
2123      * 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.
2124      *
2125      * @throws Exception May occur in some failure modes
2126      */
2127     @Test
2128     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2129     void testMakeConcurrentWithReturn() throws Exception {
2130         genericObjectPool.setTestOnBorrow(true);
2131         simpleFactory.setValid(true);
2132         // Borrow and return an instance, with a short wait
2133         final WaitingTestThread thread1 = new WaitingTestThread(genericObjectPool, 200);
2134         thread1.start();
2135         Thread.sleep(50); // wait for validation to succeed
2136         // Slow down validation and borrow an instance
2137         simpleFactory.setValidateLatency(400);
2138         final String instance = genericObjectPool.borrowObject();
2139         // Now make sure that we have not leaked an instance
2140         assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle() + 1);
2141         genericObjectPool.returnObject(instance);
2142         assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle());
2143     }
2144 
2145     @Test
2146     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2147     void testMaxIdle() throws Exception {
2148         genericObjectPool.setMaxTotal(100);
2149         genericObjectPool.setMaxIdle(8);
2150         final String[] active = new String[100];
2151         for (int i = 0; i < 100; i++) {
2152             active[i] = genericObjectPool.borrowObject();
2153         }
2154         assertEquals(100, genericObjectPool.getNumActive());
2155         assertEquals(0, genericObjectPool.getNumIdle());
2156         for (int i = 0; i < 100; i++) {
2157             genericObjectPool.returnObject(active[i]);
2158             assertEquals(99 - i, genericObjectPool.getNumActive());
2159             assertEquals(i < 8 ? i + 1 : 8, genericObjectPool.getNumIdle());
2160         }
2161     }
2162 
2163     @Test
2164     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2165     void testMaxIdleZero() throws Exception {
2166         genericObjectPool.setMaxTotal(100);
2167         genericObjectPool.setMaxIdle(0);
2168         final String[] active = new String[100];
2169         for (int i = 0; i < 100; i++) {
2170             active[i] = genericObjectPool.borrowObject();
2171         }
2172         assertEquals(100, genericObjectPool.getNumActive());
2173         assertEquals(0, genericObjectPool.getNumIdle());
2174         for (int i = 0; i < 100; i++) {
2175             genericObjectPool.returnObject(active[i]);
2176             assertEquals(99 - i, genericObjectPool.getNumActive());
2177             assertEquals(0, genericObjectPool.getNumIdle());
2178         }
2179     }
2180 
2181     /**
2182      * Showcasing a possible deadlock situation as reported in POOL-356
2183      */
2184     @Test
2185     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2186     @SuppressWarnings("rawtypes")
2187     void testMaxIdleZeroUnderLoad() {
2188         // Config
2189         final int numThreads = 199; // And main thread makes a round 200.
2190         final int numIter = 20;
2191         final int delay = 25;
2192         final int maxTotal = 10;
2193         simpleFactory.setMaxTotal(maxTotal);
2194         genericObjectPool.setMaxTotal(maxTotal);
2195         genericObjectPool.setBlockWhenExhausted(true);
2196         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
2197         // this is important to trigger POOL-356
2198         genericObjectPool.setMaxIdle(0);
2199         // Start threads to borrow objects
2200         final TestThread[] threads = new TestThread[numThreads];
2201         for (int i = 0; i < numThreads; i++) {
2202             // Factor of 2 on iterations so main thread does work whilst other
2203             // threads are running. Factor of 2 on delay so average delay for
2204             // other threads == actual delay for main thread
2205             threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2);
2206             final Thread t = new Thread(threads[i]);
2207             t.start();
2208         }
2209         // Give the threads a chance to start doing some work
2210         Waiter.sleepQuietly(100L);
2211         for (int i = 0; i < numIter; i++) {
2212             String obj = null;
2213             try {
2214                 Waiter.sleepQuietly(delay);
2215                 obj = genericObjectPool.borrowObject();
2216                 // Under load, observed numActive > maxTotal
2217                 if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) {
2218                     throw new IllegalStateException("Too many active objects");
2219                 }
2220                 Waiter.sleepQuietly(delay);
2221             } catch (final Exception e) {
2222                 // Shouldn't happen
2223                 e.printStackTrace();
2224                 fail("Exception on borrow");
2225             } finally {
2226                 if (obj != null) {
2227                     try {
2228                         genericObjectPool.returnObject(obj);
2229                     } catch (final Exception e) {
2230                         // Ignore
2231                     }
2232                 }
2233             }
2234         }
2235         for (int i = 0; i < numThreads; i++) {
2236             while (!threads[i].complete()) {
2237                 Waiter.sleepQuietly(500L);
2238             }
2239             if (threads[i].failed()) {
2240                 threads[i].error.printStackTrace();
2241                 fail("Thread " + i + " failed: " + threads[i].error.toString());
2242             }
2243         }
2244     }
2245 
2246     @Test
2247     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2248     void testMaxTotal() throws Exception {
2249         genericObjectPool.setMaxTotal(3);
2250         genericObjectPool.setBlockWhenExhausted(false);
2251         genericObjectPool.borrowObject();
2252         genericObjectPool.borrowObject();
2253         genericObjectPool.borrowObject();
2254         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
2255     }
2256 
2257     /**
2258      * Verifies that maxTotal is not exceeded when factory destroyObject has high latency, testOnReturn is set and there is high incidence of validation
2259      * failures.
2260      */
2261     @Test
2262     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2263     void testMaxTotalInvariant() {
2264         final int maxTotal = 15;
2265         simpleFactory.setEvenValid(false); // Every other validation fails
2266         simpleFactory.setDestroyLatency(100); // Destroy takes 100 ms
2267         simpleFactory.setMaxTotal(maxTotal); // (makes - destroys) bound
2268         simpleFactory.setValidationEnabled(true);
2269         genericObjectPool.setMaxTotal(maxTotal);
2270         genericObjectPool.setMaxIdle(-1);
2271         genericObjectPool.setTestOnReturn(true);
2272         genericObjectPool.setMaxWaitMillis(1000L);
2273         runTestThreads(5, 10, 50, genericObjectPool);
2274     }
2275 
2276     @Test
2277     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2278     @SuppressWarnings("rawtypes")
2279     void testMaxTotalUnderLoad() {
2280         // Config
2281         final int numThreads = 199; // And main thread makes a round 200.
2282         final int numIter = 20;
2283         final int delay = 25;
2284         final int maxTotal = 10;
2285         simpleFactory.setMaxTotal(maxTotal);
2286         genericObjectPool.setMaxTotal(maxTotal);
2287         genericObjectPool.setBlockWhenExhausted(true);
2288         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
2289         // Start threads to borrow objects
2290         final TestThread[] threads = new TestThread[numThreads];
2291         for (int i = 0; i < numThreads; i++) {
2292             // Factor of 2 on iterations so main thread does work whilst other
2293             // threads are running. Factor of 2 on delay so average delay for
2294             // other threads == actual delay for main thread
2295             threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2);
2296             final Thread t = new Thread(threads[i]);
2297             t.start();
2298         }
2299         // Give the threads a chance to start doing some work
2300         Waiter.sleepQuietly(5000);
2301         for (int i = 0; i < numIter; i++) {
2302             String obj = null;
2303             try {
2304                 Waiter.sleepQuietly(delay);
2305                 obj = genericObjectPool.borrowObject();
2306                 // Under load, observed numActive > maxTotal
2307                 if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) {
2308                     throw new IllegalStateException("Too many active objects");
2309                 }
2310                 Waiter.sleepQuietly(delay);
2311             } catch (final Exception e) {
2312                 // Shouldn't happen
2313                 e.printStackTrace();
2314                 fail("Exception on borrow");
2315             } finally {
2316                 if (obj != null) {
2317                     try {
2318                         genericObjectPool.returnObject(obj);
2319                     } catch (final Exception e) {
2320                         // Ignore
2321                     }
2322                 }
2323             }
2324         }
2325         for (int i = 0; i < numThreads; i++) {
2326             while (!threads[i].complete()) {
2327                 Waiter.sleepQuietly(500L);
2328             }
2329             if (threads[i].failed()) {
2330                 fail("Thread " + i + " failed: " + threads[i].error.toString());
2331             }
2332         }
2333     }
2334 
2335     @Test
2336     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2337     void testMaxTotalZero() throws Exception {
2338         genericObjectPool.setMaxTotal(0);
2339         genericObjectPool.setBlockWhenExhausted(false);
2340         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
2341     }
2342 
2343     /*
2344      * Test multi-threaded pool access. Multiple threads, but maxTotal only allows half the threads to succeed.
2345      *
2346      * This test was prompted by Continuum build failures in the Commons DBCP test case: TestPerUserPoolDataSource.testMultipleThreads2() Let's see if the this
2347      * fails on Continuum too!
2348      */
2349     @Test
2350     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2351     void testMaxWaitMultiThreaded() throws Exception {
2352         final long maxWait = 500; // wait for connection
2353         final long holdTime = 2 * maxWait; // how long to hold connection
2354         final int threads = 10; // number of threads to grab the object initially
2355         genericObjectPool.setBlockWhenExhausted(true);
2356         genericObjectPool.setMaxWaitMillis(maxWait);
2357         genericObjectPool.setMaxTotal(threads);
2358         // Create enough threads so half the threads will have to wait
2359         final WaitingTestThread[] wtt = new WaitingTestThread[threads * 2];
2360         for (int i = 0; i < wtt.length; i++) {
2361             wtt[i] = new WaitingTestThread(genericObjectPool, holdTime);
2362         }
2363         final long originMillis = System.currentTimeMillis() - 1000;
2364         for (final WaitingTestThread element : wtt) {
2365             element.start();
2366         }
2367         int failed = 0;
2368         for (final WaitingTestThread element : wtt) {
2369             element.join();
2370             if (element.thrown != null) {
2371                 failed++;
2372             }
2373         }
2374         if (DISPLAY_THREAD_DETAILS || wtt.length / 2 != failed) {
2375             System.out.println("MaxWait: " + maxWait + " HoldTime: " + holdTime + " MaxTotal: " + threads + " Threads: " + wtt.length + " Failed: " + failed);
2376             for (final WaitingTestThread wt : wtt) {
2377                 System.out.println("PreBorrow: " + (wt.preBorrowMillis - originMillis) + " PostBorrow: "
2378                         + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - originMillis : -1) + " BorrowTime: "
2379                         + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - wt.preBorrowMillis : -1) + " PostReturn: "
2380                         + (wt.postReturnMillis != 0 ? wt.postReturnMillis - originMillis : -1) + " Ended: " + (wt.endedMillis - originMillis) + " ObjId: "
2381                         + wt.objectId);
2382             }
2383         }
2384         assertEquals(wtt.length / 2, failed, "Expected half the threads to fail");
2385     }
2386 
2387     @Test
2388     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2389     void testMinIdle() throws Exception {
2390         genericObjectPool.setMaxIdle(500);
2391         genericObjectPool.setMinIdle(5);
2392         genericObjectPool.setMaxTotal(10);
2393         genericObjectPool.setNumTestsPerEvictionRun(0);
2394         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
2395         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
2396         genericObjectPool.setTestWhileIdle(true);
2397         Waiter.sleepQuietly(150L);
2398         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2399         final String[] active = new String[5];
2400         active[0] = genericObjectPool.borrowObject();
2401         Waiter.sleepQuietly(150L);
2402         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2403         for (int i = 1; i < 5; i++) {
2404             active[i] = genericObjectPool.borrowObject();
2405         }
2406         Waiter.sleepQuietly(150L);
2407         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2408         for (int i = 0; i < 5; i++) {
2409             genericObjectPool.returnObject(active[i]);
2410         }
2411         Waiter.sleepQuietly(150L);
2412         assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
2413     }
2414 
2415     @Test
2416     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2417     void testMinIdleMaxTotal() throws Exception {
2418         genericObjectPool.setMaxIdle(500);
2419         genericObjectPool.setMinIdle(5);
2420         genericObjectPool.setMaxTotal(10);
2421         genericObjectPool.setNumTestsPerEvictionRun(0);
2422         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
2423         genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
2424         genericObjectPool.setTestWhileIdle(true);
2425         Waiter.sleepQuietly(150L);
2426         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2427         final String[] active = new String[10];
2428         Waiter.sleepQuietly(150L);
2429         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2430         for (int i = 0; i < 5; i++) {
2431             active[i] = genericObjectPool.borrowObject();
2432         }
2433         Waiter.sleepQuietly(150L);
2434         assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
2435         for (int i = 0; i < 5; i++) {
2436             genericObjectPool.returnObject(active[i]);
2437         }
2438         Waiter.sleepQuietly(150L);
2439         assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
2440         for (int i = 0; i < 10; i++) {
2441             active[i] = genericObjectPool.borrowObject();
2442         }
2443         Waiter.sleepQuietly(150L);
2444         assertEquals(0, genericObjectPool.getNumIdle(), "Should be 0 idle, found " + genericObjectPool.getNumIdle());
2445         for (int i = 0; i < 10; i++) {
2446             genericObjectPool.returnObject(active[i]);
2447         }
2448         Waiter.sleepQuietly(150L);
2449         assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
2450     }
2451 
2452     /**
2453      * Verifies that returning an object twice (without borrow in between) causes ISE but does not re-validate or re-passivate the instance.
2454      *
2455      * JIRA: POOL-285
2456      */
2457     @Test
2458     void testMultipleReturn() throws Exception {
2459         final WaiterFactory<String> factory = new WaiterFactory<>(0, 0, 0, 0, 0, 0);
2460         try (GenericObjectPool<Waiter> pool = new GenericObjectPool<>(factory)) {
2461             pool.setTestOnReturn(true);
2462             final Waiter waiter = pool.borrowObject();
2463             pool.returnObject(waiter);
2464             assertEquals(1, waiter.getValidationCount());
2465             assertEquals(1, waiter.getPassivationCount());
2466             try {
2467                 pool.returnObject(waiter);
2468                 fail("Expecting IllegalStateException from multiple return");
2469             } catch (final IllegalStateException ex) {
2470                 // Exception is expected, now check no repeat validation/passivation
2471                 assertEquals(1, waiter.getValidationCount());
2472                 assertEquals(1, waiter.getPassivationCount());
2473             }
2474         }
2475     }
2476 
2477     // POOL-248
2478     @Test
2479     void testMultipleReturnOfSameObject() throws Exception {
2480         try (GenericObjectPool<String> pool = new GenericObjectPool<>(simpleFactory, new GenericObjectPoolConfig<>())) {
2481             assertEquals(0, pool.getNumActive());
2482             assertEquals(0, pool.getNumIdle());
2483             final String obj = pool.borrowObject();
2484             assertEquals(1, pool.getNumActive());
2485             assertEquals(0, pool.getNumIdle());
2486             pool.returnObject(obj);
2487             assertEquals(0, pool.getNumActive());
2488             assertEquals(1, pool.getNumIdle());
2489             assertThrows(IllegalStateException.class, () -> pool.returnObject(obj));
2490             assertEquals(0, pool.getNumActive());
2491             assertEquals(1, pool.getNumIdle());
2492         }
2493     }
2494 
2495     /**
2496      * 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.
2497      *
2498      * JIRA: POOL-284
2499      */
2500     @Test
2501     void testMutable() throws Exception {
2502         final HashSetFactory factory = new HashSetFactory();
2503         try (GenericObjectPool<HashSet<String>> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) {
2504             final HashSet<String> s1 = pool.borrowObject();
2505             final HashSet<String> s2 = pool.borrowObject();
2506             s1.add("One");
2507             s2.add("One");
2508             pool.returnObject(s1);
2509             pool.returnObject(s2);
2510         }
2511     }
2512 
2513     @Test
2514     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2515     void testNegativeMaxTotal() throws Exception {
2516         genericObjectPool.setMaxTotal(-1);
2517         genericObjectPool.setBlockWhenExhausted(false);
2518         final String obj = genericObjectPool.borrowObject();
2519         assertEquals(getNthObject(0), obj);
2520         genericObjectPool.returnObject(obj);
2521     }
2522 
2523     /**
2524      * Verifies that concurrent threads never "share" instances
2525      */
2526     @Test
2527     void testNoInstanceOverlap() {
2528         final int maxTotal = 5;
2529         final int numThreads = 100;
2530         final int delay = 1;
2531         final int iterations = 1000;
2532         final AtomicIntegerFactory factory = new AtomicIntegerFactory();
2533         try (GenericObjectPool<AtomicInteger> pool = new GenericObjectPool<>(factory)) {
2534             pool.setMaxTotal(maxTotal);
2535             pool.setMaxIdle(maxTotal);
2536             pool.setTestOnBorrow(true);
2537             pool.setBlockWhenExhausted(true);
2538             pool.setMaxWaitMillis(-1);
2539             runTestThreads(numThreads, iterations, delay, pool);
2540             assertEquals(0, pool.getDestroyedByBorrowValidationCount());
2541         }
2542     }
2543 
2544     /**
2545      * POOL-376
2546      */
2547     @Test
2548     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2549     void testNoInvalidateNPE() throws Exception {
2550         genericObjectPool.setMaxTotal(1);
2551         genericObjectPool.setTestOnCreate(true);
2552         genericObjectPool.setMaxWaitMillis(-1);
2553         final String obj = genericObjectPool.borrowObject();
2554         // Make validation fail - this will cause create() to return null
2555         simpleFactory.setValid(false);
2556         // Create a take waiter
2557         final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200);
2558         wtt.start();
2559         // Give wtt time to start
2560         Thread.sleep(200);
2561         genericObjectPool.invalidateObject(obj);
2562         // Now allow create to succeed so waiter can be served
2563         simpleFactory.setValid(true);
2564     }
2565 
2566     /**
2567      * Verify that when a factory returns a null object, pool methods throw NPE.
2568      *
2569      * @throws InterruptedException
2570      */
2571     @Test
2572     @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
2573     void testNPEOnFactoryNull() throws Exception {
2574         // Override default to always return null from makeObject
2575         final DisconnectingWaiterFactory<String> factory = new DisconnectingWaiterFactory<>(Suppliers.nul(),
2576                 DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_LIFECYCLE_ACTION, DisconnectingWaiterFactory.DEFAULT_DISCONNECTED_VALIDATION_ACTION);
2577         try (GenericObjectPool<Waiter> pool = new GenericObjectPool<>(factory)) {
2578             pool.setTestOnBorrow(true);
2579             pool.setMaxTotal(-1);
2580             pool.setMinIdle(1);
2581             // Disconnect the factory - will always return null in this state
2582             factory.disconnect();
2583             assertThrows(NullPointerException.class, pool::borrowObject);
2584             assertThrows(NullPointerException.class, pool::addObject);
2585             assertThrows(NullPointerException.class, pool::ensureMinIdle);
2586         }
2587     }
2588 
2589     /*
2590      * Test for POOL-419.
2591      * https://issues.apache.org/jira/browse/POOL-419
2592      */
2593     @Test
2594     void testPool419() throws Exception {
2595 
2596         final ExecutorService executor = Executors.newFixedThreadPool(100);
2597 
2598         final GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
2599 
2600         final int maxConnections = 10000;
2601 
2602         config.setMaxTotal(maxConnections);
2603         config.setMaxIdle(maxConnections);
2604         config.setMinIdle(1);
2605 
2606         final BasePooledObjectFactory<Object> pof = createPooledObjectFactory();
2607 
2608         try (ObjectPool<Object> connectionPool = new GenericObjectPool<>(pof, config)) {
2609             assertNotNull(connectionPool);
2610 
2611             final CountDownLatch startLatch = new CountDownLatch(1);
2612 
2613             final List<Object> poolObjects = new ArrayList<>();
2614 
2615             final List<FutureTask<Boolean>> tasks = new ArrayList<>();
2616 
2617             for (int i = 0; i < maxConnections; i++) {
2618                 poolObjects.add(connectionPool.borrowObject());
2619             }
2620 
2621             for (final Object poolObject : poolObjects) {
2622 
2623                 tasks.add(new FutureTask<>(() -> {
2624                     startLatch.await();
2625                     connectionPool.invalidateObject(poolObject);
2626                     return true;
2627                 }));
2628 
2629                 tasks.add(new FutureTask<>(() -> {
2630                     startLatch.await();
2631                     connectionPool.returnObject(poolObject);
2632                     return true;
2633                 }));
2634             }
2635 
2636             tasks.forEach(executor::submit);
2637 
2638             startLatch.countDown(); // Start all tasks simultaneously
2639 
2640             executor.shutdown();
2641             assertTrue(executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS));
2642 
2643             assertEquals(0, connectionPool.getNumActive(), "getNumActive() must not return a negative value");
2644 
2645         }
2646     }
2647 
2648     @Test
2649     void testPreparePool() throws Exception {
2650         genericObjectPool.setMinIdle(1);
2651         genericObjectPool.setMaxTotal(1);
2652         genericObjectPool.preparePool();
2653         assertEquals(1, genericObjectPool.getNumIdle());
2654         final String obj = genericObjectPool.borrowObject();
2655         genericObjectPool.preparePool();
2656         assertEquals(0, genericObjectPool.getNumIdle());
2657         genericObjectPool.setMinIdle(0);
2658         genericObjectPool.returnObject(obj);
2659         genericObjectPool.preparePool();
2660         assertEquals(1, genericObjectPool.getNumIdle());
2661     }
2662 
2663     /**
2664      * Verify that when thread A borrows two objects from a pool with maxTotal=2,
2665      * then two more threads try to borrow, returning one object and invalidating
2666      * the other will serve both of the waiting threads.
2667      */
2668     @Test
2669     @Timeout(value = 10_000, unit = TimeUnit.MILLISECONDS)
2670     void testReturnAndInvalidateServesWaiters() throws Exception {
2671         genericObjectPool.setMaxTotal(2);
2672         genericObjectPool.setBlockWhenExhausted(true);
2673         genericObjectPool.setMaxWait(Duration.ofMillis(100));
2674 
2675         // Thread A borrows two objects
2676         final String obj1 = genericObjectPool.borrowObject();
2677         final String obj2 = genericObjectPool.borrowObject();
2678 
2679         // Track successful borrows
2680         final AtomicInteger successfulBorrows = new AtomicInteger(0);
2681 
2682         // Create two borrowing threads that will block
2683         final Thread borrower1 = new Thread(() -> {
2684             try {
2685                 genericObjectPool.borrowObject();
2686                 successfulBorrows.incrementAndGet();
2687             } catch (final Exception e) {
2688                 // Borrow failed
2689             }
2690         });
2691 
2692         final Thread borrower2 = new Thread(() -> {
2693             try {
2694                 genericObjectPool.borrowObject();
2695                 successfulBorrows.incrementAndGet();
2696             } catch (final Exception e) {
2697                 // Borrow failed
2698             }
2699         });
2700 
2701         // Start the borrowing threads - they will block
2702         borrower1.start();
2703         borrower2.start();
2704         Thread.sleep(50); // Give threads time to start and block
2705 
2706         // Thread A returns one object and invalidates the other with no delay
2707         genericObjectPool.invalidateObject(obj1);
2708         genericObjectPool.invalidateObject(obj2);
2709 
2710         // Wait for threads to complete
2711         borrower1.join();
2712         borrower2.join();
2713 
2714         // Both threads should have been served
2715         assertEquals(2, successfulBorrows.get(),
2716             "Both waiting threads should have been served, but only " + successfulBorrows.get() + " were served");
2717     }
2718 
2719     @Test /* maxWaitMillis x2 + padding */
2720     @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
2721     void testReturnBorrowObjectWithingMaxWaitDuration() throws Exception {
2722         final Duration maxWaitDuration = Duration.ofMillis(500);
2723         try (GenericObjectPool<String> createSlowObjectFactoryPool = new GenericObjectPool<>(createSlowObjectFactory(Duration.ofSeconds(60)))) {
2724             createSlowObjectFactoryPool.setMaxTotal(1);
2725             createSlowObjectFactoryPool.setMaxWait(maxWaitDuration);
2726             // thread1 tries creating a slow object to make pool full.
2727             final WaitingTestThread thread1 = new WaitingTestThread(createSlowObjectFactoryPool, 0);
2728             thread1.start();
2729             // Wait for thread1's reaching to create().
2730             Thread.sleep(100);
2731             // another one tries borrowObject. It should return within maxWaitMillis.
2732             assertThrows(NoSuchElementException.class, () -> createSlowObjectFactoryPool.borrowObject(maxWaitDuration),
2733                     "borrowObject must fail due to timeout by maxWait");
2734             assertTrue(thread1.isAlive());
2735         }
2736     }
2737 
2738     @Test /* maxWaitMillis x2 + padding */
2739     @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
2740     void testReturnBorrowObjectWithingMaxWaitMillis() throws Exception {
2741         final long maxWaitMillis = 500;
2742         try (GenericObjectPool<String> createSlowObjectFactoryPool = new GenericObjectPool<>(createSlowObjectFactory(Duration.ofSeconds(60)))) {
2743             createSlowObjectFactoryPool.setMaxTotal(1);
2744             createSlowObjectFactoryPool.setMaxWaitMillis(maxWaitMillis);
2745             // thread1 tries creating a slow object to make pool full.
2746             final WaitingTestThread thread1 = new WaitingTestThread(createSlowObjectFactoryPool, 0);
2747             thread1.start();
2748             // Wait for thread1's reaching to create().
2749             Thread.sleep(100);
2750             // another one tries borrowObject. It should return within maxWaitMillis.
2751             assertThrows(NoSuchElementException.class, () -> createSlowObjectFactoryPool.borrowObject(maxWaitMillis),
2752                     "borrowObject must fail due to timeout by maxWait");
2753             assertTrue(thread1.isAlive());
2754         }
2755     }
2756 
2757     /**
2758      * 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
2759      * 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
2760      * call to isLifo() in the returnObject() method.
2761      */
2762     // @Test
2763     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2764     void testReturnObject() throws Exception {
2765         genericObjectPool.setMaxTotal(1);
2766         genericObjectPool.setMaxIdle(-1);
2767         final String active = genericObjectPool.borrowObject();
2768         assertEquals(1, genericObjectPool.getNumActive());
2769         assertEquals(0, genericObjectPool.getNumIdle());
2770         final Thread t = new Thread(() -> genericObjectPool.close());
2771         t.start();
2772         genericObjectPool.returnObject(active);
2773         // Wait for the close() thread to complete
2774         while (t.isAlive()) {
2775             Thread.sleep(50);
2776         }
2777         assertEquals(0, genericObjectPool.getNumIdle());
2778     }
2779 
2780     @Test
2781     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2782     void testSetConfig() throws Exception {
2783         final GenericObjectPoolConfig<String> expected = new GenericObjectPoolConfig<>();
2784         assertConfiguration(expected, genericObjectPool);
2785         expected.setMaxTotal(2);
2786         expected.setMaxIdle(3);
2787         expected.setMaxWait(Duration.ofMillis(5));
2788         expected.setMinEvictableIdleDuration(Duration.ofMillis(7L));
2789         expected.setNumTestsPerEvictionRun(9);
2790         expected.setTestOnCreate(true);
2791         expected.setTestOnBorrow(true);
2792         expected.setTestOnReturn(true);
2793         expected.setTestWhileIdle(true);
2794         expected.setTimeBetweenEvictionRuns(Duration.ofMillis(11L));
2795         expected.setBlockWhenExhausted(false);
2796         genericObjectPool.setConfig(expected);
2797         assertConfiguration(expected, genericObjectPool);
2798     }
2799 
2800     @Test
2801     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2802     void testSettersAndGetters() throws Exception {
2803         {
2804             // The object receives an Exception during its creation to prevent
2805             // memory leaks. See BaseGenericObjectPool constructor for more details.
2806             assertNotEquals("", genericObjectPool.getCreationStackTrace());
2807         }
2808         {
2809             assertEquals(0, genericObjectPool.getBorrowedCount());
2810         }
2811         {
2812             assertEquals(0, genericObjectPool.getReturnedCount());
2813         }
2814         {
2815             assertEquals(0, genericObjectPool.getCreatedCount());
2816         }
2817         {
2818             assertEquals(0, genericObjectPool.getDestroyedCount());
2819         }
2820         {
2821             assertEquals(0, genericObjectPool.getDestroyedByEvictorCount());
2822         }
2823         {
2824             assertEquals(0, genericObjectPool.getDestroyedByBorrowValidationCount());
2825         }
2826         {
2827             assertEquals(0, genericObjectPool.getMeanActiveTimeMillis());
2828         }
2829         {
2830             assertEquals(0, genericObjectPool.getMeanIdleTimeMillis());
2831         }
2832         {
2833             assertEquals(0, genericObjectPool.getMeanBorrowWaitTimeMillis());
2834         }
2835         {
2836             assertEquals(0, genericObjectPool.getMaxBorrowWaitTimeMillis());
2837         }
2838         {
2839             assertEquals(0, genericObjectPool.getNumIdle());
2840         }
2841         {
2842             genericObjectPool.setMaxTotal(123);
2843             assertEquals(123, genericObjectPool.getMaxTotal());
2844         }
2845         {
2846             genericObjectPool.setMaxIdle(12);
2847             assertEquals(12, genericObjectPool.getMaxIdle());
2848         }
2849         {
2850             genericObjectPool.setMaxWaitMillis(1234L);
2851             assertEquals(1234L, genericObjectPool.getMaxWaitMillis());
2852         }
2853         {
2854             genericObjectPool.setMinEvictableIdleTimeMillis(12345L);
2855             assertEquals(12345L, genericObjectPool.getMinEvictableIdleDuration().toMillis());
2856             assertEquals(12345L, genericObjectPool.getMinEvictableIdleTimeMillis());
2857             assertEquals(12345L, genericObjectPool.getMinEvictableIdleTime().toMillis());
2858         }
2859         {
2860             genericObjectPool.setNumTestsPerEvictionRun(11);
2861             assertEquals(11, genericObjectPool.getNumTestsPerEvictionRun());
2862         }
2863         {
2864             genericObjectPool.setTestOnBorrow(true);
2865             assertTrue(genericObjectPool.getTestOnBorrow());
2866             genericObjectPool.setTestOnBorrow(false);
2867             assertFalse(genericObjectPool.getTestOnBorrow());
2868         }
2869         {
2870             genericObjectPool.setTestOnReturn(true);
2871             assertTrue(genericObjectPool.getTestOnReturn());
2872             genericObjectPool.setTestOnReturn(false);
2873             assertFalse(genericObjectPool.getTestOnReturn());
2874         }
2875         {
2876             genericObjectPool.setTestWhileIdle(true);
2877             assertTrue(genericObjectPool.getTestWhileIdle());
2878             genericObjectPool.setTestWhileIdle(false);
2879             assertFalse(genericObjectPool.getTestWhileIdle());
2880         }
2881         {
2882             genericObjectPool.setTimeBetweenEvictionRunsMillis(11235L);
2883             assertEquals(11235L, genericObjectPool.getDurationBetweenEvictionRuns().toMillis());
2884             assertEquals(11235L, genericObjectPool.getTimeBetweenEvictionRunsMillis());
2885             assertEquals(11235L, genericObjectPool.getTimeBetweenEvictionRuns().toMillis());
2886         }
2887         {
2888             genericObjectPool.setSoftMinEvictableIdleTimeMillis(12135L);
2889             assertEquals(12135L, genericObjectPool.getSoftMinEvictableIdleDuration().toMillis());
2890             assertEquals(12135L, genericObjectPool.getSoftMinEvictableIdleTimeMillis());
2891             assertEquals(12135L, genericObjectPool.getSoftMinEvictableIdleTime().toMillis());
2892         }
2893         {
2894             genericObjectPool.setBlockWhenExhausted(true);
2895             assertTrue(genericObjectPool.getBlockWhenExhausted());
2896             genericObjectPool.setBlockWhenExhausted(false);
2897             assertFalse(genericObjectPool.getBlockWhenExhausted());
2898         }
2899     }
2900 
2901     @Test
2902     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2903     void testStartAndStopEvictor() throws Exception {
2904         // set up pool without evictor
2905         genericObjectPool.setMaxIdle(6);
2906         genericObjectPool.setMaxTotal(6);
2907         genericObjectPool.setNumTestsPerEvictionRun(6);
2908         genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
2909         for (int j = 0; j < 2; j++) {
2910             // populate the pool
2911             {
2912                 final String[] active = new String[6];
2913                 for (int i = 0; i < 6; i++) {
2914                     active[i] = genericObjectPool.borrowObject();
2915                 }
2916                 for (int i = 0; i < 6; i++) {
2917                     genericObjectPool.returnObject(active[i]);
2918                 }
2919             }
2920             // note that it stays populated
2921             assertEquals(6, genericObjectPool.getNumIdle(), "Should have 6 idle");
2922             // start the evictor
2923             genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(50));
2924             // wait a second (well, .2 seconds)
2925             Waiter.sleepQuietly(200L);
2926             // assert that the evictor has cleared out the pool
2927             assertEquals(0, genericObjectPool.getNumIdle(), "Should have 0 idle");
2928             // stop the evictor
2929             genericObjectPool.startEvictor(Duration.ZERO);
2930         }
2931     }
2932 
2933 
2934     @Test
2935     void testSwallowedExceptionListener() {
2936         genericObjectPool.setSwallowedExceptionListener(null); // must simply return
2937         final List<Exception> swallowedExceptions = new ArrayList<>();
2938         /*
2939          * A simple listener, that will throw a OOM on 3rd exception.
2940          */
2941         final SwallowedExceptionListener listener = e -> {
2942             if (swallowedExceptions.size() == 2) {
2943                 throw new OutOfMemoryError();
2944             }
2945             swallowedExceptions.add(e);
2946         };
2947         genericObjectPool.setSwallowedExceptionListener(listener);
2948         final Exception e1 = new Exception();
2949         final Exception e2 = new ArrayIndexOutOfBoundsException();
2950         genericObjectPool.swallowException(e1);
2951         genericObjectPool.swallowException(e2);
2952         assertThrows(OutOfMemoryError.class, () -> genericObjectPool.swallowException(e1));
2953         assertEquals(2, swallowedExceptions.size());
2954     }
2955 
2956     @Test
2957     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2958     void testThreaded1() throws Exception {
2959         genericObjectPool.setMaxTotal(15);
2960         genericObjectPool.setMaxIdle(15);
2961         genericObjectPool.setMaxWaitMillis(1000L);
2962         runTestThreads(20, 100, 50, genericObjectPool);
2963     }
2964 
2965     @Test
2966     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
2967     void testTimeoutNoLeak() throws Exception {
2968         genericObjectPool.setMaxTotal(2);
2969         genericObjectPool.setMaxWaitMillis(10);
2970         genericObjectPool.setBlockWhenExhausted(true);
2971         final String obj = genericObjectPool.borrowObject();
2972         final String obj2 = genericObjectPool.borrowObject();
2973         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
2974         genericObjectPool.returnObject(obj2);
2975         genericObjectPool.returnObject(obj);
2976         genericObjectPool.borrowObject();
2977         genericObjectPool.borrowObject();
2978     }
2979 
2980     /**
2981      * Tests POOL-361
2982      */
2983     @Test
2984     void testValidateOnCreate() throws Exception {
2985         genericObjectPool.setTestOnCreate(true);
2986         genericObjectPool.addObject();
2987         assertEquals(1, simpleFactory.validateCounter);
2988     }
2989 
2990     /**
2991      * Tests POOL-361
2992      */
2993     @Test
2994     void testValidateOnCreateFailure() throws Exception {
2995         genericObjectPool.setTestOnCreate(true);
2996         genericObjectPool.setTestOnBorrow(false);
2997         genericObjectPool.setMaxTotal(2);
2998         simpleFactory.setValid(false);
2999         // Make sure failed validations do not leak capacity
3000         genericObjectPool.addObject();
3001         genericObjectPool.addObject();
3002         assertEquals(0, genericObjectPool.getNumIdle());
3003         assertEquals(0, genericObjectPool.getNumActive());
3004         simpleFactory.setValid(true);
3005         final String obj = genericObjectPool.borrowObject();
3006         assertNotNull(obj);
3007         genericObjectPool.addObject();
3008         // Should have one idle, one out now
3009         assertEquals(1, genericObjectPool.getNumIdle());
3010         assertEquals(1, genericObjectPool.getNumActive());
3011     }
3012 
3013     /**
3014      * Verify that threads waiting on a depleted pool get served when a returning object fails validation.
3015      *
3016      * JIRA: POOL-240
3017      *
3018      * @throws Exception May occur in some failure modes
3019      */
3020     @Test
3021     void testValidationFailureOnReturnFreesCapacity() throws Exception {
3022         final SimpleFactory factory = new SimpleFactory();
3023         factory.setValid(false); // Validate will always fail
3024         factory.setValidationEnabled(true);
3025         try (GenericObjectPool<String> pool = new GenericObjectPool<>(factory)) {
3026             pool.setMaxTotal(2);
3027             pool.setMaxWaitMillis(1500);
3028             pool.setTestOnReturn(true);
3029             pool.setTestOnBorrow(false);
3030             // Borrow an instance and hold if for 5 seconds
3031             final WaitingTestThread thread1 = new WaitingTestThread(pool, 5000);
3032             thread1.start();
3033             // Borrow another instance and return it after 500 ms (validation will fail)
3034             final WaitingTestThread thread2 = new WaitingTestThread(pool, 500);
3035             thread2.start();
3036             Thread.sleep(50);
3037             // Try to borrow an object
3038             final String obj = pool.borrowObject();
3039             pool.returnObject(obj);
3040         }
3041     }
3042 
3043     // POOL-276
3044     @Test
3045     void testValidationOnCreateOnly() throws Exception {
3046         genericObjectPool.setMaxTotal(1);
3047         genericObjectPool.setTestOnCreate(true);
3048         genericObjectPool.setTestOnBorrow(false);
3049         genericObjectPool.setTestOnReturn(false);
3050         genericObjectPool.setTestWhileIdle(false);
3051         final String o1 = genericObjectPool.borrowObject();
3052         assertEquals("0", o1);
3053         final Timer t = new Timer();
3054         t.schedule(new TimerTask() {
3055             @Override
3056             public void run() {
3057                 genericObjectPool.returnObject(o1);
3058             }
3059         }, 3000);
3060         final String o2 = genericObjectPool.borrowObject();
3061         assertEquals("0", o2);
3062         assertEquals(1, simpleFactory.validateCounter);
3063     }
3064 
3065     @Test
3066     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
3067     void testWhenExhaustedBlock() throws Exception {
3068         genericObjectPool.setMaxTotal(1);
3069         genericObjectPool.setBlockWhenExhausted(true);
3070         genericObjectPool.setMaxWaitMillis(10L);
3071         final String obj1 = genericObjectPool.borrowObject();
3072         assertNotNull(obj1);
3073         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
3074         genericObjectPool.returnObject(obj1);
3075         genericObjectPool.close();
3076     }
3077 
3078      /**
3079      * POOL-189
3080      *
3081      * @throws Exception May occur in some failure modes
3082      */
3083     @Test
3084     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
3085     void testWhenExhaustedBlockClosePool() throws Exception {
3086         genericObjectPool.setMaxTotal(1);
3087         genericObjectPool.setBlockWhenExhausted(true);
3088         genericObjectPool.setMaxWaitMillis(-1);
3089         final Object obj1 = genericObjectPool.borrowObject();
3090         // Make sure an object was obtained
3091         assertNotNull(obj1);
3092         // Create a separate thread to try and borrow another object
3093         final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200);
3094         wtt.start();
3095         // Give wtt time to start
3096         Thread.sleep(200);
3097         // close the pool (Bug POOL-189)
3098         genericObjectPool.close();
3099         // Give interrupt time to take effect
3100         Thread.sleep(200);
3101         // Check thread was interrupted
3102         assertTrue(wtt.thrown instanceof InterruptedException);
3103     }
3104 
3105     @Test
3106     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
3107     void testWhenExhaustedBlockInterrupt() throws Exception {
3108         genericObjectPool.setMaxTotal(1);
3109         genericObjectPool.setBlockWhenExhausted(true);
3110         genericObjectPool.setMaxWaitMillis(-1);
3111         final String obj1 = genericObjectPool.borrowObject();
3112         // Make sure on object was obtained
3113         assertNotNull(obj1);
3114         // Create a separate thread to try and borrow another object
3115         final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200000);
3116         wtt.start();
3117         // Give wtt time to start
3118         Thread.sleep(200);
3119         wtt.interrupt();
3120         // Give interrupt time to take effect
3121         Thread.sleep(200);
3122         // Check thread was interrupted
3123         assertTrue(wtt.thrown instanceof InterruptedException);
3124         // Return object to the pool
3125         genericObjectPool.returnObject(obj1);
3126         // Bug POOL-162 - check there is now an object in the pool
3127         genericObjectPool.setMaxWaitMillis(10L);
3128         String obj2 = null;
3129         try {
3130             obj2 = genericObjectPool.borrowObject();
3131             assertNotNull(obj2);
3132         } catch (final NoSuchElementException e) {
3133             // Not expected
3134             fail("NoSuchElementException not expected");
3135         }
3136         genericObjectPool.returnObject(obj2);
3137         genericObjectPool.close();
3138     }
3139 
3140     @Test
3141     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
3142     void testWhenExhaustedFail() throws Exception {
3143         genericObjectPool.setMaxTotal(1);
3144         genericObjectPool.setBlockWhenExhausted(false);
3145         final String obj1 = genericObjectPool.borrowObject();
3146         assertNotNull(obj1);
3147         assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
3148         genericObjectPool.returnObject(obj1);
3149         assertEquals(1, genericObjectPool.getNumIdle());
3150         genericObjectPool.close();
3151     }
3152 }