=== modified file 'src/com/mysql/jdbc/ConnectionImpl.java' --- src/com/mysql/jdbc/ConnectionImpl.java 2009-09-22 13:50:26 +0000 +++ src/com/mysql/jdbc/ConnectionImpl.java 2009-11-06 20:33:44 +0000 @@ -1148,7 +1148,7 @@ } } - private void throwConnectionClosedException() throws SQLException { + void throwConnectionClosedException() throws SQLException { StringBuffer messageBuf = new StringBuffer( "No operations allowed after connection closed."); === modified file 'src/com/mysql/jdbc/LoadBalancingConnectionProxy.java' --- src/com/mysql/jdbc/LoadBalancingConnectionProxy.java 2009-11-02 22:33:49 +0000 +++ src/com/mysql/jdbc/LoadBalancingConnectionProxy.java 2009-11-06 22:43:33 +0000 @@ -285,18 +285,41 @@ // remove from liveConnections this.liveConnections.remove(this.connectionsToHostsMap .get(this.currentConn)); + if(this.connectionsToHostsMap.containsKey(this.currentConn)){ + int hostIndex = ((Integer) this.hostsToListIndexMap + .get(this.connectionsToHostsMap.get(this.currentConn))) + .intValue(); + + // reset the statistics for the host + synchronized (this.responseTimes) { + this.responseTimes[hostIndex] = 0; + } + } + + this.connectionsToHostsMap.remove(this.currentConn); + } + } + + private void closeAllConnections(){ + synchronized (this) { + // close all underlying connections + Iterator allConnections = this.liveConnections.values() + .iterator(); + + while (allConnections.hasNext()) { + try{ + ((Connection) allConnections.next()).close(); + } catch(SQLException e){} + } + + if (!this.isClosed) { + this.balancer.destroy(); + } - int hostIndex = ((Integer) this.hostsToListIndexMap - .get(this.connectionsToHostsMap.get(this.currentConn))) - .intValue(); - - // reset the statistics for the host - synchronized (this.responseTimes) { - this.responseTimes[hostIndex] = 0; - } - - this.connectionsToHostsMap.remove(this.currentConn); - } + this.liveConnections.clear(); + this.connectionsToHostsMap.clear(); + } + } /** @@ -321,22 +344,7 @@ } if ("close".equals(methodName)) { - synchronized (this) { - // close all underlying connections - Iterator allConnections = this.liveConnections.values() - .iterator(); - - while (allConnections.hasNext()) { - ((Connection) allConnections.next()).close(); - } - - if (!this.isClosed) { - this.balancer.destroy(); - } - - this.liveConnections.clear(); - this.connectionsToHostsMap.clear(); - } + closeAllConnections(); return null; } @@ -413,7 +421,7 @@ return; } - + Connection newConn = this.balancer.pickConnection(this, Collections.unmodifiableList(this.hostList), Collections.unmodifiableMap(this.liveConnections), @@ -423,7 +431,9 @@ newConn.setTransactionIsolation(this.currentConn .getTransactionIsolation()); newConn.setAutoCommit(this.currentConn.getAutoCommit()); + this.currentConn = newConn; + } /** @@ -477,35 +487,59 @@ } public synchronized void doPing() throws SQLException { - if(this.isGlobalBlacklistEnabled()){ - SQLException se = null; - boolean foundHost = false; - synchronized(this){ - for(Iterator i = this.hostList.iterator(); i.hasNext(); ){ - String host = (String) i.next(); - Connection conn = (Connection) this.liveConnections.get(host); - if(conn == null){ - continue; + SQLException se = null; + boolean foundHost = false; + + synchronized(this){ + for(Iterator i = this.hostList.iterator(); i.hasNext(); ){ + String host = (String) i.next(); + Connection conn = (Connection) this.liveConnections.get(host); + if(conn == null){ + continue; + } + try{ + conn.ping(); + foundHost = true; + } catch (SQLException e){ + // give up if it is the current connection, otherwise NPE faking resultset later. + if(host.equals(this.connectionsToHostsMap.get(this.currentConn))){ + // clean up underlying connections, since connection pool won't do it + closeAllConnections(); + this.isClosed = true; + throw e; } - try{ - conn.ping(); - foundHost = true; - } catch (SQLException e){ + + // if the Exception is caused by ping connection lifetime checks, don't add to blacklist + if(e.getMessage().equals(Messages.getString("Connection.exceededConnectionLifetime"))){ + // only set the return Exception if it's null + if(se == null){ + se = e; + } + } else { + // overwrite the return Exception no matter what se = e; - this.addToGlobalBlacklist(host); + if(this.isGlobalBlacklistEnabled()){ + this.addToGlobalBlacklist(host); + } } + // take the connection out of the liveConnections Map + this.liveConnections.remove(this.connectionsToHostsMap + .get(conn)); } } - if(!foundHost){ + } + // if there were no successful pings + if(!foundHost){ + closeAllConnections(); + this.isClosed = true; + // throw the stored Exception, if exists + if(se != null){ throw se; } - } else { - Iterator allConns = this.liveConnections.values().iterator(); - while (allConns.hasNext()) { - ((Connection)allConns.next()).ping(); - } + // or create a new SQLException and throw it, must be no liveConnections + ((ConnectionImpl)this.currentConn).throwConnectionClosedException(); } - + } public void addToGlobalBlacklist(String host) { === modified file 'src/testsuite/regression/ConnectionRegressionTest.java' --- src/testsuite/regression/ConnectionRegressionTest.java 2009-11-02 22:33:49 +0000 +++ src/testsuite/regression/ConnectionRegressionTest.java 2009-11-06 22:14:26 +0000 @@ -2648,5 +2648,33 @@ c.createStatement().executeQuery("SELECT 1"); c.prepareStatement("SELECT 1").executeQuery(); } + public void testBug48605() throws Exception { + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", "random"); + props.setProperty("selfDestructOnPingMaxOperations", "5"); + Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[]{"first", "second"}, props); + + assertNotNull("Connection should not be null", conn2); + conn2.setAutoCommit(false); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.commit(); + try{ + conn2.createStatement().execute("/* ping */ SELECT 1"); + // don't care about this - we want the SQLExceptions passed up early for ping failures, rather + // than waiting until commit/rollback and pickNewConnection(). + } catch(SQLException e){ } + assertTrue(conn2.isClosed()); + try{ + conn2.createStatement().execute("SELECT 1"); + fail("Should throw Exception, connection is closed."); + } catch(SQLException e){ } + + + closeMemberJDBCResources(); + } }