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.datasources;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNotEquals;
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.SQLException;
28  import java.time.Duration;
29  
30  import javax.sql.PooledConnection;
31  
32  import org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS;
33  import org.apache.commons.pool2.impl.GenericObjectPool;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  
37  /**
38   */
39  public class TestCPDSConnectionFactory {
40  
41      protected ConnectionPoolDataSourceProxy cpds;
42  
43      @BeforeEach
44      public void setUp() throws Exception {
45          cpds = new ConnectionPoolDataSourceProxy(new DriverAdapterCPDS());
46          final DriverAdapterCPDS delegate = (DriverAdapterCPDS) cpds.getDelegate();
47          delegate.setDriver("org.apache.commons.dbcp2.TesterDriver");
48          delegate.setUrl("jdbc:apache:commons:testdriver");
49          delegate.setUser("userName");
50          delegate.setPassword("password");
51      }
52  
53      /**
54       * JIRA DBCP-216
55       *
56       * Verify that pool counters are maintained properly and listeners are
57       * cleaned up when a PooledConnection throws a connectionError event.
58       */
59      @Test
60      public void testConnectionErrorCleanup() throws Exception {
61          // Setup factory
62          final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, null, Duration.ofMillis(-1), false, "userName", "password");
63          try (final GenericObjectPool<PooledConnectionAndInfo> pool = new GenericObjectPool<>(factory)) {
64              factory.setPool(pool);
65  
66              // Checkout a pair of connections
67              final PooledConnection pcon1 = pool.borrowObject().getPooledConnection();
68              try (final Connection con1 = pcon1.getConnection()) {
69                  final PooledConnection pcon2 = pool.borrowObject().getPooledConnection();
70                  assertEquals(2, pool.getNumActive());
71                  assertEquals(0, pool.getNumIdle());
72  
73                  // Verify listening
74                  final PooledConnectionProxy pc = (PooledConnectionProxy) pcon1;
75                  assertTrue(pc.getListeners().contains(factory));
76  
77                  // Throw connectionError event
78                  pc.throwConnectionError();
79  
80                  // Active count should be reduced by 1 and no idle increase
81                  assertEquals(1, pool.getNumActive());
82                  assertEquals(0, pool.getNumIdle());
83  
84                  // Throw another one - should be ignored
85                  pc.throwConnectionError();
86                  assertEquals(1, pool.getNumActive());
87                  assertEquals(0, pool.getNumIdle());
88  
89                  // Ask for another connection
90                  final PooledConnection pcon3 = pool.borrowObject().getPooledConnection();
91                  assertNotEquals(pcon3, pcon1); // better not get baddie back
92                  assertFalse(pc.getListeners().contains(factory)); // verify cleanup
93                  assertEquals(2, pool.getNumActive());
94                  assertEquals(0, pool.getNumIdle());
95  
96                  // Return good connections back to pool
97                  pcon2.getConnection().close();
98                  pcon3.getConnection().close();
99                  assertEquals(2, pool.getNumIdle());
100                 assertEquals(0, pool.getNumActive());
101 
102                 // Verify pc is closed
103                 assertThrows(SQLException.class, pc::getConnection, "Expecting SQLException using closed PooledConnection");
104 
105                 // Back from the dead - ignore the ghost!
106                 con1.close();
107                 assertEquals(2, pool.getNumIdle());
108                 assertEquals(0, pool.getNumActive());
109 
110                 // Clear pool
111                 pool.clear();
112                 assertEquals(0, pool.getNumIdle());
113             }
114         }
115     }
116 
117     /**
118      * JIRA: DBCP-442
119      */
120     @Test
121     public void testNullValidationQuery() throws Exception {
122         final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, null, Duration.ofMillis(-1), false, "userName", "password");
123         try (final GenericObjectPool<PooledConnectionAndInfo> pool = new GenericObjectPool<>(factory)) {
124             factory.setPool(pool);
125             pool.setTestOnBorrow(true);
126             final PooledConnection pcon = pool.borrowObject().getPooledConnection();
127             try (final Connection con = pcon.getConnection()) {
128             }
129         }
130     }
131 
132     /**
133      * JIRA: DBCP-442
134      */
135     @Test
136     public void testNullValidationQuery_Deprecated() throws Exception {
137         final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, null, -1, false, "userName", "password");
138         try (final GenericObjectPool<PooledConnectionAndInfo> pool = new GenericObjectPool<>(factory)) {
139             factory.setPool(pool);
140             pool.setTestOnBorrow(true);
141             final PooledConnection pcon = pool.borrowObject().getPooledConnection();
142             try (final Connection con = pcon.getConnection()) {
143             }
144         }
145     }
146 
147     @Test
148     public void testSetPasswordThenModCharArray() {
149         final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, null, Duration.ofMillis(-1), false, "userName", "password");
150         final char[] pwd = {'a'};
151         factory.setPassword(pwd);
152         assertEquals("a", String.valueOf(factory.getPasswordCharArray()));
153         pwd[0] = 'b';
154         assertEquals("a", String.valueOf(factory.getPasswordCharArray()));
155     }
156 
157     @Test
158     public void testSetPasswordThenModCharArray_Deprecated() {
159         final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, null, -1, false, "userName", "password");
160         final char[] pwd = {'a'};
161         factory.setPassword(pwd);
162         assertEquals("a", String.valueOf(factory.getPasswordCharArray()));
163         pwd[0] = 'b';
164         assertEquals("a", String.valueOf(factory.getPasswordCharArray()));
165     }
166 
167     /**
168      * JIRA DBCP-216
169      *
170      * Check PoolableConnection close triggered by destroy is handled
171      * properly. PooledConnectionProxy (dubiously) fires connectionClosed
172      * when PooledConnection itself is closed.
173      */
174     @Test
175     public void testSharedPoolDSDestroyOnReturn() throws Exception {
176         try (final PerUserPoolDataSource ds = new PerUserPoolDataSource()) {
177             ds.setConnectionPoolDataSource(cpds);
178             ds.setPerUserMaxTotal("userName", 10);
179             ds.setPerUserMaxWait("userName", Duration.ofMillis(50));
180             ds.setPerUserMaxIdle("userName", 2);
181             final Connection conn1 = ds.getConnection("userName", "password");
182             final Connection conn2 = ds.getConnection("userName", "password");
183             final Connection conn3 = ds.getConnection("userName", "password");
184             assertEquals(3, ds.getNumActive("userName"));
185             conn1.close();
186             assertEquals(1, ds.getNumIdle("userName"));
187             conn2.close();
188             assertEquals(2, ds.getNumIdle("userName"));
189             conn3.close(); // Return to pool will trigger destroy -> close sequence
190             assertEquals(2, ds.getNumIdle("userName"));
191         }
192     }
193 
194 }