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