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;
+        }
+    }
 }


Reply via email to