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