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  
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.assertNotSame;
23  import static org.junit.jupiter.api.Assertions.assertSame;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  
27  import java.sql.Connection;
28  import java.sql.PreparedStatement;
29  import java.sql.SQLException;
30  import java.sql.Statement;
31  import java.time.Duration;
32  
33  import org.apache.commons.pool2.KeyedObjectPool;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  
37  /**
38   * TestSuite for BasicDataSource with prepared statement pooling enabled
39   */
40  public class TestPStmtPoolingBasicDataSource extends TestBasicDataSource {
41  
42      @Override
43      @BeforeEach
44      public void setUp() throws Exception {
45          super.setUp();
46          // PoolPreparedStatements enabled, should not affect the basic tests
47          ds.setPoolPreparedStatements(true);
48          ds.setMaxOpenPreparedStatements(2);
49      }
50  
51      /**
52       * Verifies that the prepared statement pool behaves as an LRU cache,
53       * closing least-recently-used statements idle in the pool to make room
54       * for new ones if necessary.
55       */
56      @Test
57      void testLRUBehavior() throws Exception {
58          ds.setMaxOpenPreparedStatements(3);
59  
60          final Connection conn = getConnection();
61          assertNotNull(conn);
62  
63          // Open 3 statements and then close them into the pool
64          final PreparedStatement stmt1 = conn.prepareStatement("select 'a' from dual");
65          final PreparedStatement inner1 = (PreparedStatement) ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
66          final PreparedStatement stmt2 = conn.prepareStatement("select 'b' from dual");
67          final PreparedStatement inner2 = (PreparedStatement) ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
68          final PreparedStatement stmt3 = conn.prepareStatement("select 'c' from dual");
69          final PreparedStatement inner3 = (PreparedStatement) ((DelegatingPreparedStatement) stmt3).getInnermostDelegate();
70          stmt1.close();
71          Thread.sleep(100); // Make sure return timestamps are different
72          stmt2.close();
73          Thread.sleep(100);
74          stmt3.close();
75  
76          // Pool now has three idle statements, getting another one will force oldest (stmt1) out
77          final PreparedStatement stmt4 = conn.prepareStatement("select 'd' from dual");
78          assertNotNull(stmt4);
79  
80          // Verify that inner1 has been closed
81          assertThrows(SQLException.class, inner1::clearParameters, "expecting SQLExcption - statement should be closed");
82          // But others are still open
83          inner2.clearParameters();
84          inner3.clearParameters();
85  
86          // Now make sure stmt1 does not come back from the dead
87          final PreparedStatement stmt5 = conn.prepareStatement("select 'a' from dual");
88          final PreparedStatement inner5 = (PreparedStatement) ((DelegatingPreparedStatement) stmt5).getInnermostDelegate();
89          assertNotSame(inner5, inner1);
90  
91          // inner2 should be closed now
92          assertThrows(SQLException.class, inner2::clearParameters, "expecting SQLExcption - statement should be closed");
93  
94          // But inner3 should still be open
95          inner3.clearParameters();
96      }
97  
98      /**
99       * Tests high-concurrency contention for connections and pooled prepared statements.
100      * DBCP-414
101      */
102     @Test
103     void testMultipleThreads1() throws Exception {
104         ds.setMaxWait(Duration.ofMillis(-1));
105         ds.setMaxTotal(5);
106         ds.setMaxOpenPreparedStatements(-1);
107         multipleThreads(Duration.ofMillis(5), false, false, Duration.ofMillis(-1), 3, 100, 10000);
108     }
109 
110     @Test
111     void testPreparedStatementPooling() throws Exception {
112         final Connection conn = getConnection();
113         assertNotNull(conn);
114 
115         final PreparedStatement stmt1 = conn.prepareStatement("select 'a' from dual");
116         assertNotNull(stmt1);
117 
118         final PreparedStatement stmt2 = conn.prepareStatement("select 'b' from dual");
119         assertNotNull(stmt2);
120 
121         assertNotSame(stmt1, stmt2);
122 
123         // go over the maxOpen limit
124         assertThrows(SQLException.class, () -> conn.prepareStatement("select 'c' from dual"));
125 
126         // make idle
127         stmt2.close();
128 
129         // test cleanup the 'b' statement
130         final PreparedStatement stmt3 = conn.prepareStatement("select 'c' from dual");
131         assertNotNull(stmt3);
132         assertNotSame(stmt3, stmt1);
133         assertNotSame(stmt3, stmt2);
134 
135         // normal reuse of statement
136         stmt1.close();
137         try (final PreparedStatement stmt4 = conn.prepareStatement("select 'a' from dual")) {
138             assertNotNull(stmt4);
139         }
140     }
141 
142     // Bugzilla Bug 27246
143     // PreparedStatement cache should be different depending on the Catalog
144     @Test
145     void testPStmtCatalog() throws Exception {
146         final Connection conn = getConnection();
147         conn.setCatalog("catalog1");
148         final DelegatingPreparedStatement stmt1 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual");
149         final TesterPreparedStatement inner1 = (TesterPreparedStatement) stmt1.getInnermostDelegate();
150         assertEquals("catalog1", inner1.getCatalog());
151         stmt1.close();
152 
153         conn.setCatalog("catalog2");
154         final DelegatingPreparedStatement stmt2 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual");
155         final TesterPreparedStatement inner2 = (TesterPreparedStatement) stmt2.getInnermostDelegate();
156         assertEquals("catalog2", inner2.getCatalog());
157         stmt2.close();
158 
159         conn.setCatalog("catalog1");
160         final DelegatingPreparedStatement stmt3 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual");
161         final TesterPreparedStatement inner3 = (TesterPreparedStatement) stmt3.getInnermostDelegate();
162         assertEquals("catalog1", inner3.getCatalog());
163         stmt3.close();
164 
165         assertNotSame(inner1, inner2);
166         assertSame(inner1, inner3);
167     }
168 
169     @Test
170     void testPStmtPoolingAcrossClose() throws Exception {
171         ds.setMaxTotal(1); // only one connection in pool needed
172         ds.setMaxIdle(1);
173         ds.setAccessToUnderlyingConnectionAllowed(true);
174         final Connection conn1 = getConnection();
175         assertNotNull(conn1);
176         assertEquals(1, ds.getNumActive());
177         assertEquals(0, ds.getNumIdle());
178 
179         final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual");
180         assertNotNull(stmt1);
181 
182         final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
183         assertNotNull(inner1);
184 
185         stmt1.close();
186         conn1.close();
187 
188         assertEquals(0, ds.getNumActive());
189         assertEquals(1, ds.getNumIdle());
190 
191         final Connection conn2 = getConnection();
192         assertNotNull(conn2);
193         assertEquals(1, ds.getNumActive());
194         assertEquals(0, ds.getNumIdle());
195 
196         final PreparedStatement stmt2 = conn2.prepareStatement("select 'a' from dual");
197         assertNotNull(stmt2);
198 
199         final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
200         assertNotNull(inner2);
201 
202         assertSame(inner1, inner2);
203     }
204 
205     /**
206      * Tests clearStatementPoolOnReturn introduced with DBCP-566.
207      * When turned on, the statement pool must be empty after the connection is closed.
208      *
209      * @throws Exception
210      */
211     @Test
212     void testPStmtPoolingAcrossCloseWithClearOnReturn() throws Exception {
213         ds.setMaxTotal(1); // only one connection in pool needed
214         ds.setMaxIdle(1);
215         ds.setClearStatementPoolOnReturn(true);
216         ds.setAccessToUnderlyingConnectionAllowed(true);
217         final Connection conn1 = getConnection();
218         assertNotNull(conn1);
219         assertEquals(1, ds.getNumActive());
220         assertEquals(0, ds.getNumIdle());
221 
222         @SuppressWarnings("unchecked")
223         final DelegatingConnection<Connection> poolableConn =
224             (DelegatingConnection<Connection>) ((DelegatingConnection<Connection>) conn1).getDelegateInternal();
225         final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool =
226             ((PoolingConnection) poolableConn.getDelegateInternal()).getStatementPool();
227 
228         final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual");
229         assertNotNull(stmt1);
230         final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
231         assertNotNull(inner1);
232         stmt1.close();
233 
234         final PreparedStatement stmt2 = conn1.prepareStatement("select 'a' from dual");
235         assertNotNull(stmt2);
236         final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
237         assertNotNull(inner2);
238         assertSame(inner1, inner2); // from the same connection the statement must be pooled
239         stmt2.close();
240 
241         conn1.close();
242         assertTrue(inner1.isClosed());
243 
244         assertEquals(0, stmtPool.getNumActive());
245         assertEquals(0, stmtPool.getNumIdle());
246 
247         assertEquals(0, ds.getNumActive());
248         assertEquals(1, ds.getNumIdle());
249 
250         final Connection conn2 = getConnection();
251         assertNotNull(conn2);
252         assertEquals(1, ds.getNumActive());
253         assertEquals(0, ds.getNumIdle());
254 
255         final PreparedStatement stmt3 = conn2.prepareStatement("select 'a' from dual");
256         assertNotNull(stmt3);
257         final Statement inner3 = ((DelegatingPreparedStatement) stmt3).getInnermostDelegate();
258         assertNotNull(inner3);
259 
260         assertNotSame(inner1, inner3); // when acquiring the connection the next time, statement must be new
261 
262         conn2.close();
263     }
264 
265     @Test
266     void testPStmtPoolingWithNoClose() throws Exception {
267         ds.setMaxTotal(1); // only one connection in pool needed
268         ds.setMaxIdle(1);
269         ds.setAccessToUnderlyingConnectionAllowed(true);
270         final Connection conn1 = getConnection();
271         assertNotNull(conn1);
272         assertEquals(1, ds.getNumActive());
273         assertEquals(0, ds.getNumIdle());
274 
275         final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual");
276         assertNotNull(stmt1);
277 
278         final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
279         assertNotNull(inner1);
280 
281         stmt1.close();
282 
283         assertNotNull(conn1);
284         assertEquals(1, ds.getNumActive());
285         assertEquals(0, ds.getNumIdle());
286 
287         final PreparedStatement stmt2 = conn1.prepareStatement("select 'a' from dual");
288         assertNotNull(stmt2);
289 
290         final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
291         assertNotNull(inner2);
292 
293         assertSame(inner1, inner2);
294     }
295 }