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> resultSetList = getTrace(); 141 if (resultSetList != null) { 142 final ResultSet[] resultSets = resultSetList.toArray(Utils.EMPTY_RESULT_SET_ARRAY); 143 for (final ResultSet resultSet : resultSets) { 144 if (resultSet != null) { 145 try { 146 resultSet.close(); 147 } catch (final Exception e) { 148 if (connection != null) { 149 // Does not rethrow e. 150 connection.handleExceptionNoThrow(e); 151 } 152 thrownList.add(e); 153 } 154 } 155 } 156 clearTrace(); 157 } 158 if (statement != null) { 159 try { 160 statement.close(); 161 } catch (final Exception e) { 162 if (connection != null) { 163 // Does not rethrow e. 164 connection.handleExceptionNoThrow(e); 165 } 166 thrownList.add(e); 167 } 168 } 169 } finally { 170 closed = true; 171 statement = null; 172 if (!thrownList.isEmpty()) { 173 throw new SQLExceptionList(thrownList); 174 } 175 } 176 } 177 178 @Override 179 public void closeOnCompletion() throws SQLException { 180 checkOpen(); 181 try { 182 Jdbc41Bridge.closeOnCompletion(statement); 183 } catch (final SQLException e) { 184 handleException(e); 185 } 186 } 187 188 @Override 189 public boolean execute(final String sql) throws SQLException { 190 checkOpen(); 191 setLastUsedInParent(); 192 try { 193 return statement.execute(sql); 194 } catch (final SQLException e) { 195 handleException(e); 196 return false; 197 } 198 } 199 200 @Override 201 public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException { 202 checkOpen(); 203 setLastUsedInParent(); 204 try { 205 return statement.execute(sql, autoGeneratedKeys); 206 } catch (final SQLException e) { 207 handleException(e); 208 return false; 209 } 210 } 211 212 @Override 213 public boolean execute(final String sql, final int[] columnIndexes) throws SQLException { 214 checkOpen(); 215 setLastUsedInParent(); 216 try { 217 return statement.execute(sql, columnIndexes); 218 } catch (final SQLException e) { 219 handleException(e); 220 return false; 221 } 222 } 223 224 @Override 225 public boolean execute(final String sql, final String[] columnNames) throws SQLException { 226 checkOpen(); 227 setLastUsedInParent(); 228 try { 229 return statement.execute(sql, columnNames); 230 } catch (final SQLException e) { 231 handleException(e); 232 return false; 233 } 234 } 235 236 @Override 237 public int[] executeBatch() throws SQLException { 238 checkOpen(); 239 setLastUsedInParent(); 240 try { 241 return statement.executeBatch(); 242 } catch (final SQLException e) { 243 handleException(e); 244 throw new AssertionError(); 245 } 246 } 247 248 /** 249 * @since 2.5.0 250 */ 251 @Override 252 public long[] executeLargeBatch() throws SQLException { 253 checkOpen(); 254 setLastUsedInParent(); 255 try { 256 return statement.executeLargeBatch(); 257 } catch (final SQLException e) { 258 handleException(e); 259 return null; 260 } 261 } 262 263 /** 264 * @since 2.5.0 265 */ 266 @Override 267 public long executeLargeUpdate(final String sql) throws SQLException { 268 checkOpen(); 269 setLastUsedInParent(); 270 try { 271 return statement.executeLargeUpdate(sql); 272 } catch (final SQLException e) { 273 handleException(e); 274 return 0; 275 } 276 } 277 278 /** 279 * @since 2.5.0 280 */ 281 @Override 282 public long executeLargeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { 283 checkOpen(); 284 setLastUsedInParent(); 285 try { 286 return statement.executeLargeUpdate(sql, autoGeneratedKeys); 287 } catch (final SQLException e) { 288 handleException(e); 289 return 0; 290 } 291 } 292 293 /** 294 * @since 2.5.0 295 */ 296 @Override 297 public long executeLargeUpdate(final String sql, final int[] columnIndexes) throws SQLException { 298 checkOpen(); 299 setLastUsedInParent(); 300 try { 301 return statement.executeLargeUpdate(sql, columnIndexes); 302 } catch (final SQLException e) { 303 handleException(e); 304 return 0; 305 } 306 } 307 308 /** 309 * @since 2.5.0 310 */ 311 @Override 312 public long executeLargeUpdate(final String sql, final String[] columnNames) throws SQLException { 313 checkOpen(); 314 setLastUsedInParent(); 315 try { 316 return statement.executeLargeUpdate(sql, columnNames); 317 } catch (final SQLException e) { 318 handleException(e); 319 return 0; 320 } 321 } 322 323 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 324 @Override 325 public ResultSet executeQuery(final String sql) throws SQLException { 326 checkOpen(); 327 setLastUsedInParent(); 328 try { 329 return DelegatingResultSet.wrapResultSet(this, statement.executeQuery(sql)); 330 } catch (final SQLException e) { 331 handleException(e); 332 throw new AssertionError(); 333 } 334 } 335 336 @Override 337 public int executeUpdate(final String sql) throws SQLException { 338 checkOpen(); 339 setLastUsedInParent(); 340 try { 341 return statement.executeUpdate(sql); 342 } catch (final SQLException e) { 343 handleException(e); 344 return 0; 345 } 346 } 347 348 @Override 349 public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { 350 checkOpen(); 351 setLastUsedInParent(); 352 try { 353 return statement.executeUpdate(sql, autoGeneratedKeys); 354 } catch (final SQLException e) { 355 handleException(e); 356 return 0; 357 } 358 } 359 360 @Override 361 public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException { 362 checkOpen(); 363 setLastUsedInParent(); 364 try { 365 return statement.executeUpdate(sql, columnIndexes); 366 } catch (final SQLException e) { 367 handleException(e); 368 return 0; 369 } 370 } 371 372 @Override 373 public int executeUpdate(final String sql, final String[] columnNames) throws SQLException { 374 checkOpen(); 375 setLastUsedInParent(); 376 try { 377 return statement.executeUpdate(sql, columnNames); 378 } catch (final SQLException e) { 379 handleException(e); 380 return 0; 381 } 382 } 383 384 @Override 385 protected void finalize() throws Throwable { 386 // This is required because of statement pooling. The poolable 387 // statements will always be strongly held by the statement pool. If the 388 // delegating statements that wrap the poolable statement are not 389 // strongly held they will be garbage collected but at that point the 390 // poolable statements need to be returned to the pool else there will 391 // be a leak of statements from the pool. Closing this statement will 392 // close all the wrapped statements and return any poolable statements 393 // to the pool. 394 close(); 395 super.finalize(); 396 } 397 398 @Override 399 public Connection getConnection() throws SQLException { 400 checkOpen(); 401 return getConnectionInternal(); // return the delegating connection that created this 402 } 403 404 protected DelegatingConnection<?> getConnectionInternal() { 405 return connection; 406 } 407 408 /** 409 * Returns my underlying {@link Statement}. 410 * 411 * @return my underlying {@link Statement}. 412 * @see #getInnermostDelegate 413 */ 414 public Statement getDelegate() { 415 return statement; 416 } 417 418 @Override 419 public int getFetchDirection() throws SQLException { 420 checkOpen(); 421 try { 422 return statement.getFetchDirection(); 423 } catch (final SQLException e) { 424 handleException(e); 425 return 0; 426 } 427 } 428 429 @Override 430 public int getFetchSize() throws SQLException { 431 checkOpen(); 432 try { 433 return statement.getFetchSize(); 434 } catch (final SQLException e) { 435 handleException(e); 436 return 0; 437 } 438 } 439 440 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 441 @Override 442 public ResultSet getGeneratedKeys() throws SQLException { 443 checkOpen(); 444 try { 445 return DelegatingResultSet.wrapResultSet(this, statement.getGeneratedKeys()); 446 } catch (final SQLException e) { 447 handleException(e); 448 throw new AssertionError(); 449 } 450 } 451 452 /** 453 * If my underlying {@link Statement} is not a {@code DelegatingStatement}, returns it, otherwise recursively 454 * invokes this method on my delegate. 455 * <p> 456 * Hence this method will return the first delegate that is not a {@code DelegatingStatement} or {@code null} when 457 * no non-{@code DelegatingStatement} delegate can be found by traversing this chain. 458 * </p> 459 * <p> 460 * This method is useful when you may have nested {@code DelegatingStatement}s, and you want to make sure to obtain 461 * a "genuine" {@link Statement}. 462 * </p> 463 * 464 * @return The innermost delegate. 465 * 466 * @see #getDelegate 467 */ 468 @SuppressWarnings("resource") 469 public Statement getInnermostDelegate() { 470 Statement s = statement; 471 while (s instanceof DelegatingStatement) { 472 s = ((DelegatingStatement) s).getDelegate(); 473 if (this == s) { 474 return null; 475 } 476 } 477 return s; 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 protected void handleException(final SQLException e) throws SQLException { 631 if (connection == null) { 632 throw e; 633 } 634 connection.handleException(e); 635 } 636 637 /* 638 * Note: This method was protected prior to JDBC 4. 639 */ 640 @Override 641 public boolean isClosed() throws SQLException { 642 return closed; 643 } 644 645 protected boolean isClosedInternal() { 646 return closed; 647 } 648 649 @Override 650 public boolean isCloseOnCompletion() throws SQLException { 651 checkOpen(); 652 try { 653 return Jdbc41Bridge.isCloseOnCompletion(statement); 654 } catch (final SQLException e) { 655 handleException(e); 656 return false; 657 } 658 } 659 660 @Override 661 public boolean isPoolable() throws SQLException { 662 checkOpen(); 663 try { 664 return statement.isPoolable(); 665 } catch (final SQLException e) { 666 handleException(e); 667 return false; 668 } 669 } 670 671 @Override 672 public boolean isWrapperFor(final Class<?> iface) throws SQLException { 673 if (iface.isAssignableFrom(getClass())) { 674 return true; 675 } 676 if (iface.isAssignableFrom(statement.getClass())) { 677 return true; 678 } 679 return statement.isWrapperFor(iface); 680 } 681 682 /** 683 * 684 * @throws SQLException 685 * thrown by the delegating statement. 686 * @since 2.4.0 made public, was protected in 2.3.0. 687 */ 688 public void passivate() throws SQLException { 689 if (statement instanceof DelegatingStatement) { 690 ((DelegatingStatement) statement).passivate(); 691 } 692 } 693 694 protected void setClosedInternal(final boolean closed) { 695 this.closed = closed; 696 } 697 698 @Override 699 public void setCursorName(final String name) throws SQLException { 700 checkOpen(); 701 try { 702 statement.setCursorName(name); 703 } catch (final SQLException e) { 704 handleException(e); 705 } 706 } 707 708 /** 709 * Sets my delegate. 710 * 711 * @param statement 712 * my delegate. 713 */ 714 public void setDelegate(final Statement statement) { 715 this.statement = statement; 716 } 717 718 @Override 719 public void setEscapeProcessing(final boolean enable) throws SQLException { 720 checkOpen(); 721 try { 722 statement.setEscapeProcessing(enable); 723 } catch (final SQLException e) { 724 handleException(e); 725 } 726 } 727 728 @Override 729 public void setFetchDirection(final int direction) throws SQLException { 730 checkOpen(); 731 try { 732 statement.setFetchDirection(direction); 733 } catch (final SQLException e) { 734 handleException(e); 735 } 736 } 737 738 @Override 739 public void setFetchSize(final int rows) throws SQLException { 740 checkOpen(); 741 try { 742 statement.setFetchSize(rows); 743 } catch (final SQLException e) { 744 handleException(e); 745 } 746 } 747 748 /** 749 * @since 2.5.0 750 */ 751 @Override 752 public void setLargeMaxRows(final long max) throws SQLException { 753 checkOpen(); 754 try { 755 statement.setLargeMaxRows(max); 756 } catch (final SQLException e) { 757 handleException(e); 758 } 759 } 760 761 private void setLastUsedInParent() { 762 if (connection != null) { 763 connection.setLastUsed(); 764 } 765 } 766 767 @Override 768 public void setMaxFieldSize(final int max) throws SQLException { 769 checkOpen(); 770 try { 771 statement.setMaxFieldSize(max); 772 } catch (final SQLException e) { 773 handleException(e); 774 } 775 } 776 777 @Override 778 public void setMaxRows(final int max) throws SQLException { 779 checkOpen(); 780 try { 781 statement.setMaxRows(max); 782 } catch (final SQLException e) { 783 handleException(e); 784 } 785 } 786 787 @Override 788 public void setPoolable(final boolean poolable) throws SQLException { 789 checkOpen(); 790 try { 791 statement.setPoolable(poolable); 792 } catch (final SQLException e) { 793 handleException(e); 794 } 795 } 796 797 @Override 798 public void setQueryTimeout(final int seconds) throws SQLException { 799 checkOpen(); 800 try { 801 statement.setQueryTimeout(seconds); 802 } catch (final SQLException e) { 803 handleException(e); 804 } 805 } 806 807 /** 808 * Returns a String representation of this object. 809 * 810 * @return String 811 */ 812 @Override 813 public synchronized String toString() { 814 return statement == null ? "NULL" : statement.toString(); 815 } 816 817 @Override 818 public <T> T unwrap(final Class<T> iface) throws SQLException { 819 if (iface.isAssignableFrom(getClass())) { 820 return iface.cast(this); 821 } 822 if (iface.isAssignableFrom(statement.getClass())) { 823 return iface.cast(statement); 824 } 825 return statement.unwrap(iface); 826 } 827}