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