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