001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.dbcp2; 018 019import java.sql.Connection; 020import java.sql.ResultSet; 021import java.sql.SQLException; 022import java.sql.SQLWarning; 023import java.sql.Statement; 024import java.util.List; 025 026/** 027 * A base delegating implementation of {@link Statement}. 028 * <p> 029 * All of the methods from the {@link Statement} interface 030 * simply check to see that the {@link Statement} is active, 031 * and call the corresponding method on the "delegate" 032 * provided in my constructor. 033 * <p> 034 * Extends AbandonedTrace to implement Statement tracking and 035 * logging of code which created the Statement. Tracking the 036 * Statement ensures that the Connection which created it can 037 * close any open Statement's on Connection close. 038 * 039 * @author Rodney Waldhoff 040 * @author Glenn L. Nielsen 041 * @author James House 042 * @author Dirk Verbeeck 043 * @version $Revision: 1572242 $ $Date: 2014-02-26 20:34:39 +0000 (Wed, 26 Feb 2014) $ 044 * @since 2.0 045 */ 046public class DelegatingStatement extends AbandonedTrace implements Statement { 047 /** My delegate. */ 048 private Statement _stmt = null; 049 /** The connection that created me. **/ 050 private DelegatingConnection<?> _conn = null; 051 052 /** 053 * Create a wrapper for the Statement which traces this 054 * Statement to the Connection which created it and the 055 * code which created it. 056 * 057 * @param s the {@link Statement} to delegate all calls to. 058 * @param c the {@link DelegatingConnection} that created this statement. 059 */ 060 public DelegatingStatement(DelegatingConnection<?> c, Statement s) { 061 super(c); 062 _stmt = s; 063 _conn = c; 064 } 065 066 /** 067 * Returns my underlying {@link Statement}. 068 * @return my underlying {@link Statement}. 069 * @see #getInnermostDelegate 070 */ 071 public Statement getDelegate() { 072 return _stmt; 073 } 074 075 076 /** 077 * If my underlying {@link Statement} is not a 078 * <tt>DelegatingStatement</tt>, returns it, 079 * otherwise recursively invokes this method on 080 * my delegate. 081 * <p> 082 * Hence this method will return the first 083 * delegate that is not a <tt>DelegatingStatement</tt> 084 * or <tt>null</tt> when no non-<tt>DelegatingStatement</tt> 085 * delegate can be found by transversing this chain. 086 * <p> 087 * This method is useful when you may have nested 088 * <tt>DelegatingStatement</tt>s, and you want to make 089 * sure to obtain a "genuine" {@link Statement}. 090 * @see #getDelegate 091 */ 092 public Statement getInnermostDelegate() { 093 Statement s = _stmt; 094 while(s != null && s instanceof DelegatingStatement) { 095 s = ((DelegatingStatement)s).getDelegate(); 096 if(this == s) { 097 return null; 098 } 099 } 100 return s; 101 } 102 103 /** Sets my delegate. */ 104 public void setDelegate(Statement s) { 105 _stmt = s; 106 } 107 108 private boolean _closed = false; 109 110 protected boolean isClosedInternal() { 111 return _closed; 112 } 113 114 protected void setClosedInternal(boolean closed) { 115 this._closed = closed; 116 } 117 118 protected void checkOpen() throws SQLException { 119 if(isClosed()) { 120 throw new SQLException 121 (this.getClass().getName() + " with address: \"" + 122 this.toString() + "\" is closed."); 123 } 124 } 125 126 /** 127 * Close this DelegatingStatement, and close 128 * any ResultSets that were not explicitly closed. 129 */ 130 @Override 131 public void close() throws SQLException { 132 try { 133 try { 134 if (_conn != null) { 135 _conn.removeTrace(this); 136 _conn = null; 137 } 138 139 // The JDBC spec requires that a statment close any open 140 // ResultSet's when it is closed. 141 // FIXME The PreparedStatement we're wrapping should handle this for us. 142 // See bug 17301 for what could happen when ResultSets are closed twice. 143 List<AbandonedTrace> resultSets = getTrace(); 144 if( resultSets != null) { 145 ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]); 146 for (ResultSet element : set) { 147 element.close(); 148 } 149 clearTrace(); 150 } 151 152 if (_stmt != null) { 153 _stmt.close(); 154 } 155 } 156 catch (SQLException e) { 157 handleException(e); 158 } 159 } 160 finally { 161 _closed = true; 162 } 163 } 164 165 protected void handleException(SQLException e) throws SQLException { 166 if (_conn != null) { 167 _conn.handleException(e); 168 } 169 else { 170 throw e; 171 } 172 } 173 174 protected void activate() throws SQLException { 175 if(_stmt instanceof DelegatingStatement) { 176 ((DelegatingStatement)_stmt).activate(); 177 } 178 } 179 180 protected void passivate() throws SQLException { 181 if(_stmt instanceof DelegatingStatement) { 182 ((DelegatingStatement)_stmt).passivate(); 183 } 184 } 185 186 @Override 187 public Connection getConnection() throws SQLException { 188 checkOpen(); 189 return getConnectionInternal(); // return the delegating connection that created this 190 } 191 192 protected DelegatingConnection<?> getConnectionInternal() { 193 return _conn; 194 } 195 196 @Override 197 public ResultSet executeQuery(String sql) throws SQLException { 198 checkOpen(); 199 if (_conn != null) { 200 _conn.setLastUsed(); 201 } 202 try { 203 return DelegatingResultSet.wrapResultSet(this,_stmt.executeQuery(sql)); 204 } 205 catch (SQLException e) { 206 handleException(e); 207 throw new AssertionError(); 208 } 209 } 210 211 @Override 212 public ResultSet getResultSet() throws SQLException { 213 checkOpen(); 214 try { 215 return DelegatingResultSet.wrapResultSet(this,_stmt.getResultSet()); 216 } 217 catch (SQLException e) { 218 handleException(e); 219 throw new AssertionError(); 220 } 221 } 222 223 @Override 224 public int executeUpdate(String sql) throws SQLException { 225 checkOpen(); 226 if (_conn != null) { 227 _conn.setLastUsed(); 228 } 229 try { 230 return _stmt.executeUpdate(sql); 231 } catch (SQLException e) { 232 handleException(e); return 0; 233 } 234 } 235 236 @Override 237 public int getMaxFieldSize() throws SQLException 238 { checkOpen(); try { return _stmt.getMaxFieldSize(); } catch (SQLException e) { handleException(e); return 0; } } 239 240 @Override 241 public void setMaxFieldSize(int max) throws SQLException 242 { checkOpen(); try { _stmt.setMaxFieldSize(max); } catch (SQLException e) { handleException(e); } } 243 244 @Override 245 public int getMaxRows() throws SQLException 246 { checkOpen(); try { return _stmt.getMaxRows(); } catch (SQLException e) { handleException(e); return 0; } } 247 248 @Override 249 public void setMaxRows(int max) throws SQLException 250 { checkOpen(); try { _stmt.setMaxRows(max); } catch (SQLException e) { handleException(e); } } 251 252 @Override 253 public void setEscapeProcessing(boolean enable) throws SQLException 254 { checkOpen(); try { _stmt.setEscapeProcessing(enable); } catch (SQLException e) { handleException(e); } } 255 256 @Override 257 public int getQueryTimeout() throws SQLException 258 { checkOpen(); try { return _stmt.getQueryTimeout(); } catch (SQLException e) { handleException(e); return 0; } } 259 260 @Override 261 public void setQueryTimeout(int seconds) throws SQLException 262 { checkOpen(); try { _stmt.setQueryTimeout(seconds); } catch (SQLException e) { handleException(e); } } 263 264 @Override 265 public void cancel() throws SQLException 266 { checkOpen(); try { _stmt.cancel(); } catch (SQLException e) { handleException(e); } } 267 268 @Override 269 public SQLWarning getWarnings() throws SQLException 270 { checkOpen(); try { return _stmt.getWarnings(); } catch (SQLException e) { handleException(e); throw new AssertionError(); } } 271 272 @Override 273 public void clearWarnings() throws SQLException 274 { checkOpen(); try { _stmt.clearWarnings(); } catch (SQLException e) { handleException(e); } } 275 276 @Override 277 public void setCursorName(String name) throws SQLException 278 { checkOpen(); try { _stmt.setCursorName(name); } catch (SQLException e) { handleException(e); } } 279 280 @Override 281 public boolean execute(String sql) throws SQLException { 282 checkOpen(); 283 if (_conn != null) { 284 _conn.setLastUsed(); 285 } 286 try { 287 return _stmt.execute(sql); 288 } catch (SQLException e) { 289 handleException(e); 290 return false; 291 } 292 } 293 294 @Override 295 public int getUpdateCount() throws SQLException 296 { checkOpen(); try { return _stmt.getUpdateCount(); } catch (SQLException e) { handleException(e); return 0; } } 297 298 @Override 299 public boolean getMoreResults() throws SQLException 300 { checkOpen(); try { return _stmt.getMoreResults(); } catch (SQLException e) { handleException(e); return false; } } 301 302 @Override 303 public void setFetchDirection(int direction) throws SQLException 304 { checkOpen(); try { _stmt.setFetchDirection(direction); } catch (SQLException e) { handleException(e); } } 305 306 @Override 307 public int getFetchDirection() throws SQLException 308 { checkOpen(); try { return _stmt.getFetchDirection(); } catch (SQLException e) { handleException(e); return 0; } } 309 310 @Override 311 public void setFetchSize(int rows) throws SQLException 312 { checkOpen(); try { _stmt.setFetchSize(rows); } catch (SQLException e) { handleException(e); } } 313 314 @Override 315 public int getFetchSize() throws SQLException 316 { checkOpen(); try { return _stmt.getFetchSize(); } catch (SQLException e) { handleException(e); return 0; } } 317 318 @Override 319 public int getResultSetConcurrency() throws SQLException 320 { checkOpen(); try { return _stmt.getResultSetConcurrency(); } catch (SQLException e) { handleException(e); return 0; } } 321 322 @Override 323 public int getResultSetType() throws SQLException 324 { checkOpen(); try { return _stmt.getResultSetType(); } catch (SQLException e) { handleException(e); return 0; } } 325 326 @Override 327 public void addBatch(String sql) throws SQLException 328 { checkOpen(); try { _stmt.addBatch(sql); } catch (SQLException e) { handleException(e); } } 329 330 @Override 331 public void clearBatch() throws SQLException 332 { checkOpen(); try { _stmt.clearBatch(); } catch (SQLException e) { handleException(e); } } 333 334 @Override 335 public int[] executeBatch() throws SQLException { 336 checkOpen(); 337 if (_conn != null) { 338 _conn.setLastUsed(); 339 } 340 try { 341 return _stmt.executeBatch(); 342 } catch (SQLException e) { 343 handleException(e); 344 throw new AssertionError(); 345 } 346 } 347 348 /** 349 * Returns a String representation of this object. 350 * 351 * @return String 352 */ 353 @Override 354 public String toString() { 355 return _stmt.toString(); 356 } 357 358 @Override 359 public boolean getMoreResults(int current) throws SQLException 360 { checkOpen(); try { return _stmt.getMoreResults(current); } catch (SQLException e) { handleException(e); return false; } } 361 362 @Override 363 public ResultSet getGeneratedKeys() throws SQLException { 364 checkOpen(); 365 try { 366 return DelegatingResultSet.wrapResultSet(this, _stmt.getGeneratedKeys()); 367 } catch (SQLException e) { 368 handleException(e); 369 throw new AssertionError(); 370 } 371 } 372 373 @Override 374 public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { 375 checkOpen(); 376 if (_conn != null) { 377 _conn.setLastUsed(); 378 } 379 try { 380 return _stmt.executeUpdate(sql, autoGeneratedKeys); 381 } catch (SQLException e) { 382 handleException(e); 383 return 0; 384 } 385 } 386 387 @Override 388 public int executeUpdate(String sql, int columnIndexes[]) throws SQLException { 389 checkOpen(); 390 if (_conn != null) { 391 _conn.setLastUsed(); 392 } 393 try { 394 return _stmt.executeUpdate(sql, columnIndexes); 395 } catch (SQLException e) { 396 handleException(e); 397 return 0; 398 } 399 } 400 401 @Override 402 public int executeUpdate(String sql, String columnNames[]) throws SQLException { 403 checkOpen(); 404 if (_conn != null) { 405 _conn.setLastUsed(); 406 } 407 try { 408 return _stmt.executeUpdate(sql, columnNames); 409 } catch (SQLException e) { 410 handleException(e); 411 return 0; 412 } 413 } 414 415 @Override 416 public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { 417 checkOpen(); 418 if (_conn != null) { 419 _conn.setLastUsed(); 420 } 421 try { 422 return _stmt.execute(sql, autoGeneratedKeys); 423 } catch (SQLException e) { 424 handleException(e); 425 return false; 426 } 427 } 428 429 @Override 430 public boolean execute(String sql, int columnIndexes[]) throws SQLException { 431 checkOpen(); 432 if (_conn != null) { 433 _conn.setLastUsed(); 434 } 435 try { 436 return _stmt.execute(sql, columnIndexes); 437 } catch (SQLException e) { 438 handleException(e); 439 return false; 440 } 441 } 442 443 @Override 444 public boolean execute(String sql, String columnNames[]) throws SQLException { 445 checkOpen(); 446 if (_conn != null) { 447 _conn.setLastUsed(); 448 } 449 try { 450 return _stmt.execute(sql, columnNames); 451 } catch (SQLException e) { 452 handleException(e); 453 return false; 454 } 455 } 456 457 @Override 458 public int getResultSetHoldability() throws SQLException 459 { checkOpen(); try { return _stmt.getResultSetHoldability(); } catch (SQLException e) { handleException(e); return 0; } } 460 461 /* 462 * Note was protected prior to JDBC 4 463 */ 464 @Override 465 public boolean isClosed() throws SQLException { 466 return _closed; 467 } 468 469 470 @Override 471 public boolean isWrapperFor(Class<?> iface) throws SQLException { 472 if (iface.isAssignableFrom(getClass())) { 473 return true; 474 } else if (iface.isAssignableFrom(_stmt.getClass())) { 475 return true; 476 } else { 477 return _stmt.isWrapperFor(iface); 478 } 479 } 480 481 @Override 482 public <T> T unwrap(Class<T> iface) throws SQLException { 483 if (iface.isAssignableFrom(getClass())) { 484 return iface.cast(this); 485 } else if (iface.isAssignableFrom(_stmt.getClass())) { 486 return iface.cast(_stmt); 487 } else { 488 return _stmt.unwrap(iface); 489 } 490 } 491 492 @Override 493 public void setPoolable(boolean poolable) throws SQLException { 494 checkOpen(); 495 try { 496 _stmt.setPoolable(poolable); 497 } 498 catch (SQLException e) { 499 handleException(e); 500 } 501 } 502 503 @Override 504 public boolean isPoolable() throws SQLException { 505 checkOpen(); 506 try { 507 return _stmt.isPoolable(); 508 } 509 catch (SQLException e) { 510 handleException(e); 511 return false; 512 } 513 } 514 515 @Override 516 public void closeOnCompletion() throws SQLException { 517 checkOpen(); 518 try { 519 _stmt.closeOnCompletion(); 520 } catch (SQLException e) { 521 handleException(e); 522 } 523 } 524 525 @Override 526 public boolean isCloseOnCompletion() throws SQLException { 527 checkOpen(); 528 try { 529 return _stmt.isCloseOnCompletion(); 530 } catch (SQLException e) { 531 handleException(e); 532 return false; 533 } 534 } 535 536 @Override 537 protected void finalize() throws Throwable { 538 // This is required because of statement pooling. The poolable 539 // statements will always be strongly held by the statement pool. If the 540 // delegating statements that wrap the poolable statement are not 541 // strongly held they will be garbage collected but at that point the 542 // poolable statements need to be returned to the pool else there will 543 // be a leak of statements from the pool. Closing this statement will 544 // close all the wrapped statements and return any poolable statements 545 // to the pool. 546 close(); 547 super.finalize(); 548 } 549}