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