Author: psteitz
Date: Tue Feb 10 02:49:54 2015
New Revision: 1658616
URL: http://svn.apache.org/r1658616
Log:
Fixed connection leak when SQLException is thrown while enlisting an XA
transaction.
JIRA: DBCP-433
Reported (with patch) by Vladimir Konkov
Test case written by Thomas Neidhart
Added:
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/managed/TestManagedConnection.java
(with props)
Modified:
commons/proper/dbcp/trunk/src/changes/changes.xml
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/managed/ManagedConnection.java
Modified: commons/proper/dbcp/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/changes/changes.xml?rev=1658616&r1=1658615&r2=1658616&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/changes/changes.xml (original)
+++ commons/proper/dbcp/trunk/src/changes/changes.xml Tue Feb 10 02:49:54 2015
@@ -103,6 +103,10 @@ The <action> type attribute can be add,u
Changed BasicDataSource createDataSource method to ensure that
initialization
completes before clients get reference to newly created instances.
</action>
+ <action issue="DBCP-433" dev="psteitz" type="fix" due-to="Vladimir
Konkov">
+ Fixed connection leak when SQLException is thrown while enlisting an XA
+ transaction.
+ </action>
</release>
<release version="2.0.1" date="24 May 2014" description="This is a bug fix
release.">
<action dev="markt" type="fix">
Modified:
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/managed/ManagedConnection.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/managed/ManagedConnection.java?rev=1658616&r1=1658615&r2=1658616&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/managed/ManagedConnection.java
(original)
+++
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/managed/ManagedConnection.java
Tue Feb 10 02:49:54 2015
@@ -119,11 +119,12 @@ public class ManagedConnection<C extends
// transaction completes
isSharedConnection = true;
} else {
+ C connection = getDelegateInternal();
// if our delegate is null, create one
- if (getDelegateInternal() == null) {
+ if (connection == null) {
try {
// borrow a new connection from the pool
- C connection = pool.borrowObject();
+ connection = pool.borrowObject();
setDelegate(connection);
} catch (Exception e) {
throw new SQLException("Unable to acquire a new connection
from the pool", e);
@@ -137,10 +138,15 @@ public class ManagedConnection<C extends
// register our connection as the shared connection
try {
-
transactionContext.setSharedConnection(getDelegateInternal());
+ transactionContext.setSharedConnection(connection);
} catch (SQLException e) {
// transaction is hosed
transactionContext = null;
+ try {
+ pool.invalidateObject(connection);
+ } catch (Exception e1) {
+ // we are try but no luck
+ }
throw e;
}
}
Added:
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/managed/TestManagedConnection.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/managed/TestManagedConnection.java?rev=1658616&view=auto
==============================================================================
---
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/managed/TestManagedConnection.java
(added)
+++
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/managed/TestManagedConnection.java
Tue Feb 10 02:49:54 2015
@@ -0,0 +1,213 @@
+/**
+ *
+ * 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.*;
+
+import java.lang.reflect.Field;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.RollbackException;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAResource;
+
+import org.apache.commons.dbcp2.ConnectionFactory;
+import org.apache.commons.dbcp2.DelegatingConnection;
+import org.apache.commons.dbcp2.DriverConnectionFactory;
+import org.apache.commons.dbcp2.PoolableConnection;
+import org.apache.commons.dbcp2.PoolableConnectionFactory;
+import org.apache.commons.dbcp2.PoolingDataSource;
+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;
+
+/**
+ * TestSuite for ManagedConnection.
+ */
+public class TestManagedConnection {
+
+ protected PoolingDataSource<PoolableConnection> ds = null;
+
+ private GenericObjectPool<PoolableConnection> pool = null;
+
+ protected TransactionManager transactionManager;
+
+ @Before
+ public void setUp()
+ throws Exception {
+ // create a GeronimoTransactionManager for testing
+ transactionManager = new TransactionManagerImpl();
+
+ // create a driver connection factory
+ Properties properties = new Properties();
+ properties.setProperty("user", "username");
+ properties.setProperty("password", "password");
+ ConnectionFactory connectionFactory = new DriverConnectionFactory(new
TesterDriver(), "jdbc:apache:commons:testdriver", properties);
+
+ // wrap it with a LocalXAConnectionFactory
+ XAConnectionFactory xaConnectionFactory = new
UncooperativeLocalXAConnectionFactory(transactionManager, connectionFactory);
+
+ // create the pool object factory
+ 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);
+
+ // finally create the datasource
+ ds = new ManagedDataSource<>(pool,
xaConnectionFactory.getTransactionRegistry());
+ ds.setAccessToUnderlyingConnectionAllowed(true);
+ }
+
+ @After
+ public void tearDown()
+ throws Exception {
+ pool.close();
+ }
+
+ public Connection getConnection()
+ throws Exception {
+ return ds.getConnection();
+ }
+
+ @Test
+ public void testConnectionReturnOnErrorWhenEnlistingXAResource()
+ throws Exception {
+ // see DBCP-433
+
+ transactionManager.begin();
+ try {
+ DelegatingConnection<?> connectionA = (DelegatingConnection<?>)
getConnection();
+ connectionA.close();
+ } catch (SQLException e) {
+ // expected
+ }
+ transactionManager.commit();
+ assertEquals(1, pool.getBorrowedCount());
+ // assertEquals(1, pool.getReturnedCount());
+ assertEquals(1, pool.getDestroyedCount());
+ assertEquals(0, pool.getNumActive());
+ }
+
+ private class UncooperativeLocalXAConnectionFactory
+ extends LocalXAConnectionFactory {
+
+ public UncooperativeLocalXAConnectionFactory(TransactionManager
transactionManager, ConnectionFactory connectionFactory) {
+ super(transactionManager, connectionFactory);
+
+ try {
+ // inject our own TransactionRegistry which returns
Uncooperative Transactions which always fail to enlist a XAResource
+ Field field =
LocalXAConnectionFactory.class.getDeclaredField("transactionRegistry");
+ field.setAccessible(true);
+ field.set(this, new
UncooperativeTransactionRegistry(transactionManager));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private class UncooperativeTransactionRegistry
+ extends TransactionRegistry {
+
+ public UncooperativeTransactionRegistry(TransactionManager
transactionManager) {
+ super(transactionManager);
+ }
+
+ @Override
+ public TransactionContext getActiveTransactionContext()
+ throws SQLException {
+ try {
+ return new TransactionContext(this, new
UncooperativeTransaction(transactionManager.getTransaction()));
+ } catch (SystemException e) {
+ return null;
+ }
+ }
+
+ }
+
+ /**
+ * Transaction that always fails enlistResource.
+ */
+ private class UncooperativeTransaction
+ implements Transaction {
+
+ private Transaction wrappedTransaction;
+
+ public UncooperativeTransaction(Transaction transaction) {
+ this.wrappedTransaction = transaction;
+ }
+
+ @Override
+ public void commit()
+ throws HeuristicMixedException, HeuristicRollbackException,
RollbackException, SecurityException,
+ SystemException {
+ wrappedTransaction.commit();
+ }
+
+ @Override
+ public boolean delistResource(XAResource arg0, int arg1)
+ throws IllegalStateException, SystemException {
+ return wrappedTransaction.delistResource(arg0, arg1);
+ }
+
+ @Override
+ public int getStatus()
+ throws SystemException {
+ return wrappedTransaction.getStatus();
+ }
+
+ @Override
+ public void registerSynchronization(Synchronization arg0)
+ throws IllegalStateException, RollbackException, SystemException {
+ wrappedTransaction.registerSynchronization(arg0);
+ }
+
+ @Override
+ public void rollback()
+ throws IllegalStateException, SystemException {
+ wrappedTransaction.rollback();
+ }
+
+ @Override
+ public void setRollbackOnly()
+ throws IllegalStateException, SystemException {
+ wrappedTransaction.setRollbackOnly();
+ }
+
+ @Override
+ public synchronized boolean enlistResource(XAResource xaRes) {
+ return false;
+ }
+ }
+
+}
Propchange:
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/managed/TestManagedConnection.java
------------------------------------------------------------------------------
svn:eol-style = native