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}