    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 synchronized 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                boolean connectionIsClosed;
    228                try {
    229                    connectionIsClosed = connection.isClosed();
    230                } catch (final SQLException e) {
    231                    // not sure what the state is, so assume the connection is open.
    232                    connectionIsClosed = false;
    233                }
    234                try {
    235                    // DBCP-512: Avoid exceptions when closing a connection in mutli-threaded use case.
    236                    // Avoid closing again, which should be a no-op, but some drivers like H2 throw an exception when
    237                    // closing from multiple threads.
    238                    if (!connectionIsClosed) {
    239                        connection.close();
    240                    }
    241                } finally {
    242                    closed = true;
    243                }
    244            } else {
    245                closed = true;
    246            }
    247        }
    248    }
    249
    250    protected void handleException(final SQLException e) throws SQLException {
    251        throw e;
    252    }
    253
    254    private void initializeStatement(final DelegatingStatement ds) throws SQLException {
    255        if (defaultQueryTimeoutSeconds != null && defaultQueryTimeoutSeconds.intValue() != ds.getQueryTimeout()) {
    256            ds.setQueryTimeout(defaultQueryTimeoutSeconds.intValue());
    257        }
    258    }
    259
    260    @Override
    261    public Statement createStatement() throws SQLException {
    262        checkOpen();
    263        try {
    264            final DelegatingStatement ds = new DelegatingStatement(this, connection.createStatement());
    265            initializeStatement(ds);
    266            return ds;
    267        } catch (final SQLException e) {
    268            handleException(e);
    269            return null;
    270        }
    271    }
    272
    273    @Override
    274    public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException {
    275        checkOpen();
    276        try {
    277            final DelegatingStatement ds = new DelegatingStatement(this,
    278                    connection.createStatement(resultSetType, resultSetConcurrency));
    279            initializeStatement(ds);
    280            return ds;
    281        } catch (final SQLException e) {
    282            handleException(e);
    283            return null;
    284        }
    285    }
    286
    287    @Override
    288    public PreparedStatement prepareStatement(final String sql) throws SQLException {
    289        checkOpen();
    290        try {
    291            final DelegatingPreparedStatement dps = new DelegatingPreparedStatement(this,
    292                    connection.prepareStatement(sql));
    293            initializeStatement(dps);
    294            return dps;
    295        } catch (final SQLException e) {
    296            handleException(e);
    297            return null;
    298        }
    299    }
    300
    301    @Override
    302    public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
    303            throws SQLException {
    304        checkOpen();
    305        try {
    306            final DelegatingPreparedStatement dps = new DelegatingPreparedStatement(this,
    307                    connection.prepareStatement(sql, resultSetType, resultSetConcurrency));
    308            initializeStatement(dps);
    309            return dps;
    310        } catch (final SQLException e) {
    311            handleException(e);
    312            return null;
    313        }
    314    }
    315
    316    @Override
    317    public CallableStatement prepareCall(final String sql) throws SQLException {
    318        checkOpen();
    319        try {
    320            final DelegatingCallableStatement dcs = new DelegatingCallableStatement(this, connection.prepareCall(sql));
    321            initializeStatement(dcs);
    322            return dcs;
    323        } catch (final SQLException e) {
    324            handleException(e);
    325            return null;
    326        }
    327    }
    328
    329    @Override
    330    public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
    331            throws SQLException {
    332        checkOpen();
    333        try {
    334            final DelegatingCallableStatement dcs = new DelegatingCallableStatement(this,
    335                    connection.prepareCall(sql, resultSetType, resultSetConcurrency));
    336            initializeStatement(dcs);
    337            return dcs;
    338        } catch (final SQLException e) {
    339            handleException(e);
    340            return null;
    341        }
    342    }
    343
    344    @Override
    345    public void clearWarnings() throws SQLException {
    346        checkOpen();
    347        try {
    348            connection.clearWarnings();
    349        } catch (final SQLException e) {
    350            handleException(e);
    351        }
    352    }
    353
    354    @Override
    355    public void commit() throws SQLException {
    356        checkOpen();
    357        try {
    358            connection.commit();
    359        } catch (final SQLException e) {
    360            handleException(e);
    361        }
    362    }
    363
    364    /**
    365     * Returns the state caching flag.
    366     *
    367     * @return the state caching flag
    368     */
    369    public boolean getCacheState() {
    370        return cacheState;
    371    }
    372
    373    @Override
    374    public boolean getAutoCommit() throws SQLException {
    375        checkOpen();
    376        if (cacheState && autoCommitCached != null) {
    377            return autoCommitCached.booleanValue();
    378        }
    379        try {
    380            autoCommitCached = Boolean.valueOf(connection.getAutoCommit());
    381            return autoCommitCached.booleanValue();
    382        } catch (final SQLException e) {
    383            handleException(e);
    384            return false;
    385        }
    386    }
    387
    388    @Override
    389    public String getCatalog() throws SQLException {
    390        checkOpen();
    391        try {
    392            return connection.getCatalog();
    393        } catch (final SQLException e) {
    394            handleException(e);
    395            return null;
    396        }
    397    }
    398
    399    @Override
    400    public DatabaseMetaData getMetaData() throws SQLException {
    401        checkOpen();
    402        try {
    403            return new DelegatingDatabaseMetaData(this, connection.getMetaData());
    404        } catch (final SQLException e) {
    405            handleException(e);
    406            return null;
    407        }
    408    }
    409
    410    @Override
    411    public int getTransactionIsolation() throws SQLException {
    412        checkOpen();
    413        try {
    414            return connection.getTransactionIsolation();
    415        } catch (final SQLException e) {
    416            handleException(e);
    417            return -1;
    418        }
    419    }
    420
    421    @Override
    422    public Map<String, Class<?>> getTypeMap() throws SQLException {
    423        checkOpen();
    424        try {
    425            return connection.getTypeMap();
    426        } catch (final SQLException e) {
    427            handleException(e);
    428            return null;
    429        }
    430    }
    431
    432    @Override
    433    public SQLWarning getWarnings() throws SQLException {
    434        checkOpen();
    435        try {
    436            return connection.getWarnings();
    437        } catch (final SQLException e) {
    438            handleException(e);
    439            return null;
    440        }
    441    }
    442
    443    @Override
    444    public boolean isReadOnly() throws SQLException {
    445        checkOpen();
    446        if (cacheState && readOnlyCached != null) {
    447            return readOnlyCached.booleanValue();
    448        }
    449        try {
    450            readOnlyCached = Boolean.valueOf(connection.isReadOnly());
    451            return readOnlyCached.booleanValue();
    452        } catch (final SQLException e) {
    453            handleException(e);
    454            return false;
    455        }
    456    }
    457
    458    @Override
    459    public String nativeSQL(final String sql) throws SQLException {
    460        checkOpen();
    461        try {
    462            return connection.nativeSQL(sql);
    463        } catch (final SQLException e) {
    464            handleException(e);
    465            return null;
    466        }
    467    }
    468
    469    @Override
    470    public void rollback() throws SQLException {
    471        checkOpen();
    472        try {
    473            connection.rollback();
    474        } catch (final SQLException e) {
    475            handleException(e);
    476        }
    477    }
    478
    479    /**
    480     * Gets the default query timeout that will be used for {@link Statement}s created from this connection.
    481     * <code>null</code> means that the driver default will be used.
    482     *
    483     * @return query timeout limit in seconds; zero means there is no limit.
    484     */
    485    public Integer getDefaultQueryTimeout() {
    486        return defaultQueryTimeoutSeconds;
    487    }
    488
    489    /**
    490     * Sets the default query timeout that will be used for {@link Statement}s created from this connection.
    491     * <code>null</code> means that the driver default will be used.
    492     *
    493     * @param defaultQueryTimeoutSeconds
    494     *            the new query timeout limit in seconds; zero means there is no limit
    495     */
    496    public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
    497        this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds;
    498    }
    499
    500    /**
    501     * Sets the state caching flag.
    502     *
    503     * @param cacheState
    504     *            The new value for the state caching flag
    505     */
    506    public void setCacheState(final boolean cacheState) {
    507        this.cacheState = cacheState;
    508    }
    509
    510    /**
    511     * Can be used to clear cached state when it is known that the underlying connection may have been accessed
    512     * directly.
    513     */
    514    public void clearCachedState() {
    515        autoCommitCached = null;
    516        readOnlyCached = null;
    517        if (connection instanceof DelegatingConnection) {
    518            ((DelegatingConnection<?>) connection).clearCachedState();
    519        }
    520    }
    521
    522    @Override
    523    public void setAutoCommit(final boolean autoCommit) throws SQLException {
    524        checkOpen();
    525        try {
    526            connection.setAutoCommit(autoCommit);
    527            if (cacheState) {
    528                autoCommitCached = Boolean.valueOf(autoCommit);
    529            }
    530        } catch (final SQLException e) {
    531            autoCommitCached = null;
    532            handleException(e);
    533        }
    534    }
    535
    536    @Override
    537    public void setCatalog(final String catalog) throws SQLException {
    538        checkOpen();
    539        try {
    540            connection.setCatalog(catalog);
    541        } catch (final SQLException e) {
    542            handleException(e);
    543        }
    544    }
    545
    546    @Override
    547    public void setReadOnly(final boolean readOnly) throws SQLException {
    548        checkOpen();
    549        try {
    550            connection.setReadOnly(readOnly);
    551            if (cacheState) {
    552                readOnlyCached = Boolean.valueOf(readOnly);
    553            }
    554        } catch (final SQLException e) {
    555            readOnlyCached = null;
    556            handleException(e);
    557        }
    558    }
    559
    560    @Override
    561    public void setTransactionIsolation(final int level) throws SQLException {
    562        checkOpen();
    563        try {
    564            connection.setTransactionIsolation(level);
    565        } catch (final SQLException e) {
    566            handleException(e);
    567        }
    568    }
    569
    570    @Override
    571    public void setTypeMap(final Map<String, Class<?>> map) throws SQLException {
    572        checkOpen();
    573        try {
    574            connection.setTypeMap(map);
    575        } catch (final SQLException e) {
    576            handleException(e);
    577        }
    578    }
    579
    580    @Override
    581    public boolean isClosed() throws SQLException {
    582        return closed || connection == null || connection.isClosed();
    583    }
    584
    585    protected void checkOpen() throws SQLException {
    586        if (closed) {
    587            if (null != connection) {
    588                String label = "";
    589                try {
    590                    label = connection.toString();
    591                } catch (final Exception ex) {
    592                    // ignore, leave label empty
    593                }
    594                throw new SQLException("Connection " + label + " is closed.");
    595            }
    596            throw new SQLException("Connection is null.");
    597        }
    598    }
    599
    600    protected void activate() {
    601        closed = false;
    602        setLastUsed();
    603        if (connection instanceof DelegatingConnection) {
    604            ((DelegatingConnection<?>) connection).activate();
    605        }
    606    }
    607
    608    protected void passivate() throws SQLException {
    609        // The JDBC spec requires that a Connection close any open
    610        // Statement's when it is closed.
    611        // DBCP-288. Not all the traced objects will be statements
    612        final List<AbandonedTrace> traces = getTrace();
    613        if (traces != null && traces.size() > 0) {
    614            final Iterator<AbandonedTrace> traceIter = traces.iterator();
    615            while (traceIter.hasNext()) {
    616                final Object trace = traceIter.next();
    617                if (trace instanceof Statement) {
    618                    ((Statement) trace).close();
    619                } else if (trace instanceof ResultSet) {
    620                    // DBCP-265: Need to close the result sets that are
    621                    // generated via DatabaseMetaData
    622                    ((ResultSet) trace).close();
    623                }
    624            }
    625            clearTrace();
    626        }
    627        setLastUsed(0);
    628    }
    629
    630    @Override
    631    public int getHoldability() throws SQLException {
    632        checkOpen();
    633        try {
    634            return connection.getHoldability();
    635        } catch (final SQLException e) {
    636            handleException(e);
    637            return 0;
    638        }
    639    }
    640
    641    @Override
    642    public void setHoldability(final int holdability) throws SQLException {
    643        checkOpen();
    644        try {
    645            connection.setHoldability(holdability);
    646        } catch (final SQLException e) {
    647            handleException(e);
    648        }
    649    }
    650
    651    @Override
    652    public Savepoint setSavepoint() throws SQLException {
    653        checkOpen();
    654        try {
    655            return connection.setSavepoint();
    656        } catch (final SQLException e) {
    657            handleException(e);
    658            return null;
    659        }
    660    }
    661
    662    @Override
    663    public Savepoint setSavepoint(final String name) throws SQLException {
    664        checkOpen();
    665        try {
    666            return connection.setSavepoint(name);
    667        } catch (final SQLException e) {
    668            handleException(e);
    669            return null;
    670        }
    671    }
    672
    673    @Override
    674    public void rollback(final Savepoint savepoint) throws SQLException {
    675        checkOpen();
    676        try {
    677            connection.rollback(savepoint);
    678        } catch (final SQLException e) {
    679            handleException(e);
    680        }
    681    }
    682
    683    @Override
    684    public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
    685        checkOpen();
    686        try {
    687            connection.releaseSavepoint(savepoint);
    688        } catch (final SQLException e) {
    689            handleException(e);
    690        }
    691    }
    692
    693    @Override
    694    public Statement createStatement(final int resultSetType, final int resultSetConcurrency,
    695            final int resultSetHoldability) throws SQLException {
    696        checkOpen();
    697        try {
    698            final DelegatingStatement ds = new DelegatingStatement(this,
    699                    connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability));
    700            initializeStatement(ds);
    701            return ds;
    702        } catch (final SQLException e) {
    703            handleException(e);
    704            return null;
    705        }
    706    }
    707
    708    @Override
    709    public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
    710            final int resultSetHoldability) throws SQLException {
    711        checkOpen();
    712        try {
    713            final DelegatingPreparedStatement dps = new DelegatingPreparedStatement(this,
    714                    connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
    715            initializeStatement(dps);
    716            return dps;
    717        } catch (final SQLException e) {
    718            handleException(e);
    719            return null;
    720        }
    721    }
    722
    723    @Override
    724    public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
    725            final int resultSetHoldability) throws SQLException {
    726        checkOpen();
    727        try {
    728            final DelegatingCallableStatement dcs = new DelegatingCallableStatement(this,
    729                    connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
    730            initializeStatement(dcs);
    731            return dcs;
    732        } catch (final SQLException e) {
    733            handleException(e);
    734            return null;
    735        }
    736    }
    737
    738    @Override
    739    public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
    740        checkOpen();
    741        try {
    742            final DelegatingPreparedStatement dps = new DelegatingPreparedStatement(this,
    743                    connection.prepareStatement(sql, autoGeneratedKeys));
    744            initializeStatement(dps);
    745            return dps;
    746        } catch (final SQLException e) {
    747            handleException(e);
    748            return null;
    749        }
    750    }
    751
    752    @Override
    753    public PreparedStatement prepareStatement(final String sql, final int columnIndexes[]) throws SQLException {
    754        checkOpen();
    755        try {
    756            final DelegatingPreparedStatement dps = new DelegatingPreparedStatement(this,
    757                    connection.prepareStatement(sql, columnIndexes));
    758            initializeStatement(dps);
    759            return dps;
    760        } catch (final SQLException e) {
    761            handleException(e);
    762            return null;
    763        }
    764    }
    765
    766    @Override
    767    public PreparedStatement prepareStatement(final String sql, final String columnNames[]) throws SQLException {
    768        checkOpen();
    769        try {
    770            final DelegatingPreparedStatement dps = new DelegatingPreparedStatement(this,
    771                    connection.prepareStatement(sql, columnNames));
    772            initializeStatement(dps);
    773            return dps;
    774        } catch (final SQLException e) {
    775            handleException(e);
    776            return null;
    777        }
    778    }
    779
    780    @Override
    781    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
    782        if (iface.isAssignableFrom(getClass())) {
    783            return true;
    784        } else if (iface.isAssignableFrom(connection.getClass())) {
    785            return true;
    786        } else {
    787            return connection.isWrapperFor(iface);
    788        }
    789    }
    790
    791    @Override
    792    public <T> T unwrap(final Class<T> iface) throws SQLException {
    793        if (iface.isAssignableFrom(getClass())) {
    794            return iface.cast(this);
    795        } else if (iface.isAssignableFrom(connection.getClass())) {
    796            return iface.cast(connection);
    797        } else {
    798            return connection.unwrap(iface);
    799        }
    800    }
    801
    802    @Override
    803    public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
    804        checkOpen();
    805        try {
    806            return connection.createArrayOf(typeName, elements);
    807        } catch (final SQLException e) {
    808            handleException(e);
    809            return null;
    810        }
    811    }
    812
    813    @Override
    814    public Blob createBlob() throws SQLException {
    815        checkOpen();
    816        try {
    817            return connection.createBlob();
    818        } catch (final SQLException e) {
    819            handleException(e);
    820            return null;
    821        }
    822    }
    823
    824    @Override
    825    public Clob createClob() throws SQLException {
    826        checkOpen();
    827        try {
    828            return connection.createClob();
    829        } catch (final SQLException e) {
    830            handleException(e);
    831            return null;
    832        }
    833    }
    834
    835    @Override
    836    public NClob createNClob() throws SQLException {
    837        checkOpen();
    838        try {
    839            return connection.createNClob();
    840        } catch (final SQLException e) {
    841            handleException(e);
    842            return null;
    843        }
    844    }
    845
    846    @Override
    847    public SQLXML createSQLXML() throws SQLException {
    848        checkOpen();
    849        try {
    850            return connection.createSQLXML();
    851        } catch (final SQLException e) {
    852            handleException(e);
    853            return null;
    854        }
    855    }
    856
    857    @Override
    858    public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException {
    859        checkOpen();
    860        try {
    861            return connection.createStruct(typeName, attributes);
    862        } catch (final SQLException e) {
    863            handleException(e);
    864            return null;
    865        }
    866    }
    867
    868    @Override
    869    public boolean isValid(final int timeoutSeconds) throws SQLException {
    870        if (isClosed()) {
    871            return false;
    872        }
    873        try {
    874            return connection.isValid(timeoutSeconds);
    875        } catch (final SQLException e) {
    876            handleException(e);
    877            return false;
    878        }
    879    }
    880
    881    @Override
    882    public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
    883        try {
    884            checkOpen();
    885            connection.setClientInfo(name, value);
    886        } catch (final SQLClientInfoException e) {
    887            throw e;
    888        } catch (final SQLException e) {
    889            throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
    890        }
    891    }
    892
    893    @Override
    894    public void setClientInfo(final Properties properties) throws SQLClientInfoException {
    895        try {
    896            checkOpen();
    897            connection.setClientInfo(properties);
    898        } catch (final SQLClientInfoException e) {
    899            throw e;
    900        } catch (final SQLException e) {
    901            throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
    902        }
    903    }
    904
    905    @Override
    906    public Properties getClientInfo() throws SQLException {
    907        checkOpen();
    908        try {
    909            return connection.getClientInfo();
    910        } catch (final SQLException e) {
    911            handleException(e);
    912            return null;
    913        }
    914    }
    915
    916    @Override
    917    public String getClientInfo(final String name) throws SQLException {
    918        checkOpen();
    919        try {
    920            return connection.getClientInfo(name);
    921        } catch (final SQLException e) {
    922            handleException(e);
    923            return null;
    924        }
    925    }
    926
    927    @Override
    928    public void setSchema(final String schema) throws SQLException {
    929        checkOpen();
    930        try {
    931            Jdbc41Bridge.setSchema(connection, schema);
    932        } catch (final SQLException e) {
    933            handleException(e);
    934        }
    935    }
    936
    937    @Override
    938    public String getSchema() throws SQLException {
    939        checkOpen();
    940        try {
    941            return Jdbc41Bridge.getSchema(connection);
    942        } catch (final SQLException e) {
    943            handleException(e);
    944            return null;
    945        }
    946    }
    947
    948    @Override
    949    public void abort(final Executor executor) throws SQLException {
    950        checkOpen();
    951        try {
    952            Jdbc41Bridge.abort(connection, executor);
    953        } catch (final SQLException e) {
    954            handleException(e);
    955        }
    956    }
    957
    958    @Override
    959    public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException {
    960        checkOpen();
    961        try {
    962            Jdbc41Bridge.setNetworkTimeout(connection, executor, milliseconds);
    963        } catch (final SQLException e) {
    964            handleException(e);
    965        }
    966    }
    967
    968    @Override
    969    public int getNetworkTimeout() throws SQLException {
    970        checkOpen();
    971        try {
    972            return Jdbc41Bridge.getNetworkTimeout(connection);
    973        } catch (final SQLException e) {
    974            handleException(e);
    975            return 0;
    976        }
    977    }
    978}