    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 {@link PoolingConnection} to implement a pool of
    030 * {@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 callableStatement
    053     *            the underlying {@link CallableStatement}
    054     * @param key
    055     *            the key for this statement in the {@link KeyedObjectPool}
    056     * @param pool
    057     *            the {@link KeyedObjectPool} from which this CallableStatement was obtained
    058     * @param connection
    059     *            the {@link DelegatingConnection} that created this CallableStatement
    060     */
    061    public PoolableCallableStatement(final CallableStatement callableStatement, final PStmtKey key,
    062            final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool,
    063            final DelegatingConnection<Connection> connection) {
    064        super(connection, callableStatement);
    065        this.pool = pool;
    066        this.key = key;
    067
    068        // Remove from trace now because this statement will be
    069        // added by the activate method.
    070        if (getConnectionInternal() != null) {
    071            getConnectionInternal().removeTrace(this);
    072        }
    073    }
    074
    075    /**
    076     * Returns the CallableStatement to the pool. If {{@link #isClosed()}, this is a No-op.
    077     */
    078    @Override
    079    public void close() throws SQLException {
    080        // calling close twice should have no effect
    081        if (!isClosed()) {
    082            try {
    083                pool.returnObject(key, this);
    084            } catch (final SQLException e) {
    085                throw e;
    086            } catch (final RuntimeException e) {
    087                throw e;
    088            } catch (final Exception e) {
    089                throw new SQLException("Cannot close CallableStatement (return to pool failed)", e);
    090            }
    091        }
    092    }
    093
    094    /**
    095     * Activates after retrieval from the pool. Adds a trace for this CallableStatement to the Connection that created
    096     * it.
    097     *
    098     * @since 2.4.0 made public, was protected in 2.3.0.
    099     */
    100    @Override
    101    public void activate() throws SQLException {
    102        setClosedInternal(false);
    103        if (getConnectionInternal() != null) {
    104            getConnectionInternal().addTrace(this);
    105        }
    106        super.activate();
    107    }
    108
    109    /**
    110     * Passivates to prepare for return to the pool. Removes the trace associated with this CallableStatement from the
    111     * Connection that created it. Also closes any associated ResultSets.
    112     *
    113     * @since 2.4.0 made public, was protected in 2.3.0.
    114     */
    115    @Override
    116    public void passivate() throws SQLException {
    117        setClosedInternal(true);
    118        if (getConnectionInternal() != null) {
    119            getConnectionInternal().removeTrace(this);
    120        }
    121
    122        // The JDBC spec requires that a statement close any open
    123        // ResultSet's when it is closed.
    124        // FIXME The PreparedStatement we're wrapping should handle this for us.
    125        // See DBCP-10 for what could happen when ResultSets are closed twice.
    126        final List<AbandonedTrace> resultSets = getTrace();
    127        if (resultSets != null) {
    128            final ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]);
    129            for (final ResultSet element : set) {
    130                element.close();
    131            }
    132            clearTrace();
    133        }
    134
    135        super.passivate();
    136    }
    137
    138}