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.Connection;
021    import java.sql.ResultSet;
022    import java.sql.SQLException;
023    import java.sql.SQLWarning;
024    import java.sql.Statement;
025    import java.util.List;
026    
027    /**
028     * A base delegating implementation of {@link Statement}.
029     * <p>
030     * All of the methods from the {@link Statement} interface
031     * simply check to see that the {@link Statement} is active,
032     * and call the corresponding method on the "delegate"
033     * provided in my constructor.
034     * <p>
035     * Extends AbandonedTrace to implement Statement tracking and
036     * logging of code which created the Statement. Tracking the
037     * Statement ensures that the Connection which created it can
038     * close any open Statement's on Connection close.
039     *
040     * @author Rodney Waldhoff
041     * @author Glenn L. Nielsen
042     * @author James House
043     * @author Dirk Verbeeck
044     * @version $Revision: 892307 $ $Date: 2013-12-31 23:27:28 +0000 (Tue, 31 Dec 2013) $
045     */
046    public class DelegatingStatement extends AbandonedTrace implements Statement {
047        /** My delegate. */
048        protected Statement _stmt = null;
049        /** The connection that created me. **/
050        protected DelegatingConnection _conn = null;
051    
052        /**
053         * Create a wrapper for the Statement which traces this
054         * Statement to the Connection which created it and the
055         * code which created it.
056         *
057         * @param s the {@link Statement} to delegate all calls to.
058         * @param c the {@link DelegatingConnection} that created this statement.
059         */
060        public DelegatingStatement(DelegatingConnection c, Statement s) {
061            super(c);
062            _stmt = s;
063            _conn = c;
064        }
065    
066        /**
067         * Returns my underlying {@link Statement}.
068         * @return my underlying {@link Statement}.
069         * @see #getInnermostDelegate
070         */
071        public Statement getDelegate() {
072            return _stmt;
073        }
074    
075        /**
076         * This method considers two objects to be equal 
077         * if the underlying jdbc objects are equal.
078         */
079        public boolean equals(Object obj) {
080            Statement delegate = getInnermostDelegate();
081            if (delegate == null) {
082                return false;
083            }
084            if (obj instanceof DelegatingStatement) {
085                DelegatingStatement s = (DelegatingStatement) obj;
086                return delegate.equals(s.getInnermostDelegate());
087            }
088            else {
089                return delegate.equals(obj);
090            }
091        }
092    
093        public int hashCode() {
094            Object obj = getInnermostDelegate();
095            if (obj == null) {
096                return 0;
097            }
098            return obj.hashCode();
099        }
100        
101        /**
102         * If my underlying {@link Statement} is not a
103         * <tt>DelegatingStatement</tt>, returns it,
104         * otherwise recursively invokes this method on
105         * my delegate.
106         * <p>
107         * Hence this method will return the first
108         * delegate that is not a <tt>DelegatingStatement</tt>
109         * or <tt>null</tt> when no non-<tt>DelegatingStatement</tt>
110         * delegate can be found by transversing this chain.
111         * <p>
112         * This method is useful when you may have nested
113         * <tt>DelegatingStatement</tt>s, and you want to make
114         * sure to obtain a "genuine" {@link Statement}.
115         * @see #getDelegate
116         */
117        public Statement getInnermostDelegate() {
118            Statement s = _stmt;
119            while(s != null && s instanceof DelegatingStatement) {
120                s = ((DelegatingStatement)s).getDelegate();
121                if(this == s) {
122                    return null;
123                }
124            }
125            return s;
126        }
127    
128        /** Sets my delegate. */
129        public void setDelegate(Statement s) {
130            _stmt = s;
131        }
132    
133        protected boolean _closed = false;
134    
135        protected void checkOpen() throws SQLException {
136            if(isClosed()) {
137                throw new SQLException
138                    (this.getClass().getName() + " with address: \"" + 
139                    this.toString() + "\" is closed.");
140            }
141        }
142    
143        /**
144         * Close this DelegatingStatement, and close
145         * any ResultSets that were not explicitly closed.
146         */
147        public void close() throws SQLException {
148            try {
149                try {
150                    if (_conn != null) {
151                        _conn.removeTrace(this);
152                        _conn = null;
153                    }
154            
155                    // The JDBC spec requires that a statment close any open
156                    // ResultSet's when it is closed.
157                    // FIXME The PreparedStatement we're wrapping should handle this for us.
158                    // See bug 17301 for what could happen when ResultSets are closed twice.
159                    List resultSets = getTrace();
160                    if( resultSets != null) {
161                        ResultSet[] set = (ResultSet[]) resultSets.toArray(new ResultSet[resultSets.size()]);
162                        for (int i = 0; i < set.length; i++) {
163                            set[i].close();
164                        }
165                        clearTrace();
166                    }
167            
168                    _stmt.close();
169                }
170                catch (SQLException e) {
171                    handleException(e);
172                }
173            }
174            finally {
175                _closed = true;
176            }
177        }
178    
179        protected void handleException(SQLException e) throws SQLException {
180            if (_conn != null) {
181                _conn.handleException(e);
182            }
183            else {
184                throw e;
185            }
186        }
187    
188        protected void activate() throws SQLException {
189            if(_stmt instanceof DelegatingStatement) {
190                ((DelegatingStatement)_stmt).activate();
191            }
192        }
193    
194        protected void passivate() throws SQLException {
195            if(_stmt instanceof DelegatingStatement) {
196                ((DelegatingStatement)_stmt).passivate();
197            }
198        }
199    
200        public Connection getConnection() throws SQLException {
201            checkOpen();
202            return _conn; // return the delegating connection that created this
203        }
204    
205        public ResultSet executeQuery(String sql) throws SQLException {
206            checkOpen();
207            try {
208                return DelegatingResultSet.wrapResultSet(this,_stmt.executeQuery(sql));
209            }
210            catch (SQLException e) {
211                handleException(e);
212                throw new AssertionError();
213            }
214        }
215    
216        public ResultSet getResultSet() throws SQLException {
217            checkOpen();
218            try {
219                return DelegatingResultSet.wrapResultSet(this,_stmt.getResultSet());
220            }
221            catch (SQLException e) {
222                handleException(e);
223                throw new AssertionError();
224            }
225        }
226    
227        public int executeUpdate(String sql) throws SQLException
228        { checkOpen(); try { return _stmt.executeUpdate(sql); } catch (SQLException e) { handleException(e); return 0; } }
229    
230        public int getMaxFieldSize() throws SQLException
231        { checkOpen(); try { return _stmt.getMaxFieldSize(); } catch (SQLException e) { handleException(e); return 0; } }
232    
233        public void setMaxFieldSize(int max) throws SQLException
234        { checkOpen(); try { _stmt.setMaxFieldSize(max); } catch (SQLException e) { handleException(e); } }
235    
236        public int getMaxRows() throws SQLException
237        { checkOpen(); try { return _stmt.getMaxRows(); } catch (SQLException e) { handleException(e); return 0; } }
238    
239        public void setMaxRows(int max) throws SQLException
240        { checkOpen(); try { _stmt.setMaxRows(max); } catch (SQLException e) { handleException(e); } }
241    
242        public void setEscapeProcessing(boolean enable) throws SQLException
243        { checkOpen(); try { _stmt.setEscapeProcessing(enable); } catch (SQLException e) { handleException(e); } }
244    
245        public int getQueryTimeout() throws SQLException
246        { checkOpen(); try { return _stmt.getQueryTimeout(); } catch (SQLException e) { handleException(e); return 0; } }
247    
248        public void setQueryTimeout(int seconds) throws SQLException
249        { checkOpen(); try { _stmt.setQueryTimeout(seconds); } catch (SQLException e) { handleException(e); } }
250    
251        public void cancel() throws SQLException
252        { checkOpen(); try { _stmt.cancel(); } catch (SQLException e) { handleException(e); } }
253    
254        public SQLWarning getWarnings() throws SQLException
255        { checkOpen(); try { return _stmt.getWarnings(); } catch (SQLException e) { handleException(e); throw new AssertionError(); } }
256    
257        public void clearWarnings() throws SQLException
258        { checkOpen(); try { _stmt.clearWarnings(); } catch (SQLException e) { handleException(e); } }
259    
260        public void setCursorName(String name) throws SQLException
261        { checkOpen(); try { _stmt.setCursorName(name); } catch (SQLException e) { handleException(e); } }
262    
263        public boolean execute(String sql) throws SQLException
264        { checkOpen(); try { return _stmt.execute(sql); } catch (SQLException e) { handleException(e); return false; } }
265    
266        public int getUpdateCount() throws SQLException
267        { checkOpen(); try { return _stmt.getUpdateCount(); } catch (SQLException e) { handleException(e); return 0; } }
268    
269        public boolean getMoreResults() throws SQLException
270        { checkOpen(); try { return _stmt.getMoreResults(); } catch (SQLException e) { handleException(e); return false; } }
271    
272        public void setFetchDirection(int direction) throws SQLException
273        { checkOpen(); try { _stmt.setFetchDirection(direction); } catch (SQLException e) { handleException(e); } }
274    
275        public int getFetchDirection() throws SQLException
276        { checkOpen(); try { return _stmt.getFetchDirection(); } catch (SQLException e) { handleException(e); return 0; } }
277    
278        public void setFetchSize(int rows) throws SQLException
279        { checkOpen(); try { _stmt.setFetchSize(rows); } catch (SQLException e) { handleException(e); } }
280    
281        public int getFetchSize() throws SQLException
282        { checkOpen(); try { return _stmt.getFetchSize(); } catch (SQLException e) { handleException(e); return 0; } }
283    
284        public int getResultSetConcurrency() throws SQLException
285        { checkOpen(); try { return _stmt.getResultSetConcurrency(); } catch (SQLException e) { handleException(e); return 0; } }
286    
287        public int getResultSetType() throws SQLException
288        { checkOpen(); try { return _stmt.getResultSetType(); } catch (SQLException e) { handleException(e); return 0; } }
289    
290        public void addBatch(String sql) throws SQLException
291        { checkOpen(); try { _stmt.addBatch(sql); } catch (SQLException e) { handleException(e); } }
292    
293        public void clearBatch() throws SQLException
294        { checkOpen(); try { _stmt.clearBatch(); } catch (SQLException e) { handleException(e); } }
295    
296        public int[] executeBatch() throws SQLException
297        { checkOpen(); try { return _stmt.executeBatch(); } catch (SQLException e) { handleException(e); throw new AssertionError(); } }
298    
299        /**
300         * Returns a String representation of this object.
301         *
302         * @return String
303         * @since 1.2.2
304         */
305        public String toString() {
306        return _stmt.toString();
307        }
308    
309        public boolean getMoreResults(int current) throws SQLException
310        { checkOpen(); try { return _stmt.getMoreResults(current); } catch (SQLException e) { handleException(e); return false; } }
311    
312        public ResultSet getGeneratedKeys() throws SQLException {
313            checkOpen();
314            try {
315                return DelegatingResultSet.wrapResultSet(this, _stmt.getGeneratedKeys());
316            } catch (SQLException e) {
317                handleException(e);
318                throw new AssertionError();
319            }
320        }
321    
322        public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
323        { checkOpen(); try { return _stmt.executeUpdate(sql, autoGeneratedKeys); } catch (SQLException e) { handleException(e); return 0; } }
324    
325        public int executeUpdate(String sql, int columnIndexes[]) throws SQLException
326        { checkOpen(); try { return _stmt.executeUpdate(sql, columnIndexes); } catch (SQLException e) { handleException(e); return 0; } }
327    
328        public int executeUpdate(String sql, String columnNames[]) throws SQLException
329        { checkOpen(); try { return _stmt.executeUpdate(sql, columnNames); } catch (SQLException e) { handleException(e); return 0; } }
330    
331        public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
332        { checkOpen(); try { return _stmt.execute(sql, autoGeneratedKeys); } catch (SQLException e) { handleException(e); return false; } }
333    
334        public boolean execute(String sql, int columnIndexes[]) throws SQLException
335        { checkOpen(); try { return _stmt.execute(sql, columnIndexes); } catch (SQLException e) { handleException(e); return false; } }
336    
337        public boolean execute(String sql, String columnNames[]) throws SQLException
338        { checkOpen(); try { return _stmt.execute(sql, columnNames); } catch (SQLException e) { handleException(e); return false; } }
339    
340        public int getResultSetHoldability() throws SQLException
341        { checkOpen(); try { return _stmt.getResultSetHoldability(); } catch (SQLException e) { handleException(e); return 0; } }
342    
343        /*
344         * Note was protected prior to JDBC 4
345         * TODO Consider adding build flags to make this protected unless we are
346         *      using JDBC 4.
347         */
348        public boolean isClosed() throws SQLException {
349            return _closed;
350        }
351    
352    /*
353    
354        public boolean isWrapperFor(Class<?> iface) throws SQLException {
355            return iface.isAssignableFrom(getClass()) || _stmt.isWrapperFor(iface);
356        }
357    
358        public <T> T unwrap(Class<T> iface) throws SQLException {
359            if (iface.isAssignableFrom(getClass())) {
360                return iface.cast(this);
361            } else if (iface.isAssignableFrom(_stmt.getClass())) {
362                return iface.cast(_stmt);
363            } else {
364                return _stmt.unwrap(iface);
365            }
366        }
367    
368        public void setPoolable(boolean poolable) throws SQLException {
369            checkOpen();
370            try {
371                _stmt.setPoolable(poolable);
372            }
373            catch (SQLException e) {
374                handleException(e);
375            }
376        }
377    
378        public boolean isPoolable() throws SQLException {
379            checkOpen();
380            try {
381                return _stmt.isPoolable();
382            }
383            catch (SQLException e) {
384                handleException(e);
385                return false;
386            }
387        }
388    */
389    }