http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/64a13ee3/src/test/java/org/apache/commons/dbcp2/managed/TestLocalXaResource.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/dbcp2/managed/TestLocalXaResource.java b/src/test/java/org/apache/commons/dbcp2/managed/TestLocalXaResource.java new file mode 100644 index 0000000..7efe5ca --- /dev/null +++ b/src/test/java/org/apache/commons/dbcp2/managed/TestLocalXaResource.java @@ -0,0 +1,590 @@ +/* + * + * 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.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for LocalXAConnectionFactory$LocalXAResource + */ +public class TestLocalXaResource { + + private Connection conn; + private LocalXAConnectionFactory.LocalXAResource resource; + + @Before + public void setUp() { + conn = new TestConnection(); + resource = new LocalXAConnectionFactory.LocalXAResource(conn); + } + + @Test + public void testConstructor() { + assertEquals(0, resource.getTransactionTimeout()); + assertNull(resource.getXid()); + // the current implementation always return false, regardless of the input value + assertFalse(resource.setTransactionTimeout(100)); + // the current implementation always return an empty/zero'd array, regardless of the input value + assertEquals(0, resource.recover(100).length); + } + + @Test + public void testIsSame() { + assertTrue(resource.isSameRM(resource)); + assertFalse(resource.isSameRM(new LocalXAConnectionFactory.LocalXAResource(conn))); + } + + @Test(expected=XAException.class) + public void testStartInvalidFlag() throws XAException { + // currently, valid values are TMNOFLAGS and TMRESUME + resource.start(null, XAResource.TMENDRSCAN); + } + + @Test(expected=XAException.class) + public void testStartNoFlagButAlreadyEnlisted() throws XAException { + resource.start(new TestXid(), XAResource.TMNOFLAGS); + resource.start(new TestXid(), XAResource.TMNOFLAGS); + } + + @Test(expected=XAException.class) + public void testStartNoFlagResumeButDifferentXid() throws XAException { + resource.start(new TestXid(), XAResource.TMNOFLAGS); + resource.start(new TestXid(), XAResource.TMRESUME); + } + + @Test + public void testStartNoFlagResume() throws XAException { + Xid xid = new TestXid(); + resource.start(xid, XAResource.TMNOFLAGS); + resource.start(xid, XAResource.TMRESUME); + assertEquals(xid, resource.getXid()); + } + + @Test + public void testStartNoFlagResumeEnd() throws XAException { + Xid xid = new TestXid(); + resource.start(xid, XAResource.TMNOFLAGS); + resource.start(xid, XAResource.TMRESUME); + // flag is never used in the end + resource.end(xid, 0); + assertEquals(xid, resource.getXid()); + } + + @Test(expected=NullPointerException.class) + public void testStartNoFlagResumeEndMissingXid() throws XAException { + Xid xid = new TestXid(); + resource.start(xid, XAResource.TMNOFLAGS); + resource.start(xid, XAResource.TMRESUME); + // flag is never used in the end + resource.end(null, 0); + } + + @Test(expected=XAException.class) + public void testStartNoFlagResumeEndDifferentXid() throws XAException { + Xid xid = new TestXid(); + resource.start(xid, XAResource.TMNOFLAGS); + resource.start(xid, XAResource.TMRESUME); + // flag is never used in the end + resource.end(new TestXid(), 0); + } + + @Test + public void testForgetDifferentXid() throws XAException { + Xid xid = new TestXid(); + resource.start(xid, XAResource.TMNOFLAGS); + resource.forget(new TestXid()); + assertEquals(xid, resource.getXid()); + } + + @Test + public void testForgetMissingXid() throws XAException { + Xid xid = new TestXid(); + resource.start(xid, XAResource.TMNOFLAGS); + resource.forget(null); + assertEquals(xid, resource.getXid()); + } + + @Test + public void testForget() throws XAException { + Xid xid = new TestXid(); + resource.start(xid, XAResource.TMNOFLAGS); + resource.forget(xid); + assertNull(resource.getXid()); + } + + @Test + public void testStartReadOnlyConnectionPrepare() throws XAException, SQLException { + Xid xid = new TestXid(); + conn.setAutoCommit(false); + conn.setReadOnly(true); + resource.start(xid, XAResource.TMNOFLAGS); + resource.prepare(xid); + assertFalse(conn.getAutoCommit()); + } + + /** + * When an exception is thrown on the {@link Connection#getAutoCommit()}, then the + * value is set to {@code true} by default. + * @throws XAException when there are errors with the transaction + * @throws SQLException when there are errors with other SQL/DB parts + */ + @Test + public void testStartExceptionOnGetAutoCommit() throws XAException, SQLException { + Xid xid = new TestXid(); + ((TestConnection) conn).throwWhenGetAutoCommit = true; + conn.setAutoCommit(false); + conn.setReadOnly(true); + // the start method with no flag will call getAutoCommit, the exception will be thrown, and it will be set + // to true + resource.start(xid, XAResource.TMNOFLAGS); + // and prepare sets the value computed in start in the connection + resource.prepare(xid); + ((TestConnection) conn).throwWhenGetAutoCommit = false; + assertTrue(conn.getAutoCommit()); + } + + /** + * When an exception is thrown on the {@link Connection#getAutoCommit()}, then the + * value is set to {@code true} by default. However, if the connection is not read-only, + * then the value set by the user in the original connection will be kept. + * @throws XAException when there are errors with the transaction + * @throws SQLException when there are errors with other SQL/DB parts + */ + @Test + public void testStartReadOnlyConnectionExceptionOnGetAutoCommit() throws XAException, SQLException { + Xid xid = new TestXid(); + ((TestConnection) conn).throwWhenGetAutoCommit = true; + conn.setAutoCommit(false); + conn.setReadOnly(false); + // the start method with no flag will call getAutoCommit, the exception will be thrown, and it will be set + // to true + resource.start(xid, XAResource.TMNOFLAGS); + // and prepare sets the value computed in start in the connection + resource.prepare(xid); + ((TestConnection) conn).throwWhenGetAutoCommit = false; + assertFalse(conn.getAutoCommit()); + } + + @Test(expected=XAException.class) + public void testStartFailsWhenCannotSetAutoCommit() throws XAException, SQLException { + Xid xid = new TestXid(); + ((TestConnection) conn).throwWhenSetAutoCommit = true; + resource.start(xid, XAResource.TMNOFLAGS); + } + + @Test(expected=NullPointerException.class) + public void testCommitMissingXid() throws SQLException, XAException { + resource.commit(null, false); + } + + @Test(expected=XAException.class) + public void testCommitNoTransaction() throws SQLException, XAException { + ((TestConnection) conn).closed = false; + conn.setReadOnly(false); + resource.commit(new TestXid(), false); + } + + @Test(expected=XAException.class) + public void testCommitInvalidXid() throws SQLException, XAException { + Xid xid = new TestXid(); + ((TestConnection) conn).closed = false; + conn.setReadOnly(false); + resource.start(xid, XAResource.TMNOFLAGS); + resource.commit(new TestXid(), false); + } + + @Test(expected=XAException.class) + public void testCommitConnectionClosed() throws SQLException, XAException { + Xid xid = new TestXid(); + ((TestConnection) conn).closed = true; + conn.setReadOnly(false); + resource.start(xid, XAResource.TMNOFLAGS); + resource.commit(xid, false); + } + + @Test + public void testCommitConnectionNotReadOnly() throws SQLException, XAException { + Xid xid = new TestXid(); + ((TestConnection) conn).closed = false; + conn.setReadOnly(true); + resource.start(xid, XAResource.TMNOFLAGS); + resource.commit(xid, false); + assertFalse(((TestConnection) conn).committed); + } + + @Test + public void testCommit() throws SQLException, XAException { + Xid xid = new TestXid(); + ((TestConnection) conn).closed = false; + conn.setReadOnly(false); + resource.start(xid, XAResource.TMNOFLAGS); + resource.commit(xid, false); + assertTrue(((TestConnection) conn).committed); + } + + @Test(expected=NullPointerException.class) + public void testRollbackMissingXid() throws XAException { + resource.rollback(null); + } + + @Test(expected=XAException.class) + public void testRollbackInvalidXid() throws SQLException, XAException { + Xid xid = new TestXid(); + ((TestConnection) conn).closed = false; + conn.setReadOnly(false); + resource.start(xid, XAResource.TMNOFLAGS); + resource.rollback(new TestXid()); + } + + @Test + public void testRollback() throws SQLException, XAException { + Xid xid = new TestXid(); + ((TestConnection) conn).closed = false; + conn.setReadOnly(false); + resource.start(xid, XAResource.TMNOFLAGS); + resource.rollback(xid); + assertTrue(((TestConnection) conn).rolledback); + } + + private static class TestConnection implements Connection { + + public boolean throwWhenGetAutoCommit = false; + public boolean throwWhenSetAutoCommit = false; + boolean autoCommit = false; + boolean readOnly = false; + public boolean committed = false; + public boolean rolledback = false; + public boolean closed = false; + + @Override + public <T> T unwrap(Class<T> iface) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class<?> iface) throws SQLException { + return false; + } + + @Override + public Statement createStatement() throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + return null; + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + return null; + } + + @Override + public String nativeSQL(String sql) throws SQLException { + return null; + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + if (throwWhenSetAutoCommit) { + throw new SQLException(); + } + this.autoCommit = autoCommit; + } + + @Override + public boolean getAutoCommit() throws SQLException { + if (throwWhenGetAutoCommit) { + throw new SQLException(); + } + return autoCommit; + } + + @Override + public void commit() throws SQLException { + committed = true; + } + + @Override + public void rollback() throws SQLException { + rolledback = true; + } + + @Override + public void close() throws SQLException { + closed = true; + } + + @Override + public boolean isClosed() throws SQLException { + return closed; + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return null; + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + this.readOnly = readOnly; + } + + @Override + public boolean isReadOnly() throws SQLException { + return readOnly; + } + + @Override + public void setCatalog(String catalog) throws SQLException { + } + + @Override + public String getCatalog() throws SQLException { + return null; + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + } + + @Override + public int getTransactionIsolation() throws SQLException { + return 0; + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return null; + } + + @Override + public void clearWarnings() throws SQLException { + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) + throws SQLException { + return null; + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) + throws SQLException { + return null; + } + + @Override + public Map<String, Class<?>> getTypeMap() throws SQLException { + return null; + } + + @Override + public void setTypeMap(Map<String, Class<?>> map) throws SQLException { + } + + @Override + public void setHoldability(int holdability) throws SQLException { + } + + @Override + public int getHoldability() throws SQLException { + return 0; + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return null; + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + return null; + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, + int resultSetHoldability) throws SQLException { + return null; + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, + int resultSetHoldability) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + return null; + } + + @Override + public Clob createClob() throws SQLException { + return null; + } + + @Override + public Blob createBlob() throws SQLException { + return null; + } + + @Override + public NClob createNClob() throws SQLException { + return null; + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return null; + } + + @Override + public boolean isValid(int timeout) throws SQLException { + return false; + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + } + + @Override + public String getClientInfo(String name) throws SQLException { + return null; + } + + @Override + public Properties getClientInfo() throws SQLException { + return null; + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return null; + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return null; + } + + @Override + public void setSchema(String schema) throws SQLException { + } + + @Override + public String getSchema() throws SQLException { + return null; + } + + @Override + public void abort(Executor executor) throws SQLException { + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + } + + @Override + public int getNetworkTimeout() throws SQLException { + return 0; + } + } + + private static class TestXid implements Xid { + + @Override + public byte[] getBranchQualifier() { + return null; + } + + @Override + public int getFormatId() { + return 0; + } + + @Override + public byte[] getGlobalTransactionId() { + return null; + } + } +}
http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/64a13ee3/src/test/java/org/apache/commons/dbcp2/managed/TestManagedDataSource.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/dbcp2/managed/TestManagedDataSource.java b/src/test/java/org/apache/commons/dbcp2/managed/TestManagedDataSource.java index 320469f..5f31d7a 100644 --- a/src/test/java/org/apache/commons/dbcp2/managed/TestManagedDataSource.java +++ b/src/test/java/org/apache/commons/dbcp2/managed/TestManagedDataSource.java @@ -256,4 +256,31 @@ public class TestManagedDataSource extends TestConnectionPool { c1.close(); c2.close(); } + + @Test(expected=IllegalStateException.class) + public void testTransactionRegistryNotInitialized() throws Exception { + try (ManagedDataSource<?> ds = new ManagedDataSource<>(pool, null)) { + ds.getConnection(); + } + } + + @Test(expected=IllegalStateException.class) + public void testSetTransactionRegistryAlreadySet() { + ManagedDataSource<?> managed = (ManagedDataSource<?>) ds; + managed.setTransactionRegistry(null); + } + + @Test(expected=NullPointerException.class) + public void testSetNullTransactionRegistry() throws Exception { + try (ManagedDataSource<?> ds = new ManagedDataSource<>(pool, null)) { + ds.setTransactionRegistry(null); + } + } + + @Test() + public void testSetTransactionRegistry() throws Exception { + try (ManagedDataSource<?> ds = new ManagedDataSource<>(pool, null)) { + ds.setTransactionRegistry(new TransactionRegistry(transactionManager)); + } + } } http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/64a13ee3/src/test/java/org/apache/commons/dbcp2/managed/TestPoolableManagedConnection.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/dbcp2/managed/TestPoolableManagedConnection.java b/src/test/java/org/apache/commons/dbcp2/managed/TestPoolableManagedConnection.java new file mode 100644 index 0000000..44da212 --- /dev/null +++ b/src/test/java/org/apache/commons/dbcp2/managed/TestPoolableManagedConnection.java @@ -0,0 +1,141 @@ +/* + * + * 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.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; + +import javax.transaction.TransactionManager; + +import org.apache.commons.dbcp2.ConnectionFactory; +import org.apache.commons.dbcp2.DriverConnectionFactory; +import org.apache.commons.dbcp2.PoolableConnection; +import org.apache.commons.dbcp2.PoolableConnectionFactory; +import org.apache.commons.dbcp2.TesterDriver; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.geronimo.transaction.manager.TransactionManagerImpl; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for PoolableManagedConnection. + */ +public class TestPoolableManagedConnection { + + private TransactionManager transactionManager; + private TransactionRegistry transactionRegistry; + private GenericObjectPool<PoolableConnection> pool; + private Connection conn; + private PoolableManagedConnection poolableManagedConnection; + + @Before + public void setUp() throws Exception { + // create a GeronimoTransactionManager for testing + transactionManager = new TransactionManagerImpl(); + + // create a driver connection factory + final Properties properties = new Properties(); + properties.setProperty("user", "userName"); + properties.setProperty("password", "password"); + final ConnectionFactory connectionFactory = new DriverConnectionFactory(new TesterDriver(), "jdbc:apache:commons:testdriver", properties); + + // wrap it with a LocalXAConnectionFactory + final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(transactionManager, connectionFactory); + + // create transaction registry + transactionRegistry = xaConnectionFactory.getTransactionRegistry(); + + // create the pool object factory + final PoolableConnectionFactory factory = new PoolableConnectionFactory(xaConnectionFactory, null); + factory.setValidationQuery("SELECT DUMMY FROM DUAL"); + factory.setDefaultReadOnly(Boolean.TRUE); + factory.setDefaultAutoCommit(Boolean.TRUE); + + // create the pool + pool = new GenericObjectPool<>(factory); + factory.setPool(pool); + pool.setMaxTotal(10); + pool.setMaxWaitMillis(100); + } + + @After + public void tearDown() throws SQLException { + if (conn != null && !conn.isClosed()) + conn.close(); + if (pool != null && !pool.isClosed()) + pool.close(); + } + + @Test + public void testManagedConnection() throws Exception { + assertEquals(0, pool.getNumActive()); + // create a connection + conn = pool.borrowObject(); + assertEquals(1, pool.getNumActive()); + // create the poolable managed connection + poolableManagedConnection = new PoolableManagedConnection(transactionRegistry, conn, pool); + poolableManagedConnection.close(); + // closing a poolable managed connection won't close it, but simply return to the pool + assertEquals(1, pool.getNumActive()); + // but closing the underlying connection really closes it + conn.close(); + assertEquals(0, pool.getNumActive()); + } + + @Test + public void testPoolableConnection() throws Exception { + // create a connection + // pool uses LocalXAConnectionFactory, which register the connection with the TransactionRegistry + conn = pool.borrowObject(); + assertNotNull(transactionRegistry.getXAResource(conn)); + // create the poolable managed connection + poolableManagedConnection = new PoolableManagedConnection(transactionRegistry, conn, pool); + poolableManagedConnection.close(); + assertNotNull(transactionRegistry.getXAResource(conn)); + } + + @Test + public void testReallyClose() throws Exception { + assertEquals(0, pool.getNumActive()); + // create a connection + // pool uses LocalXAConnectionFactory, which register the connection with the TransactionRegistry + conn = pool.borrowObject(); + assertEquals(1, pool.getNumActive()); + assertNotNull(transactionRegistry.getXAResource(conn)); + // create the poolable managed connection + poolableManagedConnection = new PoolableManagedConnection(transactionRegistry, conn, pool); + poolableManagedConnection.close(); + assertNotNull(transactionRegistry.getXAResource(conn)); + assertEquals(1, pool.getNumActive()); + // this must close the managed connection, removing it from the transaction registry + poolableManagedConnection.reallyClose(); + try { + assertNull(transactionRegistry.getXAResource(conn)); + fail("Transaction registry was supposed to be empty now"); + } catch (SQLException e) {} + assertEquals(0, pool.getNumActive()); + } +} \ No newline at end of file