1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.dbcp2;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23
24 import java.sql.Connection;
25 import java.sql.SQLException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28
29 import javax.management.OperationsException;
30
31 import org.apache.commons.pool2.impl.GenericObjectPool;
32 import org.junit.jupiter.api.AfterEach;
33 import org.junit.jupiter.api.Assertions;
34 import org.junit.jupiter.api.BeforeEach;
35 import org.junit.jupiter.api.Test;
36
37
38
39 public class TestPoolableConnection {
40
41 private GenericObjectPool<PoolableConnection> pool;
42
43 @BeforeEach
44 public void setUp() throws Exception {
45 final PoolableConnectionFactory factory = new PoolableConnectionFactory(
46 new DriverConnectionFactory(new TesterDriver(), "jdbc:apache:commons:testdriver", null), null);
47 factory.setDefaultAutoCommit(Boolean.TRUE);
48 factory.setDefaultReadOnly(Boolean.TRUE);
49
50 pool = new GenericObjectPool<>(factory);
51 factory.setPool(pool);
52 }
53
54 @AfterEach
55 public void tearDown() {
56 pool.close();
57 }
58
59 @Test
60 void testClosingWrappedInDelegate() throws Exception {
61 Assertions.assertEquals(0, pool.getNumActive());
62
63 final Connection conn = pool.borrowObject();
64 final DelegatingConnection<Connection> outer = new DelegatingConnection<>(conn);
65
66 Assertions.assertFalse(outer.isClosed());
67 Assertions.assertFalse(conn.isClosed());
68 Assertions.assertEquals(1, pool.getNumActive());
69
70 outer.close();
71
72 Assertions.assertTrue(outer.isClosed());
73 Assertions.assertTrue(conn.isClosed());
74 Assertions.assertEquals(0, pool.getNumActive());
75 Assertions.assertEquals(1, pool.getNumIdle());
76 }
77
78 @Test
79 void testConnectionPool() throws Exception {
80
81 final Connection c = pool.borrowObject();
82
83 assertNotNull(c, "Connection should be created and should not be null");
84 assertEquals(1, pool.getNumActive(), "There should be exactly one active object in the pool");
85
86
87 c.close();
88
89 assertEquals(0, pool.getNumActive(), "There should now be zero active objects in the pool");
90 }
91
92 @Test
93 void testDisconnectionIgnoreSqlCodes() throws Exception {
94 pool.setTestOnReturn(true);
95 final PoolableConnectionFactory factory = (PoolableConnectionFactory) pool.getFactory();
96 factory.setFastFailValidation(true);
97 factory.setDisconnectionIgnoreSqlCodes(Arrays.asList("08S02", "08007"));
98
99 final PoolableConnection conn = pool.borrowObject();
100 final TesterConnection nativeConnection = (TesterConnection) conn.getInnermostDelegate();
101
102
103 nativeConnection.setFailure(new SQLException("Non-fatal connection error.", "08S02"));
104 assertThrows(SQLException.class, conn::createStatement);
105 nativeConnection.setFailure(null);
106
107
108 conn.close();
109 assertEquals(0, pool.getNumActive(), "The pool should have no active connections");
110 assertEquals(1, pool.getNumIdle(), "The pool should have one idle connection");
111 }
112
113 @Test
114 void testFastFailValidation() throws Exception {
115 pool.setTestOnReturn(true);
116 final PoolableConnectionFactory factory = (PoolableConnectionFactory) pool.getFactory();
117 factory.setFastFailValidation(true);
118 final PoolableConnection conn = pool.borrowObject();
119 final TesterConnection nativeConnection = (TesterConnection) conn.getInnermostDelegate();
120
121
122 nativeConnection.setFailure(new SQLException("Not fatal error.", "Invalid syntax."));
123 assertThrows(SQLException.class, conn::createStatement);
124
125 nativeConnection.setFailure(null);
126
127
128 conn.validate("SELECT 1", 1000);
129
130
131 nativeConnection.setFailure(new SQLException("Fatal connection error.", "01002"));
132 assertThrows(SQLException.class, conn::createStatement);
133
134 nativeConnection.setFailure(null);
135
136
137 assertThrows(SQLException.class, () -> conn.validate("SELECT 1", 1000), "Should throw SQL exception on validation.");
138
139
140 conn.close();
141 assertEquals(0, pool.getNumActive(), "The pool should have no active connections");
142 assertEquals(0, pool.getNumIdle(), "The pool should have no idle connections");
143 }
144
145 @Test
146 void testFastFailValidationCustomCodes() throws Exception {
147 pool.setTestOnReturn(true);
148 final PoolableConnectionFactory factory = (PoolableConnectionFactory) pool.getFactory();
149 factory.setFastFailValidation(true);
150 final ArrayList<String> disconnectionSqlCodes = new ArrayList<>();
151 disconnectionSqlCodes.add("XXX");
152 factory.setDisconnectionSqlCodes(disconnectionSqlCodes);
153 final PoolableConnection conn = pool.borrowObject();
154 final TesterConnection nativeConnection = (TesterConnection) conn.getInnermostDelegate();
155
156
157 nativeConnection.setFailure(new SQLException("Fatal connection error.", "XXX"));
158 assertThrows(SQLException.class, conn::createStatement);
159
160 nativeConnection.setFailure(null);
161
162
163 conn.close();
164 assertEquals(0, pool.getNumActive(), "The pool should have no active connections");
165 assertEquals(0, pool.getNumIdle(), "The pool should have no idle connections");
166 }
167
168 @Test
169 void testIsDisconnectionSqlExceptionStackOverflow() throws Exception {
170 final int maxDeep = 100_000;
171 final SQLException rootException = new SQLException("Data truncated", "22001");
172 SQLException parentException = rootException;
173 for (int i = 0; i <= maxDeep; i++) {
174 final SQLException childException = new SQLException("Data truncated: " + i, "22001");
175 parentException.setNextException(childException);
176 parentException = childException;
177 }
178 final Connection conn = pool.borrowObject();
179 assertFalse(((PoolableConnection) conn).isDisconnectionSqlException(rootException));
180 assertFalse(((PoolableConnection) conn).isFatalException(rootException));
181 }
182
183
184
185
186 @Test
187 void testMXBeanCompliance() throws OperationsException {
188 TestBasicDataSourceMXBean.testMXBeanCompliance(PoolableConnectionMXBean.class);
189 }
190
191
192
193 @Test
194 void testPoolableConnectionLeak() throws Exception {
195
196 final Connection conn = pool.borrowObject();
197
198
199
200 ((PoolableConnection) conn).getInnermostDelegate().close();
201
202
203
204
205
206
207 try {
208 conn.close();
209 } catch (final SQLException e) {
210
211
212 }
213
214 assertEquals(0, pool.getNumActive(), "The pool should have no active connections");
215 }
216 }