1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.dbcp2;
19
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertTrue;
23
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.io.StringWriter;
27 import java.sql.CallableStatement;
28 import java.sql.Connection;
29 import java.sql.PreparedStatement;
30 import java.sql.ResultSet;
31 import java.sql.SQLException;
32 import java.sql.Statement;
33 import java.time.Duration;
34 import java.time.Instant;
35
36 import org.apache.commons.pool2.KeyedObjectPool;
37 import org.junit.jupiter.api.Assertions;
38 import org.junit.jupiter.api.BeforeEach;
39 import org.junit.jupiter.api.Test;
40
41
42
43
44 public class TestAbandonedBasicDataSource extends TestBasicDataSource {
45
46 private StringWriter sw;
47
48
49
50
51 private void assertAndReset(final DelegatingConnection<?> con) {
52 assertTrue(con.getLastUsedInstant().compareTo(Instant.EPOCH) > 0);
53 con.setLastUsed(Instant.EPOCH);
54 }
55
56
57
58
59
60
61 private void checkLastUsedPreparedStatement(final PreparedStatement ps, final DelegatingConnection<?> conn)
62 throws Exception {
63 ps.execute();
64 assertAndReset(conn);
65 try (ResultSet rs = ps.executeQuery()) {
66 Assertions.assertNotNull(rs);
67 }
68 assertAndReset(conn);
69 ps.executeUpdate();
70 assertAndReset(conn);
71 }
72
73
74
75
76 private void checkLastUsedStatement(final Statement st, final DelegatingConnection<?> conn) throws Exception {
77 st.execute("");
78 assertAndReset(conn);
79 st.execute("", new int[] {});
80 assertAndReset(conn);
81 st.execute("", 0);
82 assertAndReset(conn);
83 st.executeBatch();
84 assertAndReset(conn);
85 st.executeLargeBatch();
86 assertAndReset(conn);
87 try (ResultSet rs = st.executeQuery("")) {
88 Assertions.assertNotNull(rs);
89 }
90 assertAndReset(conn);
91 st.executeUpdate("");
92 assertAndReset(conn);
93 st.executeUpdate("", new int[] {});
94 assertAndReset(conn);
95 st.executeLargeUpdate("", new int[] {});
96 assertAndReset(conn);
97 st.executeUpdate("", 0);
98 assertAndReset(conn);
99 st.executeLargeUpdate("", 0);
100 assertAndReset(conn);
101 st.executeUpdate("", new String[] {});
102 assertAndReset(conn);
103 st.executeLargeUpdate("", new String[] {});
104 assertAndReset(conn);
105 }
106
107 private void createStatement(final Connection conn) throws Exception{
108 final PreparedStatement ps = conn.prepareStatement("");
109 Assertions.assertNotNull(ps);
110 }
111
112 @Override
113 @BeforeEach
114 public void setUp() throws Exception {
115 super.setUp();
116
117
118 ds.setLogAbandoned(true);
119 ds.setRemoveAbandonedOnBorrow(true);
120 ds.setRemoveAbandonedOnMaintenance(true);
121 ds.setRemoveAbandonedTimeout(Duration.ofSeconds(10));
122 sw = new StringWriter();
123 ds.setAbandonedLogWriter(new PrintWriter(sw));
124 }
125
126 @Test
127 public void testAbandoned() throws Exception {
128
129 ds.setRemoveAbandonedTimeout(Duration.ZERO);
130 ds.setMaxTotal(1);
131
132 for (int i = 0; i < 3; i++) {
133 assertNotNull(ds.getConnection());
134 }
135 }
136
137 @Test
138 public void testAbandonedClose() throws Exception {
139
140 ds.setRemoveAbandonedTimeout(Duration.ZERO);
141 ds.setMaxTotal(1);
142 ds.setAccessToUnderlyingConnectionAllowed(true);
143
144 try (Connection conn1 = getConnection()) {
145 assertNotNull(conn1);
146 assertEquals(1, ds.getNumActive());
147
148 try (Connection conn2 = getConnection()) {
149
150
151 assertNotNull(conn2);
152 assertEquals(1, ds.getNumActive());
153
154 assertTrue(((DelegatingConnection<?>) conn1).getInnermostDelegate().isClosed());
155
156 final TesterConnection tCon = (TesterConnection) ((DelegatingConnection<?>) conn1)
157 .getInnermostDelegate();
158 assertTrue(tCon.isAborted());
159
160 }
161 assertEquals(0, ds.getNumActive());
162
163
164 }
165 assertEquals(0, ds.getNumActive());
166 final String string = sw.toString();
167 assertTrue(string.contains("testAbandonedClose"), string);
168 }
169
170 @Test
171 public void testAbandonedCloseWithExceptions() throws Exception {
172
173 ds.setRemoveAbandonedTimeout(Duration.ZERO);
174 ds.setMaxTotal(1);
175 ds.setAccessToUnderlyingConnectionAllowed(true);
176
177 final Connection conn1 = getConnection();
178 assertNotNull(conn1);
179 assertEquals(1, ds.getNumActive());
180
181 final Connection conn2 = getConnection();
182 assertNotNull(conn2);
183 assertEquals(1, ds.getNumActive());
184
185
186 final TesterConnection tconn1 = (TesterConnection) ((DelegatingConnection<?>) conn1).getInnermostDelegate();
187 tconn1.setFailure(new IOException("network error"));
188 final TesterConnection tconn2 = (TesterConnection) ((DelegatingConnection<?>) conn2).getInnermostDelegate();
189 tconn2.setFailure(new IOException("network error"));
190
191 try {
192 conn2.close();
193 } catch (final SQLException ex) {
194
195 }
196 assertEquals(0, ds.getNumActive());
197
198 try {
199 conn1.close();
200 } catch (final SQLException ex) {
201
202 }
203 assertEquals(0, ds.getNumActive());
204 final String string = sw.toString();
205 assertTrue(string.contains("testAbandonedCloseWithExceptions"), string);
206 }
207
208 @Test
209 public void testAbandonedStackTraces() throws Exception {
210
211 ds.setRemoveAbandonedTimeout(Duration.ZERO);
212 ds.setMaxTotal(1);
213 ds.setAccessToUnderlyingConnectionAllowed(true);
214 ds.setAbandonedUsageTracking(true);
215
216 try (Connection conn1 = getConnection()) {
217 assertNotNull(conn1);
218 assertEquals(1, ds.getNumActive());
219
220 try (Statement stmt = conn1.createStatement()) {
221 assertNotNull(stmt);
222 stmt.execute("SELECT 1 FROM DUAL");
223 }
224
225 try (Connection conn2 = getConnection()) {
226
227
228 assertNotNull(conn2);
229 assertEquals(1, ds.getNumActive());
230
231 assertTrue(((DelegatingConnection<?>) conn1).getInnermostDelegate().isClosed());
232
233 final TesterConnection tCon = (TesterConnection) ((DelegatingConnection<?>) conn1)
234 .getInnermostDelegate();
235 assertTrue(tCon.isAborted());
236
237 }
238 assertEquals(0, ds.getNumActive());
239 }
240 assertEquals(0, ds.getNumActive());
241 final String stackTrace = sw.toString();
242 assertTrue(stackTrace.contains("testAbandonedStackTraces"), stackTrace);
243 assertTrue(stackTrace.contains("Pooled object created"), stackTrace);
244 assertTrue(stackTrace.contains("The last code to use this object was:"), stackTrace);
245 }
246
247
248
249
250
251
252 @Test
253 public void testGarbageCollectorCleanUp01() throws Exception {
254 try (DelegatingConnection<?> conn = (DelegatingConnection<?>) ds.getConnection()) {
255 Assertions.assertEquals(0, conn.getTrace().size());
256 createStatement(conn);
257 Assertions.assertEquals(1, conn.getTrace().size());
258 System.gc();
259 Assertions.assertEquals(0, conn.getTrace().size());
260 }
261 }
262
263
264
265
266 @Test
267 public void testGarbageCollectorCleanUp02() throws Exception {
268 ds.setPoolPreparedStatements(true);
269 ds.setAccessToUnderlyingConnectionAllowed(true);
270 final DelegatingConnection<?> conn = (DelegatingConnection<?>) ds.getConnection();
271 final PoolableConnection poolableConn = (PoolableConnection) conn.getDelegate();
272 final PoolingConnection poolingConn = (PoolingConnection) poolableConn.getDelegate();
273 final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> gkop = poolingConn.getStatementPool();
274 Assertions.assertEquals(0, conn.getTrace().size());
275 Assertions.assertEquals(0, gkop.getNumActive());
276 createStatement(conn);
277 Assertions.assertEquals(1, conn.getTrace().size());
278 Assertions.assertEquals(1, gkop.getNumActive());
279 System.gc();
280
281
282 int count = 0;
283 while (count < 50 && gkop.getNumActive() > 0) {
284 Thread.sleep(100);
285 count++;
286 }
287 Assertions.assertEquals(0, gkop.getNumActive());
288 Assertions.assertEquals(0, conn.getTrace().size());
289 }
290
291
292
293
294
295 @Test
296 public void testLastUsed() throws Exception {
297 ds.setRemoveAbandonedTimeout(Duration.ofSeconds(1));
298 ds.setMaxTotal(2);
299 try (Connection conn1 = ds.getConnection()) {
300 Thread.sleep(500);
301 try (Statement s = conn1.createStatement()) {
302
303 }
304 Thread.sleep(800);
305 final Connection conn2 = ds.getConnection();
306 try (Statement s = conn1.createStatement()) {
307
308 }
309 conn2.close();
310 Thread.sleep(500);
311 try (PreparedStatement ps = conn1.prepareStatement("SELECT 1 FROM DUAL")) {
312
313 }
314 Thread.sleep(800);
315 try (Connection c = ds.getConnection()) {
316
317 }
318 try (Statement s = conn1.createStatement()) {
319
320 }
321 }
322 }
323
324
325
326
327
328 @Test
329 public void testLastUsedLargePreparedStatementUse() throws Exception {
330 ds.setRemoveAbandonedTimeout(Duration.ofSeconds(1));
331 ds.setMaxTotal(2);
332 try (Connection conn1 = ds.getConnection(); Statement st = conn1.createStatement()) {
333 final String querySQL = "SELECT 1 FROM DUAL";
334 Thread.sleep(500);
335 try (ResultSet rs = st.executeQuery(querySQL)) {
336 Assertions.assertNotNull(rs);
337 }
338 Thread.sleep(800);
339 try (final Connection conn2 = ds.getConnection()) {
340 try (ResultSet rs = st.executeQuery(querySQL)) {
341 Assertions.assertNotNull(rs);
342 }
343 }
344 Thread.sleep(500);
345 st.executeLargeUpdate("");
346 Thread.sleep(800);
347 try (Connection c = ds.getConnection()) {
348
349 }
350 try (Statement s = conn1.createStatement()) {
351
352 }
353 }
354 }
355
356
357
358
359
360 @Test
361 public void testLastUsedPrepareCall() throws Exception {
362 ds.setRemoveAbandonedTimeout(Duration.ofSeconds(1));
363 ds.setMaxTotal(2);
364 try (Connection conn1 = ds.getConnection()) {
365 Thread.sleep(500);
366 try (CallableStatement cs = conn1.prepareCall("{call home}")) {
367
368 }
369 Thread.sleep(800);
370 final Connection conn2 = ds.getConnection();
371 try (CallableStatement cs = conn1.prepareCall("{call home}")) {
372
373 }
374 conn2.close();
375 Thread.sleep(500);
376 try (CallableStatement cs = conn1.prepareCall("{call home}")) {
377
378 }
379 Thread.sleep(800);
380 try (Connection c = ds.getConnection()) {
381
382 }
383 try (Statement s = conn1.createStatement()) {
384
385 }
386 }
387 }
388
389
390
391
392
393 @Test
394 public void testLastUsedPreparedStatementUse() throws Exception {
395 ds.setRemoveAbandonedTimeout(Duration.ofSeconds(1));
396 ds.setMaxTotal(2);
397 try (Connection conn1 = ds.getConnection();
398 Statement st = conn1.createStatement()) {
399 final String querySQL = "SELECT 1 FROM DUAL";
400 Thread.sleep(500);
401 Assertions.assertNotNull(st.executeQuery(querySQL));
402 Thread.sleep(800);
403 final Connection conn2 = ds.getConnection();
404 Assertions.assertNotNull(st.executeQuery(querySQL));
405 conn2.close();
406 Thread.sleep(500);
407 st.executeUpdate("");
408 Thread.sleep(800);
409 try (Connection c = ds.getConnection()) {}
410 try (Statement s = conn1.createStatement()) {}
411 }
412 }
413
414
415
416
417
418 @Test
419 public void testLastUsedUpdate() throws Exception {
420 try (DelegatingConnection<?> conn = (DelegatingConnection<?>) ds.getConnection();
421 final PreparedStatement ps = conn.prepareStatement("");
422 final CallableStatement cs = conn.prepareCall("");
423 final Statement st = conn.prepareStatement("")) {
424 checkLastUsedStatement(ps, conn);
425 checkLastUsedPreparedStatement(ps, conn);
426 checkLastUsedStatement(cs, conn);
427 checkLastUsedPreparedStatement(cs, conn);
428 checkLastUsedStatement(st, conn);
429 }
430 }
431 }