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 = false; 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> thrown = 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 int size = resultSetList.size(); 143 final ResultSet[] resultSets = resultSetList.toArray(new ResultSet[size]); 144 for (final ResultSet resultSet : resultSets) { 145 if (resultSet != null) { 146 try { 147 resultSet.close(); 148 } catch (Exception e) { 149 if (connection != null) { 150 // Does not rethrow e. 151 connection.handleExceptionNoThrow(e); 152 } 153 thrown.add(e); 154 } 155 } 156 } 157 clearTrace(); 158 } 159 if (statement != null) { 160 try { 161 statement.close(); 162 } catch (Exception e) { 163 if (connection != null) { 164 // Does not rethrow e. 165 connection.handleExceptionNoThrow(e); 166 } 167 thrown.add(e); 168 } 169 } 170 } finally { 171 closed = true; 172 statement = null; 173 if (!thrown.isEmpty()) { 174 throw new SQLExceptionList(thrown); 175 } 176 } 177 } 178 179 @Override 180 public void closeOnCompletion() throws SQLException { 181 checkOpen(); 182 try { 183 Jdbc41Bridge.closeOnCompletion(statement); 184 } catch (final SQLException e) { 185 handleException(e); 186 } 187 } 188 189 @Override 190 public boolean execute(final String sql) throws SQLException { 191 checkOpen(); 192 setLastUsedInParent(); 193 try { 194 return statement.execute(sql); 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 autoGeneratedKeys) throws SQLException { 203 checkOpen(); 204 setLastUsedInParent(); 205 try { 206 return statement.execute(sql, autoGeneratedKeys); 207 } catch (final SQLException e) { 208 handleException(e); 209 return false; 210 } 211 } 212 213 @Override 214 public boolean execute(final String sql, final int columnIndexes[]) throws SQLException { 215 checkOpen(); 216 setLastUsedInParent(); 217 try { 218 return statement.execute(sql, columnIndexes); 219 } catch (final SQLException e) { 220 handleException(e); 221 return false; 222 } 223 } 224 225 @Override 226 public boolean execute(final String sql, final String columnNames[]) throws SQLException { 227 checkOpen(); 228 setLastUsedInParent(); 229 try { 230 return statement.execute(sql, columnNames); 231 } catch (final SQLException e) { 232 handleException(e); 233 return false; 234 } 235 } 236 237 @Override 238 public int[] executeBatch() throws SQLException { 239 checkOpen(); 240 setLastUsedInParent(); 241 try { 242 return statement.executeBatch(); 243 } catch (final SQLException e) { 244 handleException(e); 245 throw new AssertionError(); 246 } 247 } 248 249 /** 250 * @since 2.5.0 251 */ 252 @Override 253 public long[] executeLargeBatch() throws SQLException { 254 checkOpen(); 255 setLastUsedInParent(); 256 try { 257 return statement.executeLargeBatch(); 258 } catch (final SQLException e) { 259 handleException(e); 260 return null; 261 } 262 } 263 264 /** 265 * @since 2.5.0 266 */ 267 @Override 268 public long executeLargeUpdate(final String sql) throws SQLException { 269 checkOpen(); 270 setLastUsedInParent(); 271 try { 272 return statement.executeLargeUpdate(sql); 273 } catch (final SQLException e) { 274 handleException(e); 275 return 0; 276 } 277 } 278 279 /** 280 * @since 2.5.0 281 */ 282 @Override 283 public long executeLargeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { 284 checkOpen(); 285 setLastUsedInParent(); 286 try { 287 return statement.executeLargeUpdate(sql, autoGeneratedKeys); 288 } catch (final SQLException e) { 289 handleException(e); 290 return 0; 291 } 292 } 293 294 /** 295 * @since 2.5.0 296 */ 297 @Override 298 public long executeLargeUpdate(final String sql, final int[] columnIndexes) throws SQLException { 299 checkOpen(); 300 setLastUsedInParent(); 301 try { 302 return statement.executeLargeUpdate(sql, columnIndexes); 303 } catch (final SQLException e) { 304 handleException(e); 305 return 0; 306 } 307 } 308 309 /** 310 * @since 2.5.0 311 */ 312 @Override 313 public long executeLargeUpdate(final String sql, final String[] columnNames) throws SQLException { 314 checkOpen(); 315 setLastUsedInParent(); 316 try { 317 return statement.executeLargeUpdate(sql, columnNames); 318 } catch (final SQLException e) { 319 handleException(e); 320 return 0; 321 } 322 } 323 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 @Override 441 public ResultSet getGeneratedKeys() throws SQLException { 442 checkOpen(); 443 try { 444 return DelegatingResultSet.wrapResultSet(this, statement.getGeneratedKeys()); 445 } catch (final SQLException e) { 446 handleException(e); 447 throw new AssertionError(); 448 } 449 } 450 451 /** 452 * If my underlying {@link Statement} is not a {@code DelegatingStatement}, returns it, otherwise recursively 453 * invokes this method on my delegate. 454 * <p> 455 * Hence this method will return the first delegate that is not a {@code DelegatingStatement} or {@code null} when 456 * no non-{@code DelegatingStatement} delegate can be found by traversing this chain. 457 * </p> 458 * <p> 459 * This method is useful when you may have nested {@code DelegatingStatement}s, and you want to make sure to obtain 460 * a "genuine" {@link Statement}. 461 * </p> 462 * 463 * @return The innermost delegate. 464 * 465 * @see #getDelegate 466 */ 467 @SuppressWarnings("resource") 468 public Statement getInnermostDelegate() { 469 Statement s = statement; 470 while (s != null && s instanceof DelegatingStatement) { 471 s = ((DelegatingStatement) s).getDelegate(); 472 if (this == s) { 473 return null; 474 } 475 } 476 return s; 477 } 478 479 /** 480 * @since 2.5.0 481 */ 482 @Override 483 public long getLargeMaxRows() throws SQLException { 484 checkOpen(); 485 try { 486 return statement.getLargeMaxRows(); 487 } catch (final SQLException e) { 488 handleException(e); 489 return 0; 490 } 491 } 492 493 /** 494 * @since 2.5.0 495 */ 496 @Override 497 public long getLargeUpdateCount() throws SQLException { 498 checkOpen(); 499 try { 500 return statement.getLargeUpdateCount(); 501 } catch (final SQLException e) { 502 handleException(e); 503 return 0; 504 } 505 } 506 507 @Override 508 public int getMaxFieldSize() throws SQLException { 509 checkOpen(); 510 try { 511 return statement.getMaxFieldSize(); 512 } catch (final SQLException e) { 513 handleException(e); 514 return 0; 515 } 516 } 517 518 @Override 519 public int getMaxRows() throws SQLException { 520 checkOpen(); 521 try { 522 return statement.getMaxRows(); 523 } catch (final SQLException e) { 524 handleException(e); 525 return 0; 526 } 527 } 528 529 @Override 530 public boolean getMoreResults() throws SQLException { 531 checkOpen(); 532 try { 533 return statement.getMoreResults(); 534 } catch (final SQLException e) { 535 handleException(e); 536 return false; 537 } 538 } 539 540 @Override 541 public boolean getMoreResults(final int current) throws SQLException { 542 checkOpen(); 543 try { 544 return statement.getMoreResults(current); 545 } catch (final SQLException e) { 546 handleException(e); 547 return false; 548 } 549 } 550 551 @Override 552 public int getQueryTimeout() throws SQLException { 553 checkOpen(); 554 try { 555 return statement.getQueryTimeout(); 556 } catch (final SQLException e) { 557 handleException(e); 558 return 0; 559 } 560 } 561 562 @Override 563 public ResultSet getResultSet() throws SQLException { 564 checkOpen(); 565 try { 566 return DelegatingResultSet.wrapResultSet(this, statement.getResultSet()); 567 } catch (final SQLException e) { 568 handleException(e); 569 throw new AssertionError(); 570 } 571 } 572 573 @Override 574 public int getResultSetConcurrency() throws SQLException { 575 checkOpen(); 576 try { 577 return statement.getResultSetConcurrency(); 578 } catch (final SQLException e) { 579 handleException(e); 580 return 0; 581 } 582 } 583 584 @Override 585 public int getResultSetHoldability() throws SQLException { 586 checkOpen(); 587 try { 588 return statement.getResultSetHoldability(); 589 } catch (final SQLException e) { 590 handleException(e); 591 return 0; 592 } 593 } 594 595 @Override 596 public int getResultSetType() throws SQLException { 597 checkOpen(); 598 try { 599 return statement.getResultSetType(); 600 } catch (final SQLException e) { 601 handleException(e); 602 return 0; 603 } 604 } 605 606 @Override 607 public int getUpdateCount() throws SQLException { 608 checkOpen(); 609 try { 610 return statement.getUpdateCount(); 611 } catch (final SQLException e) { 612 handleException(e); 613 return 0; 614 } 615 } 616 617 @Override 618 public SQLWarning getWarnings() throws SQLException { 619 checkOpen(); 620 try { 621 return statement.getWarnings(); 622 } catch (final SQLException e) { 623 handleException(e); 624 throw new AssertionError(); 625 } 626 } 627 628 protected void handleException(final SQLException e) throws SQLException { 629 if (connection != null) { 630 connection.handleException(e); 631 } else { 632 throw e; 633 } 634 } 635 636 /* 637 * Note: This method was protected prior to JDBC 4. 638 */ 639 @Override 640 public boolean isClosed() throws SQLException { 641 return closed; 642 } 643 644 protected boolean isClosedInternal() { 645 return closed; 646 } 647 648 @Override 649 public boolean isCloseOnCompletion() throws SQLException { 650 checkOpen(); 651 try { 652 return Jdbc41Bridge.isCloseOnCompletion(statement); 653 } catch (final SQLException e) { 654 handleException(e); 655 return false; 656 } 657 } 658 659 @Override 660 public boolean isPoolable() throws SQLException { 661 checkOpen(); 662 try { 663 return statement.isPoolable(); 664 } catch (final SQLException e) { 665 handleException(e); 666 return false; 667 } 668 } 669 670 @Override 671 public boolean isWrapperFor(final Class<?> iface) throws SQLException { 672 if (iface.isAssignableFrom(getClass())) { 673 return true; 674 } else if (iface.isAssignableFrom(statement.getClass())) { 675 return true; 676 } else { 677 return statement.isWrapperFor(iface); 678 } 679 } 680 681 /** 682 * 683 * @throws SQLException 684 * thrown by the delegating statement. 685 * @since 2.4.0 made public, was protected in 2.3.0. 686 */ 687 public void passivate() throws SQLException { 688 if (statement instanceof DelegatingStatement) { 689 ((DelegatingStatement) statement).passivate(); 690 } 691 } 692 693 protected void setClosedInternal(final boolean closed) { 694 this.closed = closed; 695 } 696 697 @Override 698 public void setCursorName(final String name) throws SQLException { 699 checkOpen(); 700 try { 701 statement.setCursorName(name); 702 } catch (final SQLException e) { 703 handleException(e); 704 } 705 } 706 707 /** 708 * Sets my delegate. 709 * 710 * @param statement 711 * my delegate. 712 */ 713 public void setDelegate(final Statement statement) { 714 this.statement = statement; 715 } 716 717 @Override 718 public void setEscapeProcessing(final boolean enable) throws SQLException { 719 checkOpen(); 720 try { 721 statement.setEscapeProcessing(enable); 722 } catch (final SQLException e) { 723 handleException(e); 724 } 725 } 726 727 @Override 728 public void setFetchDirection(final int direction) throws SQLException { 729 checkOpen(); 730 try { 731 statement.setFetchDirection(direction); 732 } catch (final SQLException e) { 733 handleException(e); 734 } 735 } 736 737 @Override 738 public void setFetchSize(final int rows) throws SQLException { 739 checkOpen(); 740 try { 741 statement.setFetchSize(rows); 742 } catch (final SQLException e) { 743 handleException(e); 744 } 745 } 746 747 /** 748 * @since 2.5.0 749 */ 750 @Override 751 public void setLargeMaxRows(final long max) throws SQLException { 752 checkOpen(); 753 try { 754 statement.setLargeMaxRows(max); 755 } catch (final SQLException e) { 756 handleException(e); 757 } 758 } 759 760 private void setLastUsedInParent() { 761 if (connection != null) { 762 connection.setLastUsed(); 763 } 764 } 765 766 @Override 767 public void setMaxFieldSize(final int max) throws SQLException { 768 checkOpen(); 769 try { 770 statement.setMaxFieldSize(max); 771 } catch (final SQLException e) { 772 handleException(e); 773 } 774 } 775 776 @Override 777 public void setMaxRows(final int max) throws SQLException { 778 checkOpen(); 779 try { 780 statement.setMaxRows(max); 781 } catch (final SQLException e) { 782 handleException(e); 783 } 784 } 785 786 @Override 787 public void setPoolable(final boolean poolable) throws SQLException { 788 checkOpen(); 789 try { 790 statement.setPoolable(poolable); 791 } catch (final SQLException e) { 792 handleException(e); 793 } 794 } 795 796 @Override 797 public void setQueryTimeout(final int seconds) throws SQLException { 798 checkOpen(); 799 try { 800 statement.setQueryTimeout(seconds); 801 } catch (final SQLException e) { 802 handleException(e); 803 } 804 } 805 806 /** 807 * Returns a String representation of this object. 808 * 809 * @return String 810 */ 811 @Override 812 public synchronized String toString() { 813 return statement == null ? "NULL" : statement.toString(); 814 } 815 816 @Override 817 public <T> T unwrap(final Class<T> iface) throws SQLException { 818 if (iface.isAssignableFrom(getClass())) { 819 return iface.cast(this); 820 } else if (iface.isAssignableFrom(statement.getClass())) { 821 return iface.cast(statement); 822 } else { 823 return statement.unwrap(iface); 824 } 825 } 826}