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