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 $Id: DelegatingStatement.java 1658644 2015-02-10 08:59:07Z tn $
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     * {@code DelegatingStatement}, 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 {@code DelegatingStatement}
084     * or {@code null} when no non-{@code DelegatingStatement}
085     * delegate can be found by transversing this chain.
086     * <p>
087     * This method is useful when you may have nested
088     * {@code DelegatingStatement}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        if (isClosed()) {
133            return;
134        }
135        try {
136            try {
137                if (_conn != null) {
138                    _conn.removeTrace(this);
139                    _conn = null;
140                }
141
142                // The JDBC spec requires that a statement close any open
143                // ResultSet's when it is closed.
144                // FIXME The PreparedStatement we're wrapping should handle this for us.
145                // See bug 17301 for what could happen when ResultSets are closed twice.
146                List<AbandonedTrace> resultSets = getTrace();
147                if( resultSets != null) {
148                    ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]);
149                    for (ResultSet element : set) {
150                        element.close();
151                    }
152                    clearTrace();
153                }
154
155                if (_stmt != null) {
156                    _stmt.close();
157                }
158            }
159            catch (SQLException e) {
160                handleException(e);
161            }
162        }
163        finally {
164            _closed = true;
165            _stmt = null;
166        }
167    }
168
169    protected void handleException(SQLException e) throws SQLException {
170        if (_conn != null) {
171            _conn.handleException(e);
172        }
173        else {
174            throw e;
175        }
176    }
177
178    protected void activate() throws SQLException {
179        if(_stmt instanceof DelegatingStatement) {
180            ((DelegatingStatement)_stmt).activate();
181        }
182    }
183
184    protected void passivate() throws SQLException {
185        if(_stmt instanceof DelegatingStatement) {
186            ((DelegatingStatement)_stmt).passivate();
187        }
188    }
189
190    @Override
191    public Connection getConnection() throws SQLException {
192        checkOpen();
193        return getConnectionInternal(); // return the delegating connection that created this
194    }
195
196    protected DelegatingConnection<?> getConnectionInternal() {
197        return _conn;
198    }
199
200    @Override
201    public ResultSet executeQuery(String sql) throws SQLException {
202        checkOpen();
203        if (_conn != null) {
204            _conn.setLastUsed();
205        }
206        try {
207            return DelegatingResultSet.wrapResultSet(this,_stmt.executeQuery(sql));
208        }
209        catch (SQLException e) {
210            handleException(e);
211            throw new AssertionError();
212        }
213    }
214
215    @Override
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    @Override
228    public int executeUpdate(String sql) throws SQLException {
229        checkOpen();
230        if (_conn != null) {
231            _conn.setLastUsed();
232        }
233        try {
234            return _stmt.executeUpdate(sql);
235        } catch (SQLException e) {
236            handleException(e); return 0;
237        }
238    }
239
240    @Override
241    public int getMaxFieldSize() throws SQLException
242    { checkOpen(); try { return _stmt.getMaxFieldSize(); } catch (SQLException e) { handleException(e); return 0; } }
243
244    @Override
245    public void setMaxFieldSize(int max) throws SQLException
246    { checkOpen(); try { _stmt.setMaxFieldSize(max); } catch (SQLException e) { handleException(e); } }
247
248    @Override
249    public int getMaxRows() throws SQLException
250    { checkOpen(); try { return _stmt.getMaxRows(); } catch (SQLException e) { handleException(e); return 0; } }
251
252    @Override
253    public void setMaxRows(int max) throws SQLException
254    { checkOpen(); try { _stmt.setMaxRows(max); } catch (SQLException e) { handleException(e); } }
255
256    @Override
257    public void setEscapeProcessing(boolean enable) throws SQLException
258    { checkOpen(); try { _stmt.setEscapeProcessing(enable); } catch (SQLException e) { handleException(e); } }
259
260    @Override
261    public int getQueryTimeout() throws SQLException
262    { checkOpen(); try { return _stmt.getQueryTimeout(); } catch (SQLException e) { handleException(e); return 0; } }
263
264    @Override
265    public void setQueryTimeout(int seconds) throws SQLException
266    { checkOpen(); try { _stmt.setQueryTimeout(seconds); } catch (SQLException e) { handleException(e); } }
267
268    @Override
269    public void cancel() throws SQLException
270    { checkOpen(); try { _stmt.cancel(); } catch (SQLException e) { handleException(e); } }
271
272    @Override
273    public SQLWarning getWarnings() throws SQLException
274    { checkOpen(); try { return _stmt.getWarnings(); } catch (SQLException e) { handleException(e); throw new AssertionError(); } }
275
276    @Override
277    public void clearWarnings() throws SQLException
278    { checkOpen(); try { _stmt.clearWarnings(); } catch (SQLException e) { handleException(e); } }
279
280    @Override
281    public void setCursorName(String name) throws SQLException
282    { checkOpen(); try { _stmt.setCursorName(name); } catch (SQLException e) { handleException(e); } }
283
284    @Override
285    public boolean execute(String sql) throws SQLException {
286        checkOpen();
287        if (_conn != null) {
288            _conn.setLastUsed();
289        }
290        try {
291            return _stmt.execute(sql);
292        } catch (SQLException e) {
293            handleException(e);
294            return false;
295        }
296    }
297
298    @Override
299    public int getUpdateCount() throws SQLException
300    { checkOpen(); try { return _stmt.getUpdateCount(); } catch (SQLException e) { handleException(e); return 0; } }
301
302    @Override
303    public boolean getMoreResults() throws SQLException
304    { checkOpen(); try { return _stmt.getMoreResults(); } catch (SQLException e) { handleException(e); return false; } }
305
306    @Override
307    public void setFetchDirection(int direction) throws SQLException
308    { checkOpen(); try { _stmt.setFetchDirection(direction); } catch (SQLException e) { handleException(e); } }
309
310    @Override
311    public int getFetchDirection() throws SQLException
312    { checkOpen(); try { return _stmt.getFetchDirection(); } catch (SQLException e) { handleException(e); return 0; } }
313
314    @Override
315    public void setFetchSize(int rows) throws SQLException
316    { checkOpen(); try { _stmt.setFetchSize(rows); } catch (SQLException e) { handleException(e); } }
317
318    @Override
319    public int getFetchSize() throws SQLException
320    { checkOpen(); try { return _stmt.getFetchSize(); } catch (SQLException e) { handleException(e); return 0; } }
321
322    @Override
323    public int getResultSetConcurrency() throws SQLException
324    { checkOpen(); try { return _stmt.getResultSetConcurrency(); } catch (SQLException e) { handleException(e); return 0; } }
325
326    @Override
327    public int getResultSetType() throws SQLException
328    { checkOpen(); try { return _stmt.getResultSetType(); } catch (SQLException e) { handleException(e); return 0; } }
329
330    @Override
331    public void addBatch(String sql) throws SQLException
332    { checkOpen(); try { _stmt.addBatch(sql); } catch (SQLException e) { handleException(e); } }
333
334    @Override
335    public void clearBatch() throws SQLException
336    { checkOpen(); try { _stmt.clearBatch(); } catch (SQLException e) { handleException(e); } }
337
338    @Override
339    public int[] executeBatch() throws SQLException {
340        checkOpen();
341        if (_conn != null) {
342            _conn.setLastUsed();
343        }
344        try {
345            return _stmt.executeBatch();
346        } catch (SQLException e) {
347            handleException(e);
348            throw new AssertionError();
349        }
350    }
351
352    /**
353     * Returns a String representation of this object.
354     *
355     * @return String
356     */
357    @Override
358    public String toString() {
359    return _stmt == null ? "NULL" : _stmt.toString();
360    }
361
362    @Override
363    public boolean getMoreResults(int current) throws SQLException
364    { checkOpen(); try { return _stmt.getMoreResults(current); } catch (SQLException e) { handleException(e); return false; } }
365
366    @Override
367    public ResultSet getGeneratedKeys() throws SQLException {
368        checkOpen();
369        try {
370            return DelegatingResultSet.wrapResultSet(this, _stmt.getGeneratedKeys());
371        } catch (SQLException e) {
372            handleException(e);
373            throw new AssertionError();
374        }
375    }
376
377    @Override
378    public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
379        checkOpen();
380        if (_conn != null) {
381            _conn.setLastUsed();
382        }
383        try {
384            return _stmt.executeUpdate(sql, autoGeneratedKeys);
385        } catch (SQLException e) {
386            handleException(e);
387            return 0;
388        }
389    }
390
391    @Override
392    public int executeUpdate(String sql, int columnIndexes[]) throws SQLException {
393        checkOpen();
394        if (_conn != null) {
395            _conn.setLastUsed();
396        }
397        try {
398            return _stmt.executeUpdate(sql, columnIndexes);
399        } catch (SQLException e) {
400            handleException(e);
401            return 0;
402        }
403    }
404
405    @Override
406    public int executeUpdate(String sql, String columnNames[]) throws SQLException {
407        checkOpen();
408        if (_conn != null) {
409            _conn.setLastUsed();
410        }
411        try {
412            return _stmt.executeUpdate(sql, columnNames);
413        } catch (SQLException e) {
414            handleException(e);
415            return 0;
416        }
417    }
418
419    @Override
420    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
421        checkOpen();
422        if (_conn != null) {
423            _conn.setLastUsed();
424        }
425        try {
426            return _stmt.execute(sql, autoGeneratedKeys);
427        } catch (SQLException e) {
428            handleException(e);
429            return false;
430        }
431    }
432
433    @Override
434    public boolean execute(String sql, int columnIndexes[]) throws SQLException {
435        checkOpen();
436        if (_conn != null) {
437            _conn.setLastUsed();
438        }
439        try {
440            return _stmt.execute(sql, columnIndexes);
441        } catch (SQLException e) {
442            handleException(e);
443            return false;
444        }
445    }
446
447    @Override
448    public boolean execute(String sql, String columnNames[]) throws SQLException {
449        checkOpen();
450        if (_conn != null) {
451            _conn.setLastUsed();
452        }
453        try {
454            return _stmt.execute(sql, columnNames);
455        } catch (SQLException e) {
456            handleException(e);
457            return false;
458        }
459    }
460
461    @Override
462    public int getResultSetHoldability() throws SQLException
463    { checkOpen(); try { return _stmt.getResultSetHoldability(); } catch (SQLException e) { handleException(e); return 0; } }
464
465    /*
466     * Note was protected prior to JDBC 4
467     */
468    @Override
469    public boolean isClosed() throws SQLException {
470        return _closed;
471    }
472
473
474    @Override
475    public boolean isWrapperFor(Class<?> iface) throws SQLException {
476        if (iface.isAssignableFrom(getClass())) {
477            return true;
478        } else if (iface.isAssignableFrom(_stmt.getClass())) {
479            return true;
480        } else {
481            return _stmt.isWrapperFor(iface);
482        }
483    }
484
485    @Override
486    public <T> T unwrap(Class<T> iface) throws SQLException {
487        if (iface.isAssignableFrom(getClass())) {
488            return iface.cast(this);
489        } else if (iface.isAssignableFrom(_stmt.getClass())) {
490            return iface.cast(_stmt);
491        } else {
492            return _stmt.unwrap(iface);
493        }
494    }
495
496    @Override
497    public void setPoolable(boolean poolable) throws SQLException {
498        checkOpen();
499        try {
500            _stmt.setPoolable(poolable);
501        }
502        catch (SQLException e) {
503            handleException(e);
504        }
505    }
506
507    @Override
508    public boolean isPoolable() throws SQLException {
509        checkOpen();
510        try {
511            return _stmt.isPoolable();
512        }
513        catch (SQLException e) {
514            handleException(e);
515            return false;
516        }
517    }
518
519    @Override
520    public void closeOnCompletion() throws SQLException {
521        checkOpen();
522        try {
523            _stmt.closeOnCompletion();
524        } catch (SQLException e) {
525            handleException(e);
526        }
527    }
528
529    @Override
530    public boolean isCloseOnCompletion() throws SQLException {
531        checkOpen();
532        try {
533            return _stmt.isCloseOnCompletion();
534        } catch (SQLException e) {
535            handleException(e);
536            return false;
537        }
538    }
539
540    @Override
541    protected void finalize() throws Throwable {
542        // This is required because of statement pooling. The poolable
543        // statements will always be strongly held by the statement pool. If the
544        // delegating statements that wrap the poolable statement are not
545        // strongly held they will be garbage collected but at that point the
546        // poolable statements need to be returned to the pool else there will
547        // be a leak of statements from the pool. Closing this statement will
548        // close all the wrapped statements and return any poolable statements
549        // to the pool.
550        close();
551        super.finalize();
552    }
553}