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