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    *      http://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.assertTrue;
25  import static org.junit.jupiter.api.Assertions.fail;
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      public 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          try {
82              inner1.clearParameters();
83              fail("expecting SQLExcption - statement should be closed");
84          } catch (final SQLException ex) {
85              //Expected
86          }
87          // But others are still open
88          inner2.clearParameters();
89          inner3.clearParameters();
90  
91          // Now make sure stmt1 does not come back from the dead
92          final PreparedStatement stmt5 = conn.prepareStatement("select 'a' from dual");
93          final PreparedStatement inner5 = (PreparedStatement) ((DelegatingPreparedStatement) stmt5).getInnermostDelegate();
94          assertNotSame(inner5, inner1);
95  
96          // inner2 should be closed now
97          try {
98              inner2.clearParameters();
99              fail("expecting SQLExcption - statement should be closed");
100         } catch (final SQLException ex) {
101             //Expected
102         }
103         // But inner3 should still be open
104         inner3.clearParameters();
105     }
106 
107     /**
108      * Tests high-concurrency contention for connections and pooled prepared statements.
109      * DBCP-414
110      */
111     @Test
112     public void testMultipleThreads1() throws Exception {
113         ds.setMaxWait(Duration.ofMillis(-1));
114         ds.setMaxTotal(5);
115         ds.setMaxOpenPreparedStatements(-1);
116         multipleThreads(Duration.ofMillis(5), false, false, Duration.ofMillis(-1), 3, 100, 10000);
117     }
118 
119     @Test
120     public void testPreparedStatementPooling() throws Exception {
121         final Connection conn = getConnection();
122         assertNotNull(conn);
123 
124         final PreparedStatement stmt1 = conn.prepareStatement("select 'a' from dual");
125         assertNotNull(stmt1);
126 
127         final PreparedStatement stmt2 = conn.prepareStatement("select 'b' from dual");
128         assertNotNull(stmt2);
129 
130         assertNotSame(stmt1, stmt2);
131 
132         // go over the maxOpen limit
133         try (PreparedStatement ps = conn.prepareStatement("select 'c' from dual")) {
134             fail("expected SQLException");
135         }
136         catch (final SQLException e) {}
137 
138         // make idle
139         stmt2.close();
140 
141         // test cleanup the 'b' statement
142         final PreparedStatement stmt3 = conn.prepareStatement("select 'c' from dual");
143         assertNotNull(stmt3);
144         assertNotSame(stmt3, stmt1);
145         assertNotSame(stmt3, stmt2);
146 
147         // normal reuse of statement
148         stmt1.close();
149         try (final PreparedStatement stmt4 = conn.prepareStatement("select 'a' from dual")) {
150             assertNotNull(stmt4);
151         }
152     }
153 
154     // Bugzilla Bug 27246
155     // PreparedStatement cache should be different depending on the Catalog
156     @Test
157     public void testPStmtCatalog() throws Exception {
158         final Connection conn = getConnection();
159         conn.setCatalog("catalog1");
160         final DelegatingPreparedStatement stmt1 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual");
161         final TesterPreparedStatement inner1 = (TesterPreparedStatement) stmt1.getInnermostDelegate();
162         assertEquals("catalog1", inner1.getCatalog());
163         stmt1.close();
164 
165         conn.setCatalog("catalog2");
166         final DelegatingPreparedStatement stmt2 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual");
167         final TesterPreparedStatement inner2 = (TesterPreparedStatement) stmt2.getInnermostDelegate();
168         assertEquals("catalog2", inner2.getCatalog());
169         stmt2.close();
170 
171         conn.setCatalog("catalog1");
172         final DelegatingPreparedStatement stmt3 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual");
173         final TesterPreparedStatement inner3 = (TesterPreparedStatement) stmt3.getInnermostDelegate();
174         assertEquals("catalog1", inner3.getCatalog());
175         stmt3.close();
176 
177         assertNotSame(inner1, inner2);
178         assertSame(inner1, inner3);
179     }
180 
181     @Test
182     public void testPStmtPoolingAcrossClose() throws Exception {
183         ds.setMaxTotal(1); // only one connection in pool needed
184         ds.setMaxIdle(1);
185         ds.setAccessToUnderlyingConnectionAllowed(true);
186         final Connection conn1 = getConnection();
187         assertNotNull(conn1);
188         assertEquals(1, ds.getNumActive());
189         assertEquals(0, ds.getNumIdle());
190 
191         final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual");
192         assertNotNull(stmt1);
193 
194         final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
195         assertNotNull(inner1);
196 
197         stmt1.close();
198         conn1.close();
199 
200         assertEquals(0, ds.getNumActive());
201         assertEquals(1, ds.getNumIdle());
202 
203         final Connection conn2 = getConnection();
204         assertNotNull(conn2);
205         assertEquals(1, ds.getNumActive());
206         assertEquals(0, ds.getNumIdle());
207 
208         final PreparedStatement stmt2 = conn2.prepareStatement("select 'a' from dual");
209         assertNotNull(stmt2);
210 
211         final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
212         assertNotNull(inner2);
213 
214         assertSame(inner1, inner2);
215     }
216 
217     /**
218      * Tests clearStatementPoolOnReturn introduced with DBCP-566.
219      * When turned on, the statement pool must be empty after the connection is closed.
220      *
221      * @throws Exception
222      */
223     @Test
224     public void testPStmtPoolingAcrossCloseWithClearOnReturn() throws Exception {
225         ds.setMaxTotal(1); // only one connection in pool needed
226         ds.setMaxIdle(1);
227         ds.setClearStatementPoolOnReturn(true);
228         ds.setAccessToUnderlyingConnectionAllowed(true);
229         final Connection conn1 = getConnection();
230         assertNotNull(conn1);
231         assertEquals(1, ds.getNumActive());
232         assertEquals(0, ds.getNumIdle());
233 
234         @SuppressWarnings("unchecked")
235         final DelegatingConnection<Connection> poolableConn =
236             (DelegatingConnection<Connection>) ((DelegatingConnection<Connection>) conn1).getDelegateInternal();
237         final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool =
238             ((PoolingConnection) poolableConn.getDelegateInternal()).getStatementPool();
239 
240         final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual");
241         assertNotNull(stmt1);
242         final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
243         assertNotNull(inner1);
244         stmt1.close();
245 
246         final PreparedStatement stmt2 = conn1.prepareStatement("select 'a' from dual");
247         assertNotNull(stmt2);
248         final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
249         assertNotNull(inner2);
250         assertSame(inner1, inner2); // from the same connection the statement must be pooled
251         stmt2.close();
252 
253         conn1.close();
254         assertTrue(inner1.isClosed());
255 
256         assertEquals(0, stmtPool.getNumActive());
257         assertEquals(0, stmtPool.getNumIdle());
258 
259         assertEquals(0, ds.getNumActive());
260         assertEquals(1, ds.getNumIdle());
261 
262         final Connection conn2 = getConnection();
263         assertNotNull(conn2);
264         assertEquals(1, ds.getNumActive());
265         assertEquals(0, ds.getNumIdle());
266 
267         final PreparedStatement stmt3 = conn2.prepareStatement("select 'a' from dual");
268         assertNotNull(stmt3);
269         final Statement inner3 = ((DelegatingPreparedStatement) stmt3).getInnermostDelegate();
270         assertNotNull(inner3);
271 
272         assertNotSame(inner1, inner3); // when acquiring the connection the next time, statement must be new
273 
274         conn2.close();
275     }
276 
277     @Test
278     public void testPStmtPoolingWithNoClose() throws Exception {
279         ds.setMaxTotal(1); // only one connection in pool needed
280         ds.setMaxIdle(1);
281         ds.setAccessToUnderlyingConnectionAllowed(true);
282         final Connection conn1 = getConnection();
283         assertNotNull(conn1);
284         assertEquals(1, ds.getNumActive());
285         assertEquals(0, ds.getNumIdle());
286 
287         final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual");
288         assertNotNull(stmt1);
289 
290         final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
291         assertNotNull(inner1);
292 
293         stmt1.close();
294 
295         assertNotNull(conn1);
296         assertEquals(1, ds.getNumActive());
297         assertEquals(0, ds.getNumIdle());
298 
299         final PreparedStatement stmt2 = conn1.prepareStatement("select 'a' from dual");
300         assertNotNull(stmt2);
301 
302         final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
303         assertNotNull(inner2);
304 
305         assertSame(inner1, inner2);
306     }
307 }