PoolingDataSource.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.commons.dbcp2;
- import java.io.PrintWriter;
- import java.sql.Connection;
- import java.sql.SQLException;
- import java.sql.SQLFeatureNotSupportedException;
- import java.util.NoSuchElementException;
- import java.util.Objects;
- import java.util.logging.Logger;
- import javax.sql.DataSource;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.apache.commons.pool2.ObjectPool;
- import org.apache.commons.pool2.impl.GenericObjectPool;
- /**
- * A simple {@link DataSource} implementation that obtains {@link Connection}s from the specified {@link ObjectPool}.
- *
- * @param <C>
- * The connection type
- *
- * @since 2.0
- */
- public class PoolingDataSource<C extends Connection> implements DataSource, AutoCloseable {
- /**
- * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
- *
- * @since 2.0
- */
- private final class PoolGuardConnectionWrapper<D extends Connection> extends DelegatingConnection<D> {
- PoolGuardConnectionWrapper(final D delegate) {
- super(delegate);
- }
- @Override
- public void close() throws SQLException {
- if (getDelegateInternal() != null) {
- super.close();
- super.setDelegate(null);
- }
- }
- /**
- * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate()
- */
- @Override
- public D getDelegate() {
- return isAccessToUnderlyingConnectionAllowed() ? super.getDelegate() : null;
- }
- /**
- * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate()
- */
- @Override
- public Connection getInnermostDelegate() {
- return isAccessToUnderlyingConnectionAllowed() ? super.getInnermostDelegate() : null;
- }
- @Override
- public boolean isClosed() throws SQLException {
- return getDelegateInternal() == null || super.isClosed();
- }
- }
- private static final Log log = LogFactory.getLog(PoolingDataSource.class);
- /** Controls access to the underlying connection */
- private boolean accessToUnderlyingConnectionAllowed;
- /** My log writer. */
- private PrintWriter logWriter;
- private final ObjectPool<C> pool;
- /**
- * Constructs a new instance backed by the given connection pool.
- *
- * @param pool
- * the given connection pool.
- */
- public PoolingDataSource(final ObjectPool<C> pool) {
- Objects.requireNonNull(pool, "pool");
- this.pool = pool;
- // Verify that pool's factory refers back to it. If not, log a warning and try to fix.
- if (this.pool instanceof GenericObjectPool<?>) {
- final PoolableConnectionFactory pcf = (PoolableConnectionFactory) ((GenericObjectPool<?>) this.pool).getFactory();
- Objects.requireNonNull(pcf, "this.pool.getFactory()");
- if (pcf.getPool() != this.pool) {
- log.warn(Utils.getMessage("poolingDataSource.factoryConfig"));
- @SuppressWarnings("unchecked") // PCF must have a pool of PCs
- final ObjectPool<PoolableConnection> p = (ObjectPool<PoolableConnection>) this.pool;
- pcf.setPool(p);
- }
- }
- }
- /**
- * Closes and free all {@link Connection}s from the pool.
- *
- * @since 2.1
- */
- @Override
- public void close() throws SQLException {
- try {
- pool.close();
- } catch (final Exception e) {
- throw new SQLException(Utils.getMessage("pool.close.fail"), e);
- }
- }
- /**
- * Returns a {@link java.sql.Connection} from my pool, according to the contract specified by
- * {@link ObjectPool#borrowObject}.
- */
- @Override
- public Connection getConnection() throws SQLException {
- try {
- final C conn = pool.borrowObject();
- if (conn == null) {
- return null;
- }
- return new PoolGuardConnectionWrapper<>(conn);
- } catch (final NoSuchElementException e) {
- throw new SQLException("Cannot get a connection, pool error " + e.getMessage(), e);
- } catch (final SQLException | RuntimeException e) {
- throw e;
- } catch (final InterruptedException e) {
- // Reset the interrupt status so it is visible to callers
- Thread.currentThread().interrupt();
- throw new SQLException("Cannot get a connection, general error", e);
- } catch (final Exception e) {
- throw new SQLException("Cannot get a connection, general error", e);
- }
- }
- /**
- * Throws {@link UnsupportedOperationException}
- *
- * @throws UnsupportedOperationException
- * always thrown
- */
- @Override
- public Connection getConnection(final String userName, final String password) throws SQLException {
- throw new UnsupportedOperationException();
- }
- /**
- * Throws {@link UnsupportedOperationException}.
- *
- * @throws UnsupportedOperationException
- * As this implementation does not support this feature.
- */
- @Override
- public int getLoginTimeout() {
- throw new UnsupportedOperationException("Login timeout is not supported.");
- }
- /**
- * Returns my log writer.
- *
- * @return my log writer
- * @see DataSource#getLogWriter
- */
- @Override
- public PrintWriter getLogWriter() {
- return logWriter;
- }
- @Override
- public Logger getParentLogger() throws SQLFeatureNotSupportedException {
- throw new SQLFeatureNotSupportedException();
- }
- /**
- * Gets the backing object pool.
- *
- * @return the backing object pool.
- */
- protected ObjectPool<C> getPool() {
- return pool;
- }
- /**
- * Returns the value of the accessToUnderlyingConnectionAllowed property.
- *
- * @return true if access to the underlying {@link Connection} is allowed, false otherwise.
- */
- public boolean isAccessToUnderlyingConnectionAllowed() {
- return this.accessToUnderlyingConnectionAllowed;
- }
- @Override
- public boolean isWrapperFor(final Class<?> iface) throws SQLException {
- return iface != null && iface.isInstance(this);
- }
- /**
- * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
- * the underlying connection. (Default: false)
- *
- * @param allow
- * Access to the underlying connection is granted when true.
- */
- public void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
- this.accessToUnderlyingConnectionAllowed = allow;
- }
- /**
- * Throws {@link UnsupportedOperationException}.
- *
- * @throws UnsupportedOperationException
- * As this implementation does not support this feature.
- */
- @Override
- public void setLoginTimeout(final int seconds) {
- throw new UnsupportedOperationException("Login timeout is not supported.");
- }
- /**
- * Sets my log writer.
- *
- * @see DataSource#setLogWriter
- */
- @Override
- public void setLogWriter(final PrintWriter out) {
- logWriter = out;
- }
- @Override
- public <T> T unwrap(final Class<T> iface) throws SQLException {
- if (isWrapperFor(iface)) {
- return iface.cast(this);
- }
- throw new SQLException(this + " is not a wrapper for " + iface);
- }
- }