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 <rmannibu...@gmail.com> Authored: Thu Aug 11 17:22:27 2016 +0200 Committer: Romain manni-Bucau <rmannibu...@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); + } + }; + } + } }