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