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.proxy;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertNotNull;
21  import static org.junit.jupiter.api.Assertions.assertThrows;
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import java.io.PrintWriter;
25  import java.io.StringWriter;
26  import java.time.Duration;
27  
28  import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
29  import org.apache.commons.pool2.KeyedObjectPool;
30  import org.apache.commons.pool2.KeyedPooledObjectFactory;
31  import org.apache.commons.pool2.PooledObject;
32  import org.apache.commons.pool2.impl.AbandonedConfig;
33  import org.apache.commons.pool2.impl.DefaultPooledObject;
34  import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
35  import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
36  import org.junit.jupiter.api.BeforeEach;
37  import org.junit.jupiter.api.Test;
38  
39  public abstract class AbstractTestProxiedKeyedObjectPool {
40  
41      private static final class TestKeyedObjectFactory extends BaseKeyedPooledObjectFactory<String, TestObject> {
42  
43          @Override
44          public TestObject create(final String key) {
45              return new TestObjectImpl();
46          }
47  
48          @Override
49          public PooledObject<TestObject> wrap(final TestObject value) {
50              return new DefaultPooledObject<>(value);
51          }
52      }
53  
54      protected interface TestObject {
55          String getData();
56          void setData(String data);
57      }
58  
59      private static final class TestObjectImpl implements TestObject {
60  
61          private String data;
62  
63          @Override
64          public String getData() {
65              return data;
66          }
67  
68          @Override
69          public void setData(final String data) {
70              this.data = data;
71          }
72      }
73  
74      private static final String KEY1 = "key1";
75  
76      private static final String DATA1 = "data1";
77  
78      private static final Duration ABANDONED_TIMEOUT_SECS = Duration.ofSeconds(3);
79  
80      private KeyedObjectPool<String, TestObject> pool;
81  
82      private StringWriter log;
83  
84      protected abstract ProxySource<TestObject> getproxySource();
85  
86      @BeforeEach
87      public void setUp() {
88          log = new StringWriter();
89  
90          final PrintWriter pw = new PrintWriter(log);
91          final AbandonedConfig abandonedConfig = new AbandonedConfig();
92          abandonedConfig.setLogAbandoned(true);
93          abandonedConfig.setRemoveAbandonedOnBorrow(true);
94          abandonedConfig.setUseUsageTracking(true);
95          abandonedConfig.setRemoveAbandonedTimeout(ABANDONED_TIMEOUT_SECS);
96          abandonedConfig.setLogWriter(pw);
97  
98          final GenericKeyedObjectPoolConfig<TestObject> config = new GenericKeyedObjectPoolConfig<>();
99          config.setMaxTotal(3);
100 
101         final KeyedPooledObjectFactory<String, TestObject> factory = new TestKeyedObjectFactory();
102 
103         @SuppressWarnings("resource")
104         final KeyedObjectPool<String, TestObject> innerPool = new GenericKeyedObjectPool<>(factory, config, abandonedConfig);
105 
106         pool = new ProxiedKeyedObjectPool<>(innerPool, getproxySource());
107     }
108 
109     @Test
110     void testAccessAfterInvalidate() throws Exception {
111         final TestObject obj = pool.borrowObject(KEY1);
112         assertNotNull(obj);
113 
114         // Make sure proxied methods are working
115         obj.setData(DATA1);
116         assertEquals(DATA1, obj.getData());
117 
118         pool.invalidateObject(KEY1, obj);
119 
120         assertNotNull(obj);
121 
122         assertThrows(IllegalStateException.class,
123                 obj::getData);
124 
125     }
126 
127     @Test
128     void testAccessAfterReturn() throws Exception {
129         final TestObject obj = pool.borrowObject(KEY1);
130         assertNotNull(obj);
131 
132         // Make sure proxied methods are working
133         obj.setData(DATA1);
134         assertEquals(DATA1, obj.getData());
135 
136         pool.returnObject(KEY1, obj);
137 
138         assertNotNull(obj);
139         assertThrows(IllegalStateException.class,
140                 obj::getData);
141     }
142 
143     @Test
144     void testBorrowObject() throws Exception {
145         final TestObject obj = pool.borrowObject(KEY1);
146         assertNotNull(obj);
147 
148         // Make sure proxied methods are working
149         obj.setData(DATA1);
150         assertEquals(DATA1, obj.getData());
151 
152         pool.returnObject(KEY1, obj);
153     }
154 
155     @Test
156     void testPassThroughMethods01() throws Exception {
157         assertEquals(0, pool.getNumActive());
158         assertEquals(0, pool.getNumIdle());
159 
160         pool.addObject(KEY1);
161 
162         assertEquals(0, pool.getNumActive());
163         assertEquals(1, pool.getNumIdle());
164 
165         pool.clear();
166 
167         assertEquals(0, pool.getNumActive());
168         assertEquals(0, pool.getNumIdle());
169     }
170 
171     @Test
172     void testPassThroughMethods02() {
173         pool.close();
174         assertThrows(IllegalStateException.class,
175                 () -> pool.addObject(KEY1));
176     }
177 
178     @Test
179     void testUsageTracking() throws Exception {
180         final TestObject obj = pool.borrowObject(KEY1);
181         assertNotNull(obj);
182 
183         // Use the object to trigger collection of last used stack trace
184         obj.setData(DATA1);
185 
186         // Sleep long enough for the object to be considered abandoned
187         Thread.sleep(ABANDONED_TIMEOUT_SECS.plusSeconds(2).toMillis());
188 
189         // Borrow another object to trigger the abandoned object processing
190         pool.borrowObject(KEY1);
191 
192         final String logOutput = log.getBuffer().toString();
193 
194         assertTrue(logOutput.contains("Pooled object created"));
195         assertTrue(logOutput.contains("The last code to use this object was"));
196     }
197 
198 }