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 $Id: DelegatingStatement.java 1658644 2015-02-10 08:59:07Z tn $ 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 * {@code DelegatingStatement}, 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 {@code DelegatingStatement} 084 * or {@code null} when no non-{@code DelegatingStatement} 085 * delegate can be found by transversing this chain. 086 * <p> 087 * This method is useful when you may have nested 088 * {@code DelegatingStatement}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 if (isClosed()) { 133 return; 134 } 135 try { 136 try { 137 if (_conn != null) { 138 _conn.removeTrace(this); 139 _conn = null; 140 } 141 142 // The JDBC spec requires that a statement close any open 143 // ResultSet's when it is closed. 144 // FIXME The PreparedStatement we're wrapping should handle this for us. 145 // See bug 17301 for what could happen when ResultSets are closed twice. 146 List<AbandonedTrace> resultSets = getTrace(); 147 if( resultSets != null) { 148 ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]); 149 for (ResultSet element : set) { 150 element.close(); 151 } 152 clearTrace(); 153 } 154 155 if (_stmt != null) { 156 _stmt.close(); 157 } 158 } 159 catch (SQLException e) { 160 handleException(e); 161 } 162 } 163 finally { 164 _closed = true; 165 _stmt = null; 166 } 167 } 168 169 protected void handleException(SQLException e) throws SQLException { 170 if (_conn != null) { 171 _conn.handleException(e); 172 } 173 else { 174 throw e; 175 } 176 } 177 178 protected void activate() throws SQLException { 179 if(_stmt instanceof DelegatingStatement) { 180 ((DelegatingStatement)_stmt).activate(); 181 } 182 } 183 184 protected void passivate() throws SQLException { 185 if(_stmt instanceof DelegatingStatement) { 186 ((DelegatingStatement)_stmt).passivate(); 187 } 188 } 189 190 @Override 191 public Connection getConnection() throws SQLException { 192 checkOpen(); 193 return getConnectionInternal(); // return the delegating connection that created this 194 } 195 196 protected DelegatingConnection<?> getConnectionInternal() { 197 return _conn; 198 } 199 200 @Override 201 public ResultSet executeQuery(String sql) throws SQLException { 202 checkOpen(); 203 if (_conn != null) { 204 _conn.setLastUsed(); 205 } 206 try { 207 return DelegatingResultSet.wrapResultSet(this,_stmt.executeQuery(sql)); 208 } 209 catch (SQLException e) { 210 handleException(e); 211 throw new AssertionError(); 212 } 213 } 214 215 @Override 216 public ResultSet getResultSet() throws SQLException { 217 checkOpen(); 218 try { 219 return DelegatingResultSet.wrapResultSet(this,_stmt.getResultSet()); 220 } 221 catch (SQLException e) { 222 handleException(e); 223 throw new AssertionError(); 224 } 225 } 226 227 @Override 228 public int executeUpdate(String sql) throws SQLException { 229 checkOpen(); 230 if (_conn != null) { 231 _conn.setLastUsed(); 232 } 233 try { 234 return _stmt.executeUpdate(sql); 235 } catch (SQLException e) { 236 handleException(e); return 0; 237 } 238 } 239 240 @Override 241 public int getMaxFieldSize() throws SQLException 242 { checkOpen(); try { return _stmt.getMaxFieldSize(); } catch (SQLException e) { handleException(e); return 0; } } 243 244 @Override 245 public void setMaxFieldSize(int max) throws SQLException 246 { checkOpen(); try { _stmt.setMaxFieldSize(max); } catch (SQLException e) { handleException(e); } } 247 248 @Override 249 public int getMaxRows() throws SQLException 250 { checkOpen(); try { return _stmt.getMaxRows(); } catch (SQLException e) { handleException(e); return 0; } } 251 252 @Override 253 public void setMaxRows(int max) throws SQLException 254 { checkOpen(); try { _stmt.setMaxRows(max); } catch (SQLException e) { handleException(e); } } 255 256 @Override 257 public void setEscapeProcessing(boolean enable) throws SQLException 258 { checkOpen(); try { _stmt.setEscapeProcessing(enable); } catch (SQLException e) { handleException(e); } } 259 260 @Override 261 public int getQueryTimeout() throws SQLException 262 { checkOpen(); try { return _stmt.getQueryTimeout(); } catch (SQLException e) { handleException(e); return 0; } } 263 264 @Override 265 public void setQueryTimeout(int seconds) throws SQLException 266 { checkOpen(); try { _stmt.setQueryTimeout(seconds); } catch (SQLException e) { handleException(e); } } 267 268 @Override 269 public void cancel() throws SQLException 270 { checkOpen(); try { _stmt.cancel(); } catch (SQLException e) { handleException(e); } } 271 272 @Override 273 public SQLWarning getWarnings() throws SQLException 274 { checkOpen(); try { return _stmt.getWarnings(); } catch (SQLException e) { handleException(e); throw new AssertionError(); } } 275 276 @Override 277 public void clearWarnings() throws SQLException 278 { checkOpen(); try { _stmt.clearWarnings(); } catch (SQLException e) { handleException(e); } } 279 280 @Override 281 public void setCursorName(String name) throws SQLException 282 { checkOpen(); try { _stmt.setCursorName(name); } catch (SQLException e) { handleException(e); } } 283 284 @Override 285 public boolean execute(String sql) throws SQLException { 286 checkOpen(); 287 if (_conn != null) { 288 _conn.setLastUsed(); 289 } 290 try { 291 return _stmt.execute(sql); 292 } catch (SQLException e) { 293 handleException(e); 294 return false; 295 } 296 } 297 298 @Override 299 public int getUpdateCount() throws SQLException 300 { checkOpen(); try { return _stmt.getUpdateCount(); } catch (SQLException e) { handleException(e); return 0; } } 301 302 @Override 303 public boolean getMoreResults() throws SQLException 304 { checkOpen(); try { return _stmt.getMoreResults(); } catch (SQLException e) { handleException(e); return false; } } 305 306 @Override 307 public void setFetchDirection(int direction) throws SQLException 308 { checkOpen(); try { _stmt.setFetchDirection(direction); } catch (SQLException e) { handleException(e); } } 309 310 @Override 311 public int getFetchDirection() throws SQLException 312 { checkOpen(); try { return _stmt.getFetchDirection(); } catch (SQLException e) { handleException(e); return 0; } } 313 314 @Override 315 public void setFetchSize(int rows) throws SQLException 316 { checkOpen(); try { _stmt.setFetchSize(rows); } catch (SQLException e) { handleException(e); } } 317 318 @Override 319 public int getFetchSize() throws SQLException 320 { checkOpen(); try { return _stmt.getFetchSize(); } catch (SQLException e) { handleException(e); return 0; } } 321 322 @Override 323 public int getResultSetConcurrency() throws SQLException 324 { checkOpen(); try { return _stmt.getResultSetConcurrency(); } catch (SQLException e) { handleException(e); return 0; } } 325 326 @Override 327 public int getResultSetType() throws SQLException 328 { checkOpen(); try { return _stmt.getResultSetType(); } catch (SQLException e) { handleException(e); return 0; } } 329 330 @Override 331 public void addBatch(String sql) throws SQLException 332 { checkOpen(); try { _stmt.addBatch(sql); } catch (SQLException e) { handleException(e); } } 333 334 @Override 335 public void clearBatch() throws SQLException 336 { checkOpen(); try { _stmt.clearBatch(); } catch (SQLException e) { handleException(e); } } 337 338 @Override 339 public int[] executeBatch() throws SQLException { 340 checkOpen(); 341 if (_conn != null) { 342 _conn.setLastUsed(); 343 } 344 try { 345 return _stmt.executeBatch(); 346 } catch (SQLException e) { 347 handleException(e); 348 throw new AssertionError(); 349 } 350 } 351 352 /** 353 * Returns a String representation of this object. 354 * 355 * @return String 356 */ 357 @Override 358 public String toString() { 359 return _stmt == null ? "NULL" : _stmt.toString(); 360 } 361 362 @Override 363 public boolean getMoreResults(int current) throws SQLException 364 { checkOpen(); try { return _stmt.getMoreResults(current); } catch (SQLException e) { handleException(e); return false; } } 365 366 @Override 367 public ResultSet getGeneratedKeys() throws SQLException { 368 checkOpen(); 369 try { 370 return DelegatingResultSet.wrapResultSet(this, _stmt.getGeneratedKeys()); 371 } catch (SQLException e) { 372 handleException(e); 373 throw new AssertionError(); 374 } 375 } 376 377 @Override 378 public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { 379 checkOpen(); 380 if (_conn != null) { 381 _conn.setLastUsed(); 382 } 383 try { 384 return _stmt.executeUpdate(sql, autoGeneratedKeys); 385 } catch (SQLException e) { 386 handleException(e); 387 return 0; 388 } 389 } 390 391 @Override 392 public int executeUpdate(String sql, int columnIndexes[]) throws SQLException { 393 checkOpen(); 394 if (_conn != null) { 395 _conn.setLastUsed(); 396 } 397 try { 398 return _stmt.executeUpdate(sql, columnIndexes); 399 } catch (SQLException e) { 400 handleException(e); 401 return 0; 402 } 403 } 404 405 @Override 406 public int executeUpdate(String sql, String columnNames[]) throws SQLException { 407 checkOpen(); 408 if (_conn != null) { 409 _conn.setLastUsed(); 410 } 411 try { 412 return _stmt.executeUpdate(sql, columnNames); 413 } catch (SQLException e) { 414 handleException(e); 415 return 0; 416 } 417 } 418 419 @Override 420 public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { 421 checkOpen(); 422 if (_conn != null) { 423 _conn.setLastUsed(); 424 } 425 try { 426 return _stmt.execute(sql, autoGeneratedKeys); 427 } catch (SQLException e) { 428 handleException(e); 429 return false; 430 } 431 } 432 433 @Override 434 public boolean execute(String sql, int columnIndexes[]) throws SQLException { 435 checkOpen(); 436 if (_conn != null) { 437 _conn.setLastUsed(); 438 } 439 try { 440 return _stmt.execute(sql, columnIndexes); 441 } catch (SQLException e) { 442 handleException(e); 443 return false; 444 } 445 } 446 447 @Override 448 public boolean execute(String sql, String columnNames[]) throws SQLException { 449 checkOpen(); 450 if (_conn != null) { 451 _conn.setLastUsed(); 452 } 453 try { 454 return _stmt.execute(sql, columnNames); 455 } catch (SQLException e) { 456 handleException(e); 457 return false; 458 } 459 } 460 461 @Override 462 public int getResultSetHoldability() throws SQLException 463 { checkOpen(); try { return _stmt.getResultSetHoldability(); } catch (SQLException e) { handleException(e); return 0; } } 464 465 /* 466 * Note was protected prior to JDBC 4 467 */ 468 @Override 469 public boolean isClosed() throws SQLException { 470 return _closed; 471 } 472 473 474 @Override 475 public boolean isWrapperFor(Class<?> iface) throws SQLException { 476 if (iface.isAssignableFrom(getClass())) { 477 return true; 478 } else if (iface.isAssignableFrom(_stmt.getClass())) { 479 return true; 480 } else { 481 return _stmt.isWrapperFor(iface); 482 } 483 } 484 485 @Override 486 public <T> T unwrap(Class<T> iface) throws SQLException { 487 if (iface.isAssignableFrom(getClass())) { 488 return iface.cast(this); 489 } else if (iface.isAssignableFrom(_stmt.getClass())) { 490 return iface.cast(_stmt); 491 } else { 492 return _stmt.unwrap(iface); 493 } 494 } 495 496 @Override 497 public void setPoolable(boolean poolable) throws SQLException { 498 checkOpen(); 499 try { 500 _stmt.setPoolable(poolable); 501 } 502 catch (SQLException e) { 503 handleException(e); 504 } 505 } 506 507 @Override 508 public boolean isPoolable() throws SQLException { 509 checkOpen(); 510 try { 511 return _stmt.isPoolable(); 512 } 513 catch (SQLException e) { 514 handleException(e); 515 return false; 516 } 517 } 518 519 @Override 520 public void closeOnCompletion() throws SQLException { 521 checkOpen(); 522 try { 523 _stmt.closeOnCompletion(); 524 } catch (SQLException e) { 525 handleException(e); 526 } 527 } 528 529 @Override 530 public boolean isCloseOnCompletion() throws SQLException { 531 checkOpen(); 532 try { 533 return _stmt.isCloseOnCompletion(); 534 } catch (SQLException e) { 535 handleException(e); 536 return false; 537 } 538 } 539 540 @Override 541 protected void finalize() throws Throwable { 542 // This is required because of statement pooling. The poolable 543 // statements will always be strongly held by the statement pool. If the 544 // delegating statements that wrap the poolable statement are not 545 // strongly held they will be garbage collected but at that point the 546 // poolable statements need to be returned to the pool else there will 547 // be a leak of statements from the pool. Closing this statement will 548 // close all the wrapped statements and return any poolable statements 549 // to the pool. 550 close(); 551 super.finalize(); 552 } 553}