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;
020import java.sql.Connection;
021import java.sql.SQLException;
022import java.sql.SQLFeatureNotSupportedException;
023import java.util.NoSuchElementException;
024import java.util.logging.Logger;
025
026import javax.sql.DataSource;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.apache.commons.pool2.ObjectPool;
031import org.apache.commons.pool2.impl.GenericObjectPool;
032
033/**
034 * A simple {@link DataSource} implementation that obtains
035 * {@link Connection}s from the specified {@link ObjectPool}.
036 *
037 * @param <C> The connection type
038 *
039 * @author Rodney Waldhoff
040 * @author Glenn L. Nielsen
041 * @author James House
042 * @author Dirk Verbeeck
043 * @since 2.0
044 */
045public class PoolingDataSource<C extends Connection> implements DataSource, AutoCloseable {
046
047    private static final Log log = LogFactory.getLog(PoolingDataSource.class);
048
049    /** Controls access to the underlying connection */
050    private boolean accessToUnderlyingConnectionAllowed = false;
051
052    public PoolingDataSource(final ObjectPool<C> pool) {
053        if (null == pool) {
054            throw new NullPointerException("Pool must not be null.");
055        }
056        _pool = pool;
057        // Verify that _pool's factory refers back to it.  If not, log a warning and try to fix.
058        if (_pool instanceof GenericObjectPool<?>) {
059            final PoolableConnectionFactory pcf = (PoolableConnectionFactory) ((GenericObjectPool<?>) _pool).getFactory();
060            if (pcf == null) {
061                throw new NullPointerException("PoolableConnectionFactory must not be null.");
062            }
063            if (pcf.getPool() != _pool) {
064                log.warn(Utils.getMessage("poolingDataSource.factoryConfig"));
065                @SuppressWarnings("unchecked") // PCF must have a pool of PCs
066                final
067                ObjectPool<PoolableConnection> p = (ObjectPool<PoolableConnection>) _pool;
068                pcf.setPool(p);
069            }
070        }
071    }
072
073    /**
074     * Close and free all {@link Connection}s from the pool.
075     * @since 2.1
076     */
077    @Override
078    public void close() throws Exception {
079        try {
080            _pool.close();
081        } catch(final RuntimeException rte) {
082            throw new RuntimeException(Utils.getMessage("pool.close.fail"), rte);
083        } catch(final Exception e) {
084            throw new SQLException(Utils.getMessage("pool.close.fail"), e);
085        }
086    }
087
088    /**
089     * Returns the value of the accessToUnderlyingConnectionAllowed property.
090     *
091     * @return true if access to the underlying {@link Connection} is allowed, false otherwise.
092     */
093    public boolean isAccessToUnderlyingConnectionAllowed() {
094        return this.accessToUnderlyingConnectionAllowed;
095    }
096
097    /**
098     * Sets the value of the accessToUnderlyingConnectionAllowed property.
099     * It controls if the PoolGuard allows access to the underlying connection.
100     * (Default: false)
101     *
102     * @param allow Access to the underlying connection is granted when true.
103     */
104    public void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
105        this.accessToUnderlyingConnectionAllowed = allow;
106    }
107
108    /* JDBC_4_ANT_KEY_BEGIN */
109    @Override
110    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
111        return false;
112    }
113
114    @Override
115    public <T> T unwrap(final Class<T> iface) throws SQLException {
116        throw new SQLException("PoolingDataSource is not a wrapper.");
117    }
118    /* JDBC_4_ANT_KEY_END */
119
120    @Override
121    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
122        throw new SQLFeatureNotSupportedException();
123    }
124
125    //--- DataSource methods -----------------------------------------
126
127    /**
128     * Return a {@link java.sql.Connection} from my pool,
129     * according to the contract specified by {@link ObjectPool#borrowObject}.
130     */
131    @Override
132    public Connection getConnection() throws SQLException {
133        try {
134            final C conn = _pool.borrowObject();
135            if (conn == null) {
136                return null;
137            }
138            return new PoolGuardConnectionWrapper<>(conn);
139        } catch(final SQLException e) {
140            throw e;
141        } catch(final NoSuchElementException e) {
142            throw new SQLException("Cannot get a connection, pool error " + e.getMessage(), e);
143        } catch(final RuntimeException e) {
144            throw e;
145        } catch(final InterruptedException e) {
146            // Reset the interrupt status so it is visible to callers
147            Thread.currentThread().interrupt();
148            throw new SQLException("Cannot get a connection, general error", e);
149        } catch(final Exception e) {
150            throw new SQLException("Cannot get a connection, general error", e);
151        }
152    }
153
154    /**
155     * Throws {@link UnsupportedOperationException}
156     * @throws UnsupportedOperationException
157     */
158    @Override
159    public Connection getConnection(final String uname, final String passwd) throws SQLException {
160        throw new UnsupportedOperationException();
161    }
162
163    /**
164     * Returns my log writer.
165     * @return my log writer
166     * @see DataSource#getLogWriter
167     */
168    @Override
169    public PrintWriter getLogWriter() {
170        return _logWriter;
171    }
172
173    /**
174     * Throws {@link UnsupportedOperationException}.
175     * @throws UnsupportedOperationException As this
176     *   implementation does not support this feature.
177     */
178    @Override
179    public int getLoginTimeout() {
180        throw new UnsupportedOperationException("Login timeout is not supported.");
181    }
182
183    /**
184     * Throws {@link UnsupportedOperationException}.
185     * @throws UnsupportedOperationException As this
186     *   implementation does not support this feature.
187     */
188    @Override
189    public void setLoginTimeout(final int seconds) {
190        throw new UnsupportedOperationException("Login timeout is not supported.");
191    }
192
193    /**
194     * Sets my log writer.
195     * @see DataSource#setLogWriter
196     */
197    @Override
198    public void setLogWriter(final PrintWriter out) {
199        _logWriter = out;
200    }
201
202    /** My log writer. */
203    private PrintWriter _logWriter = null;
204
205    private final ObjectPool<C> _pool;
206
207    protected ObjectPool<C> getPool() {
208        return _pool;
209    }
210
211    /**
212     * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a
213     * closed connection cannot be used anymore.
214     * @since 2.0
215     */
216    private class PoolGuardConnectionWrapper<D extends Connection>
217            extends DelegatingConnection<D> {
218
219        PoolGuardConnectionWrapper(final D delegate) {
220            super(delegate);
221        }
222
223        /**
224         * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate()
225         */
226        @Override
227        public D getDelegate() {
228            if (isAccessToUnderlyingConnectionAllowed()) {
229                return super.getDelegate();
230            }
231            return null;
232        }
233
234        /**
235         * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate()
236         */
237        @Override
238        public Connection getInnermostDelegate() {
239            if (isAccessToUnderlyingConnectionAllowed()) {
240                return super.getInnermostDelegate();
241            }
242            return null;
243        }
244
245        @Override
246        public void close() throws SQLException {
247            if (getDelegateInternal() != null) {
248                super.close();
249                super.setDelegate(null);
250            }
251        }
252
253        @Override
254        public boolean isClosed() throws SQLException {
255            if (getDelegateInternal() == null) {
256                return true;
257            }
258            return super.isClosed();
259        }
260    }
261}