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