blob: 5e766c8fc24786493ccdbc87e1cb10a8562b85a8 [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;
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.Hashtable;
import java.util.Random;
import java.util.Stack;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
// 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
*/
public abstract class TestConnectionPool {
@After
public void tearDown() throws Exception {
// Close any connections opened by the test
while (!connections.isEmpty()) {
Connection conn = connections.pop();
try {
conn.close();
} catch (final Exception ex) {
// ignore
} finally {
conn = null;
}
}
}
protected abstract Connection getConnection() throws Exception;
protected int getMaxTotal() {
return 10;
}
protected long getMaxWaitMillis() {
return 100L;
}
/** Connections opened during the course of a test */
protected Stack<Connection> connections = new Stack<>();
/** Acquire a connection and push it onto the connections stack */
protected Connection newConnection() throws Exception {
final Connection connection = getConnection();
connections.push(connection);
return connection;
}
// ----------- Utility Methods ---------------------------------
protected String getUsername(final Connection conn) throws SQLException {
final Statement stmt = conn.createStatement();
final ResultSet rs = stmt.executeQuery("select username");
if (rs.next()) {
return rs.getString(1);
}
return null;
}
// ----------- tests ---------------------------------
@Test
public void testClearWarnings() throws Exception {
final Connection[] c = new Connection[getMaxTotal()];
for (int i = 0; i < c.length; i++) {
c[i] = newConnection();
assertTrue(c[i] != null);
// generate SQLWarning on connection
try (CallableStatement cs = c[i].prepareCall("warning")){
}
}
for (final Connection element : c) {
assertNotNull(element.getWarnings());
}
for (final Connection element : c) {
element.close();
}
for (int i = 0; i < c.length; i++) {
c[i] = newConnection();
}
for (final Connection element : c) {
// warnings should have been cleared by putting the connection back in the pool
assertNull(element.getWarnings());
}
for (final Connection element : c) {
element.close();
}
}
@Test
public void testIsClosed() throws Exception {
for(int i=0;i<getMaxTotal();i++) {
final Connection conn = newConnection();
assertNotNull(conn);
assertTrue(!conn.isClosed());
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();
assertTrue(conn.isClosed());
}
}
/**
* Verify the close method can be called multiple times on a single connection without
* an exception being thrown.
*/
@Test
public void testCanCloseConnectionTwice() throws Exception {
for (int i = 0; i < getMaxTotal(); i++) { // loop to show we *can* close again once we've borrowed it from the pool again
final Connection conn = newConnection();
assertNotNull(conn);
assertTrue(!conn.isClosed());
conn.close();
assertTrue(conn.isClosed());
conn.close();
assertTrue(conn.isClosed());
}
}
@Test
public void testCanCloseStatementTwice() throws Exception {
final 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
final 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();
}
@Test
public void testCanClosePreparedStatementTwice() throws Exception {
final 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
final 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();
}
@Test
public void testCanCloseCallableStatementTwice() throws Exception {
final 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
final 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();
}
@Test
public void testCanCloseResultSetTwice() throws Exception {
final 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
final PreparedStatement stmt = conn.prepareStatement("select * from dual");
assertNotNull(stmt);
final 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();
}
@Test
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(final Connection conn, final 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());
final 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());
final 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());
final 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) {
final 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));
}
}
@Test
public void testSimple() throws Exception {
final Connection conn = newConnection();
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 testRepeatedBorrowAndReturn() throws Exception {
for(int i=0;i<100;i++) {
final Connection conn = newConnection();
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 testSimple2() throws Exception {
Connection conn = newConnection();
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();
}
{
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();
try (Statement s = conn.createStatement()){
fail("Can't use closed connections");
} catch(final SQLException e) {
// expected
}
conn = newConnection();
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();
}
{
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();
conn = null;
}
@Test
public void testPooling() throws Exception {
// Grab a maximal set of open connections from the pool
final Connection[] c = new Connection[getMaxTotal()];
final Connection[] u = new Connection[getMaxTotal()];
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 (final Connection element : c) {
element.close();
final Connection con = newConnection();
final 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();
}
}
@Test
public void testAutoCommitBehavior() throws Exception {
final Connection conn0 = newConnection();
assertNotNull("connection should not be null", conn0);
assertTrue("autocommit should be true for conn0", conn0.getAutoCommit());
final Connection conn1 = newConnection();
assertTrue("autocommit should be true for conn1", conn1.getAutoCommit() );
conn1.close();
assertTrue("autocommit should be true for conn0", conn0.getAutoCommit());
conn0.setAutoCommit(false);
assertFalse("autocommit should be false for conn0", conn0.getAutoCommit());
conn0.close();
final Connection conn2 = newConnection();
assertTrue("autocommit should be true for conn2", conn2.getAutoCommit() );
final Connection conn3 = newConnection();
assertTrue("autocommit should be true for conn3", conn3.getAutoCommit() );
conn2.close();
conn3.close();
}
/** "http://issues.apache.org/bugzilla/show_bug.cgi?id=12400" */
@Test
public void testConnectionsAreDistinct() throws Exception {
final Connection[] conn = new Connection[getMaxTotal()];
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 (final Connection element : conn) {
element.close();
}
}
@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] = newConnection();
assertTrue(c[i] != null);
for (int j = 0; j <= i; j++) {
assertTrue(!c[j].isClosed());
}
}
for (final Connection element : c) {
element.close();
}
}
@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] = newConnection();
}
// close one of the connections
c[0].close();
assertTrue(c[0].isClosed());
// get a new connection
c[0] = newConnection();
for (final Connection element : c) {
element.close();
}
}
@Test
public void testMaxTotal() throws Exception {
final Connection[] c = new Connection[getMaxTotal()];
for (int i = 0; i < c.length; i++) {
c[i] = newConnection();
assertTrue(c[i] != null);
}
try {
newConnection();
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();
}
}
/**
* DBCP-128: BasicDataSource.getConnection()
* Connections don't work as hashtable keys
*/
@Test
public void testHashing() throws Exception {
final Connection con = getConnection();
final Hashtable<Connection, String> hash = new Hashtable<>();
hash.put(con, "test");
assertEquals("test", hash.get(con));
assertTrue(hash.containsKey(con));
assertTrue(hash.contains("test"));
hash.clear();
con.close();
}
@Test
public void testThreaded() {
final TestThread[] threads = new TestThread[getMaxTotal()];
for(int i=0;i<threads.length;i++) {
threads[i] = new TestThread(50,50);
final 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(final Exception e) {
// ignored
}
}
if(threads[i] != null && threads[i].failed()) {
fail("Thread failed: " + i);
}
}
}
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(final int iter) {
_iter = iter;
}
public TestThread(final int iter, final int delay) {
_iter = iter;
_delay = delay;
}
public boolean complete() {
return _complete;
}
public boolean failed() {
return _failed;
}
@Override
public void run() {
for(int i=0;i<_iter;i++) {
try {
Thread.sleep(_random.nextInt(_delay));
} catch(final Exception e) {
// ignored
}
try (Connection conn = newConnection();
PreparedStatement stmt = conn.prepareStatement(
"select 'literal', SYSDATE from dual");
ResultSet rset = stmt.executeQuery();) {
try {
Thread.sleep(_random.nextInt(_delay));
} catch(final Exception e) {
// ignored
}
} catch(final Exception e) {
e.printStackTrace();
_failed = true;
_complete = true;
break;
}
}
_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
@Test
public void testPrepareStatementOptions() throws Exception
{
final Connection conn = newConnection();
assertNotNull(conn);
final PreparedStatement stmt = conn.prepareStatement("select * from dual",
ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
assertNotNull(stmt);
final 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
@Test
public void testNoRsetClose() throws Exception {
final Connection conn = newConnection();
assertNotNull(conn);
final PreparedStatement stmt = conn.prepareStatement("test");
assertNotNull(stmt);
final 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
@Test
public void testHashCode() throws Exception {
final Connection conn1 = newConnection();
assertNotNull(conn1);
final Connection conn2 = newConnection();
assertNotNull(conn2);
assertTrue(conn1.hashCode() != conn2.hashCode());
}
protected boolean isClosed(final Statement statement) {
try {
statement.getWarnings();
return false;
} catch (final 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 enough to assume the statement
// is closed
return true;
}
}
protected boolean isClosed(final ResultSet resultSet) {
try {
resultSet.getWarnings();
return false;
} catch (final 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 enough to assume the result set
// is closed
return true;
}
}
private static final boolean DISPLAY_THREAD_DETAILS=
Boolean.valueOf(System.getProperty("TestConnectionPool.display.thread.details", "false")).booleanValue();
// To pass this to a Maven test, use:
// mvn test -DargLine="-DTestConnectionPool.display.thread.details=true"
// @see http://jira.codehaus.org/browse/SUREFIRE-121
/**
* Launches a group of 2 * getMaxTotal() threads, each of which will attempt to obtain a connection
* from the pool, hold it for {@code holdTime} ms, and then return it to the pool. If {@code loopOnce} is false,
* threads will continue this process indefinitely. If {@code expectError} is true, exactly 1/2 of the
* threads are expected to either throw exceptions or fail to complete. If {@code expectError} is false,
* all threads are expected to complete successfully.
*
* @param holdTime time in ms that a thread holds a connection before returning it to the pool
* @param expectError whether or not an error is expected
* @param loopOnce whether threads should complete the borrow - hold - return cycle only once, or loop indefinitely
* @param maxWaitMillis passed in by client - has no impact on the test itself, but does get reported
*
* @throws Exception
*/
protected void multipleThreads(final int holdTime,
final boolean expectError, final boolean loopOnce,
final long maxWaitMillis) throws Exception {
multipleThreads(holdTime, expectError, loopOnce, maxWaitMillis, 1, 2 * getMaxTotal(), 300);
}
/**
* Launches a group of {@code numThreads} threads, each of which will attempt to obtain a connection
* from the pool, hold it for {@code holdTime} ms, and then return it to the pool. If {@code loopOnce} is false,
* threads will continue this process indefinitely. If {@code expectError} is true, exactly 1/2 of the
* threads are expected to either throw exceptions or fail to complete. If {@code expectError} is false,
* all threads are expected to complete successfully. Threads are stopped after {@code duration} ms.
*
* @param holdTime time in ms that a thread holds a connection before returning it to the pool
* @param expectError whether or not an error is expected
* @param loopOnce whether threads should complete the borrow - hold - return cycle only once, or loop indefinitely
* @param maxWaitMillis passed in by client - has no impact on the test itself, but does get reported
* @param numThreads the number of threads
* @param duration duration in ms of test
*
* @throws Exception
*/
protected void multipleThreads(final int holdTime,
final boolean expectError, final boolean loopOnce,
final long maxWaitMillis, final int numStatements, final int numThreads, final long duration) throws Exception {
final long startTime = timeStamp();
final PoolTest[] pts = new PoolTest[numThreads];
// Catch Exception so we can stop all threads if one fails
final ThreadGroup threadGroup = new ThreadGroup("foo") {
@Override
public void uncaughtException(final Thread t, final Throwable e) {
for (final PoolTest pt : pts) {
pt.stop();
}
}
};
// Create all the threads
for (int i = 0; i < pts.length; i++) {
pts[i] = new PoolTest(threadGroup, holdTime, expectError, loopOnce, numStatements);
}
// Start all the threads
for (final PoolTest pt : pts) {
pt.start();
}
// Give all threads a chance to start and succeed
Thread.sleep(duration);
// Stop threads
for (final PoolTest pt : pts) {
pt.stop();
}
/*
* Wait for all threads to terminate.
* This is essential to ensure that all threads have a chance to update success[0]
* and to ensure that the variable is published correctly.
*/
int done=0;
int failed=0;
int didNotRun = 0;
int loops=0;
for (final PoolTest poolTest : pts) {
poolTest.thread.join();
loops += poolTest.loops;
final String state = poolTest.state;
if (DONE.equals(state)){
done++;
}
if (poolTest.loops == 0){
didNotRun++;
}
final Throwable thrown = poolTest.thrown;
if (thrown != null) {
failed++;
if (!expectError || !(thrown instanceof SQLException)){
System.out.println("Unexpected error: "+thrown.getMessage());
}
}
}
final long time = timeStamp() - startTime;
System.out.println("Multithread test time = " + time
+ " ms. Threads: " + pts.length
+ ". Loops: " + loops
+ ". Hold time: " + holdTime
+ ". maxWaitMillis: " + maxWaitMillis
+ ". Done: " + done
+ ". Did not run: " + didNotRun
+ ". Failed: " + failed
+ ". expectError: " + expectError
);
if (expectError) {
if (DISPLAY_THREAD_DETAILS || (pts.length/2 != failed)){
final long offset = pts[0].created - 1000; // To reduce size of output numbers, but ensure they have 4 digits
System.out.println("Offset: "+offset);
for (int i = 0; i < pts.length; i++) {
final PoolTest pt = pts[i];
System.out.println(
"Pre: " + (pt.preconnected-offset) // First, so can sort on this easily
+ ". Post: " + (pt.postconnected != 0 ? Long.toString(pt.postconnected-offset): "-")
+ ". Hash: " + pt.connHash
+ ". Startup: " + (pt.started-pt.created)
+ ". getConn(): " + (pt.connected != 0 ? Long.toString(pt.connected-pt.preconnected) : "-")
+ ". Runtime: " + (pt.ended-pt.started)
+ ". IDX: " + i
+ ". Loops: " + pt.loops
+ ". State: " + pt.state
+ ". thrown: "+ pt.thrown
+ "."
);
}
}
if (didNotRun > 0){
System.out.println("NOTE: some threads did not run the code: "+didNotRun);
}
// Perform initial sanity check:
assertTrue("Expected some of the threads to fail",failed > 0);
// Assume that threads that did not run would have timed out.
assertEquals("WARNING: Expected half the threads to fail",pts.length/2,failed+didNotRun);
} else {
assertEquals("Did not expect any threads to fail",0,failed);
}
}
private static int currentThreadCount = 0;
private static final String DONE = "Done";
protected class PoolTest implements Runnable {
/**
* The number of milliseconds to hold onto a database connection
*/
private final int connHoldTime;
private final int numStatements;
private volatile boolean isRun;
private String state; // No need to be volatile if it is read after the thread finishes
private final Thread thread;
private Throwable thrown;
private final Random random = new Random();
// Debug for DBCP-318
private final long created; // When object was created
private long started; // when thread started
private long ended; // when thread ended
private long preconnected; // just before connect
private long connected; // when thread last connected
private long postconnected; // when thread released connection
private int loops = 0;
private int connHash = 0; // Connection identity hashCode (to see which one is reused)
private final boolean stopOnException; // If true, don't rethrow Exception
private final boolean loopOnce; // If true, don't repeat loop
public PoolTest(final ThreadGroup threadGroup, final int connHoldTime, final boolean isStopOnException) {
this(threadGroup, connHoldTime, isStopOnException, false, 1);
}
public PoolTest(final ThreadGroup threadGroup, final int connHoldTime, final boolean isStopOnException, final int numStatements) {
this(threadGroup, connHoldTime, isStopOnException, false, numStatements);
}
private PoolTest(final ThreadGroup threadGroup, final int connHoldTime, final boolean isStopOnException, final boolean once, final int numStatements) {
this.loopOnce = once;
this.connHoldTime = connHoldTime;
stopOnException = isStopOnException;
isRun = true; // Must be done here so main thread is guaranteed to be able to set it false
thrown = null;
thread =
new Thread(threadGroup, this, "Thread+" + currentThreadCount++);
thread.setDaemon(false);
created = timeStamp();
this.numStatements = numStatements;
}
public void start(){
thread.start();
}
@Override
public void run() {
started = timeStamp();
try {
while (isRun) {
loops++;
state = "Getting Connection";
preconnected = timeStamp();
final Connection conn = getConnection();
connHash = System.identityHashCode(((DelegatingConnection<?>)conn).getInnermostDelegate());
connected = timeStamp();
state = "Using Connection";
assertNotNull(conn);
final String sql = numStatements == 1 ? "select * from dual" : "select count " + random.nextInt(numStatements - 1);
final PreparedStatement stmt =
conn.prepareStatement(sql);
assertNotNull(stmt);
final ResultSet rset = stmt.executeQuery();
assertNotNull(rset);
assertTrue(rset.next());
state = "Holding Connection";
Thread.sleep(connHoldTime);
state = "Closing ResultSet";
rset.close();
state = "Closing Statement";
stmt.close();
state = "Closing Connection";
conn.close();
postconnected = timeStamp();
state = "Closed";
if (loopOnce){
break; // Or could set isRun=false
}
}
state = DONE;
} catch (final Throwable t) {
thrown = t;
if (!stopOnException) {
throw new RuntimeException();
}
} finally {
ended = timeStamp();
}
}
public void stop() {
isRun = false;
}
public Thread getThread() {
return thread;
}
}
long timeStamp() {
return System.currentTimeMillis();// JVM 1.5+ System.nanoTime() / 1000000;
}
}