You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by rm...@apache.org on 2016/08/11 15:22:38 UTC

tomee git commit: TOMEE-1900 better xa pooling handling - forcing close if xa state is broken

Repository: tomee
Updated Branches:
  refs/heads/master 6d7565af8 -> 56ec4be8b


TOMEE-1900 better xa pooling handling - forcing close if xa state is broken


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/56ec4be8
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/56ec4be8
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/56ec4be8

Branch: refs/heads/master
Commit: 56ec4be8bc3db9754f5d4ddc5f57206861363537
Parents: 6d7565a
Author: Romain manni-Bucau <rm...@gmail.com>
Authored: Thu Aug 11 17:22:27 2016 +0200
Committer: Romain manni-Bucau <rm...@gmail.com>
Committed: Thu Aug 11 17:22:27 2016 +0200

----------------------------------------------------------------------
 .../jdbc/managed/local/ManagedConnection.java   |   1 +
 .../tomee/jdbc/TomcatXADataSourceTest.java      | 177 +++++++++++++++----
 2 files changed, 148 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/56ec4be8/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java
index 03f963a..f357b31 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java
@@ -132,6 +132,7 @@ public class ManagedConnection implements InvocationHandler {
                     currentTransaction = transaction;
                     try {
                         if (!transaction.enlistResource(getXAResource())) {
+                            closeConnection(true);
                             throw new SQLException("Unable to enlist connection in transaction: enlistResource returns 'false'.");
                         }
                     } catch (final RollbackException ignored) {

http://git-wip-us.apache.org/repos/asf/tomee/blob/56ec4be8/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java b/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java
index 0e19a78..306bfd2 100644
--- a/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java
+++ b/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java
@@ -5,14 +5,14 @@
  * 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.
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.tomee.jdbc;
 
@@ -32,8 +32,12 @@ import org.junit.runner.RunWith;
 import javax.annotation.Resource;
 import javax.ejb.EJB;
 import javax.ejb.Singleton;
+import javax.sql.ConnectionEventListener;
 import javax.sql.DataSource;
+import javax.sql.StatementEventListener;
+import javax.sql.XAConnection;
 import javax.transaction.Synchronization;
+import javax.transaction.xa.XAResource;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.ArrayList;
@@ -53,6 +57,9 @@ public class TomcatXADataSourceTest {
     @Resource(name = "xadb")
     private DataSource ds;
 
+    @Resource(name = "xadb2")
+    private DataSource badDs;
+
     @EJB
     private TxP tx;
 
@@ -65,28 +72,42 @@ public class TomcatXADataSourceTest {
     @Configuration
     public Properties props() {
         return new PropertiesBuilder()
-            .p("openejb.jdbc.datasource-creator", TomEEDataSourceCreator.class.getName())
-
-            .p("txMgr", "new://TransactionManager?type=TransactionManager")
-            .p("txMgr.txRecovery", "true")
-            .p("txMgr.logFileDir", "target/test/xa/howl")
-
-            // real XA datasources
-            .p("xa", "new://Resource?class-name=" + JDBCXADataSource.class.getName())
-            .p("xa.url", "jdbc:hsqldb:mem:tomcat-xa")
-            .p("xa.user", "sa")
-            .p("xa.password", "")
-            .p("xa.SkipImplicitAttributes", "true")
-            .p("xa.SkipPropertiesFallback", "true") // otherwise goes to connection properties
-
-            .p("xadb", "new://Resource?type=DataSource")
-            .p("xadb.xaDataSource", "xa")
-            .p("xadb.JtaManaged", "true")
-            .p("xadb.MaxIdle", "25")
-            .p("xadb.MaxActive", "25")
-            .p("xadb.InitialSize", "3")
-
-            .build();
+                .p("openejb.jdbc.datasource-creator", TomEEDataSourceCreator.class.getName())
+
+                .p("txMgr", "new://TransactionManager?type=TransactionManager")
+                .p("txMgr.txRecovery", "true")
+                .p("txMgr.logFileDir", "target/test/xa/howl")
+
+                // real XA datasources
+                .p("xa", "new://Resource?class-name=" + JDBCXADataSource.class.getName())
+                .p("xa.url", "jdbc:hsqldb:mem:tomcat-xa")
+                .p("xa.user", "sa")
+                .p("xa.password", "")
+                .p("xa.SkipImplicitAttributes", "true")
+                .p("xa.SkipPropertiesFallback", "true") // otherwise goes to connection properties
+
+                .p("xadb", "new://Resource?type=DataSource")
+                .p("xadb.xaDataSource", "xa")
+                .p("xadb.JtaManaged", "true")
+                .p("xadb.MaxIdle", "25")
+                .p("xadb.MaxActive", "25")
+                .p("xadb.InitialSize", "3")
+
+                .p("xa2", "new://Resource?class-name=" + BadDataSource.class.getName())
+                .p("xa2.url", "jdbc:hsqldb:mem:tomcat-xa2")
+                .p("xa2.user", "sa")
+                .p("xa2.password", "")
+                .p("xa2.SkipImplicitAttributes", "true")
+                .p("xa2.SkipPropertiesFallback", "true") // otherwise goes to connection properties
+
+                .p("xadb2", "new://Resource?type=DataSource")
+                .p("xadb2.xaDataSource", "xa2")
+                .p("xadb2.JtaManaged", "true")
+                .p("xadb2.MaxIdle", "25")
+                .p("xadb2.MaxActive", "25")
+                .p("xadb2.InitialSize", "3")
+
+                .build();
     }
 
     @Test
@@ -242,6 +263,40 @@ public class TomcatXADataSourceTest {
             assertEquals(0, tds.getActive());
             assertEquals(25, tds.getIdle());
         }
+
+        // underlying connection closed when fetch from pool
+        for (int it = 0; it < 5; it++) { // ensures it always works and not only the first time
+            for (int i = 0; i < 25; i++) {
+                tx.run(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            final Connection ref = badDs.getConnection();
+                            OpenEJB.getTransactionManager().getTransaction().registerSynchronization(new Synchronization() {
+                                @Override
+                                public void beforeCompletion() {
+                                    // no-op
+                                }
+
+                                @Override
+                                public void afterCompletion(final int status) { // JPA does it
+                                    try {
+                                        ref.close();
+                                    } catch (final SQLException e) {
+                                        fail(e.getMessage());
+                                    }
+                                }
+                            });
+                            ref.getMetaData();
+                        } catch (final Exception sql) {
+                            // we expect this
+                        }
+                    }
+                });
+            }
+            assertEquals(0, tds.getActive());
+            assertEquals(25, tds.getIdle());
+        }
     }
 
     @Singleton
@@ -250,4 +305,66 @@ public class TomcatXADataSourceTest {
             r.run();
         }
     }
+
+    public static class BadDataSource extends JDBCXADataSource {
+        public BadDataSource() throws SQLException {
+            // no-op
+        }
+
+        @Override
+        public XAConnection getXAConnection() throws SQLException {
+            return corrupt(super.getXAConnection());
+        }
+
+        @Override
+        public XAConnection getXAConnection(final String user, final String pwd) throws SQLException {
+            return corrupt(super.getXAConnection());
+        }
+
+        // this closes the underlying connection - which should cause enlist to fail
+        private XAConnection corrupt(final XAConnection xaConnection) throws SQLException {
+            return new XAConnection() {
+                private final XAConnection delegate = xaConnection;
+
+                @Override
+                public XAResource getXAResource() throws SQLException {
+                    return delegate.getXAResource();
+                }
+
+                @Override
+                public Connection getConnection() throws SQLException {
+                    final Connection connection = delegate.getConnection();
+                    if (!connection.isClosed()) {
+                        connection.close();
+                    }
+                    return connection;
+                }
+
+                @Override
+                public void close() throws SQLException {
+                    delegate.close();
+                }
+
+                @Override
+                public void addConnectionEventListener(final ConnectionEventListener listener) {
+                    delegate.addConnectionEventListener(listener);
+                }
+
+                @Override
+                public void removeConnectionEventListener(final ConnectionEventListener listener) {
+                    delegate.removeConnectionEventListener(listener);
+                }
+
+                @Override
+                public void addStatementEventListener(final StatementEventListener listener) {
+                    delegate.addStatementEventListener(listener);
+                }
+
+                @Override
+                public void removeStatementEventListener(final StatementEventListener listener) {
+                    delegate.removeStatementEventListener(listener);
+                }
+            };
+        }
+    }
 }