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.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotSame;
22  import static org.junit.jupiter.api.Assertions.assertSame;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.sql.Connection;
27  import java.sql.DriverManager;
28  import java.sql.PreparedStatement;
29  import java.sql.SQLException;
30  import java.sql.Statement;
31  
32  import javax.management.ObjectName;
33  import javax.sql.DataSource;
34  
35  import org.apache.commons.pool2.ObjectPool;
36  import org.apache.commons.pool2.impl.GenericObjectPool;
37  import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
38  import org.junit.jupiter.api.Assertions;
39  import org.junit.jupiter.api.Test;
40  
41  /**
42   * TestSuite for BasicDataSource with prepared statement pooling enabled
43   */
44  public class TestPStmtPooling {
45  
46      private DataSource createPoolingDataSource() throws Exception {
47          DriverManager.registerDriver(new TesterDriver());
48          final ConnectionFactory connFactory = new DriverManagerConnectionFactory("jdbc:apache:commons:testdriver", "u1", "p1");
49  
50          final PoolableConnectionFactory pcf = new PoolableConnectionFactory(connFactory, null);
51          pcf.setPoolStatements(true);
52          pcf.setDefaultReadOnly(Boolean.FALSE);
53          pcf.setDefaultAutoCommit(Boolean.TRUE);
54          final ObjectPool<PoolableConnection> connPool = new GenericObjectPool<>(pcf);
55          pcf.setPool(connPool);
56  
57          return new PoolingDataSource<>(connPool);
58  
59      }
60  
61      private PoolablePreparedStatement<?> getPoolablePreparedStatement(Statement s) {
62  
63          while (s != null) {
64              if (s instanceof PoolablePreparedStatement) {
65                  return (PoolablePreparedStatement<?>) s;
66              }
67              if (!(s instanceof DelegatingPreparedStatement)) {
68                  return null;
69              }
70              s = ((DelegatingPreparedStatement) s).getDelegate();
71          }
72          return null;
73      }
74  
75      @Test
76      void testBatchUpdate() throws Exception {
77          DriverManager.registerDriver(new TesterDriver());
78          final ConnectionFactory connFactory = new DriverManagerConnectionFactory("jdbc:apache:commons:testdriver", "u1", "p1");
79  
80          final PoolableConnectionFactory pcf = new PoolableConnectionFactory(connFactory, null);
81          pcf.setPoolStatements(true);
82          pcf.setDefaultReadOnly(Boolean.FALSE);
83          pcf.setDefaultAutoCommit(Boolean.TRUE);
84          final ObjectPool<PoolableConnection> connPool = new GenericObjectPool<>(pcf);
85          pcf.setPool(connPool);
86  
87          final PoolingDataSource<?> ds = new PoolingDataSource<>(connPool);
88  
89          final Connection conn = ds.getConnection();
90          final PreparedStatement ps = conn.prepareStatement("select 1 from dual");
91          final Statement inner = ((DelegatingPreparedStatement) ps).getInnermostDelegate();
92          // Check DBCP-372
93          ps.addBatch();
94          ps.close();
95          conn.close();
96          Assertions.assertFalse(inner.isClosed());
97          ds.close();
98      }
99  
100     @Test
101     void testCallableStatementPooling() throws Exception {
102         DriverManager.registerDriver(new TesterDriver());
103         final ConnectionFactory connFactory = new DriverManagerConnectionFactory("jdbc:apache:commons:testdriver", "u1", "p1");
104 
105         final ObjectName oName = new ObjectName("UnitTests:DataSource=test");
106         final PoolableConnectionFactory pcf = new PoolableConnectionFactory(connFactory, oName);
107         pcf.setPoolStatements(true);
108         pcf.setDefaultReadOnly(Boolean.FALSE);
109         pcf.setDefaultAutoCommit(Boolean.TRUE);
110 
111         final GenericObjectPoolConfig<PoolableConnection> config = new GenericObjectPoolConfig<>();
112         config.setJmxNameBase("UnitTests:DataSource=test,connectionpool=connections");
113         config.setJmxNamePrefix("");
114         final ObjectPool<PoolableConnection> connPool = new GenericObjectPool<>(pcf, config);
115         pcf.setPool(connPool);
116 
117         final PoolingDataSource<?> ds = new PoolingDataSource<>(connPool);
118 
119         try (Connection conn = ds.getConnection()) {
120             final Statement stmt1 = conn.prepareStatement("select 1 from dual");
121             final Statement ustmt1 = ((DelegatingStatement) stmt1).getInnermostDelegate();
122             final Statement cstmt1 = conn.prepareCall("{call home}");
123             final Statement ucstmt1 = ((DelegatingStatement) cstmt1).getInnermostDelegate();
124             stmt1.close(); // Return to pool
125             cstmt1.close(); // ""
126             final Statement stmt2 = conn.prepareStatement("select 1 from dual"); // Check out from pool
127             final Statement ustmt2 = ((DelegatingStatement) stmt2).getInnermostDelegate();
128             final Statement cstmt2 = conn.prepareCall("{call home}");
129             final Statement ucstmt2 = ((DelegatingStatement) cstmt2).getInnermostDelegate();
130             stmt2.close(); // Return to pool
131             cstmt2.close(); // ""
132             assertSame(ustmt1, ustmt2);
133             assertSame(ucstmt1, ucstmt2);
134             // Verify key distinguishes Callable from Prepared Statements in the pool
135             final Statement stmt3 = conn.prepareCall("select 1 from dual");
136             final Statement ustmt3 = ((DelegatingStatement) stmt3).getInnermostDelegate();
137             stmt3.close();
138             assertNotSame(ustmt1, ustmt3);
139             assertNotSame(ustmt3, ucstmt1);
140         }
141         ds.close();
142     }
143 
144     @Test
145     void testClosePool() throws Exception {
146         DriverManager.registerDriver(new TesterDriver());
147         final ConnectionFactory connFactory = new DriverManagerConnectionFactory("jdbc:apache:commons:testdriver", "u1", "p1");
148 
149         final PoolableConnectionFactory pcf = new PoolableConnectionFactory(connFactory, null);
150         pcf.setPoolStatements(true);
151         pcf.setDefaultReadOnly(Boolean.FALSE);
152         pcf.setDefaultAutoCommit(Boolean.TRUE);
153 
154         final ObjectPool<PoolableConnection> connPool = new GenericObjectPool<>(pcf);
155         pcf.setPool(connPool);
156 
157         final PoolingDataSource<?> ds = new PoolingDataSource<>(connPool);
158         ((PoolingDataSource<?>) ds).setAccessToUnderlyingConnectionAllowed(true);
159 
160         final Connection conn = ds.getConnection();
161         try (Statement s = conn.prepareStatement("select 1 from dual")) {
162         }
163 
164         final Connection poolableConnection = ((DelegatingConnection<?>) conn).getDelegate();
165         final Connection poolingConnection = ((DelegatingConnection<?>) poolableConnection).getDelegate();
166         poolingConnection.close();
167         final SQLException ex = assertThrows(SQLException.class, () -> conn.prepareStatement("select 1 from dual"));
168         assertTrue(ex.getMessage().endsWith("invalid PoolingConnection."));
169         ds.close();
170     }
171 
172     /**
173      * Verifies that executing close() on an already closed DelegatingStatement that wraps a PoolablePreparedStatement does not "re-close" the PPS (which could
174      * be in use by another client - see DBCP-414).
175      */
176     @Test
177     void testMultipleClose() throws Exception {
178         final DataSource ds = createPoolingDataSource();
179         final Connection conn = ds.getConnection();
180         final PreparedStatement stmt1 = conn.prepareStatement("select 1 from dual");
181         final PoolablePreparedStatement<?> pps1 = getPoolablePreparedStatement(stmt1);
182         conn.close();
183         assertTrue(stmt1.isClosed()); // Closing conn should close stmt
184         stmt1.close(); // Should already be closed - no-op
185         assertTrue(stmt1.isClosed());
186         final Connection conn2 = ds.getConnection();
187         final PreparedStatement stmt2 = conn2.prepareStatement("select 1 from dual");
188         // Confirm stmt2 now wraps the same PPS wrapped by stmt1
189         Assertions.assertSame(pps1, getPoolablePreparedStatement(stmt2));
190         stmt1.close(); // close should not cascade to PPS that stmt1 used to wrap
191         assertFalse(stmt2.isClosed());
192         stmt2.executeQuery(); // wrapped PPS needs to work here - pre DBCP-414 fix this throws
193         conn2.close();
194         assertTrue(stmt1.isClosed());
195         assertTrue(stmt2.isClosed());
196     }
197 
198     @Test
199     void testStmtPool() throws Exception {
200         final DataSource ds = createPoolingDataSource();
201         try (Connection conn = ds.getConnection()) {
202             final Statement stmt1 = conn.prepareStatement("select 1 from dual");
203             final Statement ustmt1 = ((DelegatingStatement) stmt1).getInnermostDelegate();
204             stmt1.close();
205             final Statement stmt2 = conn.prepareStatement("select 1 from dual");
206             final Statement ustmt2 = ((DelegatingStatement) stmt2).getInnermostDelegate();
207             stmt2.close();
208             assertSame(ustmt1, ustmt2);
209         }
210     }
211 }