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