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.Objects; 025import java.util.logging.Logger; 026 027import javax.sql.DataSource; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.apache.commons.pool2.ObjectPool; 032import org.apache.commons.pool2.impl.GenericObjectPool; 033 034/** 035 * A simple {@link DataSource} implementation that obtains {@link Connection}s from the specified {@link ObjectPool}. 036 * 037 * @param <C> 038 * The connection type 039 * 040 * @since 2.0 041 */ 042public class PoolingDataSource<C extends Connection> implements DataSource, AutoCloseable { 043 044 private static final Log log = LogFactory.getLog(PoolingDataSource.class); 045 046 /** Controls access to the underlying connection */ 047 private boolean accessToUnderlyingConnectionAllowed; 048 049 /** 050 * Constructs a new instance backed by the given connection pool. 051 * 052 * @param pool 053 * the given connection pool. 054 */ 055 public PoolingDataSource(final ObjectPool<C> pool) { 056 Objects.requireNonNull(pool, "Pool must not be null."); 057 this.pool = pool; 058 // Verify that pool's factory refers back to it. If not, log a warning and try to fix. 059 if (this.pool instanceof GenericObjectPool<?>) { 060 final PoolableConnectionFactory pcf = (PoolableConnectionFactory) ((GenericObjectPool<?>) this.pool) 061 .getFactory(); 062 Objects.requireNonNull(pcf, "PoolableConnectionFactory must not be null."); 063 if (pcf.getPool() != this.pool) { 064 log.warn(Utils.getMessage("poolingDataSource.factoryConfig")); 065 @SuppressWarnings("unchecked") // PCF must have a pool of PCs 066 final ObjectPool<PoolableConnection> p = (ObjectPool<PoolableConnection>) this.pool; 067 pcf.setPool(p); 068 } 069 } 070 } 071 072 /** 073 * Closes and free all {@link Connection}s from the pool. 074 * 075 * @since 2.1 076 */ 077 @Override 078 public void close() throws RuntimeException, SQLException { 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. It controls if the PoolGuard allows access to 099 * the underlying connection. (Default: false) 100 * 101 * @param allow 102 * 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 * Returns a {@link java.sql.Connection} from my pool, according to the contract specified by 129 * {@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 * 157 * @throws UnsupportedOperationException 158 * always thrown 159 */ 160 @Override 161 public Connection getConnection(final String uname, final String passwd) throws SQLException { 162 throw new UnsupportedOperationException(); 163 } 164 165 /** 166 * Returns my log writer. 167 * 168 * @return my log writer 169 * @see DataSource#getLogWriter 170 */ 171 @Override 172 public PrintWriter getLogWriter() { 173 return logWriter; 174 } 175 176 /** 177 * Throws {@link UnsupportedOperationException}. 178 * 179 * @throws UnsupportedOperationException 180 * As this implementation does not support this feature. 181 */ 182 @Override 183 public int getLoginTimeout() { 184 throw new UnsupportedOperationException("Login timeout is not supported."); 185 } 186 187 /** 188 * Throws {@link UnsupportedOperationException}. 189 * 190 * @throws UnsupportedOperationException 191 * As this implementation does not support this feature. 192 */ 193 @Override 194 public void setLoginTimeout(final int seconds) { 195 throw new UnsupportedOperationException("Login timeout is not supported."); 196 } 197 198 /** 199 * Sets my log writer. 200 * 201 * @see DataSource#setLogWriter 202 */ 203 @Override 204 public void setLogWriter(final PrintWriter out) { 205 logWriter = out; 206 } 207 208 /** My log writer. */ 209 private PrintWriter logWriter = null; 210 211 private final ObjectPool<C> pool; 212 213 protected ObjectPool<C> getPool() { 214 return pool; 215 } 216 217 /** 218 * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore. 219 * 220 * @since 2.0 221 */ 222 private class PoolGuardConnectionWrapper<D extends Connection> extends DelegatingConnection<D> { 223 224 PoolGuardConnectionWrapper(final D delegate) { 225 super(delegate); 226 } 227 228 /** 229 * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate() 230 */ 231 @Override 232 public D getDelegate() { 233 return isAccessToUnderlyingConnectionAllowed() ? super.getDelegate() : null; 234 } 235 236 /** 237 * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate() 238 */ 239 @Override 240 public Connection getInnermostDelegate() { 241 return isAccessToUnderlyingConnectionAllowed() ? super.getInnermostDelegate() : null; 242 } 243 244 @Override 245 public void close() throws SQLException { 246 if (getDelegateInternal() != null) { 247 super.close(); 248 super.setDelegate(null); 249 } 250 } 251 252 @Override 253 public boolean isClosed() throws SQLException { 254 return getDelegateInternal() == null ? true : super.isClosed(); 255 } 256 } 257}