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