    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}