View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.dbcp2;
19  
20  import java.sql.Array;
21  import java.sql.Blob;
22  import java.sql.CallableStatement;
23  import java.sql.ClientInfoStatus;
24  import java.sql.Clob;
25  import java.sql.Connection;
26  import java.sql.DatabaseMetaData;
27  import java.sql.NClob;
28  import java.sql.PreparedStatement;
29  import java.sql.ResultSet;
30  import java.sql.SQLClientInfoException;
31  import java.sql.SQLException;
32  import java.sql.SQLWarning;
33  import java.sql.SQLXML;
34  import java.sql.Savepoint;
35  import java.sql.Statement;
36  import java.sql.Struct;
37  import java.util.ArrayList;
38  import java.util.Collections;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Properties;
42  import java.util.concurrent.Executor;
43  
44  /**
45   * A base delegating implementation of {@link Connection}.
46   * <p>
47   * All of the methods from the {@link Connection} interface simply check to see that the {@link Connection} is active,
48   * and call the corresponding method on the "delegate" provided in my constructor.
49   * </p>
50   * <p>
51   * Extends AbandonedTrace to implement Connection tracking and logging of code which created the Connection. Tracking
52   * the Connection ensures that the AbandonedObjectPool can close this connection and recycle it if its pool of
53   * connections is nearing exhaustion and this connection's last usage is older than the removeAbandonedTimeout.
54   * </p>
55   *
56   * @param <C>
57   *            the Connection type
58   *
59   * @since 2.0
60   */
61  public class DelegatingConnection<C extends Connection> extends AbandonedTrace implements Connection {
62  
63      private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES = Collections
64              .<String, ClientInfoStatus>emptyMap();
65  
66      /** My delegate {@link Connection}. */
67      private volatile C connection;
68  
69      private volatile boolean closed;
70  
71      private boolean cacheState = true;
72      private Boolean cachedAutoCommit;
73      private Boolean cachedReadOnly;
74      private String cachedCatalog;
75      private String cachedSchema;
76      private Integer defaultQueryTimeoutSeconds;
77  
78      /**
79       * Creates a wrapper for the Connection which traces this Connection in the AbandonedObjectPool.
80       *
81       * @param connection the {@link Connection} to delegate all calls to.
82       */
83      public DelegatingConnection(final C connection) {
84          this.connection = connection;
85      }
86  
87      @Override
88      public void abort(final Executor executor) throws SQLException {
89          try {
90              Jdbc41Bridge.abort(connection, executor);
91          } catch (final SQLException e) {
92              handleException(e);
93          }
94      }
95  
96      protected void activate() {
97          closed = false;
98          setLastUsed();
99          if (connection instanceof DelegatingConnection) {
100             ((DelegatingConnection<?>) connection).activate();
101         }
102     }
103 
104     protected void checkOpen() throws SQLException {
105         if (closed) {
106             if (null != connection) {
107                 String label = "";
108                 try {
109                     label = connection.toString();
110                 } catch (final Exception ex) {
111                     // ignore, leave label empty
112                 }
113                 throw new SQLException("Connection " + label + " is closed.");
114             }
115             throw new SQLException("Connection is null.");
116         }
117     }
118 
119     /**
120      * Can be used to clear cached state when it is known that the underlying connection may have been accessed
121      * directly.
122      */
123     public void clearCachedState() {
124         cachedAutoCommit = null;
125         cachedReadOnly = null;
126         cachedSchema = null;
127         cachedCatalog = null;
128         if (connection instanceof DelegatingConnection) {
129             ((DelegatingConnection<?>) connection).clearCachedState();
130         }
131     }
132 
133     @Override
134     public void clearWarnings() throws SQLException {
135         checkOpen();
136         try {
137             connection.clearWarnings();
138         } catch (final SQLException e) {
139             handleException(e);
140         }
141     }
142 
143     /**
144      * Closes the underlying connection, and close any Statements that were not explicitly closed. Sub-classes that
145      * override this method must:
146      * <ol>
147      * <li>Call passivate()</li>
148      * <li>Call close (or the equivalent appropriate action) on the wrapped connection</li>
149      * <li>Set _closed to <code>false</code></li>
150      * </ol>
151      */
152     @Override
153     public void close() throws SQLException {
154         if (!closed) {
155             closeInternal();
156         }
157     }
158 
159     protected final void closeInternal() throws SQLException {
160         try {
161             passivate();
162         } finally {
163             if (connection != null) {
164                 boolean connectionIsClosed;
165                 try {
166                     connectionIsClosed = connection.isClosed();
167                 } catch (final SQLException e) {
168                     // not sure what the state is, so assume the connection is open.
169                     connectionIsClosed = false;
170                 }
171                 try {
172                     // DBCP-512: Avoid exceptions when closing a connection in mutli-threaded use case.
173                     // Avoid closing again, which should be a no-op, but some drivers like H2 throw an exception when
174                     // closing from multiple threads.
175                     if (!connectionIsClosed) {
176                         connection.close();
177                     }
178                 } finally {
179                     closed = true;
180                 }
181             } else {
182                 closed = true;
183             }
184         }
185     }
186 
187     @Override
188     public void commit() throws SQLException {
189         checkOpen();
190         try {
191             connection.commit();
192         } catch (final SQLException e) {
193             handleException(e);
194         }
195     }
196 
197     @Override
198     public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
199         checkOpen();
200         try {
201             return connection.createArrayOf(typeName, elements);
202         } catch (final SQLException e) {
203             handleException(e);
204             return null;
205         }
206     }
207 
208     @Override
209     public Blob createBlob() throws SQLException {
210         checkOpen();
211         try {
212             return connection.createBlob();
213         } catch (final SQLException e) {
214             handleException(e);
215             return null;
216         }
217     }
218 
219     @Override
220     public Clob createClob() throws SQLException {
221         checkOpen();
222         try {
223             return connection.createClob();
224         } catch (final SQLException e) {
225             handleException(e);
226             return null;
227         }
228     }
229 
230     @Override
231     public NClob createNClob() throws SQLException {
232         checkOpen();
233         try {
234             return connection.createNClob();
235         } catch (final SQLException e) {
236             handleException(e);
237             return null;
238         }
239     }
240 
241     @Override
242     public SQLXML createSQLXML() throws SQLException {
243         checkOpen();
244         try {
245             return connection.createSQLXML();
246         } catch (final SQLException e) {
247             handleException(e);
248             return null;
249         }
250     }
251 
252     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
253     @Override
254     public Statement createStatement() throws SQLException {
255         checkOpen();
256         try {
257             return init(new DelegatingStatement(this, connection.createStatement()));
258         } catch (final SQLException e) {
259             handleException(e);
260             return null;
261         }
262     }
263 
264     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
265     @Override
266     public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException {
267         checkOpen();
268         try {
269             return init(new DelegatingStatement(this, connection.createStatement(resultSetType, resultSetConcurrency)));
270         } catch (final SQLException e) {
271             handleException(e);
272             return null;
273         }
274     }
275 
276     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
277     @Override
278     public Statement createStatement(final int resultSetType, final int resultSetConcurrency,
279         final int resultSetHoldability) throws SQLException {
280         checkOpen();
281         try {
282             return init(new DelegatingStatement(this,
283                 connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)));
284         } catch (final SQLException e) {
285             handleException(e);
286             return null;
287         }
288     }
289 
290     @Override
291     public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException {
292         checkOpen();
293         try {
294             return connection.createStruct(typeName, attributes);
295         } catch (final SQLException e) {
296             handleException(e);
297             return null;
298         }
299     }
300 
301     @Override
302     public boolean getAutoCommit() throws SQLException {
303         checkOpen();
304         if (cacheState && cachedAutoCommit != null) {
305             return cachedAutoCommit;
306         }
307         try {
308             cachedAutoCommit = connection.getAutoCommit();
309             return cachedAutoCommit;
310         } catch (final SQLException e) {
311             handleException(e);
312             return false;
313         }
314     }
315 
316     /**
317      * Returns the state caching flag.
318      *
319      * @return the state caching flag
320      */
321     public boolean getCacheState() {
322         return cacheState;
323     }
324 
325     @Override
326     public String getCatalog() throws SQLException {
327         checkOpen();
328         if (cacheState && cachedCatalog != null) {
329             return cachedCatalog;
330         }
331         try {
332             cachedCatalog = connection.getCatalog();
333             return cachedCatalog;
334         } catch (final SQLException e) {
335             handleException(e);
336             return null;
337         }
338     }
339 
340     @Override
341     public Properties getClientInfo() throws SQLException {
342         checkOpen();
343         try {
344             return connection.getClientInfo();
345         } catch (final SQLException e) {
346             handleException(e);
347             return null;
348         }
349     }
350 
351     @Override
352     public String getClientInfo(final String name) throws SQLException {
353         checkOpen();
354         try {
355             return connection.getClientInfo(name);
356         } catch (final SQLException e) {
357             handleException(e);
358             return null;
359         }
360     }
361 
362     /**
363      * Gets the default query timeout that will be used for {@link Statement}s created from this connection.
364      * <code>null</code> means that the driver default will be used.
365      *
366      * @return query timeout limit in seconds; zero means there is no limit.
367      */
368     public Integer getDefaultQueryTimeout() {
369         return defaultQueryTimeoutSeconds;
370     }
371 
372     /**
373      * Returns my underlying {@link Connection}.
374      *
375      * @return my underlying {@link Connection}.
376      */
377     public C getDelegate() {
378         return getDelegateInternal();
379     }
380 
381     /**
382      * Gets the delegate connection.
383      *
384      * @return the delegate connection.
385      */
386     protected final C getDelegateInternal() {
387         return connection;
388     }
389 
390     @Override
391     public int getHoldability() throws SQLException {
392         checkOpen();
393         try {
394             return connection.getHoldability();
395         } catch (final SQLException e) {
396             handleException(e);
397             return 0;
398         }
399     }
400 
401     /**
402      * If my underlying {@link Connection} is not a {@code DelegatingConnection}, returns it, otherwise recursively
403      * invokes this method on my delegate.
404      * <p>
405      * Hence this method will return the first delegate that is not a {@code DelegatingConnection}, or {@code null} when
406      * no non-{@code DelegatingConnection} delegate can be found by traversing this chain.
407      * </p>
408      * <p>
409      * This method is useful when you may have nested {@code DelegatingConnection}s, and you want to make sure to obtain
410      * a "genuine" {@link Connection}.
411      * </p>
412      *
413      * @return innermost delegate.
414      */
415     public Connection getInnermostDelegate() {
416         return getInnermostDelegateInternal();
417     }
418 
419     /**
420      * Although this method is public, it is part of the internal API and should not be used by clients. The signature
421      * of this method may change at any time including in ways that break backwards compatibility.
422      *
423      * @return innermost delegate.
424      */
425     @SuppressWarnings("resource")
426     public final Connection getInnermostDelegateInternal() {
427         Connection conn = connection;
428         while (conn instanceof DelegatingConnection) {
429             conn = ((DelegatingConnection<?>) conn).getDelegateInternal();
430             if (this == conn) {
431                 return null;
432             }
433         }
434         return conn;
435     }
436 
437     @Override
438     public DatabaseMetaData getMetaData() throws SQLException {
439         checkOpen();
440         try {
441             return new DelegatingDatabaseMetaData(this, connection.getMetaData());
442         } catch (final SQLException e) {
443             handleException(e);
444             return null;
445         }
446     }
447 
448     @Override
449     public int getNetworkTimeout() throws SQLException {
450         checkOpen();
451         try {
452             return Jdbc41Bridge.getNetworkTimeout(connection);
453         } catch (final SQLException e) {
454             handleException(e);
455             return 0;
456         }
457     }
458 
459     @Override
460     public String getSchema() throws SQLException {
461         checkOpen();
462         if (cacheState && cachedSchema != null) {
463             return cachedSchema;
464         }
465         try {
466             cachedSchema = Jdbc41Bridge.getSchema(connection);
467             return cachedSchema;
468         } catch (final SQLException e) {
469             handleException(e);
470             return null;
471         }
472     }
473 
474     @Override
475     public int getTransactionIsolation() throws SQLException {
476         checkOpen();
477         try {
478             return connection.getTransactionIsolation();
479         } catch (final SQLException e) {
480             handleException(e);
481             return -1;
482         }
483     }
484 
485     @Override
486     public Map<String, Class<?>> getTypeMap() throws SQLException {
487         checkOpen();
488         try {
489             return connection.getTypeMap();
490         } catch (final SQLException e) {
491             handleException(e);
492             return null;
493         }
494     }
495 
496     @Override
497     public SQLWarning getWarnings() throws SQLException {
498         checkOpen();
499         try {
500             return connection.getWarnings();
501         } catch (final SQLException e) {
502             handleException(e);
503             return null;
504         }
505     }
506 
507     /**
508      * Handles the given exception by throwing it.
509      *
510      * @param e the exception to throw.
511      * @throws SQLException the exception to throw.
512      */
513     protected void handleException(final SQLException e) throws SQLException {
514         throw e;
515     }
516 
517     /**
518      * Handles the given {@code SQLException}.
519      *
520      * @param <T> The throwable type.
521      * @param e   The SQLException
522      * @return the given {@code SQLException}
523      * @since 2.7.0
524      */
525     protected <T extends Throwable> T handleExceptionNoThrow(final T e) {
526         return e;
527     }
528 
529     /**
530      * Initializes the given statement with this connection's settings.
531      *
532      * @param <T> The DelegatingStatement type.
533      * @param delegatingStatement The DelegatingStatement to initialize.
534      * @return The given DelegatingStatement.
535      * @throws SQLException if a database access error occurs, this method is called on a closed Statement.
536      */
537     private <T extends DelegatingStatement> T init(final T delegatingStatement) throws SQLException {
538         if (defaultQueryTimeoutSeconds != null && defaultQueryTimeoutSeconds != delegatingStatement.getQueryTimeout()) {
539             delegatingStatement.setQueryTimeout(defaultQueryTimeoutSeconds);
540         }
541         return delegatingStatement;
542     }
543 
544     /**
545      * Compares innermost delegate to the given connection.
546      *
547      * @param c
548      *            connection to compare innermost delegate with
549      * @return true if innermost delegate equals <code>c</code>
550      */
551     @SuppressWarnings("resource")
552     public boolean innermostDelegateEquals(final Connection c) {
553         final Connection innerCon = getInnermostDelegateInternal();
554         if (innerCon == null) {
555             return c == null;
556         }
557         return innerCon.equals(c);
558     }
559 
560     @Override
561     public boolean isClosed() throws SQLException {
562         return closed || connection == null || connection.isClosed();
563     }
564 
565     protected boolean isClosedInternal() {
566         return closed;
567     }
568 
569     @Override
570     public boolean isReadOnly() throws SQLException {
571         checkOpen();
572         if (cacheState && cachedReadOnly != null) {
573             return cachedReadOnly;
574         }
575         try {
576             cachedReadOnly = connection.isReadOnly();
577             return cachedReadOnly;
578         } catch (final SQLException e) {
579             handleException(e);
580             return false;
581         }
582     }
583 
584     @Override
585     public boolean isValid(final int timeoutSeconds) throws SQLException {
586         if (isClosed()) {
587             return false;
588         }
589         try {
590             return connection.isValid(timeoutSeconds);
591         } catch (final SQLException e) {
592             handleException(e);
593             return false;
594         }
595     }
596 
597     @Override
598     public boolean isWrapperFor(final Class<?> iface) throws SQLException {
599         if (iface.isAssignableFrom(getClass())) {
600             return true;
601         }
602         if (iface.isAssignableFrom(connection.getClass())) {
603             return true;
604         }
605         return connection.isWrapperFor(iface);
606     }
607 
608     @Override
609     public String nativeSQL(final String sql) throws SQLException {
610         checkOpen();
611         try {
612             return connection.nativeSQL(sql);
613         } catch (final SQLException e) {
614             handleException(e);
615             return null;
616         }
617     }
618 
619     protected void passivate() throws SQLException {
620         // The JDBC specification requires that a Connection close any open
621         // Statement's when it is closed.
622         // DBCP-288. Not all the traced objects will be statements
623         final List<AbandonedTrace> traces = getTrace();
624         if (traces != null && !traces.isEmpty()) {
625             final List<Exception> thrownList = new ArrayList<>();
626             for (final Object trace : traces) {
627                 if (trace instanceof Statement) {
628                     try {
629                         ((Statement) trace).close();
630                     } catch (final Exception e) {
631                         thrownList.add(e);
632                     }
633                 } else if (trace instanceof ResultSet) {
634                     // DBCP-265: Need to close the result sets that are
635                     // generated via DatabaseMetaData
636                     try {
637                         ((ResultSet) trace).close();
638                     } catch (final Exception e) {
639                         thrownList.add(e);
640                     }
641                 }
642             }
643             clearTrace();
644             if (!thrownList.isEmpty()) {
645                 throw new SQLExceptionList(thrownList);
646             }
647         }
648         setLastUsed(0);
649     }
650 
651     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
652     @Override
653     public CallableStatement prepareCall(final String sql) throws SQLException {
654         checkOpen();
655         try {
656             return init(new DelegatingCallableStatement(this, connection.prepareCall(sql)));
657         } catch (final SQLException e) {
658             handleException(e);
659             return null;
660         }
661     }
662 
663     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
664     @Override
665     public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
666         throws SQLException {
667         checkOpen();
668         try {
669             return init(new DelegatingCallableStatement(this,
670                 connection.prepareCall(sql, resultSetType, resultSetConcurrency)));
671         } catch (final SQLException e) {
672             handleException(e);
673             return null;
674         }
675     }
676 
677     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
678     @Override
679     public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
680         final int resultSetHoldability) throws SQLException {
681         checkOpen();
682         try {
683             return init(new DelegatingCallableStatement(this,
684                 connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
685         } catch (final SQLException e) {
686             handleException(e);
687             return null;
688         }
689     }
690 
691     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
692     @Override
693     public PreparedStatement prepareStatement(final String sql) throws SQLException {
694         checkOpen();
695         try {
696             return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql)));
697         } catch (final SQLException e) {
698             handleException(e);
699             return null;
700         }
701     }
702 
703     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
704     @Override
705     public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
706         checkOpen();
707         try {
708             return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, autoGeneratedKeys)));
709         } catch (final SQLException e) {
710             handleException(e);
711             return null;
712         }
713     }
714 
715     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
716     @Override
717     public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
718         throws SQLException {
719         checkOpen();
720         try {
721             return init(new DelegatingPreparedStatement(this,
722                 connection.prepareStatement(sql, resultSetType, resultSetConcurrency)));
723         } catch (final SQLException e) {
724             handleException(e);
725             return null;
726         }
727     }
728 
729     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
730     @Override
731     public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
732         final int resultSetHoldability) throws SQLException {
733         checkOpen();
734         try {
735             return init(new DelegatingPreparedStatement(this,
736                 connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
737         } catch (final SQLException e) {
738             handleException(e);
739             return null;
740         }
741     }
742 
743     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
744     @Override
745     public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
746         checkOpen();
747         try {
748             return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnIndexes)));
749         } catch (final SQLException e) {
750             handleException(e);
751             return null;
752         }
753     }
754 
755     @SuppressWarnings("resource") // Caller is responsible for closing the resource.
756     @Override
757     public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
758         checkOpen();
759         try {
760             return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnNames)));
761         } catch (final SQLException e) {
762             handleException(e);
763             return null;
764         }
765     }
766 
767     @Override
768     public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
769         checkOpen();
770         try {
771             connection.releaseSavepoint(savepoint);
772         } catch (final SQLException e) {
773             handleException(e);
774         }
775     }
776 
777     @Override
778     public void rollback() throws SQLException {
779         checkOpen();
780         try {
781             connection.rollback();
782         } catch (final SQLException e) {
783             handleException(e);
784         }
785     }
786 
787     @Override
788     public void rollback(final Savepoint savepoint) throws SQLException {
789         checkOpen();
790         try {
791             connection.rollback(savepoint);
792         } catch (final SQLException e) {
793             handleException(e);
794         }
795     }
796 
797     @Override
798     public void setAutoCommit(final boolean autoCommit) throws SQLException {
799         checkOpen();
800         try {
801             connection.setAutoCommit(autoCommit);
802             if (cacheState) {
803                 cachedAutoCommit = connection.getAutoCommit();
804             }
805         } catch (final SQLException e) {
806             cachedAutoCommit = null;
807             handleException(e);
808         }
809     }
810 
811     /**
812      * Sets the state caching flag.
813      *
814      * @param cacheState
815      *            The new value for the state caching flag
816      */
817     public void setCacheState(final boolean cacheState) {
818         this.cacheState = cacheState;
819     }
820 
821     @Override
822     public void setCatalog(final String catalog) throws SQLException {
823         checkOpen();
824         try {
825             connection.setCatalog(catalog);
826             if (cacheState) {
827                 cachedCatalog = connection.getCatalog();
828             }
829         } catch (final SQLException e) {
830             cachedCatalog = null;
831             handleException(e);
832         }
833     }
834 
835     @Override
836     public void setClientInfo(final Properties properties) throws SQLClientInfoException {
837         try {
838             checkOpen();
839             connection.setClientInfo(properties);
840         } catch (final SQLClientInfoException e) {
841             throw e;
842         } catch (final SQLException e) {
843             throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
844         }
845     }
846 
847     @Override
848     public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
849         try {
850             checkOpen();
851             connection.setClientInfo(name, value);
852         } catch (final SQLClientInfoException e) {
853             throw e;
854         } catch (final SQLException e) {
855             throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
856         }
857     }
858 
859     protected void setClosedInternal(final boolean closed) {
860         this.closed = closed;
861     }
862 
863     /**
864      * Sets the default query timeout that will be used for {@link Statement}s created from this connection.
865      * <code>null</code> means that the driver default will be used.
866      *
867      * @param defaultQueryTimeoutSeconds
868      *            the new query timeout limit in seconds; zero means there is no limit
869      */
870     public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
871         this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds;
872     }
873 
874     /**
875      * Sets my delegate.
876      *
877      * @param connection
878      *            my delegate.
879      */
880     public void setDelegate(final C connection) {
881         this.connection = connection;
882     }
883 
884     @Override
885     public void setHoldability(final int holdability) throws SQLException {
886         checkOpen();
887         try {
888             connection.setHoldability(holdability);
889         } catch (final SQLException e) {
890             handleException(e);
891         }
892     }
893 
894     @Override
895     public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException {
896         checkOpen();
897         try {
898             Jdbc41Bridge.setNetworkTimeout(connection, executor, milliseconds);
899         } catch (final SQLException e) {
900             handleException(e);
901         }
902     }
903 
904     @Override
905     public void setReadOnly(final boolean readOnly) throws SQLException {
906         checkOpen();
907         try {
908             connection.setReadOnly(readOnly);
909             if (cacheState) {
910                 cachedReadOnly = connection.isReadOnly();
911             }
912         } catch (final SQLException e) {
913             cachedReadOnly = null;
914             handleException(e);
915         }
916     }
917 
918     @Override
919     public Savepoint setSavepoint() throws SQLException {
920         checkOpen();
921         try {
922             return connection.setSavepoint();
923         } catch (final SQLException e) {
924             handleException(e);
925             return null;
926         }
927     }
928 
929     @Override
930     public Savepoint setSavepoint(final String name) throws SQLException {
931         checkOpen();
932         try {
933             return connection.setSavepoint(name);
934         } catch (final SQLException e) {
935             handleException(e);
936             return null;
937         }
938     }
939 
940     @Override
941     public void setSchema(final String schema) throws SQLException {
942         checkOpen();
943         try {
944             Jdbc41Bridge.setSchema(connection, schema);
945             if (cacheState) {
946                 cachedSchema = connection.getSchema();
947             }
948         } catch (final SQLException e) {
949             cachedSchema = null;
950             handleException(e);
951         }
952     }
953 
954     @Override
955     public void setTransactionIsolation(final int level) throws SQLException {
956         checkOpen();
957         try {
958             connection.setTransactionIsolation(level);
959         } catch (final SQLException e) {
960             handleException(e);
961         }
962     }
963 
964     @Override
965     public void setTypeMap(final Map<String, Class<?>> map) throws SQLException {
966         checkOpen();
967         try {
968             connection.setTypeMap(map);
969         } catch (final SQLException e) {
970             handleException(e);
971         }
972     }
973 
974     /**
975      * Returns a string representation of the metadata associated with the innermost delegate connection.
976      */
977     @SuppressWarnings("resource")
978     @Override
979     public synchronized String toString() {
980         String str = null;
981 
982         final Connection conn = this.getInnermostDelegateInternal();
983         if (conn != null) {
984             try {
985                 if (conn.isClosed()) {
986                     str = "connection is closed";
987                 } else {
988                     final StringBuilder sb = new StringBuilder();
989                     sb.append(hashCode());
990                     final DatabaseMetaData meta = conn.getMetaData();
991                     if (meta != null) {
992                         sb.append(", URL=");
993                         sb.append(meta.getURL());
994                         sb.append(", ");
995                         sb.append(meta.getDriverName());
996                         str = sb.toString();
997                     }
998                 }
999             } catch (final SQLException ex) {
1000                 // Ignore
1001             }
1002         }
1003         return str != null ? str : super.toString();
1004     }
1005 
1006     @Override
1007     public <T> T unwrap(final Class<T> iface) throws SQLException {
1008         if (iface.isAssignableFrom(getClass())) {
1009             return iface.cast(this);
1010         }
1011         if (iface.isAssignableFrom(connection.getClass())) {
1012             return iface.cast(connection);
1013         }
1014         return connection.unwrap(iface);
1015     }
1016 }