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