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