Author: markt
Date: Thu Jan 30 13:50:23 2014
New Revision: 1562825
URL: http://svn.apache.org/r1562825
Log:
Fix DBCP-376
Fix thread safety issues in the SharedPoolDataSource and the
PerUserPoolDataSource.
Test case and fix based on patches suggested by Dave Oxley.
Modified:
commons/proper/dbcp/trunk/src/changes/changes.xml
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java
commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp2/cpdsadapter/TestDriverAdapterCPDS.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=1562825&r1=1562824&r2=1562825&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/changes/changes.xml (original)
+++ commons/proper/dbcp/trunk/src/changes/changes.xml Thu Jan 30 13:50:23 2014
@@ -87,6 +87,10 @@ The <action> type attribute can be add,u
properties are available as is the connection pool and the statement
pools (if statement pooling is enabled).
</action>
+ <action dev="markt" issue="DBCP-376" type="fix" due-to="Dave Oxley">
+ Fix thread safety issues in the SharedPoolDataSource and the
+ PerUserPoolDataSource.
+ </action>
</release>
<release version="1.5.1" date="TBD" description="TBD">
<action dev="markt" issue="DBCP-400" type="fix">
Modified:
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java?rev=1562825&r1=1562824&r2=1562825&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java
(original)
+++
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java
Thu Jan 30 13:50:23 2014
@@ -20,9 +20,10 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
-import java.util.HashSet;
+import java.util.Collections;
+import java.util.Map;
import java.util.Set;
-import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
@@ -63,13 +64,14 @@ class CPDSConnectionFactory
* Map of PooledConnections for which close events are ignored.
* Connections are muted when they are being validated.
*/
- private final Set<PooledConnection> validatingSet = new HashSet<>();
+ private final Set<PooledConnection> validatingSet =
+ Collections.newSetFromMap(new
ConcurrentHashMap<PooledConnection,Boolean>());
/**
* Map of PooledConnectionAndInfo instances
*/
- private final WeakHashMap<PooledConnection, PooledConnectionAndInfo> pcMap
=
- new WeakHashMap<>();
+ private final Map<PooledConnection, PooledConnectionAndInfo> pcMap =
+ new ConcurrentHashMap<>();
/**
* Create a new <tt>PoolableConnectionFactory</tt>.
Modified:
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java?rev=1562825&r1=1562824&r2=1562825&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java
(original)
+++
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java
Thu Jan 30 13:50:23 2014
@@ -21,9 +21,10 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
-import java.util.HashSet;
+import java.util.Collections;
+import java.util.Map;
import java.util.Set;
-import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
@@ -61,13 +62,14 @@ class KeyedCPDSConnectionFactory
* Map of PooledConnections for which close events are ignored.
* Connections are muted when they are being validated.
*/
- private final Set<PooledConnection> validatingSet = new HashSet<>();
+ private final Set<PooledConnection> validatingSet =
+ Collections.newSetFromMap(new
ConcurrentHashMap<PooledConnection,Boolean>());
/**
* Map of PooledConnectionAndInfo instances
*/
- private final WeakHashMap<PooledConnection, PooledConnectionAndInfo> pcMap
=
- new WeakHashMap<>();
+ private final Map<PooledConnection, PooledConnectionAndInfo> pcMap =
+ new ConcurrentHashMap<>();
/**
* Create a new <tt>KeyedPoolableConnectionFactory</tt>.
Modified:
commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp2/cpdsadapter/TestDriverAdapterCPDS.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp2/cpdsadapter/TestDriverAdapterCPDS.java?rev=1562825&r1=1562824&r2=1562825&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp2/cpdsadapter/TestDriverAdapterCPDS.java
(original)
+++
commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp2/cpdsadapter/TestDriverAdapterCPDS.java
Thu Jan 30 13:50:23 2014
@@ -24,6 +24,11 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import javax.sql.DataSource;
+
+import org.apache.commons.dbcp2.datasources.SharedPoolDataSource;
+import org.junit.Assert;
+
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
@@ -158,4 +163,66 @@ public class TestDriverAdapterCPDS exten
pcds.getPooledConnection("foo", "bar").close();
assertEquals("bar",
pcds.getConnectionProperties().getProperty("password"));
}
+
+ // https://issues.apache.org/jira/browse/DBCP-376
+ public void testDbcp367() throws Exception {
+ ThreadDbcp367[] threads = new ThreadDbcp367[200];
+
+ pcds.setPoolPreparedStatements(true);
+ pcds.setMaxPreparedStatements(-1);
+ pcds.setAccessToUnderlyingConnectionAllowed(true);
+
+ SharedPoolDataSource spds = new SharedPoolDataSource();
+ spds.setConnectionPoolDataSource(pcds);
+ spds.setMaxTotal(threads.length + 10);
+ spds.setMaxWaitMillis(-1);
+ spds.setMaxIdle(10);
+ spds.setDefaultAutoCommit(false);
+
+ spds.setValidationQuery("SELECT 1");
+ spds.setTimeBetweenEvictionRunsMillis(10000);
+ spds.setNumTestsPerEvictionRun(-1);
+ spds.setTestWhileIdle(true);
+ spds.setTestOnBorrow(true);
+ spds.setTestOnReturn(false);
+
+ for (int i = 0; i < threads.length; i++) {
+ threads[i] = new ThreadDbcp367(spds);
+ threads[i].start();
+ }
+
+ for (int i = 0; i < threads.length; i++) {
+ threads[i].join();
+ Assert.assertFalse(threads[i].isFailed());
+ }
+ }
+
+ private static class ThreadDbcp367 extends Thread {
+
+ private final DataSource ds;
+
+ private volatile boolean failed = false;
+
+ public ThreadDbcp367(DataSource ds) {
+ this.ds = ds;
+ }
+
+ @Override
+ public void run() {
+ Connection c = null;
+ try {
+ for (int j=0; j < 5000; j++) {
+ c = ds.getConnection();
+ c.close();
+ }
+ } catch (SQLException sqle) {
+ failed = true;
+ sqle.printStackTrace();
+ }
+ }
+
+ public boolean isFailed() {
+ return failed;
+ }
+ }
}