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    
018    package org.apache.commons.dbcp;
019    
020    import java.sql.CallableStatement;
021    import java.sql.Connection;
022    import java.sql.DatabaseMetaData;
023    import java.sql.PreparedStatement;
024    import java.sql.SQLException;
025    import java.sql.SQLWarning;
026    import java.sql.Statement;
027    import java.util.Iterator;
028    import java.util.List;
029    import java.util.Map;
030    import java.sql.ResultSet;
031    /* JDBC_4_ANT_KEY_BEGIN */
032    import java.sql.Array;
033    import java.sql.Blob;
034    import java.sql.ClientInfoStatus;
035    import java.sql.Clob;
036    import java.sql.NClob;
037    import java.sql.SQLClientInfoException;
038    import java.sql.SQLXML;
039    import java.sql.Struct;
040    import java.util.Collections;
041    import java.util.Properties;
042    /* JDBC_4_ANT_KEY_END */
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     * @author Rodney Waldhoff
060     * @author Glenn L. Nielsen
061     * @author James House
062     * @author Dirk Verbeeck
063     * @version $Revision: 896719 $ $Date: 2010-01-06 18:42:22 -0500 (Wed, 06 Jan 2010) $
064     */
065    public class DelegatingConnection extends AbandonedTrace
066            implements Connection {
067    
068    /* JDBC_4_ANT_KEY_BEGIN */
069        private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES =
070            Collections.<String, ClientInfoStatus>emptyMap();
071    /* JDBC_4_ANT_KEY_END */
072    
073        /** My delegate {@link Connection}. */
074        protected Connection _conn = null;
075    
076        protected boolean _closed = false;
077        
078        /**
079         * Create a wrapper for the Connection which traces this
080         * Connection in the AbandonedObjectPool.
081         *
082         * @param c the {@link Connection} to delegate all calls to.
083         */
084        public DelegatingConnection(Connection c) {
085            super();
086            _conn = c;
087        }
088    
089        /**
090         * Create a wrapper for the Connection which traces
091         * the Statements created so that any unclosed Statements
092         * can be closed when this Connection is closed.
093         *
094         * @param c the {@link Connection} to delegate all calls to.
095         * @param config the configuration for tracing abandoned objects
096         */
097        public DelegatingConnection(Connection c, AbandonedConfig config) {
098            super(config);
099            _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    }