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