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.dbcp;
19  
20  import java.sql.CallableStatement;
21  import java.sql.Connection;
22  import java.sql.DatabaseMetaData;
23  import java.sql.PreparedStatement;
24  import java.sql.SQLException;
25  import java.sql.SQLWarning;
26  import java.sql.Statement;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.sql.ResultSet;
31  /* JDBC_4_ANT_KEY_BEGIN */
32  import java.sql.Array;
33  import java.sql.Blob;
34  import java.sql.ClientInfoStatus;
35  import java.sql.Clob;
36  import java.sql.NClob;
37  import java.sql.SQLClientInfoException;
38  import java.sql.SQLXML;
39  import java.sql.Struct;
40  import java.util.Collections;
41  import java.util.Properties;
42  /* JDBC_4_ANT_KEY_END */
43  
44  /**
45   * A base delegating implementation of {@link Connection}.
46   * <p>
47   * All of the methods from the {@link Connection} interface
48   * simply check to see that the {@link Connection} is active,
49   * and call the corresponding method on the "delegate"
50   * provided in my constructor.
51   * <p>
52   * Extends AbandonedTrace to implement Connection tracking and
53   * logging of code which created the Connection. Tracking the
54   * Connection ensures that the AbandonedObjectPool can close
55   * this connection and recycle it if its pool of connections
56   * is nearing exhaustion and this connection's last usage is
57   * older than the removeAbandonedTimeout.
58   *
59   * @author Rodney Waldhoff
60   * @author Glenn L. Nielsen
61   * @author James House
62   * @author Dirk Verbeeck
63   * @version $Revision: 1023401 $ $Date: 2010-10-16 21:54:24 -0400 (Sat, 16 Oct 2010) $
64   */
65  public class DelegatingConnection extends AbandonedTrace
66          implements Connection {
67  
68  /* JDBC_4_ANT_KEY_BEGIN */
69      private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES =
70          Collections.<String, ClientInfoStatus>emptyMap();
71  /* JDBC_4_ANT_KEY_END */
72  
73      /** My delegate {@link Connection}. */
74      protected Connection _conn = null;
75  
76      protected boolean _closed = false;
77      
78      /**
79       * Create a wrapper for the Connection which traces this
80       * Connection in the AbandonedObjectPool.
81       *
82       * @param c the {@link Connection} to delegate all calls to.
83       */
84      public DelegatingConnection(Connection c) {
85          super();
86          _conn = c;
87      }
88  
89      /**
90       * Create a wrapper for the Connection which traces
91       * the Statements created so that any unclosed Statements
92       * can be closed when this Connection is closed.
93       *
94       * @param c the {@link Connection} to delegate all calls to.
95       * @param config the configuration for tracing abandoned objects
96       */
97      public DelegatingConnection(Connection c, AbandonedConfig config) {
98          super(config);
99          _conn = c;
100     }
101 
102     /**
103      * Returns a string representation of the metadata associated with
104      * the innnermost delegate connection.
105      * 
106      * @since 1.2.2
107      */
108     public String toString() {
109         String s = null;
110         
111         Connection c = this.getInnermostDelegateInternal();
112         if (c != null) {
113             try {
114                 if (c.isClosed()) {
115                     s = "connection is closed";
116                 }
117                 else {
118                     DatabaseMetaData meta = c.getMetaData();
119                     if (meta != null) {
120                         StringBuffer sb = new StringBuffer();
121                         sb.append(meta.getURL());
122                         sb.append(", UserName=");
123                         sb.append(meta.getUserName());
124                         sb.append(", ");
125                         sb.append(meta.getDriverName());
126                         s = sb.toString();
127                     }
128                 }
129             }
130             catch (SQLException ex) {
131                 // Ignore
132             }
133         }
134         
135         if (s == null) {
136             s = super.toString();
137         }
138         
139         return s;
140     }
141 
142     /**
143      * Returns my underlying {@link Connection}.
144      * @return my underlying {@link Connection}.
145      */
146     public Connection getDelegate() {
147         return getDelegateInternal();
148     }
149     
150     /**
151      * Should be final but can't be for compatibility with previous releases.
152      */
153     protected Connection getDelegateInternal() {
154         return _conn;
155     }
156     
157     /**
158      * Compares innermost delegate to the given connection.
159      * 
160      * @param c connection to compare innermost delegate with
161      * @return true if innermost delegate equals <code>c</code>
162      * @since 1.2.2
163      */
164     public boolean innermostDelegateEquals(Connection c) {
165         Connection innerCon = getInnermostDelegateInternal();
166         if (innerCon == null) {
167             return c == null;
168         } else {
169             return innerCon.equals(c);
170         }
171     }
172 
173     /**
174      * This method considers two objects to be equal 
175      * if the underlying jdbc objects are equal.
176      */
177     public boolean equals(Object obj) {
178         if (obj == null) {
179             return false;
180         }
181         if (obj == this) {
182             return true;
183         }
184         Connection delegate = getInnermostDelegateInternal();
185         if (delegate == null) {
186             return false;
187         }
188         if (obj instanceof DelegatingConnection) {    
189             DelegatingConnection c = (DelegatingConnection) obj;
190             return c.innermostDelegateEquals(delegate);
191         }
192         else {
193             return delegate.equals(obj);
194         }
195     }
196 
197     public int hashCode() {
198         Object obj = getInnermostDelegateInternal();
199         if (obj == null) {
200             return 0;
201         }
202         return obj.hashCode();
203     }
204 
205 
206     /**
207      * If my underlying {@link Connection} is not a
208      * <tt>DelegatingConnection</tt>, returns it,
209      * otherwise recursively invokes this method on
210      * my delegate.
211      * <p>
212      * Hence this method will return the first
213      * delegate that is not a <tt>DelegatingConnection</tt>,
214      * or <tt>null</tt> when no non-<tt>DelegatingConnection</tt>
215      * delegate can be found by traversing this chain.
216      * <p>
217      * This method is useful when you may have nested
218      * <tt>DelegatingConnection</tt>s, and you want to make
219      * sure to obtain a "genuine" {@link Connection}.
220      */
221     public Connection getInnermostDelegate() {
222         return getInnermostDelegateInternal();
223     }
224 
225     protected final Connection getInnermostDelegateInternal() {
226         Connection c = _conn;
227         while(c != null && c instanceof DelegatingConnection) {
228             c = ((DelegatingConnection)c).getDelegateInternal();
229             if(this == c) {
230                 return null;
231             }
232         }
233         return c;
234     }
235     
236     /** Sets my delegate. */
237     public void setDelegate(Connection c) {
238         _conn = c;
239     }
240 
241     /**
242      * Closes the underlying connection, and close
243      * any Statements that were not explicitly closed.
244      */
245     public void close() throws SQLException {
246         passivate();
247         _conn.close();
248     }
249 
250     protected void handleException(SQLException e) throws SQLException {
251         throw e;
252     }
253 
254     public Statement createStatement() throws SQLException {
255         checkOpen();
256         try {
257             return new DelegatingStatement(this, _conn.createStatement());
258         }
259         catch (SQLException e) {
260             handleException(e);
261             return null;
262         }
263     }
264 
265     public Statement createStatement(int resultSetType,
266                                      int resultSetConcurrency) throws SQLException {
267         checkOpen();
268         try {
269             return new DelegatingStatement
270                 (this, _conn.createStatement(resultSetType,resultSetConcurrency));
271         }
272         catch (SQLException e) {
273             handleException(e);
274             return null;
275         }
276     }
277 
278     public PreparedStatement prepareStatement(String sql) throws SQLException {
279         checkOpen();
280         try {
281             return new DelegatingPreparedStatement
282                 (this, _conn.prepareStatement(sql));
283         }
284         catch (SQLException e) {
285             handleException(e);
286             return null;
287         }
288     }
289 
290     public PreparedStatement prepareStatement(String sql,
291                                               int resultSetType,
292                                               int resultSetConcurrency) throws SQLException {
293         checkOpen();
294         try {
295             return new DelegatingPreparedStatement
296                 (this, _conn.prepareStatement
297                     (sql,resultSetType,resultSetConcurrency));
298         }
299         catch (SQLException e) {
300             handleException(e);
301             return null;
302         }
303     }
304 
305     public CallableStatement prepareCall(String sql) throws SQLException {
306         checkOpen();
307         try {
308             return new DelegatingCallableStatement(this, _conn.prepareCall(sql));
309         }
310         catch (SQLException e) {
311             handleException(e);
312             return null;
313         }
314     }
315 
316     public CallableStatement prepareCall(String sql,
317                                          int resultSetType,
318                                          int resultSetConcurrency) throws SQLException {
319         checkOpen();
320         try {
321             return new DelegatingCallableStatement
322                 (this, _conn.prepareCall(sql, resultSetType,resultSetConcurrency));
323         }
324         catch (SQLException e) {
325             handleException(e);
326             return null;
327         }
328     }
329 
330     public void clearWarnings() throws SQLException
331     { checkOpen(); try { _conn.clearWarnings(); } catch (SQLException e) { handleException(e); } }
332     
333     public void commit() throws SQLException
334     { checkOpen(); try { _conn.commit(); } catch (SQLException e) { handleException(e); } }
335     
336     public boolean getAutoCommit() throws SQLException
337     { checkOpen(); try { return _conn.getAutoCommit(); } catch (SQLException e) { handleException(e); return false; } 
338     }
339     public String getCatalog() throws SQLException
340     { checkOpen(); try { return _conn.getCatalog(); } catch (SQLException e) { handleException(e); return null; } }
341     
342     public DatabaseMetaData getMetaData() throws SQLException {
343         checkOpen();
344         try {
345             return new DelegatingDatabaseMetaData(this, _conn.getMetaData());
346         } catch (SQLException e) {
347             handleException(e);
348             return null;
349         }
350     }
351     
352     public int getTransactionIsolation() throws SQLException
353     { checkOpen(); try { return _conn.getTransactionIsolation(); } catch (SQLException e) { handleException(e); return -1; } }
354     
355     public Map getTypeMap() throws SQLException
356     { checkOpen(); try { return _conn.getTypeMap(); } catch (SQLException e) { handleException(e); return null; } }
357     
358     public SQLWarning getWarnings() throws SQLException
359     { checkOpen(); try { return _conn.getWarnings(); } catch (SQLException e) { handleException(e); return null; } }
360     
361     public boolean isReadOnly() throws SQLException
362     { checkOpen(); try { return _conn.isReadOnly(); } catch (SQLException e) { handleException(e); return false; } }
363     
364     public String nativeSQL(String sql) throws SQLException
365     { checkOpen(); try { return _conn.nativeSQL(sql); } catch (SQLException e) { handleException(e); return null; } }
366     
367     public void rollback() throws SQLException
368     { checkOpen(); try {  _conn.rollback(); } catch (SQLException e) { handleException(e); } }
369     
370     public void setAutoCommit(boolean autoCommit) throws SQLException
371     { checkOpen(); try { _conn.setAutoCommit(autoCommit); } catch (SQLException e) { handleException(e); } }
372 
373     public void setCatalog(String catalog) throws SQLException
374     { checkOpen(); try { _conn.setCatalog(catalog); } catch (SQLException e) { handleException(e); } }
375 
376     public void setReadOnly(boolean readOnly) throws SQLException
377     { checkOpen(); try { _conn.setReadOnly(readOnly); } catch (SQLException e) { handleException(e); } }
378 
379     public void setTransactionIsolation(int level) throws SQLException
380     { checkOpen(); try { _conn.setTransactionIsolation(level); } catch (SQLException e) { handleException(e); } }
381 
382     public void setTypeMap(Map map) throws SQLException
383     { checkOpen(); try { _conn.setTypeMap(map); } catch (SQLException e) { handleException(e); } }
384 
385     public boolean isClosed() throws SQLException {
386         return _closed || _conn.isClosed();
387     }
388 
389     protected void checkOpen() throws SQLException {
390         if(_closed) {
391             if (null != _conn) {
392                 String label = "";
393                 try {
394                     label = _conn.toString();
395                 } catch (Exception ex) {
396                     // ignore, leave label empty
397                 }
398                 throw new SQLException
399                     ("Connection " + label + " is closed.");
400             } else {
401                 throw new SQLException
402                     ("Connection is null.");
403             }      
404         }
405     }
406 
407     protected void activate() {
408         _closed = false;
409         setLastUsed();
410         if(_conn instanceof DelegatingConnection) {
411             ((DelegatingConnection)_conn).activate();
412         }
413     }
414 
415     protected void passivate() throws SQLException {
416         try {
417             // The JDBC spec requires that a Connection close any open
418             // Statement's when it is closed.
419             // DBCP-288. Not all the traced objects will be statements
420             List traces = getTrace();
421             if(traces != null) {
422                 Iterator traceIter = traces.iterator();
423                 while (traceIter.hasNext()) {
424                     Object trace = traceIter.next();
425                     if (trace instanceof Statement) {
426                         ((Statement) trace).close();
427                     } else if (trace instanceof ResultSet) {
428                         // DBCP-265: Need to close the result sets that are
429                         // generated via DatabaseMetaData
430                         ((ResultSet) trace).close();
431                     }
432                 }
433                 clearTrace();
434             }
435             setLastUsed(0);
436             if(_conn instanceof DelegatingConnection) {
437                 ((DelegatingConnection)_conn).passivate();
438             }
439         }
440         finally {
441             _closed = true;
442         }
443     }
444 
445     public int getHoldability() throws SQLException
446     { checkOpen(); try { return _conn.getHoldability(); } catch (SQLException e) { handleException(e); return 0; } }
447 
448     public void setHoldability(int holdability) throws SQLException
449     { checkOpen(); try { _conn.setHoldability(holdability); } catch (SQLException e) { handleException(e); } }
450 
451     public java.sql.Savepoint setSavepoint() throws SQLException
452     { checkOpen(); try { return _conn.setSavepoint(); } catch (SQLException e) { handleException(e); return null; } }
453 
454     public java.sql.Savepoint setSavepoint(String name) throws SQLException
455     { checkOpen(); try { return _conn.setSavepoint(name); } catch (SQLException e) { handleException(e); return null; } }
456 
457     public void rollback(java.sql.Savepoint savepoint) throws SQLException
458     { checkOpen(); try { _conn.rollback(savepoint); } catch (SQLException e) { handleException(e); } }
459 
460     public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException
461     { checkOpen(); try { _conn.releaseSavepoint(savepoint); } catch (SQLException e) { handleException(e); } }
462 
463     public Statement createStatement(int resultSetType,
464                                      int resultSetConcurrency,
465                                      int resultSetHoldability) throws SQLException {
466         checkOpen();
467         try {
468             return new DelegatingStatement(this, _conn.createStatement(
469                 resultSetType, resultSetConcurrency, resultSetHoldability));
470         }
471         catch (SQLException e) {
472             handleException(e);
473             return null;
474         }
475     }
476 
477     public PreparedStatement prepareStatement(String sql, int resultSetType,
478                                               int resultSetConcurrency,
479                                               int resultSetHoldability) throws SQLException {
480         checkOpen();
481         try {
482             return new DelegatingPreparedStatement(this, _conn.prepareStatement(
483                 sql, resultSetType, resultSetConcurrency, resultSetHoldability));
484         }
485         catch (SQLException e) {
486             handleException(e);
487             return null;
488         }
489     }
490 
491     public CallableStatement prepareCall(String sql, int resultSetType,
492                                          int resultSetConcurrency,
493                                          int resultSetHoldability) throws SQLException {
494         checkOpen();
495         try {
496             return new DelegatingCallableStatement(this, _conn.prepareCall(
497                 sql, resultSetType, resultSetConcurrency, resultSetHoldability));
498         }
499         catch (SQLException e) {
500             handleException(e);
501             return null;
502         }
503     }
504 
505     public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
506         checkOpen();
507         try {
508             return new DelegatingPreparedStatement(this, _conn.prepareStatement(
509                 sql, autoGeneratedKeys));
510         }
511         catch (SQLException e) {
512             handleException(e);
513             return null;
514         }
515     }
516 
517     public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException {
518         checkOpen();
519         try {
520             return new DelegatingPreparedStatement(this, _conn.prepareStatement(
521                 sql, columnIndexes));
522         }
523         catch (SQLException e) {
524             handleException(e);
525             return null;
526         }
527     }
528 
529     public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException {
530         checkOpen();
531         try {
532             return new DelegatingPreparedStatement(this, _conn.prepareStatement(
533                 sql, columnNames));
534         }
535         catch (SQLException e) {
536             handleException(e);
537             return null;
538         }
539     }
540 
541 /* JDBC_4_ANT_KEY_BEGIN */
542 
543     public boolean isWrapperFor(Class<?> iface) throws SQLException {
544         return iface.isAssignableFrom(getClass()) || _conn.isWrapperFor(iface);
545     }
546 
547     public <T> T unwrap(Class<T> iface) throws SQLException {
548         if (iface.isAssignableFrom(getClass())) {
549             return iface.cast(this);
550         } else if (iface.isAssignableFrom(_conn.getClass())) {
551             return iface.cast(_conn);
552         } else {
553             return _conn.unwrap(iface);
554         }
555     }
556 
557     public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
558         checkOpen();
559         try {
560             return _conn.createArrayOf(typeName, elements);
561         }
562         catch (SQLException e) {
563             handleException(e);
564             return null;
565         }
566     }
567 
568     public Blob createBlob() throws SQLException {
569         checkOpen();
570         try {
571             return _conn.createBlob();
572         }
573         catch (SQLException e) {
574             handleException(e);
575             return null;
576         }
577     }
578 
579     public Clob createClob() throws SQLException {
580         checkOpen();
581         try {
582             return _conn.createClob();
583         }
584         catch (SQLException e) {
585             handleException(e);
586             return null;
587         }
588     }
589 
590     public NClob createNClob() throws SQLException {
591         checkOpen();
592         try {
593             return _conn.createNClob();
594         }
595         catch (SQLException e) {
596             handleException(e);
597             return null;
598         }
599     }
600 
601     public SQLXML createSQLXML() throws SQLException {
602         checkOpen();
603         try {
604             return _conn.createSQLXML();
605         }
606         catch (SQLException e) {
607             handleException(e);
608             return null;
609         }
610     }
611 
612     public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
613         checkOpen();
614         try {
615             return _conn.createStruct(typeName, attributes);
616         }
617         catch (SQLException e) {
618             handleException(e);
619             return null;
620         }
621     }
622 
623     public boolean isValid(int timeout) throws SQLException {
624         checkOpen();
625         try {
626             return _conn.isValid(timeout);
627         }
628         catch (SQLException e) {
629             handleException(e);
630             return false;
631         }
632     }
633 
634     public void setClientInfo(String name, String value) throws SQLClientInfoException {
635         try {
636             checkOpen();
637             _conn.setClientInfo(name, value);
638         }
639         catch (SQLClientInfoException e) {
640             throw e;
641         }
642         catch (SQLException e) {
643             throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
644         }
645     }
646 
647     public void setClientInfo(Properties properties) throws SQLClientInfoException {
648         try {
649             checkOpen();
650             _conn.setClientInfo(properties);
651         }
652         catch (SQLClientInfoException e) {
653             throw e;
654         }
655         catch (SQLException e) {
656             throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
657         }
658     }
659 
660     public Properties getClientInfo() throws SQLException {
661         checkOpen();
662         try {
663             return _conn.getClientInfo();
664         }
665         catch (SQLException e) {
666             handleException(e);
667             return null;
668         }
669     }
670 
671     public String getClientInfo(String name) throws SQLException {
672         checkOpen();
673         try {
674             return _conn.getClientInfo(name);
675         }
676         catch (SQLException e) {
677             handleException(e);
678             return null;
679         }
680     }
681 /* JDBC_4_ANT_KEY_END */
682 }