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 */
017package org.apache.commons.dbcp2;
018
019import java.sql.Connection;
020import java.sql.ResultSet;
021import java.sql.SQLException;
022import java.sql.SQLWarning;
023import java.sql.Statement;
024import java.util.List;
025
026/**
027 * A base delegating implementation of {@link Statement}.
028 * <p>
029 * All of the methods from the {@link Statement} interface
030 * simply check to see that the {@link Statement} is active,
031 * and call the corresponding method on the "delegate"
032 * provided in my constructor.
033 * <p>
034 * Extends AbandonedTrace to implement Statement tracking and
035 * logging of code which created the Statement. Tracking the
036 * Statement ensures that the Connection which created it can
037 * close any open Statement's on Connection close.
038 *
039 * @author Rodney Waldhoff
040 * @author Glenn L. Nielsen
041 * @author James House
042 * @author Dirk Verbeeck
043 * @version $Revision: 1572242 $ $Date: 2014-02-26 20:34:39 +0000 (Wed, 26 Feb 2014) $
044 * @since 2.0
045 */
046public class DelegatingStatement extends AbandonedTrace implements Statement {
047    /** My delegate. */
048    private Statement _stmt = null;
049    /** The connection that created me. **/
050    private 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    /**
077     * If my underlying {@link Statement} is not a
078     * <tt>DelegatingStatement</tt>, returns it,
079     * otherwise recursively invokes this method on
080     * my delegate.
081     * <p>
082     * Hence this method will return the first
083     * delegate that is not a <tt>DelegatingStatement</tt>
084     * or <tt>null</tt> when no non-<tt>DelegatingStatement</tt>
085     * delegate can be found by transversing this chain.
086     * <p>
087     * This method is useful when you may have nested
088     * <tt>DelegatingStatement</tt>s, and you want to make
089     * sure to obtain a "genuine" {@link Statement}.
090     * @see #getDelegate
091     */
092    public Statement getInnermostDelegate() {
093        Statement s = _stmt;
094        while(s != null && s instanceof DelegatingStatement) {
095            s = ((DelegatingStatement)s).getDelegate();
096            if(this == s) {
097                return null;
098            }
099        }
100        return s;
101    }
102
103    /** Sets my delegate. */
104    public void setDelegate(Statement s) {
105        _stmt = s;
106    }
107
108    private boolean _closed = false;
109
110    protected boolean isClosedInternal() {
111        return _closed;
112    }
113
114    protected void setClosedInternal(boolean closed) {
115        this._closed = closed;
116    }
117
118    protected void checkOpen() throws SQLException {
119        if(isClosed()) {
120            throw new SQLException
121                (this.getClass().getName() + " with address: \"" +
122                this.toString() + "\" is closed.");
123        }
124    }
125
126    /**
127     * Close this DelegatingStatement, and close
128     * any ResultSets that were not explicitly closed.
129     */
130    @Override
131    public void close() throws SQLException {
132        try {
133            try {
134                if (_conn != null) {
135                    _conn.removeTrace(this);
136                    _conn = null;
137                }
138
139                // The JDBC spec requires that a statment close any open
140                // ResultSet's when it is closed.
141                // FIXME The PreparedStatement we're wrapping should handle this for us.
142                // See bug 17301 for what could happen when ResultSets are closed twice.
143                List<AbandonedTrace> resultSets = getTrace();
144                if( resultSets != null) {
145                    ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]);
146                    for (ResultSet element : set) {
147                        element.close();
148                    }
149                    clearTrace();
150                }
151
152                if (_stmt != null) {
153                    _stmt.close();
154                }
155            }
156            catch (SQLException e) {
157                handleException(e);
158            }
159        }
160        finally {
161            _closed = true;
162        }
163    }
164
165    protected void handleException(SQLException e) throws SQLException {
166        if (_conn != null) {
167            _conn.handleException(e);
168        }
169        else {
170            throw e;
171        }
172    }
173
174    protected void activate() throws SQLException {
175        if(_stmt instanceof DelegatingStatement) {
176            ((DelegatingStatement)_stmt).activate();
177        }
178    }
179
180    protected void passivate() throws SQLException {
181        if(_stmt instanceof DelegatingStatement) {
182            ((DelegatingStatement)_stmt).passivate();
183        }
184    }
185
186    @Override
187    public Connection getConnection() throws SQLException {
188        checkOpen();
189        return getConnectionInternal(); // return the delegating connection that created this
190    }
191
192    protected DelegatingConnection<?> getConnectionInternal() {
193        return _conn;
194    }
195
196    @Override
197    public ResultSet executeQuery(String sql) throws SQLException {
198        checkOpen();
199        if (_conn != null) {
200            _conn.setLastUsed();
201        }
202        try {
203            return DelegatingResultSet.wrapResultSet(this,_stmt.executeQuery(sql));
204        }
205        catch (SQLException e) {
206            handleException(e);
207            throw new AssertionError();
208        }
209    }
210
211    @Override
212    public ResultSet getResultSet() throws SQLException {
213        checkOpen();
214        try {
215            return DelegatingResultSet.wrapResultSet(this,_stmt.getResultSet());
216        }
217        catch (SQLException e) {
218            handleException(e);
219            throw new AssertionError();
220        }
221    }
222
223    @Override
224    public int executeUpdate(String sql) throws SQLException {
225        checkOpen();
226        if (_conn != null) {
227            _conn.setLastUsed();
228        }
229        try {
230            return _stmt.executeUpdate(sql);
231        } catch (SQLException e) {
232            handleException(e); return 0;
233        }
234    }
235
236    @Override
237    public int getMaxFieldSize() throws SQLException
238    { checkOpen(); try { return _stmt.getMaxFieldSize(); } catch (SQLException e) { handleException(e); return 0; } }
239
240    @Override
241    public void setMaxFieldSize(int max) throws SQLException
242    { checkOpen(); try { _stmt.setMaxFieldSize(max); } catch (SQLException e) { handleException(e); } }
243
244    @Override
245    public int getMaxRows() throws SQLException
246    { checkOpen(); try { return _stmt.getMaxRows(); } catch (SQLException e) { handleException(e); return 0; } }
247
248    @Override
249    public void setMaxRows(int max) throws SQLException
250    { checkOpen(); try { _stmt.setMaxRows(max); } catch (SQLException e) { handleException(e); } }
251
252    @Override
253    public void setEscapeProcessing(boolean enable) throws SQLException
254    { checkOpen(); try { _stmt.setEscapeProcessing(enable); } catch (SQLException e) { handleException(e); } }
255
256    @Override
257    public int getQueryTimeout() throws SQLException
258    { checkOpen(); try { return _stmt.getQueryTimeout(); } catch (SQLException e) { handleException(e); return 0; } }
259
260    @Override
261    public void setQueryTimeout(int seconds) throws SQLException
262    { checkOpen(); try { _stmt.setQueryTimeout(seconds); } catch (SQLException e) { handleException(e); } }
263
264    @Override
265    public void cancel() throws SQLException
266    { checkOpen(); try { _stmt.cancel(); } catch (SQLException e) { handleException(e); } }
267
268    @Override
269    public SQLWarning getWarnings() throws SQLException
270    { checkOpen(); try { return _stmt.getWarnings(); } catch (SQLException e) { handleException(e); throw new AssertionError(); } }
271
272    @Override
273    public void clearWarnings() throws SQLException
274    { checkOpen(); try { _stmt.clearWarnings(); } catch (SQLException e) { handleException(e); } }
275
276    @Override
277    public void setCursorName(String name) throws SQLException
278    { checkOpen(); try { _stmt.setCursorName(name); } catch (SQLException e) { handleException(e); } }
279
280    @Override
281    public boolean execute(String sql) throws SQLException {
282        checkOpen();
283        if (_conn != null) {
284            _conn.setLastUsed();
285        }
286        try {
287            return _stmt.execute(sql);
288        } catch (SQLException e) {
289            handleException(e);
290            return false;
291        }
292    }
293
294    @Override
295    public int getUpdateCount() throws SQLException
296    { checkOpen(); try { return _stmt.getUpdateCount(); } catch (SQLException e) { handleException(e); return 0; } }
297
298    @Override
299    public boolean getMoreResults() throws SQLException
300    { checkOpen(); try { return _stmt.getMoreResults(); } catch (SQLException e) { handleException(e); return false; } }
301
302    @Override
303    public void setFetchDirection(int direction) throws SQLException
304    { checkOpen(); try { _stmt.setFetchDirection(direction); } catch (SQLException e) { handleException(e); } }
305
306    @Override
307    public int getFetchDirection() throws SQLException
308    { checkOpen(); try { return _stmt.getFetchDirection(); } catch (SQLException e) { handleException(e); return 0; } }
309
310    @Override
311    public void setFetchSize(int rows) throws SQLException
312    { checkOpen(); try { _stmt.setFetchSize(rows); } catch (SQLException e) { handleException(e); } }
313
314    @Override
315    public int getFetchSize() throws SQLException
316    { checkOpen(); try { return _stmt.getFetchSize(); } catch (SQLException e) { handleException(e); return 0; } }
317
318    @Override
319    public int getResultSetConcurrency() throws SQLException
320    { checkOpen(); try { return _stmt.getResultSetConcurrency(); } catch (SQLException e) { handleException(e); return 0; } }
321
322    @Override
323    public int getResultSetType() throws SQLException
324    { checkOpen(); try { return _stmt.getResultSetType(); } catch (SQLException e) { handleException(e); return 0; } }
325
326    @Override
327    public void addBatch(String sql) throws SQLException
328    { checkOpen(); try { _stmt.addBatch(sql); } catch (SQLException e) { handleException(e); } }
329
330    @Override
331    public void clearBatch() throws SQLException
332    { checkOpen(); try { _stmt.clearBatch(); } catch (SQLException e) { handleException(e); } }
333
334    @Override
335    public int[] executeBatch() throws SQLException {
336        checkOpen();
337        if (_conn != null) {
338            _conn.setLastUsed();
339        }
340        try {
341            return _stmt.executeBatch();
342        } catch (SQLException e) {
343            handleException(e);
344            throw new AssertionError();
345        }
346    }
347
348    /**
349     * Returns a String representation of this object.
350     *
351     * @return String
352     */
353    @Override
354    public String toString() {
355    return _stmt.toString();
356    }
357
358    @Override
359    public boolean getMoreResults(int current) throws SQLException
360    { checkOpen(); try { return _stmt.getMoreResults(current); } catch (SQLException e) { handleException(e); return false; } }
361
362    @Override
363    public ResultSet getGeneratedKeys() throws SQLException {
364        checkOpen();
365        try {
366            return DelegatingResultSet.wrapResultSet(this, _stmt.getGeneratedKeys());
367        } catch (SQLException e) {
368            handleException(e);
369            throw new AssertionError();
370        }
371    }
372
373    @Override
374    public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
375        checkOpen();
376        if (_conn != null) {
377            _conn.setLastUsed();
378        }
379        try {
380            return _stmt.executeUpdate(sql, autoGeneratedKeys);
381        } catch (SQLException e) {
382            handleException(e);
383            return 0;
384        }
385    }
386
387    @Override
388    public int executeUpdate(String sql, int columnIndexes[]) throws SQLException {
389        checkOpen();
390        if (_conn != null) {
391            _conn.setLastUsed();
392        }
393        try {
394            return _stmt.executeUpdate(sql, columnIndexes);
395        } catch (SQLException e) {
396            handleException(e);
397            return 0;
398        }
399    }
400
401    @Override
402    public int executeUpdate(String sql, String columnNames[]) throws SQLException {
403        checkOpen();
404        if (_conn != null) {
405            _conn.setLastUsed();
406        }
407        try {
408            return _stmt.executeUpdate(sql, columnNames);
409        } catch (SQLException e) {
410            handleException(e);
411            return 0;
412        }
413    }
414
415    @Override
416    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
417        checkOpen();
418        if (_conn != null) {
419            _conn.setLastUsed();
420        }
421        try {
422            return _stmt.execute(sql, autoGeneratedKeys);
423        } catch (SQLException e) {
424            handleException(e);
425            return false;
426        }
427    }
428
429    @Override
430    public boolean execute(String sql, int columnIndexes[]) throws SQLException {
431        checkOpen();
432        if (_conn != null) {
433            _conn.setLastUsed();
434        }
435        try {
436            return _stmt.execute(sql, columnIndexes);
437        } catch (SQLException e) {
438            handleException(e);
439            return false;
440        }
441    }
442
443    @Override
444    public boolean execute(String sql, String columnNames[]) throws SQLException {
445        checkOpen();
446        if (_conn != null) {
447            _conn.setLastUsed();
448        }
449        try {
450            return _stmt.execute(sql, columnNames);
451        } catch (SQLException e) {
452            handleException(e);
453            return false;
454        }
455    }
456
457    @Override
458    public int getResultSetHoldability() throws SQLException
459    { checkOpen(); try { return _stmt.getResultSetHoldability(); } catch (SQLException e) { handleException(e); return 0; } }
460
461    /*
462     * Note was protected prior to JDBC 4
463     */
464    @Override
465    public boolean isClosed() throws SQLException {
466        return _closed;
467    }
468
469
470    @Override
471    public boolean isWrapperFor(Class<?> iface) throws SQLException {
472        if (iface.isAssignableFrom(getClass())) {
473            return true;
474        } else if (iface.isAssignableFrom(_stmt.getClass())) {
475            return true;
476        } else {
477            return _stmt.isWrapperFor(iface);
478        }
479    }
480
481    @Override
482    public <T> T unwrap(Class<T> iface) throws SQLException {
483        if (iface.isAssignableFrom(getClass())) {
484            return iface.cast(this);
485        } else if (iface.isAssignableFrom(_stmt.getClass())) {
486            return iface.cast(_stmt);
487        } else {
488            return _stmt.unwrap(iface);
489        }
490    }
491
492    @Override
493    public void setPoolable(boolean poolable) throws SQLException {
494        checkOpen();
495        try {
496            _stmt.setPoolable(poolable);
497        }
498        catch (SQLException e) {
499            handleException(e);
500        }
501    }
502
503    @Override
504    public boolean isPoolable() throws SQLException {
505        checkOpen();
506        try {
507            return _stmt.isPoolable();
508        }
509        catch (SQLException e) {
510            handleException(e);
511            return false;
512        }
513    }
514
515    @Override
516    public void closeOnCompletion() throws SQLException {
517        checkOpen();
518        try {
519            _stmt.closeOnCompletion();
520        } catch (SQLException e) {
521            handleException(e);
522        }
523    }
524
525    @Override
526    public boolean isCloseOnCompletion() throws SQLException {
527        checkOpen();
528        try {
529            return _stmt.isCloseOnCompletion();
530        } catch (SQLException e) {
531            handleException(e);
532            return false;
533        }
534    }
535
536    @Override
537    protected void finalize() throws Throwable {
538        // This is required because of statement pooling. The poolable
539        // statements will always be strongly held by the statement pool. If the
540        // delegating statements that wrap the poolable statement are not
541        // strongly held they will be garbage collected but at that point the
542        // poolable statements need to be returned to the pool else there will
543        // be a leak of statements from the pool. Closing this statement will
544        // close all the wrapped statements and return any poolable statements
545        // to the pool.
546        close();
547        super.finalize();
548    }
549}