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.Connection;
021    import java.sql.PreparedStatement;
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 DelegatingPreparedStatement} that cooperates with
030     * {@link PoolingConnection} to implement a pool of {@link PreparedStatement}s.
031     * <p>
032     * My {@link #close} method returns me to my containing pool. (See {@link PoolingConnection}.)
033     *
034     * @see PoolingConnection
035     * @author Rodney Waldhoff
036     * @author Glenn L. Nielsen
037     * @author James House
038     * @author Dirk Verbeeck
039     * @version $Revision: 892307 $ $Date: 2013-12-31 23:27:28 +0000 (Tue, 31 Dec 2013) $
040     */
041    public class PoolablePreparedStatement extends DelegatingPreparedStatement implements PreparedStatement {
042        /**
043         * The {@link KeyedObjectPool} from which I was obtained.
044         */
045        protected KeyedObjectPool _pool = null;
046    
047        /**
048         * My "key" as used by {@link KeyedObjectPool}.
049         */
050        protected Object _key = null;
051    
052        private volatile boolean batchAdded = false;
053    
054        /**
055         * Constructor
056         * @param stmt my underlying {@link PreparedStatement}
057         * @param key my key" as used by {@link KeyedObjectPool}
058         * @param pool the {@link KeyedObjectPool} from which I was obtained.
059         * @param conn the {@link Connection} from which I was created
060         */
061        public PoolablePreparedStatement(PreparedStatement stmt, Object key, KeyedObjectPool pool, Connection conn) {
062            super((DelegatingConnection) conn, stmt);
063            _pool = pool;
064            _key = key;
065    
066            // Remove from trace now because this statement will be 
067            // added by the activate method.
068            if(_conn != null) {
069                _conn.removeTrace(this);
070            }
071        }
072    
073        /**
074         * Add batch.
075         */
076        public void addBatch() throws SQLException {
077            super.addBatch();
078            batchAdded = true;
079        }
080    
081        /**
082         * Clear Batch.
083         */
084        public void clearBatch() throws SQLException {
085            batchAdded = false;
086            super.clearBatch();
087        }
088    
089        /**
090         * Return me to my pool.
091         */
092        public void close() throws SQLException {
093            // calling close twice should have no effect
094            if (!isClosed()) {
095                try {
096                    _pool.returnObject(_key,this);
097                } catch(SQLException e) {
098                    throw e;
099                } catch(RuntimeException e) {
100                    throw e;
101                } catch(Exception e) {
102                    throw new SQLNestedException("Cannot close preparedstatement (return to pool failed)", e);
103                }
104            }
105        }
106        
107        protected void activate() throws SQLException{
108            _closed = false;
109            if(_conn != null) {
110                _conn.addTrace(this);
111            }
112            super.activate();
113        }
114      
115        protected void passivate() throws SQLException {
116            _closed = true;
117            if(_conn != null) {
118                _conn.removeTrace(this);
119            }
120    
121            // The JDBC spec requires that a statment close any open
122            // ResultSet's when it is closed.
123            // FIXME The PreparedStatement we're wrapping should handle this for us.
124            // See bug 17301 for what could happen when ResultSets are closed twice.
125            List resultSets = getTrace();
126            if( resultSets != null) {
127                ResultSet[] set = (ResultSet[]) resultSets.toArray(new ResultSet[resultSets.size()]);
128                for (int i = 0; i < set.length; i++) {
129                    set[i].close();
130                }
131                clearTrace();
132            }
133            if (batchAdded) {
134                clearBatch();
135            }
136            
137            super.passivate();
138        }
139    
140    }