blob: 24a7367c0fd5c305f77ea28ac5df4f4675fd79b7 [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.managed;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.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 javax.transaction.Synchronization;
import javax.transaction.Transaction;
import org.apache.commons.dbcp2.DelegatingConnection;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Tests ManagedDataSource with an active transaction in progress.
*/
public class TestManagedDataSourceInTx extends TestManagedDataSource {
@Override
@BeforeEach
public void setUp() throws Exception {
super.setUp();
transactionManager.begin();
}
@Override
@AfterEach
public void tearDown() throws Exception {
if (transactionManager.getTransaction() != null) {
transactionManager.commit();
}
super.tearDown();
}
/**
* @see #testSharedConnection()
*/
@Override
@Test
public void testManagedConnectionEqualsFail() throws Exception {
// this test is invalid for managed connections since because
// two connections to the same datasource are supposed to share
// a single connection
}
@Override
@Test
public void testNestedConnections() {
// Not supported
}
@Override
@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++) {
// two connections should be distinct instances
Assertions.assertNotSame(conn[j], conn[i]);
// neither should they should be equivalent even though they are
// sharing the same underlying connection
Assertions.assertNotEquals(conn[j], conn[i]);
// Check underlying connection is the same
Assertions.assertEquals(((DelegatingConnection<?>) conn[j]).getInnermostDelegateInternal(),
((DelegatingConnection<?>) conn[i]).getInnermostDelegateInternal());
}
}
for (final Connection element : conn) {
element.close();
}
}
@Test
public void testGetConnectionInAfterCompletion() throws Exception {
final DelegatingConnection<?> connection = (DelegatingConnection<?>) newConnection();
// Don't close so we can check it for warnings in afterCompletion
transactionManager.getTransaction().registerSynchronization(new Synchronization() {
@Override
public void beforeCompletion() {
// empty
}
@Override
public void afterCompletion(final int i) {
try {
final Connection connection1 = ds.getConnection();
try {
connection1.getWarnings();
fail("Could operate on closed connection");
} catch (final SQLException e) {
// This is expected
}
} catch (final SQLException e) {
fail("Should have been able to get connection");
}
}
});
connection.close();
transactionManager.commit();
}
@Test
public void testDoubleReturn() throws Exception {
transactionManager.getTransaction().registerSynchronization(new Synchronization() {
private ManagedConnection<?> conn;
@Override
public void beforeCompletion() {
try {
conn = (ManagedConnection<?>) ds.getConnection();
assertNotNull(conn);
} catch (final SQLException e) {
fail("Could not get connection");
}
}
@Override
public void afterCompletion(final int i) {
final int numActive = pool.getNumActive();
try {
conn.checkOpen();
} catch (final Exception e) {
// Ignore
}
assertEquals(numActive, pool.getNumActive());
try {
conn.close();
} catch (final Exception e) {
fail("Should have been able to close the connection");
}
// TODO Requires DBCP-515 assertTrue(numActive -1 == pool.getNumActive());
}
});
transactionManager.commit();
}
@Override
@Test
public void testHashCode() throws Exception {
final Connection conn1 = newConnection();
assertNotNull(conn1);
final Connection conn2 = newConnection();
assertNotNull(conn2);
// shared connections should not have the same hashcode
Assertions.assertNotEquals(conn1.hashCode(), conn2.hashCode());
}
@Override
@Test
public void testMaxTotal() throws Exception {
final Transaction[] transactions = new Transaction[getMaxTotal()];
final Connection[] c = new Connection[getMaxTotal()];
for (int i = 0; i < c.length; i++) {
// create a new connection in the current transaction
c[i] = newConnection();
assertNotNull(c[i]);
// suspend the current transaction and start a new one
transactions[i] = transactionManager.suspend();
assertNotNull(transactions[i]);
transactionManager.begin();
}
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
} finally {
transactionManager.commit();
for (int i = 0; i < c.length; i++) {
transactionManager.resume(transactions[i]);
c[i].close();
transactionManager.commit();
}
}
}
@Override
@Test
public void testClearWarnings() throws Exception {
// open a connection
Connection connection = newConnection();
assertNotNull(connection);
// generate SQLWarning on connection
final CallableStatement statement = connection.prepareCall("warning");
assertNotNull(connection.getWarnings());
// create a new shared connection
final Connection sharedConnection = newConnection();
// shared connection should see warning
assertNotNull(sharedConnection.getWarnings());
// close and allocate a new (original) connection
connection.close();
connection = newConnection();
// warnings should not have been cleared by closing the connection
assertNotNull(connection.getWarnings());
assertNotNull(sharedConnection.getWarnings());
statement.close();
connection.close();
sharedConnection.close();
}
@Override
@Test
public void testSharedConnection() throws Exception {
final DelegatingConnection<?> connectionA = (DelegatingConnection<?>) newConnection();
final DelegatingConnection<?> connectionB = (DelegatingConnection<?>) newConnection();
assertFalse(connectionA.equals(connectionB));
assertFalse(connectionB.equals(connectionA));
assertTrue(connectionA.innermostDelegateEquals(connectionB.getInnermostDelegate()));
assertTrue(connectionB.innermostDelegateEquals(connectionA.getInnermostDelegate()));
connectionA.close();
connectionB.close();
}
@Test
public void testSharedTransactionConversion() throws Exception {
final DelegatingConnection<?> connectionA = (DelegatingConnection<?>) newConnection();
final DelegatingConnection<?> connectionB = (DelegatingConnection<?>) newConnection();
// in a transaction the inner connections should be equal
assertFalse(connectionA.equals(connectionB));
assertFalse(connectionB.equals(connectionA));
assertTrue(connectionA.innermostDelegateEquals(connectionB.getInnermostDelegate()));
assertTrue(connectionB.innermostDelegateEquals(connectionA.getInnermostDelegate()));
transactionManager.commit();
// use the connection so it adjusts to the completed transaction
connectionA.getAutoCommit();
connectionB.getAutoCommit();
// no there is no transaction so inner connections should not be equal
assertFalse(connectionA.equals(connectionB));
assertFalse(connectionB.equals(connectionA));
assertFalse(connectionA.innermostDelegateEquals(connectionB.getInnermostDelegate()));
assertFalse(connectionB.innermostDelegateEquals(connectionA.getInnermostDelegate()));
transactionManager.begin();
// use the connection so it adjusts to the new transaction
connectionA.getAutoCommit();
connectionB.getAutoCommit();
// back in a transaction so inner connections should be equal again
assertFalse(connectionA.equals(connectionB));
assertFalse(connectionB.equals(connectionA));
assertTrue(connectionA.innermostDelegateEquals(connectionB.getInnermostDelegate()));
assertTrue(connectionB.innermostDelegateEquals(connectionA.getInnermostDelegate()));
connectionA.close();
connectionB.close();
}
@Test
public void testCloseInTransaction() throws Exception {
final DelegatingConnection<?> connectionA = (DelegatingConnection<?>) newConnection();
final DelegatingConnection<?> connectionB = (DelegatingConnection<?>) newConnection();
assertFalse(connectionA.equals(connectionB));
assertFalse(connectionB.equals(connectionA));
assertTrue(connectionA.innermostDelegateEquals(connectionB.getInnermostDelegate()));
assertTrue(connectionB.innermostDelegateEquals(connectionA.getInnermostDelegate()));
connectionA.close();
connectionB.close();
final Connection connection = newConnection();
assertFalse(connection.isClosed(), "Connection should be open");
connection.close();
assertTrue(connection.isClosed(), "Connection should be closed");
}
@Override
@Test
public void testAutoCommitBehavior() throws Exception {
final Connection connection = newConnection();
// auto commit should be off
assertFalse(connection.getAutoCommit(), "Auto-commit should be disabled");
// attempt to set auto commit
try {
connection.setAutoCommit(true);
fail("setAutoCommit method should be disabled while enlisted in a transaction");
} catch (final SQLException e) {
// expected
}
// make sure it is still disabled
assertFalse(connection.getAutoCommit(), "Auto-commit should be disabled");
// close connection
connection.close();
}
@Test
public void testCommit() throws Exception {
final Connection connection = newConnection();
// connection should be open
assertFalse(connection.isClosed(), "Connection should be open");
// attempt commit directly
try {
connection.commit();
fail("commit method should be disabled while enlisted in a transaction");
} catch (final SQLException e) {
// expected
}
// make sure it is still open
assertFalse(connection.isClosed(), "Connection should be open");
// close connection
connection.close();
}
@Test
public void testReadOnly() throws Exception {
final Connection connection = newConnection();
// NOTE: This test class uses connections that are read-only by default
// connection should be read only
assertTrue(connection.isReadOnly(), "Connection be read-only");
// attempt to setReadOnly
try {
connection.setReadOnly(true);
fail("setReadOnly method should be disabled while enlisted in a transaction");
} catch (final SQLException e) {
// expected
}
// make sure it is still read-only
assertTrue(connection.isReadOnly(), "Connection be read-only");
// attempt to setReadonly
try {
connection.setReadOnly(false);
fail("setReadOnly method should be disabled while enlisted in a transaction");
} catch (final SQLException e) {
// expected
}
// make sure it is still read-only
assertTrue(connection.isReadOnly(), "Connection be read-only");
// close connection
connection.close();
}
// can't actually test close in a transaction
@Override
protected void assertBackPointers(final Connection conn, final Statement statement) throws SQLException {
assertFalse(conn.isClosed());
assertFalse(isClosed(statement));
assertSame(conn, statement.getConnection(),
"statement.getConnection() should return the exact same connection instance that was used to create the statement");
final ResultSet resultSet = statement.getResultSet();
assertFalse(isClosed(resultSet));
assertSame(statement, resultSet.getStatement(),
"resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
final ResultSet executeResultSet = statement.executeQuery("select * from dual");
assertFalse(isClosed(executeResultSet));
assertSame(statement, executeResultSet.getStatement(),
"resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
final ResultSet keysResultSet = statement.getGeneratedKeys();
assertFalse(isClosed(keysResultSet));
assertSame(statement, keysResultSet.getStatement(),
"resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
ResultSet preparedResultSet = null;
if (statement instanceof PreparedStatement) {
final PreparedStatement preparedStatement = (PreparedStatement) statement;
preparedResultSet = preparedStatement.executeQuery();
assertFalse(isClosed(preparedResultSet));
assertSame(statement, preparedResultSet.getStatement(),
"resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
}
resultSet.getStatement().getConnection().close();
}
@Override
@Test
public void testConnectionReturnOnCommit() throws Exception {
// override with no-op test
return;
}
}