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