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.SQLException;
022    import org.apache.commons.pool.ObjectPool;
023    
024    /**
025     * A delegating connection that, rather than closing the underlying
026     * connection, returns itself to an {@link ObjectPool} when
027     * closed.
028     *
029     * @author Rodney Waldhoff
030     * @author Glenn L. Nielsen
031     * @author James House
032     * @version $Revision: 892307 $ $Date: 2013-12-31 23:27:28 +0000 (Tue, 31 Dec 2013) $
033     */
034    public class PoolableConnection extends DelegatingConnection {
035        /** The pool to which I should return. */
036        // TODO: Correct use of the pool requires that this connection is only every returned to the pool once.
037        protected ObjectPool _pool = null;
038    
039        /**
040         *
041         * @param conn my underlying connection
042         * @param pool the pool to which I should return when closed
043         */
044        public PoolableConnection(Connection conn, ObjectPool pool) {
045            super(conn);
046            _pool = pool;
047        }
048    
049        /**
050         *
051         * @param conn my underlying connection
052         * @param pool the pool to which I should return when closed
053         * @param config the abandoned configuration settings
054         */
055        public PoolableConnection(Connection conn, ObjectPool pool, AbandonedConfig config) {
056            super(conn, config);
057            _pool = pool;
058        }
059    
060    
061        /**
062         * Returns me to my pool.
063         */
064         public synchronized void close() throws SQLException {
065            if (_closed) {
066                // already closed
067                return;
068            }
069    
070            boolean isUnderlyingConectionClosed;
071            try {
072                isUnderlyingConectionClosed = _conn.isClosed();
073            } catch (SQLException e) {
074                try {
075                    _pool.invalidateObject(this); // XXX should be guarded to happen at most once
076                } catch(IllegalStateException ise) {
077                    // pool is closed, so close the connection
078                    passivate();
079                    getInnermostDelegate().close();
080                } catch (Exception ie) {
081                    // DO NOTHING the original exception will be rethrown
082                }
083                throw (SQLException) new SQLException("Cannot close connection (isClosed check failed)").initCause(e);
084            }
085    
086            if (!isUnderlyingConectionClosed) {
087                // Normal close: underlying connection is still open, so we
088                // simply need to return this proxy to the pool
089                try {
090                    _pool.returnObject(this); // XXX should be guarded to happen at most once
091                } catch(IllegalStateException e) {
092                    // pool is closed, so close the connection
093                    passivate();
094                    getInnermostDelegate().close();
095                } catch(SQLException e) {
096                    throw e;
097                } catch(RuntimeException e) {
098                    throw e;
099                } catch(Exception e) {
100                    throw (SQLException) new SQLException("Cannot close connection (return to pool failed)").initCause(e);
101                }
102            } else {
103                // Abnormal close: underlying connection closed unexpectedly, so we
104                // must destroy this proxy
105                try {
106                    _pool.invalidateObject(this); // XXX should be guarded to happen at most once
107                } catch(IllegalStateException e) {
108                    // pool is closed, so close the connection
109                    passivate();
110                    getInnermostDelegate().close();
111                } catch (Exception ie) {
112                    // DO NOTHING, "Already closed" exception thrown below
113                }
114                throw new SQLException("Already closed.");
115            }
116        }
117    
118        /**
119         * Actually close my underlying {@link Connection}.
120         */
121        public void reallyClose() throws SQLException {
122            super.close();
123        }
124    }
125