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}