/*
* 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.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* TestSuite for BasicDataSource
*
* @author Dirk Verbeeck
* @version $Revision$ $Date$
*/
public class TestBasicDataSource extends TestConnectionPool {
public TestBasicDataSource(String testName) {
super(testName);
}
public static Test suite() {
return new TestSuite(TestBasicDataSource.class);
}
protected Connection getConnection() throws Exception {
return ds.getConnection();
}
protected BasicDataSource ds = null;
private static final String CATALOG = "test catalog";
public void setUp() throws Exception {
super.setUp();
ds = createDataSource();
ds.setDriverClassName("org.apache.commons.dbcp.TesterDriver");
ds.setUrl("jdbc:apache:commons:testdriver");
ds.setMaxActive(getMaxActive());
ds.setMaxWait(getMaxWait());
ds.setDefaultAutoCommit(true);
ds.setDefaultReadOnly(false);
ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
ds.setDefaultCatalog(CATALOG);
ds.setUsername("username");
ds.setPassword("password");
ds.setValidationQuery("SELECT DUMMY FROM DUAL");
ds.setConnectionInitSqls(Arrays.asList(new String[] { "SELECT 1", "SELECT 2"}));
}
protected BasicDataSource createDataSource() throws Exception {
return new BasicDataSource();
}
public void tearDown() throws Exception {
super.tearDown();
ds.close();
ds = null;
}
public void testClose() throws Exception {
ds.setAccessToUnderlyingConnectionAllowed(true);
// active connection is held open when ds is closed
Connection activeConnection = getConnection();
Connection rawActiveConnection = ((DelegatingConnection) activeConnection).getInnermostDelegate();
assertFalse(activeConnection.isClosed());
assertFalse(rawActiveConnection.isClosed());
// idle connection is in pool but closed
Connection idleConnection = getConnection();
Connection rawIdleConnection = ((DelegatingConnection) idleConnection).getInnermostDelegate();
assertFalse(idleConnection.isClosed());
assertFalse(rawIdleConnection.isClosed());
// idle wrapper should be closed but raw connection should be open
idleConnection.close();
assertTrue(idleConnection.isClosed());
assertFalse(rawIdleConnection.isClosed());
ds.close();
// raw idle connection should now be closed
assertTrue(rawIdleConnection.isClosed());
// active connection should still be open
assertFalse(activeConnection.isClosed());
assertFalse(rawActiveConnection.isClosed());
// now close the active connection
activeConnection.close();
// both wrapper and raw active connection should be closed
assertTrue(activeConnection.isClosed());
assertTrue(rawActiveConnection.isClosed());
// Verify SQLException on getConnection after close
try {
getConnection();
fail("Expecting SQLException");
} catch (SQLException ex) {
// Expected
}
// Redundant close is OK
ds.close();
}
public void testSetProperties() throws Exception {
// normal
ds.setConnectionProperties("name1=value1;name2=value2;name3=value3");
assertEquals(3, ds.connectionProperties.size());
assertEquals("value1", ds.connectionProperties.getProperty("name1"));
assertEquals("value2", ds.connectionProperties.getProperty("name2"));
assertEquals("value3", ds.connectionProperties.getProperty("name3"));
// make sure all properties are replaced
ds.setConnectionProperties("name1=value1;name2=value2");
assertEquals(2, ds.connectionProperties.size());
assertEquals("value1", ds.connectionProperties.getProperty("name1"));
assertEquals("value2", ds.connectionProperties.getProperty("name2"));
assertFalse(ds.connectionProperties.containsKey("name3"));
// no value is empty string
ds.setConnectionProperties("name1=value1;name2");
assertEquals(2, ds.connectionProperties.size());
assertEquals("value1", ds.connectionProperties.getProperty("name1"));
assertEquals("", ds.connectionProperties.getProperty("name2"));
// no value (with equals) is empty string
ds.setConnectionProperties("name1=value1;name2=");
assertEquals(2, ds.connectionProperties.size());
assertEquals("value1", ds.connectionProperties.getProperty("name1"));
assertEquals("", ds.connectionProperties.getProperty("name2"));
// single value
ds.setConnectionProperties("name1=value1");
assertEquals(1, ds.connectionProperties.size());
assertEquals("value1", ds.connectionProperties.getProperty("name1"));
// single value with trailing ;
ds.setConnectionProperties("name1=value1;");
assertEquals(1, ds.connectionProperties.size());
assertEquals("value1", ds.connectionProperties.getProperty("name1"));
// single value wit no value
ds.setConnectionProperties("name1");
assertEquals(1, ds.connectionProperties.size());
assertEquals("", ds.connectionProperties.getProperty("name1"));
// null should throw a NullPointerException
try {
ds.setConnectionProperties(null);
fail("Expected NullPointerException");
} catch (NullPointerException e) {
// expected
}
}
public void testTransactionIsolationBehavior() throws Exception {
Connection conn = getConnection();
assertNotNull(conn);
assertEquals(Connection.TRANSACTION_READ_COMMITTED, conn.getTransactionIsolation());
conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
conn.close();
Connection conn2 = getConnection();
assertEquals(Connection.TRANSACTION_READ_COMMITTED, conn2.getTransactionIsolation());
Connection conn3 = getConnection();
assertEquals(Connection.TRANSACTION_READ_COMMITTED, conn3.getTransactionIsolation());
conn2.close();
conn3.close();
}
public void testPooling() throws Exception {
// this also needs access to the underlying connection
ds.setAccessToUnderlyingConnectionAllowed(true);
super.testPooling();
}
public void testNoAccessToUnderlyingConnectionAllowed() throws Exception {
// default: false
assertEquals(false, ds.isAccessToUnderlyingConnectionAllowed());
Connection conn = getConnection();
Connection dconn = ((DelegatingConnection) conn).getDelegate();
assertNull(dconn);
dconn = ((DelegatingConnection) conn).getInnermostDelegate();
assertNull(dconn);
}
public void testAccessToUnderlyingConnectionAllowed() throws Exception {
ds.setAccessToUnderlyingConnectionAllowed(true);
assertEquals(true, ds.isAccessToUnderlyingConnectionAllowed());
Connection conn = getConnection();
Connection dconn = ((DelegatingConnection) conn).getDelegate();
assertNotNull(dconn);
dconn = ((DelegatingConnection) conn).getInnermostDelegate();
assertNotNull(dconn);
assertTrue(dconn instanceof TesterConnection);
}
public void testEmptyValidationQuery() throws Exception {
assertNotNull(ds.getValidationQuery());
ds.setValidationQuery("");
assertNull(ds.getValidationQuery());
ds.setValidationQuery(" ");
assertNull(ds.getValidationQuery());
}
public void testInvalidValidationQuery() {
try {
ds.setValidationQuery("invalid");
ds.getConnection();
fail("expected SQLException");
}
catch (SQLException e) {
if (e.toString().indexOf("invalid") < 0) {
fail("expected detailed error message");
}
}
}
public void testValidationQueryTimoutFail() {
ds.setTestOnBorrow(true);
ds.setValidationQueryTimeout(3); // Too fast for TesterStatement
try {
ds.getConnection();
fail("expected SQLException");
} catch (SQLException ex) {
if (ex.toString().indexOf("timeout") < 0) {
fail("expected timeout error message");
}
}
}
public void testValidationQueryTimeoutZero() throws Exception {
ds.setTestOnBorrow(true);
ds.setTestOnReturn(true);
ds.setValidationQueryTimeout(0);
Connection con = ds.getConnection();
con.close();
}
public void testValidationQueryTimeoutNegative() throws Exception {
ds.setTestOnBorrow(true);
ds.setTestOnReturn(true);
ds.setValidationQueryTimeout(-1);
Connection con = ds.getConnection();
con.close();
}
public void testValidationQueryTimeoutSucceed() throws Exception {
ds.setTestOnBorrow(true);
ds.setTestOnReturn(true);
ds.setValidationQueryTimeout(100); // Works for TesterStatement
Connection con = ds.getConnection();
con.close();
}
public void testEmptyInitConnectionSql() throws Exception {
ds.setConnectionInitSqls(Arrays.asList(new String[]{"", " "}));
assertNotNull(ds.getConnectionInitSqls());
assertEquals(0, ds.getConnectionInitSqls().size());
ds.setConnectionInitSqls(null);
assertNotNull(ds.getConnectionInitSqls());
assertEquals(0, ds.getConnectionInitSqls().size());
}
public void testInvalidConnectionInitSql() {
try {
ds.setConnectionInitSqls(Arrays.asList(new String[]{"SELECT 1","invalid"}));
ds.getConnection();
fail("expected SQLException");
}
catch (SQLException e) {
if (e.toString().indexOf("invalid") < 0) {
fail("expected detailed error message");
}
}
}
public void testSetValidationTestProperties() {
// defaults
assertEquals(true, ds.getTestOnBorrow());
assertEquals(false, ds.getTestOnReturn());
assertEquals(false, ds.getTestWhileIdle());
ds.setTestOnBorrow(true);
ds.setTestOnReturn(true);
ds.setTestWhileIdle(true);
assertEquals(true, ds.getTestOnBorrow());
assertEquals(true, ds.getTestOnReturn());
assertEquals(true, ds.getTestWhileIdle());
ds.setTestOnBorrow(false);
ds.setTestOnReturn(false);
ds.setTestWhileIdle(false);
assertEquals(false, ds.getTestOnBorrow());
assertEquals(false, ds.getTestOnReturn());
assertEquals(false, ds.getTestWhileIdle());
}
public void testNoValidationQuery() throws Exception {
ds.setTestOnBorrow(true);
ds.setTestOnReturn(true);
ds.setTestWhileIdle(true);
ds.setValidationQuery("");
Connection conn = ds.getConnection();
conn.close();
assertEquals(false, ds.getTestOnBorrow());
assertEquals(false, ds.getTestOnReturn());
assertEquals(false, ds.getTestWhileIdle());
}
public void testDefaultCatalog() throws Exception {
Connection[] c = new Connection[getMaxActive()];
for (int i = 0; i < c.length; i++) {
c[i] = getConnection();
assertTrue(c[i] != null);
assertEquals(CATALOG, c[i].getCatalog());
}
for (int i = 0; i < c.length; i++) {
c[i].setCatalog("error");
c[i].close();
}
for (int i = 0; i < c.length; i++) {
c[i] = getConnection();
assertTrue(c[i] != null);
assertEquals(CATALOG, c[i].getCatalog());
}
for (int i = 0; i < c.length; i++) {
c[i].close();
}
}
public void testSetAutoCommitTrueOnClose() throws Exception {
ds.setAccessToUnderlyingConnectionAllowed(true);
ds.setDefaultAutoCommit(false);
Connection conn = getConnection();
assertNotNull(conn);
assertEquals(false, conn.getAutoCommit());
Connection dconn = ((DelegatingConnection) conn).getInnermostDelegate();
assertNotNull(dconn);
assertEquals(false, dconn.getAutoCommit());
conn.close();
assertEquals(true, dconn.getAutoCommit());
}
public void testInitialSize() throws Exception {
ds.setMaxActive(20);
ds.setMaxIdle(20);
ds.setInitialSize(10);
Connection conn = getConnection();
assertNotNull(conn);
conn.close();
assertEquals(0, ds.getNumActive());
assertEquals(10, ds.getNumIdle());
}
// Bugzilla Bug 28251: Returning dead database connections to BasicDataSource
// isClosed() failure blocks returning a connection to the pool
public void testIsClosedFailure() throws SQLException {
ds.setAccessToUnderlyingConnectionAllowed(true);
Connection conn = ds.getConnection();
assertNotNull(conn);
assertEquals(1, ds.getNumActive());
// set an IO failure causing the isClosed mathod to fail
TesterConnection tconn = (TesterConnection) ((DelegatingConnection)conn).getInnermostDelegate();
tconn.setFailure(new IOException("network error"));
try {
conn.close();
fail("Expected SQLException");
}
catch(SQLException ex) { }
assertEquals(0, ds.getNumActive());
}
/**
* Bugzilla Bug 29054:
* The BasicDataSource.setTestOnReturn(boolean) is not carried through to
* the GenericObjectPool variable _testOnReturn.
*/
public void testPropertyTestOnReturn() throws Exception {
ds.setValidationQuery("select 1 from dual");
ds.setTestOnBorrow(false);
ds.setTestWhileIdle(false);
ds.setTestOnReturn(true);
Connection conn = ds.getConnection();
assertNotNull(conn);
assertEquals(false, ds.connectionPool.getTestOnBorrow());
assertEquals(false, ds.connectionPool.getTestWhileIdle());
assertEquals(true, ds.connectionPool.getTestOnReturn());
}
/**
* Bugzilla Bug 29055: AutoCommit and ReadOnly
* The DaffodilDB driver throws an SQLException if
* trying to commit or rollback a readOnly connection.
*/
public void testRollbackReadOnly() throws Exception {
ds.setDefaultReadOnly(true);
ds.setDefaultAutoCommit(false);
Connection conn = ds.getConnection();
assertNotNull(conn);
conn.close();
}
/**
* Bugzilla Bug 29832: Broken behaviour for BasicDataSource.setMaxActive(0)
* MaxActive == 0 should throw SQLException on getConnection.
* Results from Bug 29863 in commons-pool.
*/
public void testMaxActiveZero() throws Exception {
ds.setMaxActive(0);
try {
Connection conn = ds.getConnection();
assertNotNull(conn);
fail("SQLException expected");
} catch (SQLException e) {
// test OK
}
}
/**
* JIRA DBCP-93: If an SQLException occurs after the GenericObjectPool is
* initialized in createDataSource, the evictor task is not cleaned up.
*/
public void testCreateDataSourceCleanupThreads() throws Exception {
ds.close();
ds = null;
ds = createDataSource();
ds.setDriverClassName("org.apache.commons.dbcp.TesterDriver");
ds.setUrl("jdbc:apache:commons:testdriver");
ds.setMaxActive(getMaxActive());
ds.setMaxWait(getMaxWait());
ds.setDefaultAutoCommit(true);
ds.setDefaultReadOnly(false);
ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
ds.setDefaultCatalog(CATALOG);
ds.setUsername("username");
// Set timeBetweenEvictionRuns > 0, so evictor is created
ds.setTimeBetweenEvictionRunsMillis(100);
// Make password incorrect, so createDataSource will throw
ds.setPassword("wrong");
ds.setValidationQuery("SELECT DUMMY FROM DUAL");
int threadCount = Thread.activeCount();
for (int i = 0; i < 10; i++) {
try {
ds.getConnection();
} catch (SQLException ex) {
// ignore
}
}
// Allow one extra thread for JRockit compatibility
assertTrue(Thread.activeCount() <= threadCount + 1);
}
}