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}