DelegatingConnection.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.dbcp2;

  18. import java.sql.Array;
  19. import java.sql.Blob;
  20. import java.sql.CallableStatement;
  21. import java.sql.ClientInfoStatus;
  22. import java.sql.Clob;
  23. import java.sql.Connection;
  24. import java.sql.DatabaseMetaData;
  25. import java.sql.NClob;
  26. import java.sql.PreparedStatement;
  27. import java.sql.SQLClientInfoException;
  28. import java.sql.SQLException;
  29. import java.sql.SQLWarning;
  30. import java.sql.SQLXML;
  31. import java.sql.Savepoint;
  32. import java.sql.Statement;
  33. import java.sql.Struct;
  34. import java.time.Duration;
  35. import java.time.Instant;
  36. import java.util.ArrayList;
  37. import java.util.Collections;
  38. import java.util.List;
  39. import java.util.Map;
  40. import java.util.Properties;
  41. import java.util.concurrent.Executor;

  42. import org.apache.commons.dbcp2.managed.ManagedConnection;

  43. /**
  44.  * A base delegating implementation of {@link Connection}.
  45.  * <p>
  46.  * All of the methods from the {@link Connection} interface simply check to see that the {@link Connection} is active,
  47.  * and call the corresponding method on the "delegate" provided in my constructor.
  48.  * </p>
  49.  * <p>
  50.  * Extends AbandonedTrace to implement Connection tracking and logging of code which created the Connection. Tracking
  51.  * the Connection ensures that the AbandonedObjectPool can close this connection and recycle it if its pool of
  52.  * connections is nearing exhaustion and this connection's last usage is older than the removeAbandonedTimeout.
  53.  * </p>
  54.  *
  55.  * @param <C>
  56.  *            the Connection type
  57.  *
  58.  * @since 2.0
  59.  */
  60. public class DelegatingConnection<C extends Connection> extends AbandonedTrace implements Connection {

  61.     private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES = Collections
  62.             .<String, ClientInfoStatus>emptyMap();

  63.     /** My delegate {@link Connection}. */
  64.     private volatile C connection;

  65.     private volatile boolean closed;

  66.     private boolean cacheState = true;
  67.     private Boolean cachedAutoCommit;
  68.     private Boolean cachedReadOnly;
  69.     private String cachedCatalog;
  70.     private String cachedSchema;
  71.     private Duration defaultQueryTimeoutDuration;

  72.     /**
  73.      * Creates a wrapper for the Connection which traces this Connection in the AbandonedObjectPool.
  74.      *
  75.      * @param connection the {@link Connection} to delegate all calls to, may be null (see {@link ManagedConnection}).
  76.      */
  77.     public DelegatingConnection(final C connection) {
  78.         this.connection = connection;
  79.     }

  80.     @Override
  81.     public void abort(final Executor executor) throws SQLException {
  82.         try {
  83.             Jdbc41Bridge.abort(connection, executor);
  84.         } catch (final SQLException e) {
  85.             handleException(e);
  86.         }
  87.     }

  88.     /**
  89.      * Marks this instance as used and delegates to a wrapped {@link DelegatingConnection#activate()}.
  90.      */
  91.     protected void activate() {
  92.         closed = false;
  93.         setLastUsed();
  94.         if (connection instanceof DelegatingConnection) {
  95.             ((DelegatingConnection<?>) connection).activate();
  96.         }
  97.     }

  98.     /**
  99.      * Throws a SQLException if this connection is not open.
  100.      *
  101.      * @throws SQLException Thrown if this connection is not open.
  102.      */
  103.     protected void checkOpen() throws SQLException {
  104.         if (closed) {
  105.             if (null != connection) {
  106.                 String label;
  107.                 try {
  108.                     label = connection.toString();
  109.                 } catch (final Exception e) {
  110.                     // leave label empty
  111.                     label = "";
  112.                 }
  113.                 throw new SQLException("Connection " + label + " is closed.");
  114.             }
  115.             throw new SQLException("Connection is null.");
  116.         }
  117.     }

  118.     /**
  119.      * Clears the cached state. Call when you know that the underlying connection may have been accessed
  120.      * directly.
  121.      */
  122.     public void clearCachedState() {
  123.         cachedAutoCommit = null;
  124.         cachedReadOnly = null;
  125.         cachedSchema = null;
  126.         cachedCatalog = null;
  127.         if (connection instanceof DelegatingConnection) {
  128.             ((DelegatingConnection<?>) connection).clearCachedState();
  129.         }
  130.     }

  131.     @Override
  132.     public void clearWarnings() throws SQLException {
  133.         checkOpen();
  134.         try {
  135.             connection.clearWarnings();
  136.         } catch (final SQLException e) {
  137.             handleException(e);
  138.         }
  139.     }

  140.     /**
  141.      * Closes the underlying connection, and close any Statements that were not explicitly closed. Sub-classes that
  142.      * override this method must:
  143.      * <ol>
  144.      * <li>Call {@link #passivate()}</li>
  145.      * <li>Call close (or the equivalent appropriate action) on the wrapped connection</li>
  146.      * <li>Set {@code closed} to {@code false}</li>
  147.      * </ol>
  148.      */
  149.     @Override
  150.     public void close() throws SQLException {
  151.         if (!closed) {
  152.             closeInternal();
  153.         }
  154.     }

  155.     protected final void closeInternal() throws SQLException {
  156.         try {
  157.             passivate();
  158.         } finally {
  159.             if (connection != null) {
  160.                 boolean connectionIsClosed;
  161.                 try {
  162.                     connectionIsClosed = connection.isClosed();
  163.                 } catch (final SQLException e) {
  164.                     // not sure what the state is, so assume the connection is open.
  165.                     connectionIsClosed = false;
  166.                 }
  167.                 try {
  168.                     // DBCP-512: Avoid exceptions when closing a connection in multi-threaded use case.
  169.                     // Avoid closing again, which should be a no-op, but some drivers like H2 throw an exception when
  170.                     // closing from multiple threads.
  171.                     if (!connectionIsClosed) {
  172.                         connection.close();
  173.                     }
  174.                 } finally {
  175.                     closed = true;
  176.                 }
  177.             } else {
  178.                 closed = true;
  179.             }
  180.         }
  181.     }

  182.     @Override
  183.     public void commit() throws SQLException {
  184.         checkOpen();
  185.         try {
  186.             connection.commit();
  187.         } catch (final SQLException e) {
  188.             handleException(e);
  189.         }
  190.     }

  191.     @Override
  192.     public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
  193.         checkOpen();
  194.         try {
  195.             return connection.createArrayOf(typeName, elements);
  196.         } catch (final SQLException e) {
  197.             handleException(e);
  198.             return null;
  199.         }
  200.     }

  201.     @Override
  202.     public Blob createBlob() throws SQLException {
  203.         checkOpen();
  204.         try {
  205.             return connection.createBlob();
  206.         } catch (final SQLException e) {
  207.             handleException(e);
  208.             return null;
  209.         }
  210.     }

  211.     @Override
  212.     public Clob createClob() throws SQLException {
  213.         checkOpen();
  214.         try {
  215.             return connection.createClob();
  216.         } catch (final SQLException e) {
  217.             handleException(e);
  218.             return null;
  219.         }
  220.     }

  221.     @Override
  222.     public NClob createNClob() throws SQLException {
  223.         checkOpen();
  224.         try {
  225.             return connection.createNClob();
  226.         } catch (final SQLException e) {
  227.             handleException(e);
  228.             return null;
  229.         }
  230.     }

  231.     @Override
  232.     public SQLXML createSQLXML() throws SQLException {
  233.         checkOpen();
  234.         try {
  235.             return connection.createSQLXML();
  236.         } catch (final SQLException e) {
  237.             handleException(e);
  238.             return null;
  239.         }
  240.     }

  241.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  242.     @Override
  243.     public Statement createStatement() throws SQLException {
  244.         checkOpen();
  245.         try {
  246.             return init(new DelegatingStatement(this, connection.createStatement()));
  247.         } catch (final SQLException e) {
  248.             handleException(e);
  249.             return null;
  250.         }
  251.     }

  252.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  253.     @Override
  254.     public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException {
  255.         checkOpen();
  256.         try {
  257.             return init(new DelegatingStatement(this, connection.createStatement(resultSetType, resultSetConcurrency)));
  258.         } catch (final SQLException e) {
  259.             handleException(e);
  260.             return null;
  261.         }
  262.     }

  263.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  264.     @Override
  265.     public Statement createStatement(final int resultSetType, final int resultSetConcurrency,
  266.         final int resultSetHoldability) throws SQLException {
  267.         checkOpen();
  268.         try {
  269.             return init(new DelegatingStatement(this,
  270.                 connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)));
  271.         } catch (final SQLException e) {
  272.             handleException(e);
  273.             return null;
  274.         }
  275.     }

  276.     @Override
  277.     public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException {
  278.         checkOpen();
  279.         try {
  280.             return connection.createStruct(typeName, attributes);
  281.         } catch (final SQLException e) {
  282.             handleException(e);
  283.             return null;
  284.         }
  285.     }

  286.     @Override
  287.     public boolean getAutoCommit() throws SQLException {
  288.         checkOpen();
  289.         if (cacheState && cachedAutoCommit != null) {
  290.             return cachedAutoCommit;
  291.         }
  292.         try {
  293.             cachedAutoCommit = connection.getAutoCommit();
  294.             return cachedAutoCommit;
  295.         } catch (final SQLException e) {
  296.             handleException(e);
  297.             return false;
  298.         }
  299.     }

  300.     /**
  301.      * Gets whether to cache properties. The cached properties are:
  302.      * <ul>
  303.      * <li>auto-commit</li>
  304.      * <li>catalog</li>
  305.      * <li>schema</li>
  306.      * <li>read-only</li>
  307.      * </ul>
  308.      *
  309.      * @return the state caching flag
  310.      */
  311.     public boolean getCacheState() {
  312.         return cacheState;
  313.     }

  314.     @Override
  315.     public String getCatalog() throws SQLException {
  316.         checkOpen();
  317.         if (cacheState && cachedCatalog != null) {
  318.             return cachedCatalog;
  319.         }
  320.         try {
  321.             cachedCatalog = connection.getCatalog();
  322.             return cachedCatalog;
  323.         } catch (final SQLException e) {
  324.             handleException(e);
  325.             return null;
  326.         }
  327.     }

  328.     @Override
  329.     public Properties getClientInfo() throws SQLException {
  330.         checkOpen();
  331.         try {
  332.             return connection.getClientInfo();
  333.         } catch (final SQLException e) {
  334.             handleException(e);
  335.             return null;
  336.         }
  337.     }

  338.     @Override
  339.     public String getClientInfo(final String name) throws SQLException {
  340.         checkOpen();
  341.         try {
  342.             return connection.getClientInfo(name);
  343.         } catch (final SQLException e) {
  344.             handleException(e);
  345.             return null;
  346.         }
  347.     }

  348.     /**
  349.      * Gets the default query timeout that will be used for {@link Statement}s created from this connection.
  350.      * {@code null} means that the driver default will be used.
  351.      *
  352.      * @return query timeout limit in seconds; zero means there is no limit.
  353.      * @deprecated Use {@link #getDefaultQueryTimeoutDuration()}.
  354.      */
  355.     @Deprecated
  356.     public Integer getDefaultQueryTimeout() {
  357.         return defaultQueryTimeoutDuration == null ? null : (int) defaultQueryTimeoutDuration.getSeconds();
  358.     }

  359.     /**
  360.      * Gets the default query timeout that will be used for {@link Statement}s created from this connection.
  361.      * {@code null} means that the driver default will be used.
  362.      *
  363.      * @return query timeout limit; zero means there is no limit.
  364.      * @since 2.10.0
  365.      */
  366.     public Duration getDefaultQueryTimeoutDuration() {
  367.         return defaultQueryTimeoutDuration;
  368.     }

  369.     /**
  370.      * Returns my underlying {@link Connection}.
  371.      *
  372.      * @return my underlying {@link Connection}.
  373.      */
  374.     public C getDelegate() {
  375.         return getDelegateInternal();
  376.     }

  377.     /**
  378.      * Gets the delegate connection.
  379.      *
  380.      * @return the delegate connection.
  381.      */
  382.     protected final C getDelegateInternal() {
  383.         return connection;
  384.     }

  385.     @Override
  386.     public int getHoldability() throws SQLException {
  387.         checkOpen();
  388.         try {
  389.             return connection.getHoldability();
  390.         } catch (final SQLException e) {
  391.             handleException(e);
  392.             return 0;
  393.         }
  394.     }

  395.     /**
  396.      * If my underlying {@link Connection} is not a {@code DelegatingConnection}, returns it, otherwise recursively
  397.      * invokes this method on my delegate.
  398.      * <p>
  399.      * Hence this method will return the first delegate that is not a {@code DelegatingConnection}, or {@code null} when
  400.      * no non-{@code DelegatingConnection} delegate can be found by traversing this chain.
  401.      * </p>
  402.      * <p>
  403.      * This method is useful when you may have nested {@code DelegatingConnection}s, and you want to make sure to obtain
  404.      * a "genuine" {@link Connection}.
  405.      * </p>
  406.      *
  407.      * @return innermost delegate.
  408.      */
  409.     public Connection getInnermostDelegate() {
  410.         return getInnermostDelegateInternal();
  411.     }

  412.     /**
  413.      * Although this method is public, it is part of the internal API and should not be used by clients. The signature
  414.      * of this method may change at any time including in ways that break backwards compatibility.
  415.      *
  416.      * @return innermost delegate.
  417.      */
  418.     @SuppressWarnings("resource")
  419.     public final Connection getInnermostDelegateInternal() {
  420.         Connection conn = connection;
  421.         while (conn instanceof DelegatingConnection) {
  422.             conn = ((DelegatingConnection<?>) conn).getDelegateInternal();
  423.             if (this == conn) {
  424.                 return null;
  425.             }
  426.         }
  427.         return conn;
  428.     }

  429.     @Override
  430.     public DatabaseMetaData getMetaData() throws SQLException {
  431.         checkOpen();
  432.         try {
  433.             return new DelegatingDatabaseMetaData(this, connection.getMetaData());
  434.         } catch (final SQLException e) {
  435.             handleException(e);
  436.             return null;
  437.         }
  438.     }

  439.     @Override
  440.     public int getNetworkTimeout() throws SQLException {
  441.         checkOpen();
  442.         try {
  443.             return Jdbc41Bridge.getNetworkTimeout(connection);
  444.         } catch (final SQLException e) {
  445.             handleException(e);
  446.             return 0;
  447.         }
  448.     }

  449.     @Override
  450.     public String getSchema() throws SQLException {
  451.         checkOpen();
  452.         if (cacheState && cachedSchema != null) {
  453.             return cachedSchema;
  454.         }
  455.         try {
  456.             cachedSchema = Jdbc41Bridge.getSchema(connection);
  457.             return cachedSchema;
  458.         } catch (final SQLException e) {
  459.             handleException(e);
  460.             return null;
  461.         }
  462.     }

  463.     @Override
  464.     public int getTransactionIsolation() throws SQLException {
  465.         checkOpen();
  466.         try {
  467.             return connection.getTransactionIsolation();
  468.         } catch (final SQLException e) {
  469.             handleException(e);
  470.             return -1;
  471.         }
  472.     }

  473.     @Override
  474.     public Map<String, Class<?>> getTypeMap() throws SQLException {
  475.         checkOpen();
  476.         try {
  477.             return connection.getTypeMap();
  478.         } catch (final SQLException e) {
  479.             handleException(e);
  480.             return null;
  481.         }
  482.     }

  483.     @Override
  484.     public SQLWarning getWarnings() throws SQLException {
  485.         checkOpen();
  486.         try {
  487.             return connection.getWarnings();
  488.         } catch (final SQLException e) {
  489.             handleException(e);
  490.             return null;
  491.         }
  492.     }

  493.     /**
  494.      * Handles the given exception by throwing it.
  495.      *
  496.      * @param e the exception to throw.
  497.      * @throws SQLException the exception to throw.
  498.      */
  499.     protected void handleException(final SQLException e) throws SQLException {
  500.         throw e;
  501.     }

  502.     /**
  503.      * Handles the given {@code SQLException}.
  504.      *
  505.      * @param <T> The throwable type.
  506.      * @param e   The SQLException
  507.      * @return the given {@code SQLException}
  508.      * @since 2.7.0
  509.      */
  510.     protected <T extends Throwable> T handleExceptionNoThrow(final T e) {
  511.         return e;
  512.     }

  513.     /**
  514.      * Initializes the given statement with this connection's settings.
  515.      *
  516.      * @param <T> The DelegatingStatement type.
  517.      * @param delegatingStatement The DelegatingStatement to initialize.
  518.      * @return The given DelegatingStatement.
  519.      * @throws SQLException if a database access error occurs, this method is called on a closed Statement.
  520.      */
  521.     private <T extends DelegatingStatement> T init(final T delegatingStatement) throws SQLException {
  522.         if (defaultQueryTimeoutDuration != null && defaultQueryTimeoutDuration.getSeconds() != delegatingStatement.getQueryTimeout()) {
  523.             delegatingStatement.setQueryTimeout((int) defaultQueryTimeoutDuration.getSeconds());
  524.         }
  525.         return delegatingStatement;
  526.     }

  527.     /**
  528.      * Compares innermost delegate to the given connection.
  529.      *
  530.      * @param c
  531.      *            connection to compare innermost delegate with
  532.      * @return true if innermost delegate equals {@code c}
  533.      */
  534.     @SuppressWarnings("resource")
  535.     public boolean innermostDelegateEquals(final Connection c) {
  536.         final Connection innerCon = getInnermostDelegateInternal();
  537.         if (innerCon == null) {
  538.             return c == null;
  539.         }
  540.         return innerCon.equals(c);
  541.     }

  542.     @Override
  543.     public boolean isClosed() throws SQLException {
  544.         return closed || connection == null || connection.isClosed();
  545.     }

  546.     /**
  547.      * Tests the raw internal closed state.
  548.      *
  549.      * @return the raw internal closed state.
  550.      */
  551.     protected boolean isClosedInternal() {
  552.         return closed;
  553.     }

  554.     @Override
  555.     public boolean isReadOnly() throws SQLException {
  556.         checkOpen();
  557.         if (cacheState && cachedReadOnly != null) {
  558.             return cachedReadOnly;
  559.         }
  560.         try {
  561.             cachedReadOnly = connection.isReadOnly();
  562.             return cachedReadOnly;
  563.         } catch (final SQLException e) {
  564.             handleException(e);
  565.             return false;
  566.         }
  567.     }

  568.     /**
  569.      * Tests if the connection has not been closed and is still valid.
  570.      *
  571.      * @param timeout The duration to wait for the database operation used to validate the connection to complete.
  572.      * @return See {@link Connection#isValid(int)}.
  573.      * @throws SQLException See {@link Connection#isValid(int)}.
  574.      * @see Connection#isValid(int)
  575.      * @since 2.10.0
  576.      */
  577.     public boolean isValid(final Duration timeout) throws SQLException {
  578.         if (isClosed()) {
  579.             return false;
  580.         }
  581.         try {
  582.             return connection.isValid((int) timeout.getSeconds());
  583.         } catch (final SQLException e) {
  584.             handleException(e);
  585.             return false;
  586.         }
  587.     }

  588.     /**
  589.      * @deprecated Use {@link #isValid(Duration)}.
  590.      */
  591.     @Override
  592.     @Deprecated
  593.     public boolean isValid(final int timeoutSeconds) throws SQLException {
  594.         return isValid(Duration.ofSeconds(timeoutSeconds));
  595.     }

  596.     @Override
  597.     public boolean isWrapperFor(final Class<?> iface) throws SQLException {
  598.         if (iface.isAssignableFrom(getClass())) {
  599.             return true;
  600.         }
  601.         if (iface.isAssignableFrom(connection.getClass())) {
  602.             return true;
  603.         }
  604.         return connection.isWrapperFor(iface);
  605.     }

  606.     @Override
  607.     public String nativeSQL(final String sql) throws SQLException {
  608.         checkOpen();
  609.         try {
  610.             return connection.nativeSQL(sql);
  611.         } catch (final SQLException e) {
  612.             handleException(e);
  613.             return null;
  614.         }
  615.     }

  616.     /**
  617.      * Clears the list of objects being traced by this object.
  618.      *
  619.      * @throws SQLException Thrown if not all traced objects were closed.
  620.      */
  621.     protected void passivate() throws SQLException {
  622.         // The JDBC specification requires that a Connection close any open
  623.         // Statements when it is closed.
  624.         // DBCP-288. Not all the traced objects will be statements
  625.         final List<AbandonedTrace> traceList = getTrace();
  626.         if (!Utils.isEmpty(traceList)) {
  627.             final List<Exception> thrownList = new ArrayList<>();
  628.             traceList.forEach(trace -> trace.close(thrownList::add));
  629.             clearTrace();
  630.             if (!thrownList.isEmpty()) {
  631.                 throw new SQLExceptionList(thrownList);
  632.             }
  633.         }
  634.         setLastUsed(Instant.EPOCH);
  635.     }

  636.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  637.     @Override
  638.     public CallableStatement prepareCall(final String sql) throws SQLException {
  639.         checkOpen();
  640.         try {
  641.             return init(new DelegatingCallableStatement(this, connection.prepareCall(sql)));
  642.         } catch (final SQLException e) {
  643.             handleException(e);
  644.             return null;
  645.         }
  646.     }

  647.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  648.     @Override
  649.     public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
  650.         throws SQLException {
  651.         checkOpen();
  652.         try {
  653.             return init(new DelegatingCallableStatement(this,
  654.                 connection.prepareCall(sql, resultSetType, resultSetConcurrency)));
  655.         } catch (final SQLException e) {
  656.             handleException(e);
  657.             return null;
  658.         }
  659.     }

  660.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  661.     @Override
  662.     public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
  663.         final int resultSetHoldability) throws SQLException {
  664.         checkOpen();
  665.         try {
  666.             return init(new DelegatingCallableStatement(this,
  667.                 connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
  668.         } catch (final SQLException e) {
  669.             handleException(e);
  670.             return null;
  671.         }
  672.     }

  673.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  674.     @Override
  675.     public PreparedStatement prepareStatement(final String sql) throws SQLException {
  676.         checkOpen();
  677.         try {
  678.             return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql)));
  679.         } catch (final SQLException e) {
  680.             handleException(e);
  681.             return null;
  682.         }
  683.     }

  684.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  685.     @Override
  686.     public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
  687.         checkOpen();
  688.         try {
  689.             return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, autoGeneratedKeys)));
  690.         } catch (final SQLException e) {
  691.             handleException(e);
  692.             return null;
  693.         }
  694.     }

  695.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  696.     @Override
  697.     public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
  698.         throws SQLException {
  699.         checkOpen();
  700.         try {
  701.             return init(new DelegatingPreparedStatement(this,
  702.                 connection.prepareStatement(sql, resultSetType, resultSetConcurrency)));
  703.         } catch (final SQLException e) {
  704.             handleException(e);
  705.             return null;
  706.         }
  707.     }

  708.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  709.     @Override
  710.     public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
  711.         final int resultSetHoldability) throws SQLException {
  712.         checkOpen();
  713.         try {
  714.             return init(new DelegatingPreparedStatement(this,
  715.                 connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
  716.         } catch (final SQLException e) {
  717.             handleException(e);
  718.             return null;
  719.         }
  720.     }

  721.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  722.     @Override
  723.     public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
  724.         checkOpen();
  725.         try {
  726.             return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnIndexes)));
  727.         } catch (final SQLException e) {
  728.             handleException(e);
  729.             return null;
  730.         }
  731.     }

  732.     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
  733.     @Override
  734.     public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
  735.         checkOpen();
  736.         try {
  737.             return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnNames)));
  738.         } catch (final SQLException e) {
  739.             handleException(e);
  740.             return null;
  741.         }
  742.     }

  743.     @Override
  744.     public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
  745.         checkOpen();
  746.         try {
  747.             connection.releaseSavepoint(savepoint);
  748.         } catch (final SQLException e) {
  749.             handleException(e);
  750.         }
  751.     }

  752.     @Override
  753.     public void rollback() throws SQLException {
  754.         checkOpen();
  755.         try {
  756.             connection.rollback();
  757.         } catch (final SQLException e) {
  758.             handleException(e);
  759.         }
  760.     }

  761.     @Override
  762.     public void rollback(final Savepoint savepoint) throws SQLException {
  763.         checkOpen();
  764.         try {
  765.             connection.rollback(savepoint);
  766.         } catch (final SQLException e) {
  767.             handleException(e);
  768.         }
  769.     }

  770.     @Override
  771.     public void setAutoCommit(final boolean autoCommit) throws SQLException {
  772.         checkOpen();
  773.         try {
  774.             connection.setAutoCommit(autoCommit);
  775.             if (cacheState) {
  776.                 cachedAutoCommit = connection.getAutoCommit();
  777.             }
  778.         } catch (final SQLException e) {
  779.             cachedAutoCommit = null;
  780.             handleException(e);
  781.         }
  782.     }

  783.     /**
  784.      * Sets whether to cache properties. The cached properties are:
  785.      * <ul>
  786.      * <li>auto-commit</li>
  787.      * <li>catalog</li>
  788.      * <li>schema</li>
  789.      * <li>read-only</li>
  790.      * </ul>
  791.      *
  792.      * @param cacheState The new value for the state caching flag
  793.      */
  794.     public void setCacheState(final boolean cacheState) {
  795.         this.cacheState = cacheState;
  796.     }

  797.     @Override
  798.     public void setCatalog(final String catalog) throws SQLException {
  799.         checkOpen();
  800.         try {
  801.             connection.setCatalog(catalog);
  802.             if (cacheState) {
  803.                 cachedCatalog = connection.getCatalog();
  804.             }
  805.         } catch (final SQLException e) {
  806.             cachedCatalog = null;
  807.             handleException(e);
  808.         }
  809.     }

  810.     @Override
  811.     public void setClientInfo(final Properties properties) throws SQLClientInfoException {
  812.         try {
  813.             checkOpen();
  814.             connection.setClientInfo(properties);
  815.         } catch (final SQLClientInfoException e) {
  816.             throw e;
  817.         } catch (final SQLException e) {
  818.             throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
  819.         }
  820.     }

  821.     @Override
  822.     public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
  823.         try {
  824.             checkOpen();
  825.             connection.setClientInfo(name, value);
  826.         } catch (final SQLClientInfoException e) {
  827.             throw e;
  828.         } catch (final SQLException e) {
  829.             throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
  830.         }
  831.     }

  832.     /**
  833.      * Sets the raw internal closed state.
  834.      *
  835.      * @param closed the raw internal closed state.
  836.      */
  837.     protected void setClosedInternal(final boolean closed) {
  838.         this.closed = closed;
  839.     }

  840.     /**
  841.      * Sets the default query timeout that will be used for {@link Statement}s created from this connection.
  842.      * {@code null} means that the driver default will be used.
  843.      *
  844.      * @param defaultQueryTimeoutDuration
  845.      *            the new query timeout limit Duration; zero means there is no limit.
  846.      * @since 2.10.0
  847.      */
  848.     public void setDefaultQueryTimeout(final Duration defaultQueryTimeoutDuration) {
  849.         this.defaultQueryTimeoutDuration = defaultQueryTimeoutDuration;
  850.     }

  851.     /**
  852.      * Sets the default query timeout that will be used for {@link Statement}s created from this connection.
  853.      * {@code null} means that the driver default will be used.
  854.      *
  855.      * @param defaultQueryTimeoutSeconds
  856.      *            the new query timeout limit in seconds; zero means there is no limit.
  857.      * @deprecated Use {@link #setDefaultQueryTimeout(Duration)}.
  858.      */
  859.     @Deprecated
  860.     public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
  861.         this.defaultQueryTimeoutDuration = defaultQueryTimeoutSeconds == null ? null : Duration.ofSeconds(defaultQueryTimeoutSeconds);
  862.     }

  863.     /**
  864.      * Sets my delegate.
  865.      *
  866.      * @param connection
  867.      *            my delegate, may be null.
  868.      */
  869.     public void setDelegate(final C connection) {
  870.         this.connection = connection;
  871.     }

  872.     @Override
  873.     public void setHoldability(final int holdability) throws SQLException {
  874.         checkOpen();
  875.         try {
  876.             connection.setHoldability(holdability);
  877.         } catch (final SQLException e) {
  878.             handleException(e);
  879.         }
  880.     }

  881.     @Override
  882.     public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException {
  883.         checkOpen();
  884.         try {
  885.             Jdbc41Bridge.setNetworkTimeout(connection, executor, milliseconds);
  886.         } catch (final SQLException e) {
  887.             handleException(e);
  888.         }
  889.     }

  890.     @Override
  891.     public void setReadOnly(final boolean readOnly) throws SQLException {
  892.         checkOpen();
  893.         try {
  894.             connection.setReadOnly(readOnly);
  895.             if (cacheState) {
  896.                 cachedReadOnly = connection.isReadOnly();
  897.             }
  898.         } catch (final SQLException e) {
  899.             cachedReadOnly = null;
  900.             handleException(e);
  901.         }
  902.     }

  903.     @Override
  904.     public Savepoint setSavepoint() throws SQLException {
  905.         checkOpen();
  906.         try {
  907.             return connection.setSavepoint();
  908.         } catch (final SQLException e) {
  909.             handleException(e);
  910.             return null;
  911.         }
  912.     }

  913.     @Override
  914.     public Savepoint setSavepoint(final String name) throws SQLException {
  915.         checkOpen();
  916.         try {
  917.             return connection.setSavepoint(name);
  918.         } catch (final SQLException e) {
  919.             handleException(e);
  920.             return null;
  921.         }
  922.     }

  923.     @Override
  924.     public void setSchema(final String schema) throws SQLException {
  925.         checkOpen();
  926.         try {
  927.             Jdbc41Bridge.setSchema(connection, schema);
  928.             if (cacheState) {
  929.                 cachedSchema = Jdbc41Bridge.getSchema(connection);
  930.             }
  931.         } catch (final SQLException e) {
  932.             cachedSchema = null;
  933.             handleException(e);
  934.         }
  935.     }

  936.     @Override
  937.     public void setTransactionIsolation(final int level) throws SQLException {
  938.         checkOpen();
  939.         try {
  940.             connection.setTransactionIsolation(level);
  941.         } catch (final SQLException e) {
  942.             handleException(e);
  943.         }
  944.     }

  945.     @Override
  946.     public void setTypeMap(final Map<String, Class<?>> map) throws SQLException {
  947.         checkOpen();
  948.         try {
  949.             connection.setTypeMap(map);
  950.         } catch (final SQLException e) {
  951.             handleException(e);
  952.         }
  953.     }

  954.     /**
  955.      * Returns a string representation of the metadata associated with the innermost delegate connection.
  956.      */
  957.     @SuppressWarnings("resource")
  958.     @Override
  959.     public synchronized String toString() {
  960.         String str = null;

  961.         final Connection conn = this.getInnermostDelegateInternal();
  962.         if (conn != null) {
  963.             try {
  964.                 if (conn.isClosed()) {
  965.                     str = "connection is closed";
  966.                 } else {
  967.                     final StringBuilder sb = new StringBuilder();
  968.                     sb.append(hashCode());
  969.                     final DatabaseMetaData meta = conn.getMetaData();
  970.                     if (meta != null) {
  971.                         sb.append(", URL=");
  972.                         sb.append(meta.getURL());
  973.                         sb.append(", ");
  974.                         sb.append(meta.getDriverName());
  975.                         str = sb.toString();
  976.                     }
  977.                 }
  978.             } catch (final SQLException ignored) {
  979.                 // Ignore
  980.             }
  981.         }
  982.         return str != null ? str : super.toString();
  983.     }

  984.     @Override
  985.     public <T> T unwrap(final Class<T> iface) throws SQLException {
  986.         if (iface.isAssignableFrom(getClass())) {
  987.             return iface.cast(this);
  988.         }
  989.         if (iface.isAssignableFrom(connection.getClass())) {
  990.             return iface.cast(connection);
  991.         }
  992.         return connection.unwrap(iface);
  993.     }
  994. }