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 */
017package org.apache.commons.dbcp2;
018
019import java.io.PrintWriter;
020
021import java.sql.Connection;
022import java.sql.SQLException;
023import java.sql.SQLFeatureNotSupportedException;
024import java.util.NoSuchElementException;
025import java.util.logging.Logger;
026
027import javax.sql.DataSource;
028
029import org.apache.commons.pool2.ObjectPool;
030
031/**
032 * A simple {@link DataSource} implementation that obtains
033 * {@link Connection}s from the specified {@link ObjectPool}.
034 *
035 * @param <C> The connection type
036 *
037 * @author Rodney Waldhoff
038 * @author Glenn L. Nielsen
039 * @author James House
040 * @author Dirk Verbeeck
041 * @version $Revision: 1572244 $ $Date: 2014-02-26 20:38:08 +0000 (Wed, 26 Feb 2014) $
042 * @since 2.0
043 */
044public class PoolingDataSource<C extends Connection> implements DataSource {
045
046    /** Controls access to the underlying connection */
047    private boolean accessToUnderlyingConnectionAllowed = false;
048
049    public PoolingDataSource(ObjectPool<C> pool) {
050        if (null == pool) {
051            throw new NullPointerException("Pool must not be null.");
052        }
053        _pool = pool;
054    }
055
056    /**
057     * Returns the value of the accessToUnderlyingConnectionAllowed property.
058     *
059     * @return true if access to the underlying is allowed, false otherwise.
060     */
061    public boolean isAccessToUnderlyingConnectionAllowed() {
062        return this.accessToUnderlyingConnectionAllowed;
063    }
064
065    /**
066     * Sets the value of the accessToUnderlyingConnectionAllowed property.
067     * It controls if the PoolGuard allows access to the underlying connection.
068     * (Default: false)
069     *
070     * @param allow Access to the underlying connection is granted when true.
071     */
072    public void setAccessToUnderlyingConnectionAllowed(boolean allow) {
073        this.accessToUnderlyingConnectionAllowed = allow;
074    }
075
076    /* JDBC_4_ANT_KEY_BEGIN */
077    @Override
078    public boolean isWrapperFor(Class<?> iface) throws SQLException {
079        return false;
080    }
081
082    @Override
083    public <T> T unwrap(Class<T> iface) throws SQLException {
084        throw new SQLException("PoolingDataSource is not a wrapper.");
085    }
086    /* JDBC_4_ANT_KEY_END */
087
088    @Override
089    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
090        throw new SQLFeatureNotSupportedException();
091    }
092
093    //--- DataSource methods -----------------------------------------
094
095    /**
096     * Return a {@link java.sql.Connection} from my pool,
097     * according to the contract specified by {@link ObjectPool#borrowObject}.
098     */
099    @Override
100    public Connection getConnection() throws SQLException {
101        try {
102            C conn = _pool.borrowObject();
103            if (conn == null) {
104                return null;
105            }
106            return new PoolGuardConnectionWrapper<>(conn);
107        } catch(SQLException e) {
108            throw e;
109        } catch(NoSuchElementException e) {
110            throw new SQLException("Cannot get a connection, pool error " + e.getMessage(), e);
111        } catch(RuntimeException e) {
112            throw e;
113        } catch(Exception e) {
114            throw new SQLException("Cannot get a connection, general error", e);
115        }
116    }
117
118    /**
119     * Throws {@link UnsupportedOperationException}
120     * @throws UnsupportedOperationException
121     */
122    @Override
123    public Connection getConnection(String uname, String passwd) throws SQLException {
124        throw new UnsupportedOperationException();
125    }
126
127    /**
128     * Returns my log writer.
129     * @return my log writer
130     * @see DataSource#getLogWriter
131     */
132    @Override
133    public PrintWriter getLogWriter() {
134        return _logWriter;
135    }
136
137    /**
138     * Throws {@link UnsupportedOperationException}.
139     * @throws UnsupportedOperationException As this
140     *   implementation does not support this feature.
141     */
142    @Override
143    public int getLoginTimeout() {
144        throw new UnsupportedOperationException("Login timeout is not supported.");
145    }
146
147    /**
148     * Throws {@link UnsupportedOperationException}.
149     * @throws UnsupportedOperationException As this
150     *   implementation does not support this feature.
151     */
152    @Override
153    public void setLoginTimeout(int seconds) {
154        throw new UnsupportedOperationException("Login timeout is not supported.");
155    }
156
157    /**
158     * Sets my log writer.
159     * @see DataSource#setLogWriter
160     */
161    @Override
162    public void setLogWriter(PrintWriter out) {
163        _logWriter = out;
164    }
165
166    /** My log writer. */
167    private PrintWriter _logWriter = null;
168
169    private final ObjectPool<C> _pool;
170
171    protected ObjectPool<C> getPool() {
172        return _pool;
173    }
174
175    /**
176     * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a
177     * closed connection cannot be used anymore.
178     * @since 2.0
179     */
180    private class PoolGuardConnectionWrapper<D extends Connection>
181            extends DelegatingConnection<D> {
182
183        PoolGuardConnectionWrapper(D delegate) {
184            super(delegate);
185        }
186
187        /**
188         * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate()
189         */
190        @Override
191        public D getDelegate() {
192            if (isAccessToUnderlyingConnectionAllowed()) {
193                return super.getDelegate();
194            }
195            return null;
196        }
197
198        /**
199         * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate()
200         */
201        @Override
202        public Connection getInnermostDelegate() {
203            if (isAccessToUnderlyingConnectionAllowed()) {
204                return super.getInnermostDelegate();
205            }
206            return null;
207        }
208
209        @Override
210        public void close() throws SQLException {
211            if (getDelegateInternal() != null) {
212                super.close();
213                super.setDelegate(null);
214            }
215        }
216
217        @Override
218        public boolean isClosed() throws SQLException {
219            if (getDelegateInternal() == null) {
220                return true;
221            }
222            return super.isClosed();
223        }
224    }
225}