blob: 357215cd37191a799f272863a8e2ed05174f75db [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.dbcp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Hashtable;
import java.util.Stack;
import junit.framework.TestCase;
// XXX FIX ME XXX
// this class still needs some cleanup, but at least
// this consolidates most of the relevant test code
// in a fairly re-usable fashion
// XXX FIX ME XXX
/**
* Base test suite for DBCP pools.
*
* @author Rodney Waldhoff
* @author Sean C. Sullivan
* @author John McNally
* @author Dirk Verbeeck
* @version $Revision$ $Date$
*/
public abstract class TestConnectionPool extends TestCase {
public TestConnectionPool(String testName) {
super(testName);
}
public void setUp() throws Exception {
super.setUp();
}
public void tearDown() throws Exception {
super.tearDown();
// Close any connections opened by the test
while (!connections.isEmpty()) {
Connection conn = (Connection) connections.pop();
try {
conn.close();
} catch (Exception ex) {
// ignore
} finally {
conn = null;
}
}
}
protected abstract Connection getConnection() throws Exception;
protected int getMaxActive() {
return 10;
}
protected long getMaxWait() {
return 100L;
}
/** Connections opened during the course of a test */
protected Stack connections = new Stack();
/** Acquire a connection and push it onto the connections stack */
protected Connection newConnection() throws Exception {
Connection connection = getConnection();
connections.push(connection);
return connection;
}
// ----------- Utility Methods ---------------------------------
protected String getUsername(Connection conn) throws SQLException {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select username");
if (rs.next()) {
return rs.getString(1);
}
return null;
}
// ----------- tests ---------------------------------
public void testClearWarnings() throws Exception {
Connection[] c = new Connection[getMaxActive()];
for (int i = 0; i < c.length; i++) {
c[i] = newConnection();
assertTrue(c[i] != null);
// generate SQLWarning on connection
c[i].prepareCall("warning");
}
for (int i = 0; i < c.length; i++) {
assertNotNull(c[i].getWarnings());
}
for (int i = 0; i < c.length; i++) {
c[i].close();
}
for (int i = 0; i < c.length; i++) {
c[i] = newConnection();
}
for (int i = 0; i < c.length; i++) {
// warnings should have been cleared by putting the connection back in the pool
assertNull(c[i].getWarnings());
}
for (int i = 0; i < c.length; i++) {
c[i].close();
}
}
public void testIsClosed() throws Exception {
for(int i=0;i<getMaxActive();i++) {
Connection conn = newConnection();
assertNotNull(conn);
assertTrue(!conn.isClosed());
PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
conn.close();
assertTrue(conn.isClosed());
}
}
/**
* Verify the close method can be called multiple times on a single connection without
* an exception being thrown.
*/
public void testCanCloseConnectionTwice() throws Exception {
for (int i = 0; i < getMaxActive(); i++) { // loop to show we *can* close again once we've borrowed it from the pool again
Connection conn = newConnection();
assertNotNull(conn);
assertTrue(!conn.isClosed());
conn.close();
assertTrue(conn.isClosed());
conn.close();
assertTrue(conn.isClosed());
}
}
public void testCanCloseStatementTwice() throws Exception {
Connection conn = newConnection();
assertNotNull(conn);
assertTrue(!conn.isClosed());
for(int i=0;i<2;i++) { // loop to show we *can* close again once we've borrowed it from the pool again
Statement stmt = conn.createStatement();
assertNotNull(stmt);
assertFalse(isClosed(stmt));
stmt.close();
assertTrue(isClosed(stmt));
stmt.close();
assertTrue(isClosed(stmt));
stmt.close();
assertTrue(isClosed(stmt));
}
conn.close();
}
public void testCanClosePreparedStatementTwice() throws Exception {
Connection conn = newConnection();
assertNotNull(conn);
assertTrue(!conn.isClosed());
for(int i=0;i<2;i++) { // loop to show we *can* close again once we've borrowed it from the pool again
PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
assertFalse(isClosed(stmt));
stmt.close();
assertTrue(isClosed(stmt));
stmt.close();
assertTrue(isClosed(stmt));
stmt.close();
assertTrue(isClosed(stmt));
}
conn.close();
}
public void testCanCloseCallableStatementTwice() throws Exception {
Connection conn = newConnection();
assertNotNull(conn);
assertTrue(!conn.isClosed());
for(int i=0;i<2;i++) { // loop to show we *can* close again once we've borrowed it from the pool again
PreparedStatement stmt = conn.prepareCall("select * from dual");
assertNotNull(stmt);
assertFalse(isClosed(stmt));
stmt.close();
assertTrue(isClosed(stmt));
stmt.close();
assertTrue(isClosed(stmt));
stmt.close();
assertTrue(isClosed(stmt));
}
conn.close();
}
public void testCanCloseResultSetTwice() throws Exception {
Connection conn = newConnection();
assertNotNull(conn);
assertTrue(!conn.isClosed());
for(int i=0;i<2;i++) { // loop to show we *can* close again once we've borrowed it from the pool again
PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertFalse(isClosed(rset));
rset.close();
assertTrue(isClosed(rset));
rset.close();
assertTrue(isClosed(rset));
rset.close();
assertTrue(isClosed(rset));
}
conn.close();
}
public void testBackPointers() throws Exception {
// normal statement
Connection conn = newConnection();
assertBackPointers(conn, conn.createStatement());
conn = newConnection();
assertBackPointers(conn, conn.createStatement(0, 0));
conn = newConnection();
assertBackPointers(conn, conn.createStatement(0, 0, 0));
// prepared statement
conn = newConnection();
assertBackPointers(conn, conn.prepareStatement("select * from dual"));
conn = newConnection();
assertBackPointers(conn, conn.prepareStatement("select * from dual", 0));
conn = newConnection();
assertBackPointers(conn, conn.prepareStatement("select * from dual", 0, 0));
conn = newConnection();
assertBackPointers(conn, conn.prepareStatement("select * from dual", 0, 0, 0));
conn = newConnection();
assertBackPointers(conn, conn.prepareStatement("select * from dual", new int[0]));
conn = newConnection();
assertBackPointers(conn, conn.prepareStatement("select * from dual", new String[0]));
// callable statement
conn = newConnection();
assertBackPointers(conn, conn.prepareCall("select * from dual"));
conn = newConnection();
assertBackPointers(conn, conn.prepareCall("select * from dual", 0, 0));
conn = newConnection();
assertBackPointers(conn, conn.prepareCall("select * from dual", 0, 0, 0));
}
protected void assertBackPointers(Connection conn, Statement statement) throws SQLException {
assertFalse(conn.isClosed());
assertFalse(isClosed(statement));
assertSame("statement.getConnection() should return the exact same connection instance that was used to create the statement",
conn, statement.getConnection());
ResultSet resultSet = statement.getResultSet();
assertFalse(isClosed(resultSet));
assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set",
statement, resultSet.getStatement());
ResultSet executeResultSet = statement.executeQuery("select * from dual");
assertFalse(isClosed(executeResultSet));
assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set",
statement, executeResultSet.getStatement());
ResultSet keysResultSet = statement.getGeneratedKeys();
assertFalse(isClosed(keysResultSet));
assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set",
statement, keysResultSet.getStatement());
ResultSet preparedResultSet = null;
if (statement instanceof PreparedStatement) {
PreparedStatement preparedStatement = (PreparedStatement) statement;
preparedResultSet = preparedStatement.executeQuery();
assertFalse(isClosed(preparedResultSet));
assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set",
statement, preparedResultSet.getStatement());
}
resultSet.getStatement().getConnection().close();
assertTrue(conn.isClosed());
assertTrue(isClosed(statement));
assertTrue(isClosed(resultSet));
assertTrue(isClosed(executeResultSet));
assertTrue(isClosed(keysResultSet));
if (preparedResultSet != null) {
assertTrue(isClosed(preparedResultSet));
}
}
public void testSimple() throws Exception {
Connection conn = newConnection();
assertNotNull(conn);
PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
conn.close();
}
public void testRepeatedBorrowAndReturn() throws Exception {
for(int i=0;i<100;i++) {
Connection conn = newConnection();
assertNotNull(conn);
PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
conn.close();
}
}
public void testSimple2() throws Exception {
Connection conn = newConnection();
assertNotNull(conn);
{
PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
}
{
PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
}
conn.close();
try {
conn.createStatement();
fail("Can't use closed connections");
} catch(SQLException e) {
// expected
}
conn = newConnection();
assertNotNull(conn);
{
PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
}
{
PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
rset.close();
stmt.close();
}
conn.close();
conn = null;
}
public void testPooling() throws Exception {
// Grab a maximal set of open connections from the pool
Connection[] c = new Connection[getMaxActive()];
Connection[] u = new Connection[getMaxActive()];
for (int i = 0; i < c.length; i++) {
c[i] = newConnection();
if (c[i] instanceof DelegatingConnection) {
u[i] = ((DelegatingConnection) c[i]).getInnermostDelegate();
} else {
for (int j = 0; j <= i; j++) {
c[j].close();
}
return; // skip this test
}
}
// Close connections one at a time and get new ones, making sure
// the new ones come from the pool
for (int i = 0; i < c.length; i++) {
c[i].close();
Connection con = newConnection();
Connection underCon =
((DelegatingConnection) con).getInnermostDelegate();
assertTrue("Failed to get connection", underCon != null);
boolean found = false;
for (int j = 0; j < c.length; j++) {
if (underCon == u[j]) {
found = true;
break;
}
}
assertTrue("New connection not from pool", found);
con.close();
}
}
public void testAutoCommitBehavior() throws Exception {
Connection conn = newConnection();
assertNotNull(conn);
assertTrue(conn.getAutoCommit());
conn.setAutoCommit(false);
conn.close();
Connection conn2 = newConnection();
assertTrue( conn2.getAutoCommit() );
Connection conn3 = newConnection();
assertTrue( conn3.getAutoCommit() );
conn2.close();
conn3.close();
}
/** @see "http://issues.apache.org/bugzilla/show_bug.cgi?id=12400" */
public void testConnectionsAreDistinct() throws Exception {
Connection[] conn = new Connection[getMaxActive()];
for(int i=0;i<conn.length;i++) {
conn[i] = newConnection();
for(int j=0;j<i;j++) {
assertTrue(conn[j] != conn[i]);
assertTrue(!conn[j].equals(conn[i]));
}
}
for(int i=0;i<conn.length;i++) {
conn[i].close();
}
}
public void testOpening() throws Exception {
Connection[] c = new Connection[getMaxActive()];
// test that opening new connections is not closing previous
for (int i = 0; i < c.length; i++) {
c[i] = newConnection();
assertTrue(c[i] != null);
for (int j = 0; j <= i; j++) {
assertTrue(!c[j].isClosed());
}
}
for (int i = 0; i < c.length; i++) {
c[i].close();
}
}
public void testClosing() throws Exception {
Connection[] c = new Connection[getMaxActive()];
// open the maximum connections
for (int i = 0; i < c.length; i++) {
c[i] = newConnection();
}
// close one of the connections
c[0].close();
assertTrue(c[0].isClosed());
// get a new connection
c[0] = newConnection();
for (int i = 0; i < c.length; i++) {
c[i].close();
}
}
public void testMaxActive() throws Exception {
Connection[] c = new Connection[getMaxActive()];
for (int i = 0; i < c.length; i++) {
c[i] = newConnection();
assertTrue(c[i] != null);
}
try {
newConnection();
fail("Allowed to open more than DefaultMaxActive connections.");
} catch (java.sql.SQLException e) {
// should only be able to open 10 connections, so this test should
// throw an exception
}
for (int i = 0; i < c.length; i++) {
c[i].close();
}
}
/**
* DBCP-128: BasicDataSource.getConnection()
* Connections don't work as hashtable keys
*/
public void testHashing() throws Exception {
Connection con = getConnection();
Hashtable hash = new Hashtable();
hash.put(con, "test");
assertEquals("test", hash.get(con));
assertTrue(hash.containsKey(con));
assertTrue(hash.contains("test"));
hash.clear();
con.close();
}
public void testThreaded() {
TestThread[] threads = new TestThread[getMaxActive()];
for(int i=0;i<threads.length;i++) {
threads[i] = new TestThread(50,50);
Thread t = new Thread(threads[i]);
t.start();
}
for(int i=0;i<threads.length;i++) {
while(!(threads[i]).complete()) {
try {
Thread.sleep(100L);
} catch(Exception e) {
// ignored
}
}
if(threads[i].failed()) {
fail();
}
}
}
class TestThread implements Runnable {
java.util.Random _random = new java.util.Random();
boolean _complete = false;
boolean _failed = false;
int _iter = 100;
int _delay = 50;
public TestThread() {
}
public TestThread(int iter) {
_iter = iter;
}
public TestThread(int iter, int delay) {
_iter = iter;
_delay = delay;
}
public boolean complete() {
return _complete;
}
public boolean failed() {
return _failed;
}
public void run() {
for(int i=0;i<_iter;i++) {
try {
Thread.sleep(_random.nextInt(_delay));
} catch(Exception e) {
// ignored
}
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rset = null;
try {
conn = newConnection();
stmt = conn.prepareStatement("select 'literal', SYSDATE from dual");
rset = stmt.executeQuery();
try {
Thread.sleep(_random.nextInt(_delay));
} catch(Exception e) {
// ignored
}
} catch(Exception e) {
e.printStackTrace();
_failed = true;
_complete = true;
break;
} finally {
try { if (rset != null) rset.close(); } catch(Exception e) { }
try { if (stmt != null) stmt.close(); } catch(Exception e) { }
try { if (conn != null) conn.close(); } catch(Exception e) { }
}
}
_complete = true;
}
}
// Bugzilla Bug 24328: PooledConnectionImpl ignores resultsetType
// and Concurrency if statement pooling is not enabled
// http://issues.apache.org/bugzilla/show_bug.cgi?id=24328
public void testPrepareStatementOptions() throws Exception
{
Connection conn = newConnection();
assertNotNull(conn);
PreparedStatement stmt = conn.prepareStatement("select * from dual",
ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
assertNotNull(stmt);
ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
assertEquals(ResultSet.TYPE_SCROLL_SENSITIVE, rset.getType());
assertEquals(ResultSet.CONCUR_UPDATABLE, rset.getConcurrency());
rset.close();
stmt.close();
conn.close();
}
// Bugzilla Bug 24966: NullPointer with Oracle 9 driver
// wrong order of passivate/close when a rset isn't closed
public void testNoRsetClose() throws Exception {
Connection conn = newConnection();
assertNotNull(conn);
PreparedStatement stmt = conn.prepareStatement("test");
assertNotNull(stmt);
ResultSet rset = stmt.getResultSet();
assertNotNull(rset);
// forget to close the resultset: rset.close();
stmt.close();
conn.close();
}
// Bugzilla Bug 26966: Connectionpool's connections always returns same
public void testHashCode() throws Exception {
Connection conn1 = newConnection();
assertNotNull(conn1);
Connection conn2 = newConnection();
assertNotNull(conn2);
assertTrue(conn1.hashCode() != conn2.hashCode());
}
protected boolean isClosed(Statement statement) {
try {
statement.getWarnings();
return false;
} catch (SQLException e) {
// getWarnings throws an exception if the statement is
// closed, but could throw an exception for other reasons
// in this case it is good enought to assume the statement
// is closed
return true;
}
}
protected boolean isClosed(ResultSet resultSet) {
try {
resultSet.getWarnings();
return false;
} catch (SQLException e) {
// getWarnings throws an exception if the statement is
// closed, but could throw an exception for other reasons
// in this case it is good enought to assume the result set
// is closed
return true;
}
}
}