    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
    018package org.apache.commons.dbcp2;
    019
    020import java.sql.CallableStatement;
    021import java.sql.Connection;
    022import java.sql.ResultSet;
    023import java.sql.SQLException;
    024import java.util.List;
    025
    026import org.apache.commons.pool2.KeyedObjectPool;
    027
    028/**
    029 * A {@link DelegatingCallableStatement} that cooperates with
    030 * {@link PoolingConnection} to implement a pool of {@link CallableStatement}s.
    031 * <p>
    032 * The {@link #close} method returns this statement to its containing pool. (See {@link PoolingConnection}.)
    033 *
    034 * @see PoolingConnection
    035 * @since 2.0
    036 */
    037public class PoolableCallableStatement extends DelegatingCallableStatement {
    038
    039    /**
    040     * The {@link KeyedObjectPool} from which this CallableStatement was obtained.
    041     */
    042    private final KeyedObjectPool<PStmtKey,DelegatingPreparedStatement> _pool;
    043
    044    /**
    045     * Key for this statement in the containing {@link KeyedObjectPool}.
    046     */
    047    private final PStmtKey _key;
    048
    049    /**
    050     * Constructor.
    051     *
    052     * @param stmt the underlying {@link CallableStatement}
    053     * @param key the key for this statement in the {@link KeyedObjectPool}
    054     * @param pool the {@link KeyedObjectPool} from which this CallableStatement was obtained
    055     * @param conn the {@link DelegatingConnection} that created this CallableStatement
    056     */
    057    public PoolableCallableStatement(final CallableStatement stmt, final PStmtKey key,
    058            final KeyedObjectPool<PStmtKey,DelegatingPreparedStatement> pool,
    059            final DelegatingConnection<Connection> conn) {
    060        super(conn, stmt);
    061        _pool = pool;
    062        _key = key;
    063
    064        // Remove from trace now because this statement will be
    065        // added by the activate method.
    066        if(getConnectionInternal() != null) {
    067            getConnectionInternal().removeTrace(this);
    068        }
    069    }
    070
    071    /**
    072     * Returns the CallableStatement to the pool.  If {{@link #isClosed()}, this is a No-op.
    073     */
    074    @Override
    075    public void close() throws SQLException {
    076        // calling close twice should have no effect
    077        if (!isClosed()) {
    078            try {
    079                _pool.returnObject(_key,this);
    080            } catch(final SQLException e) {
    081                throw e;
    082            } catch(final RuntimeException e) {
    083                throw e;
    084            } catch(final Exception e) {
    085                throw new SQLException("Cannot close CallableStatement (return to pool failed)", e);
    086            }
    087        }
    088    }
    089
    090    /**
    091     * Activates after retrieval from the pool. Adds a trace for this CallableStatement to the Connection
    092     * that created it.
    093     */
    094    @Override
    095    protected void activate() throws SQLException {
    096        setClosedInternal(false);
    097        if( getConnectionInternal() != null ) {
    098            getConnectionInternal().addTrace( this );
    099        }
    100        super.activate();
    101    }
    102
    103    /**
    104     * Passivates to prepare for return to the pool.  Removes the trace associated with this CallableStatement
    105     * from the Connection that created it.  Also closes any associated ResultSets.
    106     */
    107    @Override
    108    protected void passivate() throws SQLException {
    109        setClosedInternal(true);
    110        if( getConnectionInternal() != null ) {
    111            getConnectionInternal().removeTrace(this);
    112        }
    113
    114        // The JDBC spec requires that a statement close any open
    115        // ResultSet's when it is closed.
    116        // FIXME The PreparedStatement we're wrapping should handle this for us.
    117        // See DBCP-10 for what could happen when ResultSets are closed twice.
    118        final List<AbandonedTrace> resultSets = getTrace();
    119        if(resultSets != null) {
    120            final ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]);
    121            for (final ResultSet element : set) {
    122                element.close();
    123            }
    124            clearTrace();
    125        }
    126
    127        super.passivate();
    128    }
    129
    130}