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  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     protected 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 minCapacity Minimum capacity of the pool to create
213      * @return the newly created keyed object pool
214      */
215     protected abstract <E extends Exception> KeyedObjectPool<Object, Object> makeEmptyPool(int minCapacity);
216 
217     /**
218      * Creates an {@code KeyedObjectPool} with the specified factory.
219      * The pool should be in a default configuration and conform to the expected
220      * behaviors described in {@link KeyedObjectPool}.
221      * Generally speaking there should be no limits on the various object counts.
222      *
223      * @param <E>     The type of exception thrown by the pool
224      * @param factory Factory to use to associate with the pool
225      * @return The newly created empty pool
226      */
227     protected abstract <E extends Exception> KeyedObjectPool<Object, Object> makeEmptyPool(
228             KeyedPooledObjectFactory<Object, Object> factory);
229 
230     protected abstract Object makeKey(int n);
231 
232     private <E extends Exception> void reset(final KeyedObjectPool<Object, Object> pool,
233             final FailingKeyedPooledObjectFactory factory,
234             final List<MethodCall> expectedMethods) throws Exception {
235         pool.clear();
236         clear(factory, expectedMethods);
237         factory.reset();
238     }
239 
240     @AfterEach
241     public void tearDown() {
242         pool = null;
243     }
244 
245     @Test
246     public void testBaseAddObject() throws Exception {
247         try {
248             pool = makeEmptyPool(3);
249         } catch (final UnsupportedOperationException uoe) {
250             return; // skip this test if unsupported
251         }
252         final Object key = makeKey(0);
253         try {
254             assertEquals(0, pool.getNumIdle());
255             assertEquals(0, pool.getNumActive());
256             assertEquals(0, pool.getNumIdle(key));
257             assertEquals(0, pool.getNumActive(key));
258             pool.addObject(key);
259             assertEquals(1, pool.getNumIdle());
260             assertEquals(0, pool.getNumActive());
261             assertEquals(1, pool.getNumIdle(key));
262             assertEquals(0, pool.getNumActive(key));
263             final Object obj = pool.borrowObject(key);
264             assertEquals(getNthObject(key, 0), obj);
265             assertEquals(0, pool.getNumIdle());
266             assertEquals(1, pool.getNumActive());
267             assertEquals(0, pool.getNumIdle(key));
268             assertEquals(1, pool.getNumActive(key));
269             pool.returnObject(key, obj);
270             assertEquals(1, pool.getNumIdle());
271             assertEquals(0, pool.getNumActive());
272             assertEquals(1, pool.getNumIdle(key));
273             assertEquals(0, pool.getNumActive(key));
274         } catch (final UnsupportedOperationException e) {
275             return; // skip this test if one of those calls is unsupported
276         } finally {
277             pool.close();
278         }
279     }
280 
281     @Test
282     public void testBaseBorrow() throws Exception {
283         try {
284             pool = makeEmptyPool(3);
285         } catch (final UnsupportedOperationException uoe) {
286             return; // skip this test if unsupported
287         }
288         final Object keya = makeKey(0);
289         final Object keyb = makeKey(1);
290         assertEquals(getNthObject(keya, 0), pool.borrowObject(keya), "1");
291         assertEquals(getNthObject(keyb, 0), pool.borrowObject(keyb), "2");
292         assertEquals(getNthObject(keyb, 1), pool.borrowObject(keyb), "3");
293         assertEquals(getNthObject(keya, 1), pool.borrowObject(keya), "4");
294         assertEquals(getNthObject(keyb, 2), pool.borrowObject(keyb), "5");
295         assertEquals(getNthObject(keya, 2), pool.borrowObject(keya), "6");
296         pool.close();
297     }
298 
299     @Test
300     public void testBaseBorrowReturn() throws Exception {
301         try {
302             pool = makeEmptyPool(3);
303         } catch (final UnsupportedOperationException uoe) {
304             return; // skip this test if unsupported
305         }
306         final Object keya = makeKey(0);
307         Object obj0 = pool.borrowObject(keya);
308         assertEquals(getNthObject(keya, 0), obj0);
309         Object obj1 = pool.borrowObject(keya);
310         assertEquals(getNthObject(keya, 1), obj1);
311         Object obj2 = pool.borrowObject(keya);
312         assertEquals(getNthObject(keya, 2), obj2);
313         pool.returnObject(keya, obj2);
314         obj2 = pool.borrowObject(keya);
315         assertEquals(getNthObject(keya, 2), obj2);
316         pool.returnObject(keya, obj1);
317         obj1 = pool.borrowObject(keya);
318         assertEquals(getNthObject(keya, 1), obj1);
319         pool.returnObject(keya, obj0);
320         pool.returnObject(keya, obj2);
321         obj2 = pool.borrowObject(keya);
322         if (isLifo()) {
323             assertEquals(getNthObject(keya, 2), obj2);
324         }
325         if (isFifo()) {
326             assertEquals(getNthObject(keya, 0), obj2);
327         }
328         obj0 = pool.borrowObject(keya);
329         if (isLifo()) {
330             assertEquals(getNthObject(keya, 0), obj0);
331         }
332         if (isFifo()) {
333             assertEquals(getNthObject(keya, 2), obj0);
334         }
335         pool.close();
336     }
337 
338     @Test
339     public void testBaseClear() throws Exception {
340         try {
341             pool = makeEmptyPool(3);
342         } catch (final UnsupportedOperationException uoe) {
343             return; // skip this test if unsupported
344         }
345         final Object keya = makeKey(0);
346         assertEquals(0, pool.getNumActive(keya));
347         assertEquals(0, pool.getNumIdle(keya));
348         final Object obj0 = pool.borrowObject(keya);
349         final Object obj1 = pool.borrowObject(keya);
350         assertEquals(2, pool.getNumActive(keya));
351         assertEquals(0, pool.getNumIdle(keya));
352         pool.returnObject(keya, obj1);
353         pool.returnObject(keya, obj0);
354         assertEquals(0, pool.getNumActive(keya));
355         assertEquals(2, pool.getNumIdle(keya));
356         pool.clear(keya);
357         assertEquals(0, pool.getNumActive(keya));
358         assertEquals(0, pool.getNumIdle(keya));
359         final Object obj2 = pool.borrowObject(keya);
360         assertEquals(getNthObject(keya, 2), obj2);
361         pool.close();
362     }
363 
364     @Test
365     public void testBaseInvalidateObject() throws Exception {
366         try {
367             pool = makeEmptyPool(3);
368         } catch (final UnsupportedOperationException uoe) {
369             return; // skip this test if unsupported
370         }
371         final Object keya = makeKey(0);
372         assertEquals(0, pool.getNumActive(keya));
373         assertEquals(0, pool.getNumIdle(keya));
374         final Object obj0 = pool.borrowObject(keya);
375         final Object obj1 = pool.borrowObject(keya);
376         assertEquals(2, pool.getNumActive(keya));
377         assertEquals(0, pool.getNumIdle(keya));
378         pool.invalidateObject(keya, obj0);
379         assertEquals(1, pool.getNumActive(keya));
380         assertEquals(0, pool.getNumIdle(keya));
381         pool.invalidateObject(keya, obj1);
382         assertEquals(0, pool.getNumActive(keya));
383         assertEquals(0, pool.getNumIdle(keya));
384         pool.close();
385     }
386 
387     @Test
388     public void testBaseNumActiveNumIdle() throws Exception {
389         try {
390             pool = makeEmptyPool(3);
391         } catch (final UnsupportedOperationException uoe) {
392             return; // skip this test if unsupported
393         }
394         final Object keya = makeKey(0);
395         assertEquals(0, pool.getNumActive(keya));
396         assertEquals(0, pool.getNumIdle(keya));
397         final Object obj0 = pool.borrowObject(keya);
398         assertEquals(1, pool.getNumActive(keya));
399         assertEquals(0, pool.getNumIdle(keya));
400         final Object obj1 = pool.borrowObject(keya);
401         assertEquals(2, pool.getNumActive(keya));
402         assertEquals(0, pool.getNumIdle(keya));
403         pool.returnObject(keya, obj1);
404         assertEquals(1, pool.getNumActive(keya));
405         assertEquals(1, pool.getNumIdle(keya));
406         pool.returnObject(keya, obj0);
407         assertEquals(0, pool.getNumActive(keya));
408         assertEquals(2, pool.getNumIdle(keya));
409 
410         assertEquals(0, pool.getNumActive("xyzzy12345"));
411         assertEquals(0, pool.getNumIdle("xyzzy12345"));
412 
413         pool.close();
414     }
415 
416     @Test
417     public void testBaseNumActiveNumIdle2() throws Exception {
418         try {
419             pool = makeEmptyPool(6);
420         } catch (final UnsupportedOperationException uoe) {
421             return; // skip this test if unsupported
422         }
423         final Object keya = makeKey(0);
424         final Object keyb = makeKey(1);
425         assertEquals(0, pool.getNumActive());
426         assertEquals(0, pool.getNumIdle());
427         assertEquals(0, pool.getNumActive(keya));
428         assertEquals(0, pool.getNumIdle(keya));
429         assertEquals(0, pool.getNumActive(keyb));
430         assertEquals(0, pool.getNumIdle(keyb));
431 
432         final Object objA0 = pool.borrowObject(keya);
433         final Object objB0 = pool.borrowObject(keyb);
434 
435         assertEquals(2, pool.getNumActive());
436         assertEquals(0, pool.getNumIdle());
437         assertEquals(1, pool.getNumActive(keya));
438         assertEquals(0, pool.getNumIdle(keya));
439         assertEquals(1, pool.getNumActive(keyb));
440         assertEquals(0, pool.getNumIdle(keyb));
441 
442         final Object objA1 = pool.borrowObject(keya);
443         final Object objB1 = pool.borrowObject(keyb);
444 
445         assertEquals(4, pool.getNumActive());
446         assertEquals(0, pool.getNumIdle());
447         assertEquals(2, pool.getNumActive(keya));
448         assertEquals(0, pool.getNumIdle(keya));
449         assertEquals(2, pool.getNumActive(keyb));
450         assertEquals(0, pool.getNumIdle(keyb));
451 
452         pool.returnObject(keya, objA0);
453         pool.returnObject(keyb, objB0);
454 
455         assertEquals(2, pool.getNumActive());
456         assertEquals(2, pool.getNumIdle());
457         assertEquals(1, pool.getNumActive(keya));
458         assertEquals(1, pool.getNumIdle(keya));
459         assertEquals(1, pool.getNumActive(keyb));
460         assertEquals(1, pool.getNumIdle(keyb));
461 
462         pool.returnObject(keya, objA1);
463         pool.returnObject(keyb, objB1);
464 
465         assertEquals(0, pool.getNumActive());
466         assertEquals(4, pool.getNumIdle());
467         assertEquals(0, pool.getNumActive(keya));
468         assertEquals(2, pool.getNumIdle(keya));
469         assertEquals(0, pool.getNumActive(keyb));
470         assertEquals(2, pool.getNumIdle(keyb));
471 
472         pool.close();
473     }
474 
475     @Test
476     public void testClosedPoolBehavior() throws Exception {
477         final KeyedObjectPool<Object, Object> pool;
478         try {
479             pool = makeEmptyPool(new TestFactory());
480         } catch (final UnsupportedOperationException uoe) {
481             return; // test not supported
482         }
483 
484         final Object o1 = pool.borrowObject(KEY);
485         final Object o2 = pool.borrowObject(KEY);
486 
487         pool.close();
488 
489         assertThrows(IllegalStateException.class, () -> pool.addObject(KEY),
490                 "A closed pool must throw an IllegalStateException when addObject is called.");
491 
492         assertThrows(IllegalStateException.class, () -> pool.borrowObject(KEY),
493                 "A closed pool must throw an IllegalStateException when borrowObject is called.");
494 
495         // The following should not throw exceptions just because the pool is closed.
496         assertEquals(0, pool.getNumIdle(KEY), "A closed pool shouldn't have any idle objects.");
497         assertEquals(0, pool.getNumIdle(), "A closed pool shouldn't have any idle objects.");
498         pool.getNumActive();
499         pool.getNumActive(KEY);
500         pool.returnObject(KEY, o1);
501         assertEquals(0, pool.getNumIdle(KEY),
502                 "returnObject should not add items back into the idle object pool for a closed pool.");
503         assertEquals(0, pool.getNumIdle(),
504                 "returnObject should not add items back into the idle object pool for a closed pool.");
505         pool.invalidateObject(KEY, o2);
506         pool.clear(KEY);
507         pool.clear();
508         pool.close();
509     }
510 
511     @Test
512     public void testKPOFAddObjectUsage() throws Exception {
513         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
514         final KeyedObjectPool<Object, Object> pool;
515         try {
516             pool = makeEmptyPool(factory);
517         } catch (final UnsupportedOperationException uoe) {
518             return; // test not supported
519         }
520         final List<MethodCall> expectedMethods = new ArrayList<>();
521 
522         // addObject should make a new object, passivate it and put it in the pool
523         pool.addObject(KEY);
524         expectedMethods.add(new MethodCall("makeObject", KEY).returned(ZERO));
525         expectedMethods.add(new MethodCall("passivateObject", KEY, ZERO));
526         assertEquals(expectedMethods, factory.getMethodCalls());
527 
528         // Test exception handling of addObject
529         reset(pool, factory, expectedMethods);
530 
531         // makeObject Exceptions should be propagated to client code from addObject
532         factory.setMakeObjectFail(true);
533         assertThrows(PrivateException.class, () -> pool.addObject(KEY),
534                 "Expected addObject to propagate makeObject exception.");
535         expectedMethods.add(new MethodCall("makeObject", KEY));
536         assertEquals(expectedMethods, factory.getMethodCalls());
537 
538         clear(factory, expectedMethods);
539 
540         // passivateObject Exceptions should be propagated to client code from addObject
541         factory.setMakeObjectFail(false);
542         factory.setPassivateObjectFail(true);
543         assertThrows(PrivateException.class, () -> pool.addObject(KEY),
544                 "Expected addObject to propagate passivateObject exception.");
545         expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
546         expectedMethods.add(new MethodCall("passivateObject", KEY, ONE));
547         assertEquals(expectedMethods, factory.getMethodCalls());
548         pool.close();
549     }
550 
551     @Test
552     public void testKPOFBorrowObjectUsages() throws Exception {
553         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
554         final KeyedObjectPool<Object, Object> pool;
555         try {
556             pool = makeEmptyPool(factory);
557         } catch (final UnsupportedOperationException uoe) {
558             return; // test not supported
559         }
560         final List<MethodCall> expectedMethods = new ArrayList<>();
561         Object obj;
562 
563         if (pool instanceof GenericKeyedObjectPool) {
564             ((GenericKeyedObjectPool<Object, Object>) pool).setTestOnBorrow(true);
565         }
566 
567         // Test correct behavior code paths
568 
569         // existing idle object should be activated and validated
570         pool.addObject(KEY);
571         clear(factory, expectedMethods);
572         obj = pool.borrowObject(KEY);
573         expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
574         expectedMethods.add(new MethodCall("validateObject", KEY, ZERO).returned(Boolean.TRUE));
575         assertEquals(expectedMethods, factory.getMethodCalls());
576         pool.returnObject(KEY, obj);
577 
578         // Test exception handling of borrowObject
579         reset(pool, factory, expectedMethods);
580 
581         // makeObject Exceptions should be propagated to client code from borrowObject
582         factory.setMakeObjectFail(true);
583         assertThrows(PrivateException.class, () -> pool.borrowObject(KEY),
584                 "Expected borrowObject to propagate makeObject exception.");
585         expectedMethods.add(new MethodCall("makeObject", KEY));
586         assertEquals(expectedMethods, factory.getMethodCalls());
587 
588         // when activateObject fails in borrowObject, a new object should be
589         // borrowed/created
590         reset(pool, factory, expectedMethods);
591         pool.addObject(KEY);
592         clear(factory, expectedMethods);
593 
594         factory.setActivateObjectFail(true);
595         expectedMethods.add(new MethodCall("activateObject", KEY, obj));
596         assertThrows(NoSuchElementException.class, () -> pool.borrowObject(KEY));
597         // After idle object fails validation, new on is created and activation
598         // fails again for the new one.
599         expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
600         expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
601         AbstractTestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject
602                                                                                   // is flexible here.
603         assertEquals(expectedMethods, factory.getMethodCalls());
604 
605         // when validateObject fails in borrowObject, a new object should be
606         // borrowed/created
607         reset(pool, factory, expectedMethods);
608         pool.addObject(KEY);
609         clear(factory, expectedMethods);
610 
611         factory.setValidateObjectFail(true);
612         // testOnBorrow is on, so this will throw when the newly created instance
613         // fails validation
614         assertThrows(NoSuchElementException.class, () -> pool.borrowObject(KEY));
615         // Activate, then validate for idle instance
616         expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
617         expectedMethods.add(new MethodCall("validateObject", KEY, ZERO));
618         // Make new instance, activate succeeds, validate fails
619         expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
620         expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
621         expectedMethods.add(new MethodCall("validateObject", KEY, ONE));
622         AbstractTestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
623         assertEquals(expectedMethods, factory.getMethodCalls());
624         pool.close();
625     }
626 
627     @Test
628     public void testKPOFClearUsages() throws Exception {
629         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
630         final KeyedObjectPool<Object, Object> pool;
631         try {
632             pool = makeEmptyPool(factory);
633         } catch (final UnsupportedOperationException uoe) {
634             return; // test not supported
635         }
636         final List<MethodCall> expectedMethods = new ArrayList<>();
637 
638         // Test correct behavior code paths
639         pool.addObjects(KEY, 5);
640         pool.clear();
641 
642         // Test exception handling clear should swallow destroy object failures
643         reset(pool, factory, expectedMethods);
644         factory.setDestroyObjectFail(true);
645         pool.addObjects(KEY, 5);
646         pool.clear();
647         pool.close();
648     }
649 
650     @Test
651     public void testKPOFCloseUsages() throws Exception {
652         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
653         KeyedObjectPool<Object, Object> pool;
654         try {
655             pool = makeEmptyPool(factory);
656         } catch (final UnsupportedOperationException uoe) {
657             return; // test not supported
658         }
659         final List<MethodCall> expectedMethods = new ArrayList<>();
660 
661         // Test correct behavior code paths
662         pool.addObjects(KEY, 5);
663         pool.close();
664 
665         // Test exception handling close should swallow failures
666         try (final KeyedObjectPool<Object, Object> pool2 = makeEmptyPool(factory)) {
667             reset(pool2, factory, expectedMethods);
668             factory.setDestroyObjectFail(true);
669             pool2.addObjects(KEY, 5);
670         }
671     }
672 
673     @Test
674     public void testKPOFInvalidateObjectUsages() throws Exception {
675         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
676         final KeyedObjectPool<Object, Object> pool;
677         try {
678             pool = makeEmptyPool(factory);
679         } catch (final UnsupportedOperationException uoe) {
680             return; // test not supported
681         }
682         final List<MethodCall> expectedMethods = new ArrayList<>();
683         Object obj;
684 
685         // Test correct behavior code paths
686 
687         obj = pool.borrowObject(KEY);
688         clear(factory, expectedMethods);
689 
690         // invalidated object should be destroyed
691         pool.invalidateObject(KEY, obj);
692         expectedMethods.add(new MethodCall("destroyObject", KEY, obj));
693         assertEquals(expectedMethods, factory.getMethodCalls());
694 
695         // Test exception handling of invalidateObject
696         reset(pool, factory, expectedMethods);
697         final Object obj2 = pool.borrowObject(KEY);
698         clear(factory, expectedMethods);
699         factory.setDestroyObjectFail(true);
700         assertThrows(PrivateException.class, () -> pool.invalidateObject(KEY, obj2),
701                 "Expecting destroy exception to propagate");
702         Thread.sleep(250); // could be defered
703         AbstractTestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
704         assertEquals(expectedMethods, factory.getMethodCalls());
705         pool.close();
706     }
707 
708     @Test
709     public void testKPOFReturnObjectUsages() throws Exception {
710         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
711         final KeyedObjectPool<Object, Object> pool;
712         try {
713             pool = makeEmptyPool(factory);
714         } catch (final UnsupportedOperationException uoe) {
715             return; // test not supported
716         }
717         final List<MethodCall> expectedMethods = new ArrayList<>();
718         Object obj;
719 
720         // Test correct behavior code paths
721         obj = pool.borrowObject(KEY);
722         clear(factory, expectedMethods);
723 
724         // returned object should be passivated
725         pool.returnObject(KEY, obj);
726         expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
727         assertEquals(expectedMethods, factory.getMethodCalls());
728 
729         // Test exception handling of returnObject
730         reset(pool, factory, expectedMethods);
731 
732         // passivateObject should swallow exceptions and not add the object to the pool
733         pool.addObject(KEY);
734         pool.addObject(KEY);
735         pool.addObject(KEY);
736         assertEquals(3, pool.getNumIdle(KEY));
737         obj = pool.borrowObject(KEY);
738         obj = pool.borrowObject(KEY);
739         assertEquals(1, pool.getNumIdle(KEY));
740         assertEquals(2, pool.getNumActive(KEY));
741         clear(factory, expectedMethods);
742         factory.setPassivateObjectFail(true);
743         pool.returnObject(KEY, obj);
744         expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
745         AbstractTestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject
746                                                                                   // is flexible here.
747         assertEquals(expectedMethods, factory.getMethodCalls());
748         assertEquals(1, pool.getNumIdle(KEY)); // Not added
749         assertEquals(1, pool.getNumActive(KEY)); // But not active
750 
751         reset(pool, factory, expectedMethods);
752         obj = pool.borrowObject(KEY);
753         clear(factory, expectedMethods);
754         factory.setPassivateObjectFail(true);
755         factory.setDestroyObjectFail(true);
756         try {
757             pool.returnObject(KEY, obj);
758             if (!(pool instanceof GenericKeyedObjectPool)) { // ugh, 1.3-compat
759                 fail("Expecting destroyObject exception to be propagated");
760             }
761         } catch (final PrivateException ex) {
762             // Expected
763         }
764         pool.close();
765     }
766 
767     @Test
768     public void testToString() {
769         final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
770         try (final KeyedObjectPool<Object, Object> pool = makeEmptyPool(factory)) {
771             pool.toString();
772         } catch (final UnsupportedOperationException uoe) {
773             return; // test not supported
774         }
775     }
776 }