View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.pool2;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertThrows;
21  import static org.junit.jupiter.api.Assertions.fail;
22  
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.NoSuchElementException;
26  
27  import org.apache.commons.pool2.impl.DefaultPooledObject;
28  import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
29  import org.junit.jupiter.api.AfterEach;
30  import org.junit.jupiter.api.Test;
31  
32  /**
33   * Abstract test case for {@link ObjectPool} implementations.
34   */
35  public abstract class AbstractTestKeyedObjectPool {
36  
37      protected static class FailingKeyedPooledObjectFactory implements KeyedPooledObjectFactory<Object, Object> {
38          private final List<MethodCall> methodCalls = new ArrayList<>();
39          private int count;
40          private boolean makeObjectFail;
41          private boolean activateObjectFail;
42          private boolean validateObjectFail;
43          private boolean passivateObjectFail;
44          private boolean destroyObjectFail;
45  
46          public FailingKeyedPooledObjectFactory() {
47          }
48  
49          @Override
50          public void activateObject(final Object key, final PooledObject<Object> obj) {
51              methodCalls.add(new MethodCall("activateObject", key, obj.getObject()));
52              if (activateObjectFail) {
53                  throw new PrivateException("activateObject");
54              }
55          }
56  
57          @Override
58          public void destroyObject(final Object key, final PooledObject<Object> obj) {
59              methodCalls.add(new MethodCall("destroyObject", key, obj.getObject()));
60              if (destroyObjectFail) {
61                  throw new PrivateException("destroyObject");
62              }
63          }
64  
65          public int getCurrentCount() {
66              return count;
67          }
68  
69          public List<MethodCall> getMethodCalls() {
70              return methodCalls;
71          }
72  
73          public boolean isActivateObjectFail() {
74              return activateObjectFail;
75          }
76  
77          public boolean isDestroyObjectFail() {
78              return destroyObjectFail;
79          }
80  
81          public boolean isMakeObjectFail() {
82              return makeObjectFail;
83          }
84  
85          public boolean isPassivateObjectFail() {
86              return passivateObjectFail;
87          }
88  
89          public boolean isValidateObjectFail() {
90              return validateObjectFail;
91          }
92  
93          @Override
94          public PooledObject<Object> makeObject(final Object key) {
95              final MethodCall call = new MethodCall("makeObject", key);
96              methodCalls.add(call);
97              final int originalCount = this.count++;
98              if (makeObjectFail) {
99                  throw new PrivateException("makeObject");
100             }
101             // Deliberate choice to create new object in case future unit test
102             // checks for a specific object
103             final Integer obj = Integer.valueOf(originalCount);
104             call.setReturned(obj);
105             return new DefaultPooledObject<>(obj);
106         }
107 
108         @Override
109         public void passivateObject(final Object key, final PooledObject<Object> obj) {
110             methodCalls.add(new MethodCall("passivateObject", key, obj.getObject()));
111             if (passivateObjectFail) {
112                 throw new PrivateException("passivateObject");
113             }
114         }
115 
116         public void reset() {
117             count = 0;
118             getMethodCalls().clear();
119             setMakeObjectFail(false);
120             setActivateObjectFail(false);
121             setValidateObjectFail(false);
122             setPassivateObjectFail(false);
123             setDestroyObjectFail(false);
124         }
125 
126         public void setActivateObjectFail(final boolean activateObjectFail) {
127             this.activateObjectFail = activateObjectFail;
128         }
129 
130         public void setCurrentCount(final int count) {
131             this.count = count;
132         }
133 
134         public void setDestroyObjectFail(final boolean destroyObjectFail) {
135             this.destroyObjectFail = destroyObjectFail;
136         }
137 
138         public void setMakeObjectFail(final boolean makeObjectFail) {
139             this.makeObjectFail = makeObjectFail;
140         }
141 
142         public void setPassivateObjectFail(final boolean passivateObjectFail) {
143             this.passivateObjectFail = passivateObjectFail;
144         }
145 
146         public void setValidateObjectFail(final boolean validateObjectFail) {
147             this.validateObjectFail = validateObjectFail;
148         }
149 
150         @Override
151         public boolean validateObject(final Object key, final PooledObject<Object> obj) {
152             final MethodCall call = new MethodCall("validateObject", key, obj.getObject());
153             methodCalls.add(call);
154             if (validateObjectFail) {
155                 throw new PrivateException("validateObject");
156             }
157             final boolean r = true;
158             call.returned(Boolean.valueOf(r));
159             return r;
160         }
161     }
162 
163     private static final class TestFactory extends BaseKeyedPooledObjectFactory<Object, Object> {
164         @Override
165         public Object create(final Object key) {
166             return new Object();
167         }
168 
169         @Override
170         public PooledObject<Object> wrap(final Object value) {
171             return new DefaultPooledObject<>(value);
172         }
173     }
174 
175     private static final String KEY = "key";
176 
177     private KeyedObjectPool<Object, Object> pool;
178 
179     // Deliberate choice to create a new object in case future unit tests check
180     // for a specific object.
181     private final Integer ZERO = Integer.valueOf(0);
182 
183     private final Integer ONE = Integer.valueOf(1);
184 
185     private void clear(final FailingKeyedPooledObjectFactory factory, final List<MethodCall> expectedMethods) {
186         factory.getMethodCalls().clear();
187         expectedMethods.clear();
188     }
189 
190     /**
191      * Return what we expect to be the n<sup>th</sup>
192      * object (zero indexed) created by the pool
193      * for the given key.
194      *
195      * @param key Key for the object to be obtained
196      * @param n   index of the object to be obtained
197      * @return the requested object
198      */
199     protected abstract Object getNthObject(Object key, int n);
200 
201     protected abstract boolean isFifo();
202 
203     protected abstract boolean isLifo();
204 
205     /**
206      * Creates an {@link KeyedObjectPool} instance
207      * that can contain at least <em>minCapacity</em>
208      * idle and active objects, or
209      * throw {@link IllegalArgumentException}
210      * if such a pool cannot be created.
211      *
212      * @param <E> The exception type.
213      * @param minCapacity Minimum capacity of the pool to create
214      * @return the newly created keyed object pool
215      */
216     protected abstract <E extends Exception> KeyedObjectPool<Object, Object> makeEmptyPool(int minCapacity);
217 
218     /**
219      * Creates an {@code KeyedObjectPool} with the specified factory.
220      * The pool should be in a default configuration and conform to the expected
221      * behaviors described in {@link KeyedObjectPool}.
222      * Generally speaking there should be no limits on the various object counts.
223      *
224      * @param <E>     The type of exception thrown by the pool
225      * @param factory Factory to use to associate with the pool
226      * @return The newly created empty pool
227      */
228     protected abstract <E extends Exception> KeyedObjectPool<Object, Object> makeEmptyPool(
229             KeyedPooledObjectFactory<Object, Object> factory);
230 
231     protected abstract Object makeKey(int n);
232 
233     private <E extends Exception> void reset(final KeyedObjectPool<Object, Object> pool,
234             final FailingKeyedPooledObjectFactory factory,
235             final List<MethodCall> expectedMethods) throws Exception {
236         pool.clear();
237         clear(factory, expectedMethods);
238         factory.reset();
239     }
240 
241     @AfterEach
242     public void tearDown() {
243         pool = null;
244     }
245 
246     @Test
247     void testBaseAddObject() throws Exception {
248         try {
249             pool = makeEmptyPool(3);
250         } catch (final UnsupportedOperationException uoe) {
251             return; // skip this test if unsupported
252         }
253         final Object key = makeKey(0);
254         try {
255             assertEquals(0, pool.getNumIdle());
256             assertEquals(0, pool.getNumActive());
257             assertEquals(0, pool.getNumIdle(key));
258             assertEquals(0, pool.getNumActive(key));
259             pool.addObject(key);
260             assertEquals(1, pool.getNumIdle());
261             assertEquals(0, pool.getNumActive());
262             assertEquals(1, pool.getNumIdle(key));
263             assertEquals(0, pool.getNumActive(key));
264             final Object obj = pool.borrowObject(key);
265             assertEquals(getNthObject(key, 0), obj);
266             assertEquals(0, pool.getNumIdle());
267             assertEquals(1, pool.getNumActive());
268             assertEquals(0, pool.getNumIdle(key));
269             assertEquals(1, pool.getNumActive(key));
270             pool.returnObject(key, obj);
271             assertEquals(1, pool.getNumIdle());
272             assertEquals(0, pool.getNumActive());
273             assertEquals(1, pool.getNumIdle(key));
274             assertEquals(0, pool.getNumActive(key));
275         } catch (final UnsupportedOperationException e) {
276             return; // skip this test if one of those calls is unsupported
277         } finally {
278             pool.close();
279         }
280     }
281 
282     @Test
283     void testBaseBorrow() throws Exception {
284         try {
285             pool = makeEmptyPool(3);
286         } catch (final UnsupportedOperationException uoe) {
287             return; // skip this test if unsupported
288         }
289         final Object keya = makeKey(0);
290         final Object keyb = makeKey(1);
291         assertEquals(getNthObject(keya, 0), pool.borrowObject(keya), "1");
292         assertEquals(getNthObject(keyb, 0), pool.borrowObject(keyb), "2");
293         assertEquals(getNthObject(keyb, 1), pool.borrowObject(keyb), "3");
294         assertEquals(getNthObject(keya, 1), pool.borrowObject(keya), "4");
295         assertEquals(getNthObject(keyb, 2), pool.borrowObject(keyb), "5");
296         assertEquals(getNthObject(keya, 2), pool.borrowObject(keya), "6");
297         pool.close();
298     }
299 
300     @Test
301     void testBaseBorrowReturn() throws Exception {
302         try {
303             pool = makeEmptyPool(3);
304         } catch (final UnsupportedOperationException uoe) {
305             return; // skip this test if unsupported
306         }
307         final Object keya = makeKey(0);
308         Object obj0 = pool.borrowObject(keya);
309         assertEquals(getNthObject(keya, 0), obj0);
310         Object obj1 = pool.borrowObject(keya);
311         assertEquals(getNthObject(keya, 1), obj1);
312         Object obj2 = pool.borrowObject(keya);
313         assertEquals(getNthObject(keya, 2), obj2);
314         pool.returnObject(keya, obj2);
315         obj2 = pool.borrowObject(keya);
316         assertEquals(getNthObject(keya, 2), obj2);
317         pool.returnObject(keya, obj1);
318         obj1 = pool.borrowObject(keya);
319         assertEquals(getNthObject(keya, 1), obj1);
320         pool.returnObject(keya, obj0);
321         pool.returnObject(keya, obj2);
322         obj2 = pool.borrowObject(keya);
323         if (isLifo()) {
324             assertEquals(getNthObject(keya, 2), obj2);
325         }
326         if (isFifo()) {
327             assertEquals(getNthObject(keya, 0), obj2);
328         }
329         obj0 = pool.borrowObject(keya);
330         if (isLifo()) {
331             assertEquals(getNthObject(keya, 0), obj0);
332         }
333         if (isFifo()) {
334             assertEquals(getNthObject(keya, 2), obj0);
335         }
336         pool.close();
337     }
338 
339     @Test
340     void testBaseClear() throws Exception {
341         try {
342             pool = makeEmptyPool(3);
343         } catch (final UnsupportedOperationException uoe) {
344             return; // skip this test if unsupported
345         }
346         final Object keya = makeKey(0);
347         assertEquals(0, pool.getNumActive(keya));
348         assertEquals(0, pool.getNumIdle(keya));
349         final Object obj0 = pool.borrowObject(keya);
350         final Object obj1 = pool.borrowObject(keya);
351         assertEquals(2, pool.getNumActive(keya));
352         assertEquals(0, pool.getNumIdle(keya));
353         pool.returnObject(keya, obj1);
354         pool.returnObject(keya, obj0);
355         assertEquals(0, pool.getNumActive(keya));
356         assertEquals(2, pool.getNumIdle(keya));
357         pool.clear(keya);
358         assertEquals(0, pool.getNumActive(keya));
359         assertEquals(0, pool.getNumIdle(keya));
360         final Object obj2 = pool.borrowObject(keya);
361         assertEquals(getNthObject(keya, 2), obj2);
362         pool.close();
363     }
364 
365     @Test
366     void testBaseInvalidateObject() throws Exception {
367         try {
368             pool = makeEmptyPool(3);
369         } catch (final UnsupportedOperationException uoe) {
370             return; // skip this test if unsupported
371         }
372         final Object keya = makeKey(0);
373         assertEquals(0, pool.getNumActive(keya));
374         assertEquals(0, pool.getNumIdle(keya));
375         final Object obj0 = pool.borrowObject(keya);
376         final Object obj1 = pool.borrowObject(keya);
377         assertEquals(2, pool.getNumActive(keya));
378         assertEquals(0, pool.getNumIdle(keya));
379         pool.invalidateObject(keya, obj0);
380         assertEquals(1, pool.getNumActive(keya));
381         assertEquals(0, pool.getNumIdle(keya));
382         pool.invalidateObject(keya, obj1);
383         assertEquals(0, pool.getNumActive(keya));
384         assertEquals(0, pool.getNumIdle(keya));
385         pool.close();
386     }
387 
388     @Test
389     void testBaseNumActiveNumIdle() throws Exception {
390         try {
391             pool = makeEmptyPool(3);
392         } catch (final UnsupportedOperationException uoe) {
393             return; // skip this test if unsupported
394         }
395         final Object keya = makeKey(0);
396         assertEquals(0, pool.getNumActive(keya));
397         assertEquals(0, pool.getNumIdle(keya));
398         final Object obj0 = pool.borrowObject(keya);
399         assertEquals(1, pool.getNumActive(keya));
400         assertEquals(0, pool.getNumIdle(keya));
401         final Object obj1 = pool.borrowObject(keya);
402         assertEquals(2, pool.getNumActive(keya));
403         assertEquals(0, pool.getNumIdle(keya));
404         pool.returnObject(keya, obj1);
405         assertEquals(1, pool.getNumActive(keya));
406         assertEquals(1, pool.getNumIdle(keya));
407         pool.returnObject(keya, obj0);
408         assertEquals(0, pool.getNumActive(keya));
409         assertEquals(2, pool.getNumIdle(keya));
410 
411         assertEquals(0, pool.getNumActive("xyzzy12345"));
412         assertEquals(0, pool.getNumIdle("xyzzy12345"));
413 
414         pool.close();
415     }
416 
417     @Test
418     void testBaseNumActiveNumIdle2() throws Exception {
419         try {
420             pool = makeEmptyPool(6);
421         } catch (final UnsupportedOperationException uoe) {
422             return; // skip this test if unsupported
423         }
424         final Object keya = makeKey(0);
425         final Object keyb = makeKey(1);
426         assertEquals(0, pool.getNumActive());
427         assertEquals(0, pool.getNumIdle());
428         assertEquals(0, pool.getNumActive(keya));
429         assertEquals(0, pool.getNumIdle(keya));
430         assertEquals(0, pool.getNumActive(keyb));
431         assertEquals(0, pool.getNumIdle(keyb));
432 
433         final Object objA0 = pool.borrowObject(keya);
434         final Object objB0 = pool.borrowObject(keyb);
435 
436         assertEquals(2, pool.getNumActive());
437         assertEquals(0, pool.getNumIdle());
438         assertEquals(1, pool.getNumActive(keya));
439         assertEquals(0, pool.getNumIdle(keya));
440         assertEquals(1, pool.getNumActive(keyb));
441         assertEquals(0, pool.getNumIdle(keyb));
442 
443         final Object objA1 = pool.borrowObject(keya);
444         final Object objB1 = pool.borrowObject(keyb);
445 
446         assertEquals(4, pool.getNumActive());
447         assertEquals(0, pool.getNumIdle());
448         assertEquals(2, pool.getNumActive(keya));
449         assertEquals(0, pool.getNumIdle(keya));
450         assertEquals(2, pool.getNumActive(keyb));
451         assertEquals(0, pool.getNumIdle(keyb));
452 
453         pool.returnObject(keya, objA0);
454         pool.returnObject(keyb, objB0);
455 
456         assertEquals(2, pool.getNumActive());
457         assertEquals(2, pool.getNumIdle());
458         assertEquals(1, pool.getNumActive(keya));
459         assertEquals(1, pool.getNumIdle(keya));
460         assertEquals(1, pool.getNumActive(keyb));
461         assertEquals(1, pool.getNumIdle(keyb));
462 
463         pool.returnObject(keya, objA1);
464         pool.returnObject(keyb, objB1);
465 
466         assertEquals(0, pool.getNumActive());
467         assertEquals(4, pool.getNumIdle());
468         assertEquals(0, pool.getNumActive(keya));
469         assertEquals(2, pool.getNumIdle(keya));
470         assertEquals(0, pool.getNumActive(keyb));
471         assertEquals(2, pool.getNumIdle(keyb));
472 
473         pool.close();
474     }
475 
476     @Test
477     void testClosedPoolBehavior() throws Exception {
478         final KeyedObjectPool<Object, Object> pool;
479         try {
480             pool = makeEmptyPool(new TestFactory());
481         } catch (final UnsupportedOperationException uoe) {
482             return; // test not supported
483         }
484 
485         final Object o1 = pool.borrowObject(KEY);
486         final Object o2 = pool.borrowObject(KEY);
487 
488         pool.close();
489 
490         assertThrows(IllegalStateException.class, () -> pool.addObject(KEY),
491                 "A closed pool must throw an IllegalStateException when addObject is called.");
492 
493         assertThrows(IllegalStateException.class, () -> pool.borrowObject(KEY),
494                 "A closed pool must throw an IllegalStateException when borrowObject is called.");
495 
496         // The following should not throw exceptions just because the pool is closed.
497         assertEquals(0, pool.getNumIdle(KEY), "A closed pool shouldn't have any idle objects.");
498         assertEquals(0, pool.getNumIdle(), "A closed pool shouldn't have any idle objects.");
499         pool.getNumActive();
500         pool.getNumActive(KEY);
501         pool.returnObject(KEY, o1);
502         assertEquals(0, pool.getNumIdle(KEY),
503                 "returnObject should not add items back into the idle object pool for a closed pool.");
504         assertEquals(0, pool.getNumIdle(),
505                 "returnObject should not add items back into the idle object pool for a closed pool.");
506         pool.invalidateObject(KEY, o2);
507         pool.clear(KEY);
508         pool.clear();
509         pool.close();
510     }
511 
512     @Test
513     void testKPOFAddObjectUsage() throws Exception {
514         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
515         final KeyedObjectPool<Object, Object> pool;
516         try {
517             pool = makeEmptyPool(factory);
518         } catch (final UnsupportedOperationException uoe) {
519             return; // test not supported
520         }
521         final List<MethodCall> expectedMethods = new ArrayList<>();
522 
523         // addObject should make a new object, passivate it and put it in the pool
524         pool.addObject(KEY);
525         expectedMethods.add(new MethodCall("makeObject", KEY).returned(ZERO));
526         expectedMethods.add(new MethodCall("passivateObject", KEY, ZERO));
527         assertEquals(expectedMethods, factory.getMethodCalls());
528 
529         // Test exception handling of addObject
530         reset(pool, factory, expectedMethods);
531 
532         // makeObject Exceptions should be propagated to client code from addObject
533         factory.setMakeObjectFail(true);
534         assertThrows(PrivateException.class, () -> pool.addObject(KEY),
535                 "Expected addObject to propagate makeObject exception.");
536         expectedMethods.add(new MethodCall("makeObject", KEY));
537         assertEquals(expectedMethods, factory.getMethodCalls());
538 
539         clear(factory, expectedMethods);
540 
541         // passivateObject Exceptions should be propagated to client code from addObject
542         factory.setMakeObjectFail(false);
543         factory.setPassivateObjectFail(true);
544         assertThrows(PrivateException.class, () -> pool.addObject(KEY),
545                 "Expected addObject to propagate passivateObject exception.");
546         expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
547         expectedMethods.add(new MethodCall("passivateObject", KEY, ONE));
548         assertEquals(expectedMethods, factory.getMethodCalls());
549         pool.close();
550     }
551 
552     @Test
553     void testKPOFBorrowObjectUsages() throws Exception {
554         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
555         final KeyedObjectPool<Object, Object> pool;
556         try {
557             pool = makeEmptyPool(factory);
558         } catch (final UnsupportedOperationException uoe) {
559             return; // test not supported
560         }
561         final List<MethodCall> expectedMethods = new ArrayList<>();
562         final Object obj;
563         if (pool instanceof GenericKeyedObjectPool) {
564             ((GenericKeyedObjectPool<Object, Object>) pool).setTestOnBorrow(true);
565         }
566         // Test correct behavior code paths
567         // existing idle object should be activated and validated
568         pool.addObject(KEY);
569         clear(factory, expectedMethods);
570         obj = pool.borrowObject(KEY);
571         expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
572         expectedMethods.add(new MethodCall("validateObject", KEY, ZERO).returned(Boolean.TRUE));
573         assertEquals(expectedMethods, factory.getMethodCalls());
574         pool.returnObject(KEY, obj);
575         // Test exception handling of borrowObject
576         reset(pool, factory, expectedMethods);
577         // makeObject Exceptions should be propagated to client code from borrowObject
578         factory.setMakeObjectFail(true);
579         assertThrows(PrivateException.class, () -> pool.borrowObject(KEY), "Expected borrowObject to propagate makeObject exception.");
580         expectedMethods.add(new MethodCall("makeObject", KEY));
581         assertEquals(expectedMethods, factory.getMethodCalls());
582         // when activateObject fails in borrowObject, a new object should be
583         // borrowed/created
584         reset(pool, factory, expectedMethods);
585         pool.addObject(KEY);
586         clear(factory, expectedMethods);
587         factory.setActivateObjectFail(true);
588         expectedMethods.add(new MethodCall("activateObject", KEY, obj));
589         assertThrows(NoSuchElementException.class, () -> pool.borrowObject(KEY));
590         // After idle object fails validation, new on is created and activation
591         // fails again for the new one.
592         expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
593         expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
594         AbstractTestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject
595                                                                                   // is flexible here.
596         assertEquals(expectedMethods, factory.getMethodCalls());
597         // when validateObject fails in borrowObject, a new object should be
598         // borrowed/created
599         reset(pool, factory, expectedMethods);
600         pool.addObject(KEY);
601         clear(factory, expectedMethods);
602         factory.setValidateObjectFail(true);
603         // testOnBorrow is on, so this will throw when the newly created instance
604         // fails validation
605         assertThrows(NoSuchElementException.class, () -> pool.borrowObject(KEY));
606         // Activate, then validate for idle instance
607         expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
608         expectedMethods.add(new MethodCall("validateObject", KEY, ZERO));
609         // Make new instance, activate succeeds, validate fails
610         expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
611         expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
612         expectedMethods.add(new MethodCall("validateObject", KEY, ONE));
613         AbstractTestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
614         assertEquals(expectedMethods, factory.getMethodCalls());
615         pool.close();
616     }
617 
618     @Test
619     void testKPOFClearUsages() throws Exception {
620         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
621         final KeyedObjectPool<Object, Object> pool;
622         try {
623             pool = makeEmptyPool(factory);
624         } catch (final UnsupportedOperationException uoe) {
625             return; // test not supported
626         }
627         final List<MethodCall> expectedMethods = new ArrayList<>();
628 
629         // Test correct behavior code paths
630         pool.addObjects(KEY, 5);
631         pool.clear();
632 
633         // Test exception handling clear should swallow destroy object failures
634         reset(pool, factory, expectedMethods);
635         factory.setDestroyObjectFail(true);
636         pool.addObjects(KEY, 5);
637         pool.clear();
638         pool.close();
639     }
640 
641     @Test
642     void testKPOFCloseUsages() throws Exception {
643         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
644         final KeyedObjectPool<Object, Object> pool;
645         try {
646             pool = makeEmptyPool(factory);
647         } catch (final UnsupportedOperationException uoe) {
648             return; // test not supported
649         }
650         final List<MethodCall> expectedMethods = new ArrayList<>();
651 
652         // Test correct behavior code paths
653         pool.addObjects(KEY, 5);
654         pool.close();
655 
656         // Test exception handling close should swallow failures
657         try (KeyedObjectPool<Object, Object> pool2 = makeEmptyPool(factory)) {
658             reset(pool2, factory, expectedMethods);
659             factory.setDestroyObjectFail(true);
660             pool2.addObjects(KEY, 5);
661         }
662     }
663 
664     @Test
665     void testKPOFInvalidateObjectUsages() throws Exception {
666         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
667         final KeyedObjectPool<Object, Object> pool;
668         try {
669             pool = makeEmptyPool(factory);
670         } catch (final UnsupportedOperationException uoe) {
671             return; // test not supported
672         }
673         final List<MethodCall> expectedMethods = new ArrayList<>();
674         // Test correct behavior code paths
675         final Object obj = pool.borrowObject(KEY);
676         clear(factory, expectedMethods);
677 
678         // invalidated object should be destroyed
679         pool.invalidateObject(KEY, obj);
680         expectedMethods.add(new MethodCall("destroyObject", KEY, obj));
681         assertEquals(expectedMethods, factory.getMethodCalls());
682 
683         // Test exception handling of invalidateObject
684         reset(pool, factory, expectedMethods);
685         final Object obj2 = pool.borrowObject(KEY);
686         clear(factory, expectedMethods);
687         factory.setDestroyObjectFail(true);
688         assertThrows(PrivateException.class, () -> pool.invalidateObject(KEY, obj2),
689                 "Expecting destroy exception to propagate");
690         Thread.sleep(250); // could be defered
691         AbstractTestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
692         assertEquals(expectedMethods, factory.getMethodCalls());
693         pool.close();
694     }
695 
696     @Test
697     void testKPOFReturnObjectUsages() throws Exception {
698         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
699         final KeyedObjectPool<Object, Object> pool;
700         try {
701             pool = makeEmptyPool(factory);
702         } catch (final UnsupportedOperationException uoe) {
703             return; // test not supported
704         }
705         final List<MethodCall> expectedMethods = new ArrayList<>();
706         Object obj;
707 
708         // Test correct behavior code paths
709         obj = pool.borrowObject(KEY);
710         clear(factory, expectedMethods);
711 
712         // returned object should be passivated
713         pool.returnObject(KEY, obj);
714         expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
715         assertEquals(expectedMethods, factory.getMethodCalls());
716 
717         // Test exception handling of returnObject
718         reset(pool, factory, expectedMethods);
719 
720         // passivateObject should swallow exceptions and not add the object to the pool
721         pool.addObject(KEY);
722         pool.addObject(KEY);
723         pool.addObject(KEY);
724         assertEquals(3, pool.getNumIdle(KEY));
725         obj = pool.borrowObject(KEY);
726         obj = pool.borrowObject(KEY);
727         assertEquals(1, pool.getNumIdle(KEY));
728         assertEquals(2, pool.getNumActive(KEY));
729         clear(factory, expectedMethods);
730         factory.setPassivateObjectFail(true);
731         pool.returnObject(KEY, obj);
732         expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
733         AbstractTestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject
734                                                                                   // is flexible here.
735         assertEquals(expectedMethods, factory.getMethodCalls());
736         assertEquals(1, pool.getNumIdle(KEY)); // Not added
737         assertEquals(1, pool.getNumActive(KEY)); // But not active
738 
739         reset(pool, factory, expectedMethods);
740         obj = pool.borrowObject(KEY);
741         clear(factory, expectedMethods);
742         factory.setPassivateObjectFail(true);
743         factory.setDestroyObjectFail(true);
744         try {
745             pool.returnObject(KEY, obj);
746             if (!(pool instanceof GenericKeyedObjectPool)) { // ugh, 1.3-compat
747                 fail("Expecting destroyObject exception to be propagated");
748             }
749         } catch (final PrivateException ex) {
750             // Expected
751         }
752         pool.close();
753     }
754 
755     @Test
756     void testToString() {
757         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
758         try (KeyedObjectPool<Object, Object> pool = makeEmptyPool(factory)) {
759             pool.toString();
760         } catch (final UnsupportedOperationException uoe) {
761             return; // test not supported
762         }
763     }
764 }