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