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 simply check to see that the {@link Statement} is active, and 030 * call the corresponding method on the "delegate" provided in my constructor. 031 * <p> 032 * Extends AbandonedTrace to implement Statement tracking and logging of code which created the Statement. Tracking the 033 * Statement ensures that the Connection which created it can close any open Statement's on Connection close. 034 * 035 * @since 2.0 036 */ 037public class DelegatingStatement extends AbandonedTrace implements Statement { 038 039 /** My delegate. */ 040 private Statement statement; 041 042 /** The connection that created me. **/ 043 private DelegatingConnection<?> connection; 044 045 /** 046 * Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code 047 * which created it. 048 * 049 * @param statement 050 * the {@link Statement} to delegate all calls to. 051 * @param connection 052 * the {@link DelegatingConnection} that created this statement. 053 */ 054 public DelegatingStatement(final DelegatingConnection<?> connection, final Statement statement) { 055 super(connection); 056 this.statement = statement; 057 this.connection = connection; 058 } 059 060 /** 061 * Returns my underlying {@link Statement}. 062 * 063 * @return my underlying {@link Statement}. 064 * @see #getInnermostDelegate 065 */ 066 public Statement getDelegate() { 067 return statement; 068 } 069 070 /** 071 * If my underlying {@link Statement} is not a {@code DelegatingStatement}, returns it, otherwise recursively 072 * invokes this method on my delegate. 073 * <p> 074 * Hence this method will return the first delegate that is not a {@code DelegatingStatement} or {@code null} when 075 * no non-{@code DelegatingStatement} delegate can be found by traversing this chain. 076 * </p> 077 * <p> 078 * This method is useful when you may have nested {@code DelegatingStatement}s, and you want to make sure to obtain 079 * a "genuine" {@link Statement}. 080 * </p> 081 * 082 * @return The innermost delegate. 083 * 084 * @see #getDelegate 085 */ 086 public Statement getInnermostDelegate() { 087 Statement s = statement; 088 while (s != null && s instanceof DelegatingStatement) { 089 s = ((DelegatingStatement) s).getDelegate(); 090 if (this == s) { 091 return null; 092 } 093 } 094 return s; 095 } 096 097 /** 098 * Sets my delegate. 099 * 100 * @param statement 101 * my delegate. 102 */ 103 public void setDelegate(final Statement statement) { 104 this.statement = statement; 105 } 106 107 private boolean closed = false; 108 109 protected boolean isClosedInternal() { 110 return closed; 111 } 112 113 protected void setClosedInternal(final boolean closed) { 114 this.closed = closed; 115 } 116 117 protected void checkOpen() throws SQLException { 118 if (isClosed()) { 119 throw new SQLException(this.getClass().getName() + " with address: \"" + this.toString() + "\" is closed."); 120 } 121 } 122 123 /** 124 * Close this DelegatingStatement, and close any ResultSets that were not explicitly closed. 125 */ 126 @Override 127 public void close() throws SQLException { 128 if (isClosed()) { 129 return; 130 } 131 try { 132 try { 133 if (connection != null) { 134 connection.removeTrace(this); 135 connection = null; 136 } 137 138 // The JDBC spec requires that a statement close any open 139 // ResultSet's when it is closed. 140 // FIXME The PreparedStatement we're wrapping should handle this for us. 141 // See bug 17301 for what could happen when ResultSets are closed twice. 142 final List<AbandonedTrace> resultSets = getTrace(); 143 if (resultSets != null) { 144 final ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]); 145 for (final ResultSet element : set) { 146 element.close(); 147 } 148 clearTrace(); 149 } 150 151 if (statement != null) { 152 statement.close(); 153 } 154 } catch (final SQLException e) { 155 handleException(e); 156 } 157 } finally { 158 closed = true; 159 statement = null; 160 } 161 } 162 163 protected void handleException(final SQLException e) throws SQLException { 164 if (connection != null) { 165 connection.handleException(e); 166 } else { 167 throw e; 168 } 169 } 170 171 /** 172 * 173 * @throws SQLException 174 * thrown by the delegating statement. 175 * @since 2.4.0 made public, was protected in 2.3.0. 176 */ 177 public void activate() throws SQLException { 178 if (statement instanceof DelegatingStatement) { 179 ((DelegatingStatement) statement).activate(); 180 } 181 } 182 183 /** 184 * 185 * @throws SQLException 186 * thrown by the delegating statement. 187 * @since 2.4.0 made public, was protected in 2.3.0. 188 */ 189 public void passivate() throws SQLException { 190 if (statement instanceof DelegatingStatement) { 191 ((DelegatingStatement) statement).passivate(); 192 } 193 } 194 195 @Override 196 public Connection getConnection() throws SQLException { 197 checkOpen(); 198 return getConnectionInternal(); // return the delegating connection that created this 199 } 200 201 protected DelegatingConnection<?> getConnectionInternal() { 202 return connection; 203 } 204 205 @Override 206 public ResultSet executeQuery(final String sql) throws SQLException { 207 checkOpen(); 208 if (connection != null) { 209 connection.setLastUsed(); 210 } 211 try { 212 return DelegatingResultSet.wrapResultSet(this, statement.executeQuery(sql)); 213 } catch (final SQLException e) { 214 handleException(e); 215 throw new AssertionError(); 216 } 217 } 218 219 @Override 220 public ResultSet getResultSet() throws SQLException { 221 checkOpen(); 222 try { 223 return DelegatingResultSet.wrapResultSet(this, statement.getResultSet()); 224 } catch (final SQLException e) { 225 handleException(e); 226 throw new AssertionError(); 227 } 228 } 229 230 @Override 231 public int executeUpdate(final String sql) throws SQLException { 232 checkOpen(); 233 if (connection != null) { 234 connection.setLastUsed(); 235 } 236 try { 237 return statement.executeUpdate(sql); 238 } catch (final SQLException e) { 239 handleException(e); 240 return 0; 241 } 242 } 243 244 @Override 245 public int getMaxFieldSize() throws SQLException { 246 checkOpen(); 247 try { 248 return statement.getMaxFieldSize(); 249 } catch (final SQLException e) { 250 handleException(e); 251 return 0; 252 } 253 } 254 255 @Override 256 public void setMaxFieldSize(final int max) throws SQLException { 257 checkOpen(); 258 try { 259 statement.setMaxFieldSize(max); 260 } catch (final SQLException e) { 261 handleException(e); 262 } 263 } 264 265 @Override 266 public int getMaxRows() throws SQLException { 267 checkOpen(); 268 try { 269 return statement.getMaxRows(); 270 } catch (final SQLException e) { 271 handleException(e); 272 return 0; 273 } 274 } 275 276 @Override 277 public void setMaxRows(final int max) throws SQLException { 278 checkOpen(); 279 try { 280 statement.setMaxRows(max); 281 } catch (final SQLException e) { 282 handleException(e); 283 } 284 } 285 286 @Override 287 public void setEscapeProcessing(final boolean enable) throws SQLException { 288 checkOpen(); 289 try { 290 statement.setEscapeProcessing(enable); 291 } catch (final SQLException e) { 292 handleException(e); 293 } 294 } 295 296 @Override 297 public int getQueryTimeout() throws SQLException { 298 checkOpen(); 299 try { 300 return statement.getQueryTimeout(); 301 } catch (final SQLException e) { 302 handleException(e); 303 return 0; 304 } 305 } 306 307 @Override 308 public void setQueryTimeout(final int seconds) throws SQLException { 309 checkOpen(); 310 try { 311 statement.setQueryTimeout(seconds); 312 } catch (final SQLException e) { 313 handleException(e); 314 } 315 } 316 317 @Override 318 public void cancel() throws SQLException { 319 checkOpen(); 320 try { 321 statement.cancel(); 322 } catch (final SQLException e) { 323 handleException(e); 324 } 325 } 326 327 @Override 328 public SQLWarning getWarnings() throws SQLException { 329 checkOpen(); 330 try { 331 return statement.getWarnings(); 332 } catch (final SQLException e) { 333 handleException(e); 334 throw new AssertionError(); 335 } 336 } 337 338 @Override 339 public void clearWarnings() throws SQLException { 340 checkOpen(); 341 try { 342 statement.clearWarnings(); 343 } catch (final SQLException e) { 344 handleException(e); 345 } 346 } 347 348 @Override 349 public void setCursorName(final String name) throws SQLException { 350 checkOpen(); 351 try { 352 statement.setCursorName(name); 353 } catch (final SQLException e) { 354 handleException(e); 355 } 356 } 357 358 @Override 359 public boolean execute(final String sql) throws SQLException { 360 checkOpen(); 361 if (connection != null) { 362 connection.setLastUsed(); 363 } 364 try { 365 return statement.execute(sql); 366 } catch (final SQLException e) { 367 handleException(e); 368 return false; 369 } 370 } 371 372 @Override 373 public int getUpdateCount() throws SQLException { 374 checkOpen(); 375 try { 376 return statement.getUpdateCount(); 377 } catch (final SQLException e) { 378 handleException(e); 379 return 0; 380 } 381 } 382 383 @Override 384 public boolean getMoreResults() throws SQLException { 385 checkOpen(); 386 try { 387 return statement.getMoreResults(); 388 } catch (final SQLException e) { 389 handleException(e); 390 return false; 391 } 392 } 393 394 @Override 395 public void setFetchDirection(final int direction) throws SQLException { 396 checkOpen(); 397 try { 398 statement.setFetchDirection(direction); 399 } catch (final SQLException e) { 400 handleException(e); 401 } 402 } 403 404 @Override 405 public int getFetchDirection() throws SQLException { 406 checkOpen(); 407 try { 408 return statement.getFetchDirection(); 409 } catch (final SQLException e) { 410 handleException(e); 411 return 0; 412 } 413 } 414 415 @Override 416 public void setFetchSize(final int rows) throws SQLException { 417 checkOpen(); 418 try { 419 statement.setFetchSize(rows); 420 } catch (final SQLException e) { 421 handleException(e); 422 } 423 } 424 425 @Override 426 public int getFetchSize() throws SQLException { 427 checkOpen(); 428 try { 429 return statement.getFetchSize(); 430 } catch (final SQLException e) { 431 handleException(e); 432 return 0; 433 } 434 } 435 436 @Override 437 public int getResultSetConcurrency() throws SQLException { 438 checkOpen(); 439 try { 440 return statement.getResultSetConcurrency(); 441 } catch (final SQLException e) { 442 handleException(e); 443 return 0; 444 } 445 } 446 447 @Override 448 public int getResultSetType() throws SQLException { 449 checkOpen(); 450 try { 451 return statement.getResultSetType(); 452 } catch (final SQLException e) { 453 handleException(e); 454 return 0; 455 } 456 } 457 458 @Override 459 public void addBatch(final String sql) throws SQLException { 460 checkOpen(); 461 try { 462 statement.addBatch(sql); 463 } catch (final SQLException e) { 464 handleException(e); 465 } 466 } 467 468 @Override 469 public void clearBatch() throws SQLException { 470 checkOpen(); 471 try { 472 statement.clearBatch(); 473 } catch (final SQLException e) { 474 handleException(e); 475 } 476 } 477 478 @Override 479 public int[] executeBatch() throws SQLException { 480 checkOpen(); 481 if (connection != null) { 482 connection.setLastUsed(); 483 } 484 try { 485 return statement.executeBatch(); 486 } catch (final SQLException e) { 487 handleException(e); 488 throw new AssertionError(); 489 } 490 } 491 492 /** 493 * Returns a String representation of this object. 494 * 495 * @return String 496 */ 497 @Override 498 public String toString() { 499 return statement == null ? "NULL" : statement.toString(); 500 } 501 502 @Override 503 public boolean getMoreResults(final int current) throws SQLException { 504 checkOpen(); 505 try { 506 return statement.getMoreResults(current); 507 } catch (final SQLException e) { 508 handleException(e); 509 return false; 510 } 511 } 512 513 @Override 514 public ResultSet getGeneratedKeys() throws SQLException { 515 checkOpen(); 516 try { 517 return DelegatingResultSet.wrapResultSet(this, statement.getGeneratedKeys()); 518 } catch (final SQLException e) { 519 handleException(e); 520 throw new AssertionError(); 521 } 522 } 523 524 @Override 525 public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { 526 checkOpen(); 527 if (connection != null) { 528 connection.setLastUsed(); 529 } 530 try { 531 return statement.executeUpdate(sql, autoGeneratedKeys); 532 } catch (final SQLException e) { 533 handleException(e); 534 return 0; 535 } 536 } 537 538 @Override 539 public int executeUpdate(final String sql, final int columnIndexes[]) throws SQLException { 540 checkOpen(); 541 if (connection != null) { 542 connection.setLastUsed(); 543 } 544 try { 545 return statement.executeUpdate(sql, columnIndexes); 546 } catch (final SQLException e) { 547 handleException(e); 548 return 0; 549 } 550 } 551 552 @Override 553 public int executeUpdate(final String sql, final String columnNames[]) throws SQLException { 554 checkOpen(); 555 if (connection != null) { 556 connection.setLastUsed(); 557 } 558 try { 559 return statement.executeUpdate(sql, columnNames); 560 } catch (final SQLException e) { 561 handleException(e); 562 return 0; 563 } 564 } 565 566 @Override 567 public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException { 568 checkOpen(); 569 if (connection != null) { 570 connection.setLastUsed(); 571 } 572 try { 573 return statement.execute(sql, autoGeneratedKeys); 574 } catch (final SQLException e) { 575 handleException(e); 576 return false; 577 } 578 } 579 580 @Override 581 public boolean execute(final String sql, final int columnIndexes[]) throws SQLException { 582 checkOpen(); 583 if (connection != null) { 584 connection.setLastUsed(); 585 } 586 try { 587 return statement.execute(sql, columnIndexes); 588 } catch (final SQLException e) { 589 handleException(e); 590 return false; 591 } 592 } 593 594 @Override 595 public boolean execute(final String sql, final String columnNames[]) throws SQLException { 596 checkOpen(); 597 if (connection != null) { 598 connection.setLastUsed(); 599 } 600 try { 601 return statement.execute(sql, columnNames); 602 } catch (final SQLException e) { 603 handleException(e); 604 return false; 605 } 606 } 607 608 @Override 609 public int getResultSetHoldability() throws SQLException { 610 checkOpen(); 611 try { 612 return statement.getResultSetHoldability(); 613 } catch (final SQLException e) { 614 handleException(e); 615 return 0; 616 } 617 } 618 619 /* 620 * Note was protected prior to JDBC 4 621 */ 622 @Override 623 public boolean isClosed() throws SQLException { 624 return closed; 625 } 626 627 @Override 628 public boolean isWrapperFor(final Class<?> iface) throws SQLException { 629 if (iface.isAssignableFrom(getClass())) { 630 return true; 631 } else if (iface.isAssignableFrom(statement.getClass())) { 632 return true; 633 } else { 634 return statement.isWrapperFor(iface); 635 } 636 } 637 638 @Override 639 public <T> T unwrap(final Class<T> iface) throws SQLException { 640 if (iface.isAssignableFrom(getClass())) { 641 return iface.cast(this); 642 } else if (iface.isAssignableFrom(statement.getClass())) { 643 return iface.cast(statement); 644 } else { 645 return statement.unwrap(iface); 646 } 647 } 648 649 @Override 650 public void setPoolable(final boolean poolable) throws SQLException { 651 checkOpen(); 652 try { 653 statement.setPoolable(poolable); 654 } catch (final SQLException e) { 655 handleException(e); 656 } 657 } 658 659 @Override 660 public boolean isPoolable() throws SQLException { 661 checkOpen(); 662 try { 663 return statement.isPoolable(); 664 } catch (final SQLException e) { 665 handleException(e); 666 return false; 667 } 668 } 669 670 @Override 671 public void closeOnCompletion() throws SQLException { 672 checkOpen(); 673 try { 674 statement.closeOnCompletion(); 675 } catch (final SQLException e) { 676 handleException(e); 677 } 678 } 679 680 @Override 681 public boolean isCloseOnCompletion() throws SQLException { 682 checkOpen(); 683 try { 684 return statement.isCloseOnCompletion(); 685 } catch (final SQLException e) { 686 handleException(e); 687 return false; 688 } 689 } 690 691 @Override 692 protected void finalize() throws Throwable { 693 // This is required because of statement pooling. The poolable 694 // statements will always be strongly held by the statement pool. If the 695 // delegating statements that wrap the poolable statement are not 696 // strongly held they will be garbage collected but at that point the 697 // poolable statements need to be returned to the pool else there will 698 // be a leak of statements from the pool. Closing this statement will 699 // close all the wrapped statements and return any poolable statements 700 // to the pool. 701 close(); 702 super.finalize(); 703 } 704}