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