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