You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/07/02 13:24:42 UTC

[commons-dbcp] 06/07: Refactor duplicate code

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-dbcp.git

commit 316ed1b324c141b82fb1e2d0cb4cbc91e94cb961
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Jul 2 09:17:57 2022 -0400

    Refactor duplicate code
---
 .../dbcp2/managed/SynchronizationAdapter.java      |  36 +
 .../commons/dbcp2/managed/TransactionContext.java  |   7 +-
 .../dbcp2/managed/TestBasicManagedDataSource.java  | 489 ++++++------
 .../dbcp2/managed/TestManagedDataSourceInTx.java   | 877 ++++++++++-----------
 4 files changed, 715 insertions(+), 694 deletions(-)

diff --git a/src/main/java/org/apache/commons/dbcp2/managed/SynchronizationAdapter.java b/src/main/java/org/apache/commons/dbcp2/managed/SynchronizationAdapter.java
new file mode 100644
index 00000000..f5a0211d
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbcp2/managed/SynchronizationAdapter.java
@@ -0,0 +1,36 @@
+/*
+ * 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 javax.transaction.Synchronization;
+
+/**
+ * Implements {@link Synchronization} for subclasses.
+ */
+class SynchronizationAdapter implements Synchronization {
+
+    @Override
+    public void afterCompletion(final int status) {
+        // Noop
+    }
+
+    @Override
+    public void beforeCompletion() {
+        // Noop
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/managed/TransactionContext.java b/src/main/java/org/apache/commons/dbcp2/managed/TransactionContext.java
index aac64a01..e44a487d 100644
--- a/src/main/java/org/apache/commons/dbcp2/managed/TransactionContext.java
+++ b/src/main/java/org/apache/commons/dbcp2/managed/TransactionContext.java
@@ -91,16 +91,11 @@ public class TransactionContext {
                 listener.afterCompletion(this, transaction != null && transaction.getStatus() == Status.STATUS_COMMITTED);
                 return;
             }
-            final Synchronization s = new Synchronization() {
+            final Synchronization s = new SynchronizationAdapter() {
                 @Override
                 public void afterCompletion(final int status) {
                     listener.afterCompletion(TransactionContext.this, status == Status.STATUS_COMMITTED);
                 }
-
-                @Override
-                public void beforeCompletion() {
-                    // empty
-                }
             };
             if (transactionSynchronizationRegistry != null) {
                 transactionSynchronizationRegistry.registerInterposedSynchronization(s);
diff --git a/src/test/java/org/apache/commons/dbcp2/managed/TestBasicManagedDataSource.java b/src/test/java/org/apache/commons/dbcp2/managed/TestBasicManagedDataSource.java
index 8fa5e7bf..7dbc3cf2 100644
--- a/src/test/java/org/apache/commons/dbcp2/managed/TestBasicManagedDataSource.java
+++ b/src/test/java/org/apache/commons/dbcp2/managed/TestBasicManagedDataSource.java
@@ -1,247 +1,242 @@
-/*
-
-  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.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import javax.sql.XADataSource;
-import javax.transaction.Synchronization;
-import javax.transaction.TransactionManager;
-import javax.transaction.TransactionSynchronizationRegistry;
-import javax.transaction.xa.XAException;
-
-import org.apache.commons.dbcp2.BasicDataSource;
-import org.apache.commons.dbcp2.TestBasicDataSource;
-import org.apache.geronimo.transaction.manager.TransactionManagerImpl;
-import org.h2.Driver;
-import org.h2.jdbcx.JdbcDataSource;
-import org.junit.jupiter.api.Test;
-
-import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple;
-import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple;
-
-/**
- * TestSuite for BasicManagedDataSource
- */
-public class TestBasicManagedDataSource extends TestBasicDataSource {
-
-    @Override
-    protected BasicDataSource createDataSource() throws Exception {
-        final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource();
-        final TransactionManagerImpl transactionManager = new TransactionManagerImpl();
-        basicManagedDataSource.setTransactionManager(transactionManager);
-        basicManagedDataSource.setTransactionSynchronizationRegistry(transactionManager);
-        return basicManagedDataSource;
-    }
-
-    @Test
-    public void testCreateXaDataSourceNewInstance() throws SQLException, XAException {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            basicManagedDataSource.setXADataSource(JdbcDataSource.class.getCanonicalName());
-            basicManagedDataSource.setDriverClassName(Driver.class.getName());
-            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
-            assertNotNull(basicManagedDataSource.createConnectionFactory());
-        }
-    }
-
-    @Test
-    public void testCreateXaDataSourceNoInstanceSetAndNoDataSource() throws SQLException, XAException {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
-            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
-            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
-            assertNotNull(basicManagedDataSource.createConnectionFactory());
-        }
-    }
-
-    /**
-     * JIRA: DBCP-294
-     * Verify that PoolableConnections created by BasicManagedDataSource unregister themselves
-     * when reallyClosed.
-     */
-    @Test
-    public void testReallyClose() throws Exception {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
-            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
-            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
-            basicManagedDataSource.setUsername("userName");
-            basicManagedDataSource.setPassword("password");
-            basicManagedDataSource.setMaxIdle(1);
-            // Create two connections
-            final ManagedConnection<?> conn = (ManagedConnection<?>) basicManagedDataSource.getConnection();
-            assertNotNull(basicManagedDataSource.getTransactionRegistry().getXAResource(conn));
-            final ManagedConnection<?> conn2 = (ManagedConnection<?>) basicManagedDataSource.getConnection();
-            conn2.close(); // Return one connection to the pool
-            conn.close(); // No room at the inn - this will trigger reallyClose(), which should unregister
-            try {
-                basicManagedDataSource.getTransactionRegistry().getXAResource(conn);
-                fail("Expecting SQLException - XAResources orphaned");
-            } catch (final SQLException ex) {
-                // expected
-            }
-            conn2.close();
-        }
-    }
-
-    @Test
-    public void testRuntimeExceptionsAreRethrown() throws SQLException, XAException {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
-            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
-            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
-            basicManagedDataSource.setUsername("userName");
-            basicManagedDataSource.setPassword("password");
-            basicManagedDataSource.setMaxIdle(1);
-            // results in a NPE
-            assertThrows(NullPointerException.class, () -> basicManagedDataSource.createPoolableConnectionFactory(null));
-        }
-    }
-
-    @Test
-    public void testSetDriverName() throws SQLException {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            basicManagedDataSource.setDriverClassName("adams");
-            assertEquals("adams", basicManagedDataSource.getDriverClassName());
-            basicManagedDataSource.setDriverClassName(null);
-            assertNull(basicManagedDataSource.getDriverClassName());
-        }
-    }
-
-    @Test
-    public void testSetNullXaDataSourceInstance() throws SQLException, XAException {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
-            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
-            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
-            basicManagedDataSource.setUsername("userName");
-            basicManagedDataSource.setPassword("password");
-            basicManagedDataSource.setMaxIdle(1);
-            basicManagedDataSource.setXaDataSourceInstance(null);
-            assertNull(basicManagedDataSource.getXaDataSourceInstance());
-        }
-    }
-
-    /** DBCP-564 */
-    @Test
-    public void testSetRollbackOnlyBeforeGetConnectionDoesNotLeak() throws Exception {
-        final TransactionManager transactionManager = ((BasicManagedDataSource) ds).getTransactionManager();
-        final int n = 3;
-        ds.setMaxIdle(n);
-        ds.setMaxTotal(n);
-
-        for (int i = 0; i <= n; i++) { // loop n+1 times
-            transactionManager.begin();
-            transactionManager.setRollbackOnly();
-            try (final Connection conn = getConnection()) {
-                assertNotNull(conn);
-            }
-            transactionManager.rollback();
-        }
-
-        assertEquals(0, ds.getNumActive());
-        assertEquals(1, ds.getNumIdle());
-    }
-
-    @Test
-    public void testSetXaDataSourceInstance() throws SQLException, XAException {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
-            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
-            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
-            basicManagedDataSource.setUsername("userName");
-            basicManagedDataSource.setPassword("password");
-            basicManagedDataSource.setMaxIdle(1);
-            basicManagedDataSource.setXaDataSourceInstance(new JdbcDataSource());
-            assertNotNull(basicManagedDataSource.createConnectionFactory());
-        }
-    }
-
-    @Test
-    public void testTransactionManagerNotSet() throws SQLException {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            assertThrows(SQLException.class, basicManagedDataSource::createConnectionFactory);
-        }
-    }
-
-    @Test
-    public void testTransactionSynchronizationRegistry() throws Exception {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            basicManagedDataSource.setTransactionManager(new TransactionManagerImple());
-            final TransactionSynchronizationRegistry tsr = new TransactionSynchronizationRegistryImple();
-            basicManagedDataSource.setTransactionSynchronizationRegistry(tsr);
-            final JdbcDataSource xaDataSource = new JdbcDataSource();
-            xaDataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
-            basicManagedDataSource.setXaDataSourceInstance(xaDataSource);
-            basicManagedDataSource.setMaxIdle(1);
-
-            final TransactionManager tm = basicManagedDataSource.getTransactionManager();
-            tm.begin();
-            tsr.registerInterposedSynchronization(new Synchronization() {
-                @Override
-                public void afterCompletion(final int i) {
-
-                }
-
-                @Override
-                public void beforeCompletion() {
-                    Connection connection = null;
-                    try {
-                        connection = basicManagedDataSource.getConnection();
-                        assertNotNull(connection);
-                    } catch (final SQLException e) {
-                        fail(e.getMessage());
-                    } finally {
-                        if (connection != null) {
-                            try {
-                                connection.close();
-                            } catch (final SQLException e) {
-                                fail(e.getMessage());
-                            }
-                        }
-                    }
-                }
-            });
-            tm.commit();
-        }
-    }
-
-    @Test
-    public void testXADataSource() throws SQLException {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            basicManagedDataSource.setXADataSource("anything");
-            assertEquals("anything", basicManagedDataSource.getXADataSource());
-        }
-    }
-
-    @Test
-    public void testXaDataSourceInstance() throws SQLException {
-        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
-            final XADataSource ds = new JdbcDataSource();
-            basicManagedDataSource.setXaDataSourceInstance(ds);
-            assertEquals(ds, basicManagedDataSource.getXaDataSourceInstance());
-        }
-    }
-}
+/*
+
+  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.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.sql.XADataSource;
+import javax.transaction.Synchronization;
+import javax.transaction.TransactionManager;
+import javax.transaction.TransactionSynchronizationRegistry;
+import javax.transaction.xa.XAException;
+
+import org.apache.commons.dbcp2.BasicDataSource;
+import org.apache.commons.dbcp2.TestBasicDataSource;
+import org.apache.geronimo.transaction.manager.TransactionManagerImpl;
+import org.h2.Driver;
+import org.h2.jdbcx.JdbcDataSource;
+import org.junit.jupiter.api.Test;
+
+import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple;
+
+/**
+ * TestSuite for BasicManagedDataSource
+ */
+public class TestBasicManagedDataSource extends TestBasicDataSource {
+
+    @Override
+    protected BasicDataSource createDataSource() throws Exception {
+        final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource();
+        final TransactionManagerImpl transactionManager = new TransactionManagerImpl();
+        basicManagedDataSource.setTransactionManager(transactionManager);
+        basicManagedDataSource.setTransactionSynchronizationRegistry(transactionManager);
+        return basicManagedDataSource;
+    }
+
+    @Test
+    public void testCreateXaDataSourceNewInstance() throws SQLException, XAException {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            basicManagedDataSource.setXADataSource(JdbcDataSource.class.getCanonicalName());
+            basicManagedDataSource.setDriverClassName(Driver.class.getName());
+            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
+            assertNotNull(basicManagedDataSource.createConnectionFactory());
+        }
+    }
+
+    @Test
+    public void testCreateXaDataSourceNoInstanceSetAndNoDataSource() throws SQLException, XAException {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
+            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
+            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
+            assertNotNull(basicManagedDataSource.createConnectionFactory());
+        }
+    }
+
+    /**
+     * JIRA: DBCP-294
+     * Verify that PoolableConnections created by BasicManagedDataSource unregister themselves
+     * when reallyClosed.
+     */
+    @Test
+    public void testReallyClose() throws Exception {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
+            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
+            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
+            basicManagedDataSource.setUsername("userName");
+            basicManagedDataSource.setPassword("password");
+            basicManagedDataSource.setMaxIdle(1);
+            // Create two connections
+            final ManagedConnection<?> conn = (ManagedConnection<?>) basicManagedDataSource.getConnection();
+            assertNotNull(basicManagedDataSource.getTransactionRegistry().getXAResource(conn));
+            final ManagedConnection<?> conn2 = (ManagedConnection<?>) basicManagedDataSource.getConnection();
+            conn2.close(); // Return one connection to the pool
+            conn.close(); // No room at the inn - this will trigger reallyClose(), which should unregister
+            try {
+                basicManagedDataSource.getTransactionRegistry().getXAResource(conn);
+                fail("Expecting SQLException - XAResources orphaned");
+            } catch (final SQLException ex) {
+                // expected
+            }
+            conn2.close();
+        }
+    }
+
+    @Test
+    public void testRuntimeExceptionsAreRethrown() throws SQLException, XAException {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
+            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
+            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
+            basicManagedDataSource.setUsername("userName");
+            basicManagedDataSource.setPassword("password");
+            basicManagedDataSource.setMaxIdle(1);
+            // results in a NPE
+            assertThrows(NullPointerException.class, () -> basicManagedDataSource.createPoolableConnectionFactory(null));
+        }
+    }
+
+    @Test
+    public void testSetDriverName() throws SQLException {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            basicManagedDataSource.setDriverClassName("adams");
+            assertEquals("adams", basicManagedDataSource.getDriverClassName());
+            basicManagedDataSource.setDriverClassName(null);
+            assertNull(basicManagedDataSource.getDriverClassName());
+        }
+    }
+
+    @Test
+    public void testSetNullXaDataSourceInstance() throws SQLException, XAException {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
+            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
+            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
+            basicManagedDataSource.setUsername("userName");
+            basicManagedDataSource.setPassword("password");
+            basicManagedDataSource.setMaxIdle(1);
+            basicManagedDataSource.setXaDataSourceInstance(null);
+            assertNull(basicManagedDataSource.getXaDataSourceInstance());
+        }
+    }
+
+    /** DBCP-564 */
+    @Test
+    public void testSetRollbackOnlyBeforeGetConnectionDoesNotLeak() throws Exception {
+        final TransactionManager transactionManager = ((BasicManagedDataSource) ds).getTransactionManager();
+        final int n = 3;
+        ds.setMaxIdle(n);
+        ds.setMaxTotal(n);
+
+        for (int i = 0; i <= n; i++) { // loop n+1 times
+            transactionManager.begin();
+            transactionManager.setRollbackOnly();
+            try (final Connection conn = getConnection()) {
+                assertNotNull(conn);
+            }
+            transactionManager.rollback();
+        }
+
+        assertEquals(0, ds.getNumActive());
+        assertEquals(1, ds.getNumIdle());
+    }
+
+    @Test
+    public void testSetXaDataSourceInstance() throws SQLException, XAException {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
+            basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
+            basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
+            basicManagedDataSource.setUsername("userName");
+            basicManagedDataSource.setPassword("password");
+            basicManagedDataSource.setMaxIdle(1);
+            basicManagedDataSource.setXaDataSourceInstance(new JdbcDataSource());
+            assertNotNull(basicManagedDataSource.createConnectionFactory());
+        }
+    }
+
+    @Test
+    public void testTransactionManagerNotSet() throws SQLException {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            assertThrows(SQLException.class, basicManagedDataSource::createConnectionFactory);
+        }
+    }
+
+    @Test
+    public void testTransactionSynchronizationRegistry() throws Exception {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            basicManagedDataSource.setTransactionManager(new TransactionManagerImple());
+            final TransactionSynchronizationRegistry tsr = new TransactionSynchronizationRegistryImple();
+            basicManagedDataSource.setTransactionSynchronizationRegistry(tsr);
+            final JdbcDataSource xaDataSource = new JdbcDataSource();
+            xaDataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
+            basicManagedDataSource.setXaDataSourceInstance(xaDataSource);
+            basicManagedDataSource.setMaxIdle(1);
+
+            final TransactionManager tm = basicManagedDataSource.getTransactionManager();
+            tm.begin();
+            tsr.registerInterposedSynchronization(new SynchronizationAdapter() {
+                @Override
+                public void beforeCompletion() {
+                    Connection connection = null;
+                    try {
+                        connection = basicManagedDataSource.getConnection();
+                        assertNotNull(connection);
+                    } catch (final SQLException e) {
+                        fail(e.getMessage());
+                    } finally {
+                        if (connection != null) {
+                            try {
+                                connection.close();
+                            } catch (final SQLException e) {
+                                fail(e.getMessage());
+                            }
+                        }
+                    }
+                }
+            });
+            tm.commit();
+        }
+    }
+
+    @Test
+    public void testXADataSource() throws SQLException {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            basicManagedDataSource.setXADataSource("anything");
+            assertEquals("anything", basicManagedDataSource.getXADataSource());
+        }
+    }
+
+    @Test
+    public void testXaDataSourceInstance() throws SQLException {
+        try (final BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource()) {
+            final XADataSource ds = new JdbcDataSource();
+            basicManagedDataSource.setXaDataSourceInstance(ds);
+            assertEquals(ds, basicManagedDataSource.getXaDataSourceInstance());
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/dbcp2/managed/TestManagedDataSourceInTx.java b/src/test/java/org/apache/commons/dbcp2/managed/TestManagedDataSourceInTx.java
index dc6b6f75..d6db5efb 100644
--- a/src/test/java/org/apache/commons/dbcp2/managed/TestManagedDataSourceInTx.java
+++ b/src/test/java/org/apache/commons/dbcp2/managed/TestManagedDataSourceInTx.java
@@ -1,441 +1,436 @@
-/*
-
-  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.assertNotEquals;
-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 {
-
-    // 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
-    @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();
-    }
-
-    @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();
-    }
-
-    @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();
-    }
-
-    @Test
-    public void testCloseInTransaction() throws Exception {
-        final DelegatingConnection<?> connectionA = (DelegatingConnection<?>) newConnection();
-        final DelegatingConnection<?> connectionB = (DelegatingConnection<?>) newConnection();
-
-        assertNotEquals(connectionA, connectionB);
-        assertNotEquals(connectionB, 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");
-    }
-
-    @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();
-    }
-
-    @Override
-    @Test
-    public void testConnectionReturnOnCommit() throws Exception {
-       // override with no-op test
-    }
-
-    @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 testDoubleReturn() throws Exception {
-        transactionManager.getTransaction().registerSynchronization(new Synchronization() {
-            private ManagedConnection<?> conn;
-
-            @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());
-            }
-
-            @Override
-            public void beforeCompletion() {
-                try {
-                    conn = (ManagedConnection<?>) ds.getConnection();
-                    assertNotNull(conn);
-                } catch (final SQLException e) {
-                    fail("Could not get connection");
-                }
-            }
-        });
-        transactionManager.commit();
-    }
-
-    @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 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");
-                }
-            }
-
-            @Override
-            public void beforeCompletion() {
-                // empty
-            }
-        });
-        connection.close();
-        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());
-    }
-
-    /**
-     * @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 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 testNestedConnections() {
-        // Not supported
-    }
-
-    @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();
-    }
-
-    @Override
-    @Test
-    public void testSharedConnection() throws Exception {
-        final DelegatingConnection<?> connectionA = (DelegatingConnection<?>) newConnection();
-        final DelegatingConnection<?> connectionB = (DelegatingConnection<?>) newConnection();
-
-        assertNotEquals(connectionA, connectionB);
-        assertNotEquals(connectionB, 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
-        assertNotEquals(connectionA, connectionB);
-        assertNotEquals(connectionB, 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
-        assertNotEquals(connectionA, connectionB);
-        assertNotEquals(connectionB, 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
-        assertNotEquals(connectionA, connectionB);
-        assertNotEquals(connectionB, connectionA);
-        assertTrue(connectionA.innermostDelegateEquals(connectionB.getInnermostDelegate()));
-        assertTrue(connectionB.innermostDelegateEquals(connectionA.getInnermostDelegate()));
-
-        connectionA.close();
-        connectionB.close();
-    }
-}
+/*
+
+  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.assertNotEquals;
+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 {
+
+    // 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
+    @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();
+    }
+
+    @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();
+    }
+
+    @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();
+    }
+
+    @Test
+    public void testCloseInTransaction() throws Exception {
+        final DelegatingConnection<?> connectionA = (DelegatingConnection<?>) newConnection();
+        final DelegatingConnection<?> connectionB = (DelegatingConnection<?>) newConnection();
+
+        assertNotEquals(connectionA, connectionB);
+        assertNotEquals(connectionB, 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");
+    }
+
+    @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();
+    }
+
+    @Override
+    @Test
+    public void testConnectionReturnOnCommit() throws Exception {
+       // override with no-op test
+    }
+
+    @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 testDoubleReturn() throws Exception {
+        transactionManager.getTransaction().registerSynchronization(new Synchronization() {
+            private ManagedConnection<?> conn;
+
+            @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());
+            }
+
+            @Override
+            public void beforeCompletion() {
+                try {
+                    conn = (ManagedConnection<?>) ds.getConnection();
+                    assertNotNull(conn);
+                } catch (final SQLException e) {
+                    fail("Could not get connection");
+                }
+            }
+        });
+        transactionManager.commit();
+    }
+
+    @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 SynchronizationAdapter() {
+            @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();
+    }
+
+    @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());
+    }
+
+    /**
+     * @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 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 testNestedConnections() {
+        // Not supported
+    }
+
+    @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();
+    }
+
+    @Override
+    @Test
+    public void testSharedConnection() throws Exception {
+        final DelegatingConnection<?> connectionA = (DelegatingConnection<?>) newConnection();
+        final DelegatingConnection<?> connectionB = (DelegatingConnection<?>) newConnection();
+
+        assertNotEquals(connectionA, connectionB);
+        assertNotEquals(connectionB, 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
+        assertNotEquals(connectionA, connectionB);
+        assertNotEquals(connectionB, 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
+        assertNotEquals(connectionA, connectionB);
+        assertNotEquals(connectionB, 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
+        assertNotEquals(connectionA, connectionB);
+        assertNotEquals(connectionB, connectionA);
+        assertTrue(connectionA.innermostDelegateEquals(connectionB.getInnermostDelegate()));
+        assertTrue(connectionB.innermostDelegateEquals(connectionA.getInnermostDelegate()));
+
+        connectionA.close();
+        connectionB.close();
+    }
+}