blob: 8862a71746ae92816c53272ba7a95ca52491b2cb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.dbcp2.datasources;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.DelegatingStatement;
import org.apache.commons.dbcp2.TestConnectionPool;
import org.apache.commons.dbcp2.TesterDriver;
import org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS;
import org.junit.Before;
import org.junit.Test;
/**
*/
public class TestSharedPoolDataSource extends TestConnectionPool {
@Override
protected Connection getConnection() throws Exception {
return ds.getConnection("foo","bar");
}
private DriverAdapterCPDS pcds;
private DataSource ds;
@Before
public void setUp() throws Exception {
pcds = new DriverAdapterCPDS();
pcds.setDriver("org.apache.commons.dbcp2.TesterDriver");
pcds.setUrl("jdbc:apache:commons:testdriver");
pcds.setUser("foo");
pcds.setPassword("bar");
pcds.setPoolPreparedStatements(false);
pcds.setAccessToUnderlyingConnectionAllowed(true);
final SharedPoolDataSource tds = new SharedPoolDataSource();
tds.setConnectionPoolDataSource(pcds);
tds.setMaxTotal(getMaxTotal());
tds.setDefaultMaxWaitMillis((int)getMaxWaitMillis());
tds.setDefaultTransactionIsolation(
Connection.TRANSACTION_READ_COMMITTED);
tds.setDefaultAutoCommit(Boolean.TRUE);
ds = tds;
}
/**
* Starting with a successful connection, then incorrect password,
* then correct password for same user illustrates
* JIRA: DBCP-245
*/
@Test
public void testIncorrectPassword() throws Exception {
ds.getConnection("u2", "p2").close();
try (Connection c = ds.getConnection("u1", "zlsafjk")){ // Use bad password
fail("Able to retrieve connection with incorrect password");
} catch (final SQLException e1) {
// should fail
}
// Use good password
ds.getConnection("u1", "p1").close();
try (Connection c = ds.getConnection("u1", "x")) {
fail("Able to retrieve connection with incorrect password");
} catch (final SQLException e) {
if (!e.getMessage().startsWith("Given password did not match")) {
throw e;
}
// else the exception was expected
}
// Make sure we can still use our good password.
ds.getConnection("u1", "p1").close();
// Try related users and passwords
ds.getConnection("foo", "bar").close();
try (Connection c = ds.getConnection("u1", "ar")) {
fail("Should have caused an SQLException");
} catch (final SQLException expected) {
}
try (Connection c = ds.getConnection("u1", "baz")) {
fail("Should have generated SQLException");
} catch (final SQLException expected) {
}
}
@Override
@Test
public void testSimple() throws Exception
{
final Connection conn = ds.getConnection();
assertNotNull(conn);
final PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
final ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
conn.close();
}
@Test
public void testSimpleWithUsername() throws Exception
{
final Connection conn = ds.getConnection("u1", "p1");
assertNotNull(conn);
final PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
final ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
conn.close();
}
@Test
public void testClosingWithUserName()
throws Exception
{
final Connection[] c = new Connection[getMaxTotal()];
// open the maximum connections
for (int i=0; i<c.length; i++)
{
c[i] = ds.getConnection("u1", "p1");
}
// close one of the connections
c[0].close();
assertTrue(c[0].isClosed());
// get a new connection
c[0] = ds.getConnection("u1", "p1");
for (final Connection element : c) {
element.close();
}
// open the maximum connections
for (int i=0; i<c.length; i++)
{
c[i] = ds.getConnection("u1", "p1");
}
for (final Connection element : c) {
element.close();
}
}
@Override
@Test
public void testSimple2()
throws Exception
{
Connection conn = ds.getConnection();
assertNotNull(conn);
PreparedStatement stmt =
conn.prepareStatement("select * from dual");
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
conn.close();
try (Statement s = conn.createStatement()){
fail("Can't use closed connections");
} catch(final SQLException e) {
// expected
}
conn = ds.getConnection();
assertNotNull(conn);
stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
conn.close();
conn = null;
}
@Override
@Test
public void testOpening()
throws Exception
{
final Connection[] c = new Connection[getMaxTotal()];
// test that opening new connections is not closing previous
for (int i=0; i<c.length; i++)
{
c[i] = ds.getConnection();
assertTrue(c[i] != null);
for (int j=0; j<=i; j++)
{
assertTrue(!c[j].isClosed());
}
}
for (final Connection element : c) {
element.close();
}
}
@Override
@Test
public void testClosing()
throws Exception
{
final Connection[] c = new Connection[getMaxTotal()];
// open the maximum connections
for (int i=0; i<c.length; i++)
{
c[i] = ds.getConnection();
}
// close one of the connections
c[0].close();
assertTrue(c[0].isClosed());
// get a new connection
c[0] = ds.getConnection();
for (final Connection element : c) {
element.close();
}
}
/**
* Test pool close. Illustrates BZ 37359.
*
* @throws Exception
*/
@Test
public void testClosePool() throws Exception {
((SharedPoolDataSource)ds).close();
final SharedPoolDataSource tds = new SharedPoolDataSource();
// NPE before BZ 37359 fix
tds.close();
}
@Override
@Test
public void testMaxTotal() throws Exception {
final Connection[] c = new Connection[getMaxTotal()];
for (int i=0; i<c.length; i++) {
c[i] = ds.getConnection();
assertTrue(c[i] != null);
}
try (Connection conn = ds.getConnection()){
fail("Allowed to open more than DefaultMaxTotal connections.");
} catch(final java.sql.SQLException e) {
// should only be able to open 10 connections, so this test should
// throw an exception
}
for (final Connection element : c) {
element.close();
}
}
@Test
public void testMaxWaitMillis() throws Exception {
final int maxWaitMillis = 1000;
final int theadCount = 20;
((SharedPoolDataSource)ds).setDefaultMaxWaitMillis(maxWaitMillis);
// Obtain all the connections from the pool
final Connection[] c = new Connection[getMaxTotal()];
for (int i=0; i<c.length; i++) {
c[i] = ds.getConnection("foo","bar");
assertTrue(c[i] != null);
}
final long start = System.currentTimeMillis();
// Run a thread test with minimal hold time
// All threads should end after maxWaitMillis - DBCP-291
final PoolTest[] pts = new PoolTest[theadCount];
final ThreadGroup threadGroup = new ThreadGroup("testMaxWaitMillis");
// Should take ~maxWaitMillis for threads to stop
for (int i = 0; i < pts.length; i++) {
(pts[i] = new PoolTest(threadGroup, 1, true)).start();
}
// Wait for all the threads to complete
for (final PoolTest poolTest : pts) {
poolTest.getThread().join();
}
final long end = System.currentTimeMillis();
System.out.println("testMaxWaitMillis took " + (end-start) +
" ms. maxWaitMillis: "+maxWaitMillis);
// Threads should time out in parallel - allow double that to be safe
assertTrue(end-start < 2 * maxWaitMillis);
// Put all the connections back in the pool
for (final Connection element : c) {
element.close();
}
}
@Test
public void testMultipleThreads1() throws Exception {
// Override wait time in order to allow for Thread.sleep(1) sometimes taking a lot longer on
// some JVMs, e.g. Windows.
final int defaultMaxWaitMillis = 430;
((SharedPoolDataSource) ds).setDefaultMaxWaitMillis(defaultMaxWaitMillis);
multipleThreads(1, false, false, defaultMaxWaitMillis);
}
@Test
public void testMultipleThreads2() throws Exception {
final int defaultMaxWaitMillis = 500;
((SharedPoolDataSource) ds).setDefaultMaxWaitMillis(defaultMaxWaitMillis);
multipleThreads(2 * defaultMaxWaitMillis, true, true, defaultMaxWaitMillis);
}
@Test
public void testTransactionIsolationBehavior() throws Exception {
final Connection conn = getConnection();
assertNotNull(conn);
assertEquals(Connection.TRANSACTION_READ_COMMITTED,
conn.getTransactionIsolation());
conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
conn.close();
final Connection conn2 = getConnection();
assertEquals(Connection.TRANSACTION_READ_COMMITTED,
conn2.getTransactionIsolation());
final Connection conn3 = getConnection();
assertEquals(Connection.TRANSACTION_READ_COMMITTED,
conn3.getTransactionIsolation());
conn2.close();
conn3.close();
}
// Bugzilla Bug 24136 ClassCastException in DriverAdapterCPDS
// when setPoolPreparedStatements(true)
@Test
public void testPoolPrepareCall() throws Exception {
pcds.setPoolPreparedStatements(true);
final Connection conn = ds.getConnection();
assertNotNull(conn);
final PreparedStatement stmt = conn.prepareCall("{call home()}");
assertNotNull(stmt);
final ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
conn.close();
}
@Test
public void testPoolPrepareStatement() throws Exception {
pcds.setPoolPreparedStatements(true);
final Connection conn = ds.getConnection();
assertNotNull(conn);
final PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
final ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
conn.close();
}
// There are 3 different prepareCall statement methods so add a little
// complexity to reduce what would otherwise be lots of copy and paste
private static abstract class PrepareCallCallback {
protected Connection conn;
void setConnection(final Connection conn) {
this.conn = conn;
}
abstract CallableStatement getCallableStatement() throws SQLException;
}
private static class CscbString extends PrepareCallCallback {
@Override
CallableStatement getCallableStatement() throws SQLException {
return conn.prepareCall("{call home()}");
}
}
private static class CscbStringIntInt extends PrepareCallCallback {
@Override
CallableStatement getCallableStatement() throws SQLException {
return conn.prepareCall("{call home()}", 0, 0);
}
}
private static class CscbStringIntIntInt extends PrepareCallCallback {
@Override
CallableStatement getCallableStatement() throws SQLException {
return conn.prepareCall("{call home()}", 0, 0, 0);
}
}
// There are 6 different prepareStatement statement methods so add a little
// complexity to reduce what would otherwise be lots of copy and paste
private static abstract class PrepareStatementCallback {
protected Connection conn;
void setConnection(final Connection conn) {
this.conn = conn;
}
abstract PreparedStatement getPreparedStatement() throws SQLException;
}
private static class PscbString extends PrepareStatementCallback {
@Override
PreparedStatement getPreparedStatement() throws SQLException {
return conn.prepareStatement("select * from dual");
}
}
private static class PscbStringIntInt extends PrepareStatementCallback {
@Override
PreparedStatement getPreparedStatement() throws SQLException {
return conn.prepareStatement("select * from dual", 0, 0);
}
}
private static class PscbStringInt extends PrepareStatementCallback {
@Override
PreparedStatement getPreparedStatement() throws SQLException {
return conn.prepareStatement("select * from dual", 0);
}
}
private static class PscbStringIntArray extends PrepareStatementCallback {
@Override
PreparedStatement getPreparedStatement() throws SQLException {
return conn.prepareStatement("select * from dual", new int[0]);
}
}
private static class PscbStringStringArray extends PrepareStatementCallback {
@Override
PreparedStatement getPreparedStatement() throws SQLException {
return conn.prepareStatement("select * from dual", new String[0]);
}
}
private static class PscbStringIntIntInt extends PrepareStatementCallback {
@Override
PreparedStatement getPreparedStatement() throws SQLException {
return conn.prepareStatement("select * from dual", 0, 0, 0);
}
}
private void doTestPoolCallableStatements(final PrepareCallCallback callBack)
throws Exception {
final DriverAdapterCPDS myPcds = new DriverAdapterCPDS();
DataSource myDs = null;
myPcds.setDriver("org.apache.commons.dbcp2.TesterDriver");
myPcds.setUrl("jdbc:apache:commons:testdriver");
myPcds.setUser("foo");
myPcds.setPassword("bar");
myPcds.setPoolPreparedStatements(true);
myPcds.setMaxPreparedStatements(10);
final SharedPoolDataSource spDs = new SharedPoolDataSource();
spDs.setConnectionPoolDataSource(myPcds);
spDs.setMaxTotal(getMaxTotal());
spDs.setDefaultMaxWaitMillis((int) getMaxWaitMillis());
spDs.setDefaultTransactionIsolation(
Connection.TRANSACTION_READ_COMMITTED);
myDs = spDs;
Connection conn = ds.getConnection();
callBack.setConnection(conn);
CallableStatement stmt = null;
ResultSet rset = null;
assertNotNull(conn);
stmt = callBack.getCallableStatement();
assertNotNull(stmt);
final long l1HashCode = ((DelegatingStatement) stmt).getDelegate().hashCode();
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
stmt = callBack.getCallableStatement();
assertNotNull(stmt);
final long l2HashCode = ((DelegatingStatement) stmt).getDelegate().hashCode();
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
// statement pooling is not enabled, we should get different statements
assertTrue(l1HashCode != l2HashCode);
conn.close();
conn = null;
conn = myDs.getConnection();
callBack.setConnection(conn);
stmt = callBack.getCallableStatement();
assertNotNull(stmt);
final long l3HashCode = ((DelegatingStatement) stmt).getDelegate().hashCode();
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
stmt = callBack.getCallableStatement();
assertNotNull(stmt);
final long l4HashCode = ((DelegatingStatement) stmt).getDelegate().hashCode();
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
// prepared statement pooling is working
assertTrue(l3HashCode == l4HashCode);
conn.close();
conn = null;
spDs.close();
}
private void doTestPoolPreparedStatements(final PrepareStatementCallback callBack)
throws Exception {
final DriverAdapterCPDS mypcds = new DriverAdapterCPDS();
DataSource myds = null;
mypcds.setDriver("org.apache.commons.dbcp2.TesterDriver");
mypcds.setUrl("jdbc:apache:commons:testdriver");
mypcds.setUser("foo");
mypcds.setPassword("bar");
mypcds.setPoolPreparedStatements(true);
mypcds.setMaxPreparedStatements(10);
final SharedPoolDataSource tds = new SharedPoolDataSource();
tds.setConnectionPoolDataSource(mypcds);
tds.setMaxTotal(getMaxTotal());
tds.setDefaultMaxWaitMillis((int)getMaxWaitMillis());
tds.setDefaultTransactionIsolation(
Connection.TRANSACTION_READ_COMMITTED);
myds = tds;
Connection conn = ds.getConnection();
callBack.setConnection(conn);
PreparedStatement stmt = null;
ResultSet rset = null;
assertNotNull(conn);
stmt = callBack.getPreparedStatement();
assertNotNull(stmt);
final long l1HashCode = ((DelegatingStatement) stmt).getDelegate().hashCode();
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
stmt = callBack.getPreparedStatement();
assertNotNull(stmt);
final long l2HashCode = ((DelegatingStatement) stmt).getDelegate().hashCode();
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
// statement pooling is not enabled, we should get different statements
assertTrue(l1HashCode != l2HashCode);
conn.close();
conn = null;
conn = myds.getConnection();
callBack.setConnection(conn);
stmt = callBack.getPreparedStatement();
assertNotNull(stmt);
final long l3HashCode = ((DelegatingStatement) stmt).getDelegate().hashCode();
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
stmt = callBack.getPreparedStatement();
assertNotNull(stmt);
final long l4HashCode = ((DelegatingStatement) stmt).getDelegate().hashCode();
rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
// prepared statement pooling is working
assertTrue(l3HashCode == l4HashCode);
conn.close();
conn = null;
tds.close();
}
@Test
public void testPoolPreparedCalls() throws Exception {
doTestPoolCallableStatements(new CscbString());
doTestPoolCallableStatements(new CscbStringIntInt());
doTestPoolCallableStatements(new CscbStringIntIntInt());
}
@Test
public void testPoolPreparedStatements() throws Exception {
doTestPoolPreparedStatements(new PscbString());
doTestPoolPreparedStatements(new PscbStringIntInt());
doTestPoolPreparedStatements(new PscbStringInt());
doTestPoolPreparedStatements(new PscbStringIntArray());
doTestPoolPreparedStatements(new PscbStringStringArray());
doTestPoolPreparedStatements(new PscbStringIntIntInt());
}
// See DBCP-8
@Test
public void testChangePassword() throws Exception {
try (Connection c = ds.getConnection("foo", "bay")){
fail("Should have generated SQLException");
} catch (final SQLException expected) {
}
final Connection con1 = ds.getConnection("foo", "bar");
final Connection con2 = ds.getConnection("foo", "bar");
final Connection con3 = ds.getConnection("foo", "bar");
con1.close();
con2.close();
TesterDriver.addUser("foo","bay"); // change the user/password setting
try (Connection con4 = ds.getConnection("foo", "bay")) { // new password
// Idle instances with old password should have been cleared
assertEquals("Should be no idle connections in the pool",
0, ((SharedPoolDataSource) ds).getNumIdle());
con4.close();
// Should be one idle instance with new pwd
assertEquals("Should be one idle connection in the pool",
1, ((SharedPoolDataSource) ds).getNumIdle());
try (Connection con4b = ds.getConnection("foo", "bar")) { // old password
fail("Should have generated SQLException");
} catch (final SQLException expected) {
}
final Connection con5 = ds.getConnection("foo", "bay"); // take the idle one
con3.close(); // Return a connection with the old password
ds.getConnection("foo", "bay").close(); // will try bad returned connection and destroy it
assertEquals("Should be one idle connection in the pool",
1, ((SharedPoolDataSource) ds).getNumIdle());
con5.close();
} finally {
TesterDriver.addUser("foo","bar");
}
}
@Test
public void testDbcp369() {
final ArrayList<SharedPoolDataSource> dataSources = new ArrayList<>();
for (int j = 0; j < 10000; j++) {
final SharedPoolDataSource dataSource = new SharedPoolDataSource();
dataSources.add(dataSource);
}
final Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (final SharedPoolDataSource dataSource : dataSources) {
dataSource.setDataSourceName("a");
}
}
});
final Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (final SharedPoolDataSource dataSource : dataSources) {
try {
dataSource.close();
} catch (final Exception e) {
// Ignore
}
}
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (final InterruptedException ie) {
// Ignore
}
}
}