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, may return null. 454 * @see #getDelegate 455 */ 456 @SuppressWarnings("resource") 457 public Statement getInnermostDelegate() { 458 Statement stmt = statement; 459 while (stmt instanceof DelegatingStatement) { 460 stmt = ((DelegatingStatement) stmt).getDelegate(); 461 if (this == stmt) { 462 return null; 463 } 464 } 465 return stmt; 466 } 467 468 /** 469 * @since 2.5.0 470 */ 471 @Override 472 public long getLargeMaxRows() throws SQLException { 473 checkOpen(); 474 try { 475 return statement.getLargeMaxRows(); 476 } catch (final SQLException e) { 477 handleException(e); 478 return 0; 479 } 480 } 481 482 /** 483 * @since 2.5.0 484 */ 485 @Override 486 public long getLargeUpdateCount() throws SQLException { 487 checkOpen(); 488 try { 489 return statement.getLargeUpdateCount(); 490 } catch (final SQLException e) { 491 handleException(e); 492 return 0; 493 } 494 } 495 496 @Override 497 public int getMaxFieldSize() throws SQLException { 498 checkOpen(); 499 try { 500 return statement.getMaxFieldSize(); 501 } catch (final SQLException e) { 502 handleException(e); 503 return 0; 504 } 505 } 506 507 @Override 508 public int getMaxRows() throws SQLException { 509 checkOpen(); 510 try { 511 return statement.getMaxRows(); 512 } catch (final SQLException e) { 513 handleException(e); 514 return 0; 515 } 516 } 517 518 @Override 519 public boolean getMoreResults() throws SQLException { 520 checkOpen(); 521 try { 522 return statement.getMoreResults(); 523 } catch (final SQLException e) { 524 handleException(e); 525 return false; 526 } 527 } 528 529 @Override 530 public boolean getMoreResults(final int current) throws SQLException { 531 checkOpen(); 532 try { 533 return statement.getMoreResults(current); 534 } catch (final SQLException e) { 535 handleException(e); 536 return false; 537 } 538 } 539 540 @Override 541 public int getQueryTimeout() throws SQLException { 542 checkOpen(); 543 try { 544 return statement.getQueryTimeout(); 545 } catch (final SQLException e) { 546 handleException(e); 547 return 0; 548 } 549 } 550 551 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 552 @Override 553 public ResultSet getResultSet() throws SQLException { 554 checkOpen(); 555 try { 556 return DelegatingResultSet.wrapResultSet(this, statement.getResultSet()); 557 } catch (final SQLException e) { 558 handleException(e); 559 throw new AssertionError(); 560 } 561 } 562 563 @Override 564 public int getResultSetConcurrency() throws SQLException { 565 checkOpen(); 566 try { 567 return statement.getResultSetConcurrency(); 568 } catch (final SQLException e) { 569 handleException(e); 570 return 0; 571 } 572 } 573 574 @Override 575 public int getResultSetHoldability() throws SQLException { 576 checkOpen(); 577 try { 578 return statement.getResultSetHoldability(); 579 } catch (final SQLException e) { 580 handleException(e); 581 return 0; 582 } 583 } 584 585 @Override 586 public int getResultSetType() throws SQLException { 587 checkOpen(); 588 try { 589 return statement.getResultSetType(); 590 } catch (final SQLException e) { 591 handleException(e); 592 return 0; 593 } 594 } 595 596 @Override 597 public int getUpdateCount() throws SQLException { 598 checkOpen(); 599 try { 600 return statement.getUpdateCount(); 601 } catch (final SQLException e) { 602 handleException(e); 603 return 0; 604 } 605 } 606 607 @Override 608 public SQLWarning getWarnings() throws SQLException { 609 checkOpen(); 610 try { 611 return statement.getWarnings(); 612 } catch (final SQLException e) { 613 handleException(e); 614 throw new AssertionError(); 615 } 616 } 617 618 protected void handleException(final SQLException e) throws SQLException { 619 if (connection == null) { 620 throw e; 621 } 622 connection.handleException(e); 623 } 624 625 /* 626 * Note: This method was protected prior to JDBC 4. 627 */ 628 @Override 629 public boolean isClosed() throws SQLException { 630 return closed; 631 } 632 633 protected boolean isClosedInternal() { 634 return closed; 635 } 636 637 @Override 638 public boolean isCloseOnCompletion() throws SQLException { 639 checkOpen(); 640 try { 641 return Jdbc41Bridge.isCloseOnCompletion(statement); 642 } catch (final SQLException e) { 643 handleException(e); 644 return false; 645 } 646 } 647 648 @Override 649 public boolean isPoolable() throws SQLException { 650 checkOpen(); 651 try { 652 return statement.isPoolable(); 653 } catch (final SQLException e) { 654 handleException(e); 655 return false; 656 } 657 } 658 659 @Override 660 public boolean isWrapperFor(final Class<?> iface) throws SQLException { 661 if (iface.isAssignableFrom(getClass())) { 662 return true; 663 } 664 if (iface.isAssignableFrom(statement.getClass())) { 665 return true; 666 } 667 return statement.isWrapperFor(iface); 668 } 669 670 /** 671 * 672 * @throws SQLException 673 * thrown by the delegating statement. 674 * @since 2.4.0 made public, was protected in 2.3.0. 675 */ 676 public void passivate() throws SQLException { 677 if (statement instanceof DelegatingStatement) { 678 ((DelegatingStatement) statement).passivate(); 679 } 680 } 681 682 protected void setClosedInternal(final boolean closed) { 683 this.closed = closed; 684 } 685 686 @Override 687 public void setCursorName(final String name) throws SQLException { 688 checkOpen(); 689 try { 690 statement.setCursorName(name); 691 } catch (final SQLException e) { 692 handleException(e); 693 } 694 } 695 696 /** 697 * Sets my delegate. 698 * 699 * @param statement 700 * my delegate. 701 */ 702 public void setDelegate(final Statement statement) { 703 this.statement = statement; 704 } 705 706 @Override 707 public void setEscapeProcessing(final boolean enable) throws SQLException { 708 checkOpen(); 709 try { 710 statement.setEscapeProcessing(enable); 711 } catch (final SQLException e) { 712 handleException(e); 713 } 714 } 715 716 @Override 717 public void setFetchDirection(final int direction) throws SQLException { 718 checkOpen(); 719 try { 720 statement.setFetchDirection(direction); 721 } catch (final SQLException e) { 722 handleException(e); 723 } 724 } 725 726 @Override 727 public void setFetchSize(final int rows) throws SQLException { 728 checkOpen(); 729 try { 730 statement.setFetchSize(rows); 731 } catch (final SQLException e) { 732 handleException(e); 733 } 734 } 735 736 /** 737 * @since 2.5.0 738 */ 739 @Override 740 public void setLargeMaxRows(final long max) throws SQLException { 741 checkOpen(); 742 try { 743 statement.setLargeMaxRows(max); 744 } catch (final SQLException e) { 745 handleException(e); 746 } 747 } 748 749 private void setLastUsedInParent() { 750 if (connection != null) { 751 connection.setLastUsed(); 752 } 753 } 754 755 @Override 756 public void setMaxFieldSize(final int max) throws SQLException { 757 checkOpen(); 758 try { 759 statement.setMaxFieldSize(max); 760 } catch (final SQLException e) { 761 handleException(e); 762 } 763 } 764 765 @Override 766 public void setMaxRows(final int max) throws SQLException { 767 checkOpen(); 768 try { 769 statement.setMaxRows(max); 770 } catch (final SQLException e) { 771 handleException(e); 772 } 773 } 774 775 @Override 776 public void setPoolable(final boolean poolable) throws SQLException { 777 checkOpen(); 778 try { 779 statement.setPoolable(poolable); 780 } catch (final SQLException e) { 781 handleException(e); 782 } 783 } 784 785 @Override 786 public void setQueryTimeout(final int seconds) throws SQLException { 787 checkOpen(); 788 try { 789 statement.setQueryTimeout(seconds); 790 } catch (final SQLException e) { 791 handleException(e); 792 } 793 } 794 795 /** 796 * Returns a String representation of this object. 797 * 798 * @return String 799 */ 800 @Override 801 public synchronized String toString() { 802 return statement == null ? "NULL" : statement.toString(); 803 } 804 805 @Override 806 public <T> T unwrap(final Class<T> iface) throws SQLException { 807 if (iface.isAssignableFrom(getClass())) { 808 return iface.cast(this); 809 } 810 if (iface.isAssignableFrom(statement.getClass())) { 811 return iface.cast(statement); 812 } 813 return statement.unwrap(iface); 814 } 815}