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.Array; 020import java.sql.Blob; 021import java.sql.CallableStatement; 022import java.sql.ClientInfoStatus; 023import java.sql.Clob; 024import java.sql.Connection; 025import java.sql.DatabaseMetaData; 026import java.sql.NClob; 027import java.sql.PreparedStatement; 028import java.sql.SQLClientInfoException; 029import java.sql.SQLException; 030import java.sql.SQLWarning; 031import java.sql.SQLXML; 032import java.sql.Savepoint; 033import java.sql.Statement; 034import java.sql.Struct; 035import java.time.Duration; 036import java.time.Instant; 037import java.util.ArrayList; 038import java.util.Collections; 039import java.util.List; 040import java.util.Map; 041import java.util.Properties; 042import java.util.concurrent.Executor; 043 044import org.apache.commons.dbcp2.managed.ManagedConnection; 045 046/** 047 * A base delegating implementation of {@link Connection}. 048 * <p> 049 * All of the methods from the {@link Connection} interface simply check to see that the {@link Connection} is active, 050 * and call the corresponding method on the "delegate" provided in my constructor. 051 * </p> 052 * <p> 053 * Extends AbandonedTrace to implement Connection tracking and logging of code which created the Connection. Tracking 054 * the Connection ensures that the AbandonedObjectPool can close this connection and recycle it if its pool of 055 * connections is nearing exhaustion and this connection's last usage is older than the removeAbandonedTimeout. 056 * </p> 057 * 058 * @param <C> 059 * the Connection type 060 * 061 * @since 2.0 062 */ 063public class DelegatingConnection<C extends Connection> extends AbandonedTrace implements Connection { 064 065 private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES = Collections 066 .<String, ClientInfoStatus>emptyMap(); 067 068 /** My delegate {@link Connection}. */ 069 private volatile C connection; 070 071 private volatile boolean closed; 072 private volatile boolean cacheState = true; 073 private Boolean cachedAutoCommit; 074 private Boolean cachedReadOnly; 075 private String cachedCatalog; 076 private String cachedSchema; 077 private Duration defaultQueryTimeoutDuration; 078 079 /** 080 * Creates a wrapper for the Connection which traces this Connection in the AbandonedObjectPool. 081 * 082 * @param connection the {@link Connection} to delegate all calls to, may be null (see {@link ManagedConnection}). 083 */ 084 public DelegatingConnection(final C connection) { 085 this.connection = connection; 086 } 087 088 @Override 089 public void abort(final Executor executor) throws SQLException { 090 try { 091 Jdbc41Bridge.abort(connection, executor); 092 } catch (final SQLException e) { 093 handleException(e); 094 } 095 } 096 097 /** 098 * Marks this instance as used and delegates to a wrapped {@link DelegatingConnection#activate()}. 099 */ 100 protected void activate() { 101 closed = false; 102 setLastUsed(); 103 if (connection instanceof DelegatingConnection) { 104 ((DelegatingConnection<?>) connection).activate(); 105 } 106 } 107 108 /** 109 * Throws a SQLException if this connection is not open. 110 * 111 * @throws SQLException Thrown if this connection is not open. 112 */ 113 protected void checkOpen() throws SQLException { 114 if (closed) { 115 if (null != connection) { 116 String label; 117 try { 118 label = connection.toString(); 119 } catch (final Exception e) { 120 // leave label empty 121 label = ""; 122 } 123 throw new SQLException("Connection " + label + " is closed."); 124 } 125 throw new SQLException("Connection is null."); 126 } 127 } 128 129 /** 130 * Clears the cached state. Call when you know that the underlying connection may have been accessed 131 * directly. 132 */ 133 public void clearCachedState() { 134 cachedAutoCommit = null; 135 cachedReadOnly = null; 136 cachedSchema = null; 137 cachedCatalog = null; 138 if (connection instanceof DelegatingConnection) { 139 ((DelegatingConnection<?>) connection).clearCachedState(); 140 } 141 } 142 143 @Override 144 public void clearWarnings() throws SQLException { 145 checkOpen(); 146 try { 147 connection.clearWarnings(); 148 } catch (final SQLException e) { 149 handleException(e); 150 } 151 } 152 153 /** 154 * Closes the underlying connection, and close any Statements that were not explicitly closed. Sub-classes that 155 * override this method must: 156 * <ol> 157 * <li>Call {@link #passivate()}</li> 158 * <li>Call close (or the equivalent appropriate action) on the wrapped connection</li> 159 * <li>Set {@code closed} to {@code false}</li> 160 * </ol> 161 */ 162 @Override 163 public void close() throws SQLException { 164 if (!closed) { 165 closeInternal(); 166 } 167 } 168 169 /** 170 * Closes the underlying connection for {@link #close()}. 171 * 172 * @throws SQLException SQLException if a database access error occurs. 173 */ 174 protected final void closeInternal() throws SQLException { 175 try { 176 passivate(); 177 } finally { 178 if (connection != null) { 179 boolean connectionIsClosed; 180 try { 181 connectionIsClosed = connection.isClosed(); 182 } catch (final SQLException e) { 183 // not sure what the state is, so assume the connection is open. 184 connectionIsClosed = false; 185 } 186 try { 187 // DBCP-512: Avoid exceptions when closing a connection in multi-threaded use case. 188 // Avoid closing again, which should be a no-op, but some drivers like H2 throw an exception when 189 // closing from multiple threads. 190 if (!connectionIsClosed) { 191 connection.close(); 192 } 193 } finally { 194 closed = true; 195 } 196 } else { 197 closed = true; 198 } 199 } 200 } 201 202 @Override 203 public void commit() throws SQLException { 204 checkOpen(); 205 try { 206 connection.commit(); 207 } catch (final SQLException e) { 208 handleException(e); 209 } 210 } 211 212 @Override 213 public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException { 214 checkOpen(); 215 try { 216 return connection.createArrayOf(typeName, elements); 217 } catch (final SQLException e) { 218 handleException(e); 219 return null; 220 } 221 } 222 223 @Override 224 public Blob createBlob() throws SQLException { 225 checkOpen(); 226 try { 227 return connection.createBlob(); 228 } catch (final SQLException e) { 229 handleException(e); 230 return null; 231 } 232 } 233 234 @Override 235 public Clob createClob() throws SQLException { 236 checkOpen(); 237 try { 238 return connection.createClob(); 239 } catch (final SQLException e) { 240 handleException(e); 241 return null; 242 } 243 } 244 245 @Override 246 public NClob createNClob() throws SQLException { 247 checkOpen(); 248 try { 249 return connection.createNClob(); 250 } catch (final SQLException e) { 251 handleException(e); 252 return null; 253 } 254 } 255 256 @Override 257 public SQLXML createSQLXML() throws SQLException { 258 checkOpen(); 259 try { 260 return connection.createSQLXML(); 261 } catch (final SQLException e) { 262 handleException(e); 263 return null; 264 } 265 } 266 267 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 268 @Override 269 public Statement createStatement() throws SQLException { 270 checkOpen(); 271 try { 272 return init(new DelegatingStatement(this, connection.createStatement())); 273 } catch (final SQLException e) { 274 handleException(e); 275 return null; 276 } 277 } 278 279 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 280 @Override 281 public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException { 282 checkOpen(); 283 try { 284 return init(new DelegatingStatement(this, connection.createStatement(resultSetType, resultSetConcurrency))); 285 } catch (final SQLException e) { 286 handleException(e); 287 return null; 288 } 289 } 290 291 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 292 @Override 293 public Statement createStatement(final int resultSetType, final int resultSetConcurrency, 294 final int resultSetHoldability) throws SQLException { 295 checkOpen(); 296 try { 297 return init(new DelegatingStatement(this, 298 connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability))); 299 } catch (final SQLException e) { 300 handleException(e); 301 return null; 302 } 303 } 304 305 @Override 306 public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException { 307 checkOpen(); 308 try { 309 return connection.createStruct(typeName, attributes); 310 } catch (final SQLException e) { 311 handleException(e); 312 return null; 313 } 314 } 315 316 @Override 317 public boolean getAutoCommit() throws SQLException { 318 checkOpen(); 319 if (cacheState && cachedAutoCommit != null) { 320 return cachedAutoCommit; 321 } 322 try { 323 cachedAutoCommit = connection.getAutoCommit(); 324 return cachedAutoCommit; 325 } catch (final SQLException e) { 326 handleException(e); 327 return false; 328 } 329 } 330 331 /** 332 * Gets whether to cache properties. The cached properties are: 333 * <ul> 334 * <li>auto-commit</li> 335 * <li>catalog</li> 336 * <li>schema</li> 337 * <li>read-only</li> 338 * </ul> 339 * 340 * @return the state caching flag 341 */ 342 public boolean getCacheState() { 343 return cacheState; 344 } 345 346 @Override 347 public String getCatalog() throws SQLException { 348 checkOpen(); 349 if (cacheState && cachedCatalog != null) { 350 return cachedCatalog; 351 } 352 try { 353 cachedCatalog = connection.getCatalog(); 354 return cachedCatalog; 355 } catch (final SQLException e) { 356 handleException(e); 357 return null; 358 } 359 } 360 361 @Override 362 public Properties getClientInfo() throws SQLException { 363 checkOpen(); 364 try { 365 return connection.getClientInfo(); 366 } catch (final SQLException e) { 367 handleException(e); 368 return null; 369 } 370 } 371 372 @Override 373 public String getClientInfo(final String name) throws SQLException { 374 checkOpen(); 375 try { 376 return connection.getClientInfo(name); 377 } catch (final SQLException e) { 378 handleException(e); 379 return null; 380 } 381 } 382 383 /** 384 * Gets the default query timeout that will be used for {@link Statement}s created from this connection. 385 * {@code null} means that the driver default will be used. 386 * 387 * @return query timeout limit in seconds; zero means there is no limit. 388 * @deprecated Use {@link #getDefaultQueryTimeoutDuration()}. 389 */ 390 @Deprecated 391 public Integer getDefaultQueryTimeout() { 392 return defaultQueryTimeoutDuration == null ? null : (int) defaultQueryTimeoutDuration.getSeconds(); 393 } 394 395 /** 396 * Gets the default query timeout that will be used for {@link Statement}s created from this connection. 397 * {@code null} means that the driver default will be used. 398 * 399 * @return query timeout limit; zero means there is no limit. 400 * @since 2.10.0 401 */ 402 public Duration getDefaultQueryTimeoutDuration() { 403 return defaultQueryTimeoutDuration; 404 } 405 406 /** 407 * Returns my underlying {@link Connection}. 408 * 409 * @return my underlying {@link Connection}. 410 */ 411 public C getDelegate() { 412 return getDelegateInternal(); 413 } 414 415 /** 416 * Gets the delegate connection. 417 * 418 * @return the delegate connection. 419 */ 420 protected final C getDelegateInternal() { 421 return connection; 422 } 423 424 @Override 425 public int getHoldability() throws SQLException { 426 checkOpen(); 427 try { 428 return connection.getHoldability(); 429 } catch (final SQLException e) { 430 handleException(e); 431 return 0; 432 } 433 } 434 435 /** 436 * If my underlying {@link Connection} is not a {@link DelegatingConnection}, returns it, otherwise recursively 437 * invokes this method on my delegate. 438 * <p> 439 * Hence this method will return the first delegate that is not a {@link DelegatingConnection}, or {@code null} when 440 * no non-{@link DelegatingConnection} delegate can be found by traversing this chain. 441 * </p> 442 * <p> 443 * This method is useful when you may have nested {@link DelegatingConnection}s, and you want to make sure to obtain 444 * a "genuine" {@link Connection}. 445 * </p> 446 * 447 * @return innermost delegate. 448 */ 449 public Connection getInnermostDelegate() { 450 return getInnermostDelegateInternal(); 451 } 452 453 /** 454 * Although this method is public, it is part of the internal API and should not be used by clients. The signature 455 * of this method may change at any time including in ways that break backwards compatibility. 456 * 457 * @return innermost delegate. 458 */ 459 @SuppressWarnings("resource") 460 public final Connection getInnermostDelegateInternal() { 461 Connection conn = connection; 462 while (conn instanceof DelegatingConnection) { 463 conn = ((DelegatingConnection<?>) conn).getDelegateInternal(); 464 if (this == conn) { 465 return null; 466 } 467 } 468 return conn; 469 } 470 471 @Override 472 public DatabaseMetaData getMetaData() throws SQLException { 473 checkOpen(); 474 try { 475 return new DelegatingDatabaseMetaData(this, connection.getMetaData()); 476 } catch (final SQLException e) { 477 handleException(e); 478 return null; 479 } 480 } 481 482 @Override 483 public int getNetworkTimeout() throws SQLException { 484 checkOpen(); 485 try { 486 return Jdbc41Bridge.getNetworkTimeout(connection); 487 } catch (final SQLException e) { 488 handleException(e); 489 return 0; 490 } 491 } 492 493 @Override 494 public String getSchema() throws SQLException { 495 checkOpen(); 496 if (cacheState && cachedSchema != null) { 497 return cachedSchema; 498 } 499 try { 500 cachedSchema = Jdbc41Bridge.getSchema(connection); 501 return cachedSchema; 502 } catch (final SQLException e) { 503 handleException(e); 504 return null; 505 } 506 } 507 508 @Override 509 public int getTransactionIsolation() throws SQLException { 510 checkOpen(); 511 try { 512 return connection.getTransactionIsolation(); 513 } catch (final SQLException e) { 514 handleException(e); 515 return -1; 516 } 517 } 518 519 @Override 520 public Map<String, Class<?>> getTypeMap() throws SQLException { 521 checkOpen(); 522 try { 523 return connection.getTypeMap(); 524 } catch (final SQLException e) { 525 handleException(e); 526 return null; 527 } 528 } 529 530 @Override 531 public SQLWarning getWarnings() throws SQLException { 532 checkOpen(); 533 try { 534 return connection.getWarnings(); 535 } catch (final SQLException e) { 536 handleException(e); 537 return null; 538 } 539 } 540 541 /** 542 * Handles the given exception by throwing it. 543 * 544 * @param e the exception to throw. 545 * @throws SQLException the exception to throw. 546 */ 547 protected void handleException(final SQLException e) throws SQLException { 548 throw e; 549 } 550 551 /** 552 * Handles the given {@link SQLException}. 553 * 554 * @param <T> The throwable type. 555 * @param e The SQLException 556 * @return the given {@link SQLException} 557 * @since 2.7.0 558 */ 559 protected <T extends Throwable> T handleExceptionNoThrow(final T e) { 560 return e; 561 } 562 563 /** 564 * Initializes the given statement with this connection's settings. 565 * 566 * @param <T> The DelegatingStatement type. 567 * @param delegatingStatement The DelegatingStatement to initialize. 568 * @return The given DelegatingStatement. 569 * @throws SQLException if a database access error occurs, this method is called on a closed Statement. 570 */ 571 private <T extends DelegatingStatement> T init(final T delegatingStatement) throws SQLException { 572 if (defaultQueryTimeoutDuration != null && defaultQueryTimeoutDuration.getSeconds() != delegatingStatement.getQueryTimeout()) { 573 delegatingStatement.setQueryTimeout((int) defaultQueryTimeoutDuration.getSeconds()); 574 } 575 return delegatingStatement; 576 } 577 578 /** 579 * Compares innermost delegate to the given connection. 580 * 581 * @param c 582 * connection to compare innermost delegate with 583 * @return true if innermost delegate equals {@code c} 584 */ 585 @SuppressWarnings("resource") 586 public boolean innermostDelegateEquals(final Connection c) { 587 final Connection innerCon = getInnermostDelegateInternal(); 588 if (innerCon == null) { 589 return c == null; 590 } 591 return innerCon.equals(c); 592 } 593 594 @Override 595 public boolean isClosed() throws SQLException { 596 return closed || connection == null || connection.isClosed(); 597 } 598 599 /** 600 * Tests the raw internal closed state. 601 * 602 * @return the raw internal closed state. 603 */ 604 protected boolean isClosedInternal() { 605 return closed; 606 } 607 608 @Override 609 public boolean isReadOnly() throws SQLException { 610 checkOpen(); 611 if (cacheState && cachedReadOnly != null) { 612 return cachedReadOnly; 613 } 614 try { 615 cachedReadOnly = connection.isReadOnly(); 616 return cachedReadOnly; 617 } catch (final SQLException e) { 618 handleException(e); 619 return false; 620 } 621 } 622 623 /** 624 * Tests if the connection has not been closed and is still valid. 625 * 626 * @param timeout The duration to wait for the database operation used to validate the connection to complete. 627 * @return See {@link Connection#isValid(int)}. 628 * @throws SQLException See {@link Connection#isValid(int)}. 629 * @see Connection#isValid(int) 630 * @since 2.10.0 631 */ 632 public boolean isValid(final Duration timeout) throws SQLException { 633 if (isClosed()) { 634 return false; 635 } 636 try { 637 return connection.isValid((int) timeout.getSeconds()); 638 } catch (final SQLException e) { 639 handleException(e); 640 return false; 641 } 642 } 643 644 /** 645 * @deprecated Use {@link #isValid(Duration)}. 646 */ 647 @Override 648 @Deprecated 649 public boolean isValid(final int timeoutSeconds) throws SQLException { 650 return isValid(Duration.ofSeconds(timeoutSeconds)); 651 } 652 653 @Override 654 public boolean isWrapperFor(final Class<?> iface) throws SQLException { 655 if (iface.isAssignableFrom(getClass()) || iface.isAssignableFrom(connection.getClass())) { 656 return true; 657 } 658 return connection.isWrapperFor(iface); 659 } 660 661 @Override 662 public String nativeSQL(final String sql) throws SQLException { 663 checkOpen(); 664 try { 665 return connection.nativeSQL(sql); 666 } catch (final SQLException e) { 667 handleException(e); 668 return null; 669 } 670 } 671 672 /** 673 * Clears the list of objects being traced by this object. 674 * 675 * @throws SQLException Thrown if not all traced objects were closed. 676 */ 677 protected void passivate() throws SQLException { 678 // The JDBC specification requires that a Connection close any open 679 // Statements when it is closed. 680 // DBCP-288. Not all the traced objects will be statements 681 final List<AbandonedTrace> traceList = getTrace(); 682 if (!Utils.isEmpty(traceList)) { 683 final List<Exception> thrownList = new ArrayList<>(); 684 traceList.forEach(trace -> trace.close(thrownList::add)); 685 clearTrace(); 686 if (!thrownList.isEmpty()) { 687 throw new SQLExceptionList(thrownList); 688 } 689 } 690 setLastUsed(Instant.EPOCH); 691 } 692 693 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 694 @Override 695 public CallableStatement prepareCall(final String sql) throws SQLException { 696 checkOpen(); 697 try { 698 return init(new DelegatingCallableStatement(this, connection.prepareCall(sql))); 699 } catch (final SQLException e) { 700 handleException(e); 701 return null; 702 } 703 } 704 705 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 706 @Override 707 public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) 708 throws SQLException { 709 checkOpen(); 710 try { 711 return init(new DelegatingCallableStatement(this, 712 connection.prepareCall(sql, resultSetType, resultSetConcurrency))); 713 } catch (final SQLException e) { 714 handleException(e); 715 return null; 716 } 717 } 718 719 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 720 @Override 721 public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency, 722 final int resultSetHoldability) throws SQLException { 723 checkOpen(); 724 try { 725 return init(new DelegatingCallableStatement(this, 726 connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability))); 727 } catch (final SQLException e) { 728 handleException(e); 729 return null; 730 } 731 } 732 733 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 734 @Override 735 public PreparedStatement prepareStatement(final String sql) throws SQLException { 736 checkOpen(); 737 try { 738 return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql))); 739 } catch (final SQLException e) { 740 handleException(e); 741 return null; 742 } 743 } 744 745 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 746 @Override 747 public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException { 748 checkOpen(); 749 try { 750 return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, autoGeneratedKeys))); 751 } catch (final SQLException e) { 752 handleException(e); 753 return null; 754 } 755 } 756 757 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 758 @Override 759 public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) 760 throws SQLException { 761 checkOpen(); 762 try { 763 return init(new DelegatingPreparedStatement(this, 764 connection.prepareStatement(sql, resultSetType, resultSetConcurrency))); 765 } catch (final SQLException e) { 766 handleException(e); 767 return null; 768 } 769 } 770 771 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 772 @Override 773 public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency, 774 final int resultSetHoldability) throws SQLException { 775 checkOpen(); 776 try { 777 return init(new DelegatingPreparedStatement(this, 778 connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability))); 779 } catch (final SQLException e) { 780 handleException(e); 781 return null; 782 } 783 } 784 785 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 786 @Override 787 public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException { 788 checkOpen(); 789 try { 790 return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnIndexes))); 791 } catch (final SQLException e) { 792 handleException(e); 793 return null; 794 } 795 } 796 797 @SuppressWarnings("resource") // Caller is responsible for closing the resource. 798 @Override 799 public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException { 800 checkOpen(); 801 try { 802 return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnNames))); 803 } catch (final SQLException e) { 804 handleException(e); 805 return null; 806 } 807 } 808 809 @Override 810 public void releaseSavepoint(final Savepoint savepoint) throws SQLException { 811 checkOpen(); 812 try { 813 connection.releaseSavepoint(savepoint); 814 } catch (final SQLException e) { 815 handleException(e); 816 } 817 } 818 819 @Override 820 public void rollback() throws SQLException { 821 checkOpen(); 822 try { 823 connection.rollback(); 824 } catch (final SQLException e) { 825 handleException(e); 826 } 827 } 828 829 @Override 830 public void rollback(final Savepoint savepoint) throws SQLException { 831 checkOpen(); 832 try { 833 connection.rollback(savepoint); 834 } catch (final SQLException e) { 835 handleException(e); 836 } 837 } 838 839 @Override 840 public void setAutoCommit(final boolean autoCommit) throws SQLException { 841 checkOpen(); 842 try { 843 connection.setAutoCommit(autoCommit); 844 if (cacheState) { 845 cachedAutoCommit = connection.getAutoCommit(); 846 } 847 } catch (final SQLException e) { 848 cachedAutoCommit = null; 849 handleException(e); 850 } 851 } 852 853 /** 854 * Sets whether to cache properties. The cached properties are: 855 * <ul> 856 * <li>auto-commit</li> 857 * <li>catalog</li> 858 * <li>schema</li> 859 * <li>read-only</li> 860 * </ul> 861 * 862 * @param cacheState The new value for the state caching flag 863 */ 864 public void setCacheState(final boolean cacheState) { 865 this.cacheState = cacheState; 866 } 867 868 @Override 869 public void setCatalog(final String catalog) throws SQLException { 870 checkOpen(); 871 try { 872 connection.setCatalog(catalog); 873 if (cacheState) { 874 cachedCatalog = connection.getCatalog(); 875 } 876 } catch (final SQLException e) { 877 cachedCatalog = null; 878 handleException(e); 879 } 880 } 881 882 @Override 883 public void setClientInfo(final Properties properties) throws SQLClientInfoException { 884 try { 885 checkOpen(); 886 connection.setClientInfo(properties); 887 } catch (final SQLClientInfoException e) { 888 throw e; 889 } catch (final SQLException e) { 890 throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e); 891 } 892 } 893 894 @Override 895 public void setClientInfo(final String name, final String value) throws SQLClientInfoException { 896 try { 897 checkOpen(); 898 connection.setClientInfo(name, value); 899 } catch (final SQLClientInfoException e) { 900 throw e; 901 } catch (final SQLException e) { 902 throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e); 903 } 904 } 905 906 /** 907 * Sets the raw internal closed state. 908 * 909 * @param closed the raw internal closed state. 910 */ 911 protected void setClosedInternal(final boolean closed) { 912 this.closed = closed; 913 } 914 915 /** 916 * Sets the default query timeout that will be used for {@link Statement}s created from this connection. 917 * {@code null} means that the driver default will be used. 918 * 919 * @param defaultQueryTimeoutDuration 920 * the new query timeout limit Duration; zero means there is no limit. 921 * @since 2.10.0 922 */ 923 public void setDefaultQueryTimeout(final Duration defaultQueryTimeoutDuration) { 924 this.defaultQueryTimeoutDuration = defaultQueryTimeoutDuration; 925 } 926 927 /** 928 * Sets the default query timeout that will be used for {@link Statement}s created from this connection. 929 * {@code null} means that the driver default will be used. 930 * 931 * @param defaultQueryTimeoutSeconds 932 * the new query timeout limit in seconds; zero means there is no limit. 933 * @deprecated Use {@link #setDefaultQueryTimeout(Duration)}. 934 */ 935 @Deprecated 936 public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) { 937 this.defaultQueryTimeoutDuration = defaultQueryTimeoutSeconds == null ? null : Duration.ofSeconds(defaultQueryTimeoutSeconds); 938 } 939 940 /** 941 * Sets my delegate. 942 * 943 * @param connection 944 * my delegate, may be null. 945 */ 946 public void setDelegate(final C connection) { 947 this.connection = connection; 948 } 949 950 @Override 951 public void setHoldability(final int holdability) throws SQLException { 952 checkOpen(); 953 try { 954 connection.setHoldability(holdability); 955 } catch (final SQLException e) { 956 handleException(e); 957 } 958 } 959 960 @Override 961 public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException { 962 checkOpen(); 963 try { 964 Jdbc41Bridge.setNetworkTimeout(connection, executor, milliseconds); 965 } catch (final SQLException e) { 966 handleException(e); 967 } 968 } 969 970 @Override 971 public void setReadOnly(final boolean readOnly) throws SQLException { 972 checkOpen(); 973 try { 974 connection.setReadOnly(readOnly); 975 if (cacheState) { 976 cachedReadOnly = connection.isReadOnly(); 977 } 978 } catch (final SQLException e) { 979 cachedReadOnly = null; 980 handleException(e); 981 } 982 } 983 984 @Override 985 public Savepoint setSavepoint() throws SQLException { 986 checkOpen(); 987 try { 988 return connection.setSavepoint(); 989 } catch (final SQLException e) { 990 handleException(e); 991 return null; 992 } 993 } 994 995 @Override 996 public Savepoint setSavepoint(final String name) throws SQLException { 997 checkOpen(); 998 try { 999 return connection.setSavepoint(name); 1000 } catch (final SQLException e) { 1001 handleException(e); 1002 return null; 1003 } 1004 } 1005 1006 @Override 1007 public void setSchema(final String schema) throws SQLException { 1008 checkOpen(); 1009 try { 1010 Jdbc41Bridge.setSchema(connection, schema); 1011 if (cacheState) { 1012 cachedSchema = Jdbc41Bridge.getSchema(connection); 1013 } 1014 } catch (final SQLException e) { 1015 cachedSchema = null; 1016 handleException(e); 1017 } 1018 } 1019 1020 @Override 1021 public void setTransactionIsolation(final int level) throws SQLException { 1022 checkOpen(); 1023 try { 1024 connection.setTransactionIsolation(level); 1025 } catch (final SQLException e) { 1026 handleException(e); 1027 } 1028 } 1029 1030 @Override 1031 public void setTypeMap(final Map<String, Class<?>> map) throws SQLException { 1032 checkOpen(); 1033 try { 1034 connection.setTypeMap(map); 1035 } catch (final SQLException e) { 1036 handleException(e); 1037 } 1038 } 1039 1040 /** 1041 * Returns a string representation of the metadata associated with the innermost delegate connection. 1042 */ 1043 @SuppressWarnings("resource") 1044 @Override 1045 public synchronized String toString() { 1046 String str = null; 1047 1048 final Connection conn = this.getInnermostDelegateInternal(); 1049 if (conn != null) { 1050 try { 1051 if (conn.isClosed()) { 1052 str = "connection is closed"; 1053 } else { 1054 final StringBuilder sb = new StringBuilder(); 1055 sb.append(hashCode()); 1056 final DatabaseMetaData meta = conn.getMetaData(); 1057 if (meta != null) { 1058 sb.append(", URL="); 1059 sb.append(meta.getURL()); 1060 sb.append(", "); 1061 sb.append(meta.getDriverName()); 1062 str = sb.toString(); 1063 } 1064 } 1065 } catch (final SQLException ignored) { 1066 // Ignore 1067 } 1068 } 1069 return str != null ? str : super.toString(); 1070 } 1071 1072 @Override 1073 public <T> T unwrap(final Class<T> iface) throws SQLException { 1074 if (iface.isAssignableFrom(getClass())) { 1075 return iface.cast(this); 1076 } 1077 if (iface.isAssignableFrom(connection.getClass())) { 1078 return iface.cast(connection); 1079 } 1080 return connection.unwrap(iface); 1081 } 1082}