Coverage Report - org.apache.commons.dbcp.PoolingConnection
 
Classes in this File Line Coverage Branch Coverage Complexity
PoolingConnection
62%
57/91
72%
16/22
3.815
PoolingConnection$PStmtKey
60%
34/56
71%
23/32
3.815
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  * 
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  * 
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.dbcp;
 19  
 
 20  
 import java.sql.CallableStatement;
 21  
 import java.sql.Connection;
 22  
 import java.sql.PreparedStatement;
 23  
 import java.sql.SQLException;
 24  
 
 25  
 import java.util.NoSuchElementException;
 26  
 
 27  
 import org.apache.commons.pool.KeyedObjectPool;
 28  
 import org.apache.commons.pool.KeyedPoolableObjectFactory;
 29  
 
 30  
 /**
 31  
  * A {@link DelegatingConnection} that pools {@link PreparedStatement}s.
 32  
  * <p>
 33  
  * The {@link #prepareStatement} and {@link #prepareCall} methods, rather than creating a new PreparedStatement
 34  
  * each time, may actually pull the statement from a pool of unused statements.
 35  
  * The {@link PreparedStatement#close} method of the returned statement doesn't
 36  
  * actually close the statement, but rather returns it to the pool. 
 37  
  * (See {@link PoolablePreparedStatement}, {@link PoolableCallableStatement}.)
 38  
  * 
 39  
  *
 40  
  * @see PoolablePreparedStatement
 41  
  * @author Rodney Waldhoff
 42  
  * @author Dirk Verbeeck
 43  
  * @version $Revision: 1023401 $ $Date: 2010-10-16 21:54:24 -0400 (Sat, 16 Oct 2010) $
 44  
  */
 45  
 public class PoolingConnection extends DelegatingConnection implements Connection, KeyedPoolableObjectFactory {
 46  
     /** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */
 47  569
     protected KeyedObjectPool _pstmtPool = null;
 48  
 
 49  
     /** Prepared Statement type */
 50  
     private static final byte STATEMENT_PREPAREDSTMT = 0;
 51  
     
 52  
     /** Callable Statement type */
 53  
     private static final byte STATEMENT_CALLABLESTMT = 1;
 54  
      
 55  
     
 56  
     /**
 57  
      * Constructor.
 58  
      * @param c the underlying {@link Connection}.
 59  
      */
 60  
     public PoolingConnection(Connection c) {
 61  0
         super(c);
 62  0
     }
 63  
 
 64  
     /**
 65  
      * Constructor.
 66  
      * @param c the underlying {@link Connection}.
 67  
      * @param pool {@link KeyedObjectPool} of {@link PreparedStatement}s and {@link CallableStatement}s.
 68  
      */
 69  
     public PoolingConnection(Connection c, KeyedObjectPool pool) {
 70  569
         super(c);
 71  569
         _pstmtPool = pool;
 72  569
     }
 73  
 
 74  
 
 75  
     /**
 76  
      * Close and free all {@link PreparedStatement}s or {@link CallableStatement} from the pool, and
 77  
      * close the underlying connection.
 78  
      */
 79  
     public synchronized void close() throws SQLException {
 80  529
         if(null != _pstmtPool) {
 81  527
             KeyedObjectPool oldpool = _pstmtPool;            
 82  527
             _pstmtPool = null;
 83  
             try {
 84  527
                 oldpool.close();
 85  0
             } catch(RuntimeException e) {
 86  0
                 throw e;
 87  0
             } catch(SQLException e) {
 88  0
                 throw e;
 89  0
             } catch(Exception e) {
 90  0
                 throw (SQLException) new SQLException("Cannot close connection").initCause(e);
 91  527
             }
 92  
         }
 93  529
         getInnermostDelegate().close();
 94  527
     }
 95  
 
 96  
     /**
 97  
      * Create or obtain a {@link PreparedStatement} from the pool.
 98  
      * @param sql the sql string used to define the PreparedStatement
 99  
      * @return a {@link PoolablePreparedStatement}
 100  
      */
 101  
     public PreparedStatement prepareStatement(String sql) throws SQLException {
 102  3772
         if (null == _pstmtPool) {
 103  2
             throw new SQLException(
 104  
                     "Statement pool is null - closed or invalid PoolingConnection.");
 105  
         }
 106  
         try {
 107  3770
             return(PreparedStatement)(_pstmtPool.borrowObject(createKey(sql)));
 108  2
         } catch(NoSuchElementException e) {
 109  2
             throw (SQLException) new SQLException("MaxOpenPreparedStatements limit reached").initCause(e); 
 110  0
         } catch(RuntimeException e) {
 111  0
             throw e;
 112  0
         } catch(Exception e) {
 113  0
             throw new SQLNestedException("Borrow prepareStatement from pool failed", e);
 114  
         }
 115  
     }
 116  
 
 117  
     /**
 118  
      * Create or obtain a {@link PreparedStatement} from the pool.
 119  
      * @param sql the sql string used to define the PreparedStatement
 120  
      * @param resultSetType result set type
 121  
      * @param resultSetConcurrency result set concurrency
 122  
      * @return a {@link PoolablePreparedStatement}
 123  
      */
 124  
     public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
 125  12
         if (null == _pstmtPool) {
 126  0
             throw new SQLException(
 127  
                     "Statement pool is null - closed or invalid PoolingConnection.");
 128  
         }
 129  
         try {
 130  12
             return(PreparedStatement)(_pstmtPool.borrowObject(createKey(sql,resultSetType,resultSetConcurrency)));
 131  0
         } catch(NoSuchElementException e) {
 132  0
             throw (SQLException) new SQLException("MaxOpenPreparedStatements limit reached").initCause(e); 
 133  0
         } catch(RuntimeException e) {
 134  0
             throw e;
 135  0
         } catch(Exception e) {
 136  0
             throw (SQLException) new SQLException("Borrow prepareStatement from pool failed").initCause(e);
 137  
         }
 138  
     }
 139  
     
 140  
     /**
 141  
      * Create or obtain a {@link CallableStatement} from the pool.
 142  
      * @param sql the sql string used to define the CallableStatement
 143  
      * @return a {@link PoolableCallableStatement}
 144  
      * @throws SQLException
 145  
      * @since 1.3
 146  
      */
 147  
     public CallableStatement prepareCall(String sql) throws SQLException {
 148  
         try {
 149  84
             return (CallableStatement) (_pstmtPool.borrowObject(createKey(sql, STATEMENT_CALLABLESTMT)));
 150  0
         } catch (NoSuchElementException e) {
 151  0
             throw new SQLNestedException("MaxOpenCallableStatements limit reached", e);
 152  0
         } catch (RuntimeException e) {
 153  0
             throw e;
 154  0
         } catch (Exception e) {
 155  0
             throw new SQLNestedException("Borrow callableStatement from pool failed", e);
 156  
         }
 157  
     }
 158  
     
 159  
     /**
 160  
      * Create or obtain a {@link CallableStatement} from the pool.
 161  
      * @param sql the sql string used to define the CallableStatement
 162  
      * @param resultSetType result set type
 163  
      * @param resultSetConcurrency result set concurrency
 164  
      * @return a {@link PoolableCallableStatement}
 165  
      * @throws SQLException
 166  
      * @since 1.3
 167  
      */
 168  
     public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
 169  
         try {
 170  6
             return (CallableStatement) (_pstmtPool.borrowObject(createKey(sql, resultSetType,
 171  
                             resultSetConcurrency, STATEMENT_CALLABLESTMT)));
 172  0
         } catch (NoSuchElementException e) {
 173  0
             throw new SQLNestedException("MaxOpenCallableStatements limit reached", e);
 174  0
         } catch (RuntimeException e) {
 175  0
             throw e;
 176  0
         } catch (Exception e) {
 177  0
             throw new SQLNestedException("Borrow callableStatement from pool failed", e);
 178  
         }
 179  
     }
 180  
     
 181  
 
 182  
 //    TODO: possible enhancement, cache these preparedStatements as well
 183  
 
 184  
 //    public PreparedStatement prepareStatement(String sql, int resultSetType,
 185  
 //                                              int resultSetConcurrency,
 186  
 //                                              int resultSetHoldability)
 187  
 //        throws SQLException {
 188  
 //        return super.prepareStatement(
 189  
 //            sql, resultSetType, resultSetConcurrency, resultSetHoldability);
 190  
 //    }
 191  
 //
 192  
 //    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
 193  
 //        throws SQLException {
 194  
 //        return super.prepareStatement(sql, autoGeneratedKeys);
 195  
 //    }
 196  
 //
 197  
 //    public PreparedStatement prepareStatement(String sql, int columnIndexes[])
 198  
 //        throws SQLException {
 199  
 //        return super.prepareStatement(sql, columnIndexes);
 200  
 //    }
 201  
 //
 202  
 //    public PreparedStatement prepareStatement(String sql, String columnNames[])
 203  
 //        throws SQLException {
 204  
 //        return super.prepareStatement(sql, columnNames);
 205  
 //    }
 206  
 
 207  
     /**
 208  
      * Create a PStmtKey for the given arguments.
 209  
      * @param sql the sql string used to define the statement
 210  
      * @param resultSetType result set type
 211  
      * @param resultSetConcurrency result set concurrency
 212  
      */
 213  
     protected Object createKey(String sql, int resultSetType, int resultSetConcurrency) {
 214  12
         String catalog = null;
 215  
         try {
 216  12
             catalog = getCatalog();
 217  12
         } catch (SQLException e) {}
 218  12
         return new PStmtKey(normalizeSQL(sql), catalog, resultSetType, resultSetConcurrency);
 219  
     }
 220  
     
 221  
     /**
 222  
      * Create a PStmtKey for the given arguments.
 223  
      * @param sql the sql string used to define the statement
 224  
      * @param resultSetType result set type
 225  
      * @param resultSetConcurrency result set concurrency
 226  
      * @param stmtType statement type - either {@link #STATEMENT_CALLABLESTMT} or {@link #STATEMENT_PREPAREDSTMT}
 227  
      */
 228  
     protected Object createKey(String sql, int resultSetType, int resultSetConcurrency, byte stmtType) {
 229  6
         String catalog = null;
 230  
         try {
 231  6
             catalog = getCatalog();
 232  6
         } catch (SQLException e) {}
 233  6
         return new PStmtKey(normalizeSQL(sql), catalog, resultSetType, resultSetConcurrency, stmtType);
 234  
     }
 235  
 
 236  
     /**
 237  
      * Create a PStmtKey for the given arguments.
 238  
      * @param sql the sql string used to define the statement
 239  
      */
 240  
     protected Object createKey(String sql) {
 241  3770
         String catalog = null;
 242  
         try {
 243  3770
             catalog = getCatalog();
 244  3770
         } catch (SQLException e) {}
 245  3770
         return new PStmtKey(normalizeSQL(sql), catalog);
 246  
     }
 247  
     
 248  
     /**
 249  
      * Create a PStmtKey for the given arguments.
 250  
      * @param sql the sql string used to define the statement
 251  
      * @param stmtType statement type - either {@link #STATEMENT_CALLABLESTMT} or {@link #STATEMENT_PREPAREDSTMT}
 252  
      */
 253  
     protected Object createKey(String sql, byte stmtType) {
 254  84
         String catalog = null;
 255  
         try {
 256  84
             catalog = getCatalog();
 257  84
         } catch (SQLException e) {}
 258  84
         return new PStmtKey(normalizeSQL(sql), catalog, stmtType);
 259  
     }
 260  
 
 261  
     /**
 262  
      * Normalize the given SQL statement, producing a
 263  
      * cannonical form that is semantically equivalent to the original.
 264  
      */
 265  
     protected String normalizeSQL(String sql) {
 266  3872
         return sql.trim();
 267  
     }
 268  
 
 269  
     /**
 270  
      * {@link KeyedPoolableObjectFactory} method for creating
 271  
      * {@link PoolablePreparedStatement}s or {@link PoolableCallableStatement}s.
 272  
      * The <code>stmtType</code> field in the key determines whether 
 273  
      * a PoolablePreparedStatement or PoolableCallableStatement is created.
 274  
      * 
 275  
      * @param obj the key for the {@link PreparedStatement} to be created
 276  
      * @see #createKey(String, int, int, byte)
 277  
      */
 278  
     public Object makeObject(Object obj) throws Exception {
 279  228
         if(null == obj || !(obj instanceof PStmtKey)) {
 280  0
             throw new IllegalArgumentException("Prepared statement key is null or invalid.");
 281  
         } else {
 282  228
             PStmtKey key = (PStmtKey)obj;
 283  228
             if( null == key._resultSetType && null == key._resultSetConcurrency ) {
 284  210
                 if (key._stmtType == STATEMENT_PREPAREDSTMT ) {
 285  134
                     return new PoolablePreparedStatement(getDelegate().prepareStatement( key._sql), key, _pstmtPool, this); 
 286  
                 } else {
 287  76
                     return new PoolableCallableStatement(getDelegate().prepareCall( key._sql), key, _pstmtPool, this);
 288  
                 }
 289  
             } else { // Both _resultSetType and _resultSetConcurrency are non-null here (both or neither are set by constructors)
 290  18
                 if(key._stmtType == STATEMENT_PREPAREDSTMT) {
 291  12
                     return new PoolablePreparedStatement(getDelegate().prepareStatement(
 292  
                         key._sql, key._resultSetType.intValue(),key._resultSetConcurrency.intValue()), key, _pstmtPool, this);
 293  
                 } else {
 294  6
                     return new PoolableCallableStatement( getDelegate().prepareCall(
 295  
                         key._sql,key._resultSetType.intValue(), key._resultSetConcurrency.intValue()), key, _pstmtPool, this);
 296  
                 }
 297  
             }
 298  
         }
 299  
     }
 300  
 
 301  
     /**
 302  
      * {@link KeyedPoolableObjectFactory} method for destroying
 303  
      * PoolablePreparedStatements and PoolableCallableStatements.
 304  
      * Closes the underlying statement.
 305  
      * 
 306  
      * @param key ignored
 307  
      * @param obj the pooled statement to be destroyed.
 308  
      */
 309  
     public void destroyObject(Object key, Object obj) throws Exception {
 310  143
         if(obj instanceof DelegatingPreparedStatement) {
 311  143
             ((DelegatingPreparedStatement)obj).getInnermostDelegate().close();
 312  
         } else {
 313  0
             ((PreparedStatement)obj).close();
 314  
         }
 315  143
     }
 316  
 
 317  
     /**
 318  
      * {@link KeyedPoolableObjectFactory} method for validating
 319  
      * pooled statements. Currently always returns true.
 320  
      * 
 321  
      * @param key ignored
 322  
      * @param obj ignored
 323  
      * @return <tt>true</tt>
 324  
      */
 325  
     public boolean validateObject(Object key, Object obj) {
 326  5096
         return true;
 327  
     }
 328  
 
 329  
     /**
 330  
      * {@link KeyedPoolableObjectFactory} method for activating
 331  
      * pooled statements.
 332  
      * 
 333  
      * @param key ignored
 334  
      * @param obj pooled statement to be activated
 335  
      */
 336  
     public void activateObject(Object key, Object obj) throws Exception {
 337  3870
         ((DelegatingPreparedStatement)obj).activate();
 338  3870
     }
 339  
 
 340  
     /**
 341  
      * {@link KeyedPoolableObjectFactory} method for passivating
 342  
      * {@link PreparedStatement}s or {@link CallableStatement}s.
 343  
      * Invokes {@link PreparedStatement#clearParameters}.
 344  
      * 
 345  
      * @param key ignored
 346  
      * @param obj a {@link PreparedStatement}
 347  
      */
 348  
     public void passivateObject(Object key, Object obj) throws Exception {
 349  3856
         ((PreparedStatement)obj).clearParameters();
 350  3856
         ((DelegatingPreparedStatement)obj).passivate();
 351  3856
     }
 352  
 
 353  
     public String toString() {
 354  2
         if (_pstmtPool != null ) {
 355  0
             return "PoolingConnection: " + _pstmtPool.toString();
 356  
         } else {
 357  2
             return "PoolingConnection: null";
 358  
         }
 359  
     }
 360  
 
 361  
     /**
 362  
      * A key uniquely identifiying {@link PreparedStatement}s.
 363  
      */
 364  
     static class PStmtKey {
 365  
         
 366  
         /** SQL defining Prepared or Callable Statement */
 367  3872
         protected String _sql = null;
 368  
         
 369  
         /** Result set type */
 370  3872
         protected Integer _resultSetType = null;
 371  
         
 372  
         /** Result set concurrency */
 373  3872
         protected Integer _resultSetConcurrency = null;
 374  
         
 375  
         /** Database catalog */
 376  3872
         protected String _catalog = null;
 377  
         
 378  
         /** 
 379  
          *  Statement type. Either STATEMENT_PREPAREDSTMT (PreparedStatement)
 380  
          *  or STATEMENT_CALLABLESTMT (CallableStatement) 
 381  
          */
 382  3872
         protected byte _stmtType = STATEMENT_PREPAREDSTMT;
 383  
         
 384  0
         PStmtKey(String sql) {
 385  0
             _sql = sql;
 386  0
         }
 387  
 
 388  3770
         PStmtKey(String sql, String catalog) {
 389  3770
             _sql = sql;
 390  3770
             _catalog = catalog;
 391  3770
         }
 392  
         
 393  84
         PStmtKey(String sql, String catalog, byte stmtType) {
 394  84
             _sql = sql;
 395  84
             _catalog = catalog;
 396  84
             _stmtType = stmtType;
 397  84
         }
 398  
 
 399  0
         PStmtKey(String sql, int resultSetType, int resultSetConcurrency) {
 400  0
             _sql = sql;
 401  0
             _resultSetType = new Integer(resultSetType);
 402  0
             _resultSetConcurrency = new Integer(resultSetConcurrency);
 403  0
         }
 404  
 
 405  12
         PStmtKey(String sql, String catalog, int resultSetType, int resultSetConcurrency) {
 406  12
             _sql = sql;
 407  12
             _catalog = catalog;
 408  12
             _resultSetType = new Integer(resultSetType);
 409  12
             _resultSetConcurrency = new Integer(resultSetConcurrency);
 410  12
         }
 411  
         
 412  6
         PStmtKey(String sql, String catalog, int resultSetType, int resultSetConcurrency, byte stmtType) {
 413  6
             _sql = sql;
 414  6
             _catalog = catalog;
 415  6
             _resultSetType = new Integer(resultSetType);
 416  6
             _resultSetConcurrency = new Integer(resultSetConcurrency);
 417  6
             _stmtType = stmtType;
 418  6
         }
 419  
 
 420  
         public boolean equals(Object that) {
 421  
             try {
 422  4593
                 PStmtKey key = (PStmtKey)that;
 423  4593
                 return( ((null == _sql && null == key._sql) || _sql.equals(key._sql)) &&  
 424  
                         ((null == _catalog && null == key._catalog) || _catalog.equals(key._catalog)) &&
 425  
                         ((null == _resultSetType && null == key._resultSetType) || _resultSetType.equals(key._resultSetType)) &&
 426  
                         ((null == _resultSetConcurrency && null == key._resultSetConcurrency) || _resultSetConcurrency.equals(key._resultSetConcurrency)) &&
 427  
                         (_stmtType == key._stmtType)
 428  
                       );
 429  0
             } catch(ClassCastException e) {
 430  0
                 return false;
 431  62
             } catch(NullPointerException e) {
 432  62
                 return false;
 433  
             }
 434  
         }
 435  
 
 436  
         public int hashCode() {
 437  8433
             if (_catalog==null)
 438  5461
                 return(null == _sql ? 0 : _sql.hashCode());
 439  
             else
 440  2972
                 return(null == _sql ? _catalog.hashCode() : (_catalog + _sql).hashCode());
 441  
         }
 442  
 
 443  
         public String toString() {
 444  0
             StringBuffer buf = new StringBuffer();
 445  0
             buf.append("PStmtKey: sql=");
 446  0
             buf.append(_sql);
 447  0
             buf.append(", catalog=");
 448  0
             buf.append(_catalog);
 449  0
             buf.append(", resultSetType=");
 450  0
             buf.append(_resultSetType);
 451  0
             buf.append(", resultSetConcurrency=");
 452  0
             buf.append(_resultSetConcurrency);
 453  0
             buf.append(", statmentType=");
 454  0
             buf.append(_stmtType);
 455  0
             return buf.toString();
 456  
         }
 457  
     }
 458  
 }