Changeset 3541

Show
Ignore:
Timestamp:
02/10/10 11:50:31 (6 months ago)
Author:
Maverick
Message:

Bugfixes for connection leaking. This was caused by a HashMap<String, Connection> where the surrounding code was trying to store multiple Connections on the same key (which only overwrites the previous Connection with the same key).

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/twcore/src/twcore/core/sql/SQLConnectionPool.java

    r2383 r3541  
    1010import java.util.Map; 
    1111import java.util.Vector; 
     12 
     13import twcore.core.util.Tools; 
    1214 
    1315/** 
     
    3234                                                //         connection is available 
    3335    private Vector<Connection>  availableConnections;       // Connections not in use 
    34     private Map<String, Connection>  busyConnections;            // Connections currently querying 
     36    private Map<String, Vector<Connection>>  busyConnections;            // Connections currently querying 
    3537 
    3638    private boolean connectionPending = false;  // True if a connection is being made 
     
    6769        } 
    6870        availableConnections = new Vector<Connection>(initialConnections); 
    69         busyConnections = Collections.synchronizedMap(new HashMap<String, Connection>()); 
     71        busyConnections = Collections.synchronizedMap(new HashMap<String, Vector<Connection>>()); 
    7072        for(int i=0; i<initialConnections; i++) { 
    7173            availableConnections.addElement(makeNewConnection()); 
     
    8688        try{ 
    8789            Statement stmt = conn.createStatement(); 
     90             
    8891            stmt.execute( query ); 
    89             free( conn ); 
     92             
    9093            ResultSet set = stmt.getResultSet(); 
    9194            // If ResultSet is null (INSERT statement), get auto-generated ID if available 
     
    9497            else 
    9598                return stmt.getGeneratedKeys(); 
    96         }catch( SQLException e ){ 
    97             free( conn ); 
    98  
    99             //throw e; 
    100  
    101             // DEBUG: Returning null to generate NullPointerExceptions in bots that 
    102             // do not catch SQLExceptions 
    103             return null; 
     99        } catch( SQLException e ){ 
     100                throw e; 
     101                 
     102        } finally { 
     103            free( DEFAULT_UNIQUE_ID, conn ); 
    104104        } 
    105105    } 
     
    121121     */ 
    122122    public synchronized Connection getConnection(String uniqueID) throws SQLException { 
    123         if(uniqueID.equals("0")==false) { 
     123        if(uniqueID.equals(DEFAULT_UNIQUE_ID)==false) { 
    124124            if(busyConnections.containsKey(uniqueID)) { 
    125                 return busyConnections.get(uniqueID); 
     125                return busyConnections.get(uniqueID).firstElement(); 
    126126            } 
    127127        } 
    128128         
    129         if (availableConnections.isEmpty()==false) { 
    130             Connection existingConnection = availableConnections.lastElement(); 
     129        if (availableConnections.isEmpty()==false) { // if there are availableConnections 
     130                 
     131                // Get an connection from list of available connections and remove it from the list 
     132                Connection availableConnection = availableConnections.lastElement(); 
    131133            int lastIndex = availableConnections.size() - 1; 
    132134            availableConnections.removeElementAt(lastIndex); 
    133             if (existingConnection.isClosed()) { 
     135             
     136            if (availableConnection.isClosed()) { 
     137                 
     138                // if the connection is closed, try again by calling this method again - the availableConnection is lost 
    134139                notifyAll(); // Freed up a spot for anybody waiting 
    135140                return(getConnection()); 
     141                 
    136142            } else { 
    137                 busyConnections.put(uniqueID, existingConnection); 
    138                 return(existingConnection); 
    139             } 
    140         } else { 
     143                if(!busyConnections.containsKey(uniqueID)) { 
     144                        busyConnections.put(uniqueID, new Vector<Connection>()); 
     145                } 
     146                busyConnections.get(uniqueID).add(availableConnection); 
     147                return availableConnection; 
     148            } 
     149             
     150        } else {        // there are no available connections 
     151                 
     152                // Check if we reached the maximum amount of connections 
    141153            if(( totalConnections() < maxConnections ) && !connectionPending ) { 
    142154                makeBackgroundConnection(); 
     
    144156                throw new SQLException("Connection limit reached"); 
    145157            } 
     158             
    146159            try { 
    147160                wait(); 
    148161            } catch(InterruptedException ie) {} 
    149             // Someone freed up a connection, so try again. 
     162            // Someone freed up a connection or a new connection has been made, try again. 
     163             
    150164            return getConnection(uniqueID); 
    151165        } 
     
    188202        try { 
    189203            Connection connection = makeNewConnection(); 
     204             
    190205            synchronized(this) { 
    191206                availableConnections.addElement(connection); 
     
    193208                notifyAll(); 
    194209            } 
    195         } catch(Exception e) { 
     210        } catch(SQLException e) { 
     211                Tools.printStackTrace(e); 
    196212        } 
    197213    } 
     
    216232 
    217233    /** 
    218      * Free a connection back to the available connection pool.  This will notify 
     234     * Free a specific connection back to the available connection pool.  This will notify 
    219235     * other threads that are waiting (using wait()) for a connection in getConnection() 
    220236     * that a new connection is now available. 
     
    222238     */ 
    223239    public synchronized void free( String uniqueID, Connection connection ) { 
    224         if(busyConnections.containsKey(uniqueID)) { 
    225             busyConnections.remove(uniqueID); 
     240        if(busyConnections.containsKey(uniqueID) && busyConnections.get(uniqueID).contains(connection)) { 
     241                 
     242                // Remove connection from the vector 
     243                busyConnections.get(uniqueID).remove(connection); 
     244                 
     245                // Remove HashMap entry if vector has become empty 
     246                if(busyConnections.get(uniqueID).isEmpty()) { 
     247                        busyConnections.remove(uniqueID); 
     248                } 
     249                 
    226250            availableConnections.addElement(connection); 
     251             
    227252            // Wake up threads that are waiting for a connection 
    228253            notifyAll(); 
     
    230255    } 
    231256     
    232     public synchronized void free( Connection connection ) { 
    233         this.free( DEFAULT_UNIQUE_ID, connection); 
    234     } 
    235  
    236257    /** 
    237258     * @return Total number of connections either available or currently busy 
    238259     */ 
    239260    public synchronized int totalConnections() { 
    240         return(availableConnections.size() + 
    241         busyConnections.size()); 
     261        return(availableConnections.size() + busyConnections.size()); 
    242262    } 
    243263 
     
    249269        availableConnections = new Vector<Connection>(); 
    250270        closeBusyConnections(busyConnections); 
    251         busyConnections = new HashMap<String,Connection>(); 
     271        busyConnections = new HashMap<String,Vector<Connection>>(); 
    252272    } 
    253273 
     
    271291     * @param connections Vector containing connections to close 
    272292     */ 
    273     private void closeBusyConnections(Map<String,Connection> connections) { 
    274         try { 
    275             for(Connection conn:connections.values()) { 
    276                 if (!conn.isClosed()) { 
    277                     conn.close(); 
    278                 } 
    279             } 
     293    private void closeBusyConnections(Map<String,Vector<Connection>> connections) { 
     294        try { 
     295                for(Vector<Connection> conns:connections.values()) { 
     296                    for(Connection conn:conns) { 
     297                        if (!conn.isClosed()) { 
     298                            conn.close(); 
     299                        } 
     300                    } 
     301                } 
    280302        } catch(SQLException sqle) { 
    281303        } 
     
    293315     */ 
    294316    public synchronized String toString() { 
    295         String info = 
     317        /*String info = 
    296318        "SQL pool " + poolName + ": " + totalConnections() + "/" 
    297319        + maxConnections + " connections online, " + busyConnections.size() 
    298         + " in use"; 
    299         return(info); 
     320        + " in use";*/ 
     321        return "SQL pool "+ poolName + ": " + busyConnections.size() + "/" + availableConnections.size() + " connections online (background: "+getNumBackground()+") (max: "+maxConnections+")"; 
    300322    } 
    301323