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 * @version $Id: PoolingDataSource.java 1677110 2015-05-01 11:15:22Z sebb $ 044 * @since 2.0 045 */ 046public class PoolingDataSource<C extends Connection> implements DataSource, AutoCloseable { 047 048 private static final Log log = LogFactory.getLog(PoolingDataSource.class); 049 050 /** Controls access to the underlying connection */ 051 private boolean accessToUnderlyingConnectionAllowed = false; 052 053 public PoolingDataSource(ObjectPool<C> pool) { 054 if (null == pool) { 055 throw new NullPointerException("Pool must not be null."); 056 } 057 _pool = pool; 058 // Verify that _pool's factory refers back to it. If not, log a warning and try to fix. 059 if (_pool instanceof GenericObjectPool<?>) { 060 PoolableConnectionFactory pcf = (PoolableConnectionFactory) ((GenericObjectPool<?>) _pool).getFactory(); 061 if (pcf == null) { 062 throw new NullPointerException("PoolableConnectionFactory must not be null."); 063 } 064 if (pcf.getPool() != _pool) { 065 log.warn(Utils.getMessage("poolingDataSource.factoryConfig")); 066 @SuppressWarnings("unchecked") // PCF must have a pool of PCs 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(RuntimeException rte) { 082 throw new RuntimeException(Utils.getMessage("pool.close.fail"), rte); 083 } catch(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(boolean allow) { 105 this.accessToUnderlyingConnectionAllowed = allow; 106 } 107 108 /* JDBC_4_ANT_KEY_BEGIN */ 109 @Override 110 public boolean isWrapperFor(Class<?> iface) throws SQLException { 111 return false; 112 } 113 114 @Override 115 public <T> T unwrap(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 C conn = _pool.borrowObject(); 135 if (conn == null) { 136 return null; 137 } 138 return new PoolGuardConnectionWrapper<>(conn); 139 } catch(SQLException e) { 140 throw e; 141 } catch(NoSuchElementException e) { 142 throw new SQLException("Cannot get a connection, pool error " + e.getMessage(), e); 143 } catch(RuntimeException e) { 144 throw e; 145 } catch(Exception e) { 146 throw new SQLException("Cannot get a connection, general error", e); 147 } 148 } 149 150 /** 151 * Throws {@link UnsupportedOperationException} 152 * @throws UnsupportedOperationException 153 */ 154 @Override 155 public Connection getConnection(String uname, String passwd) throws SQLException { 156 throw new UnsupportedOperationException(); 157 } 158 159 /** 160 * Returns my log writer. 161 * @return my log writer 162 * @see DataSource#getLogWriter 163 */ 164 @Override 165 public PrintWriter getLogWriter() { 166 return _logWriter; 167 } 168 169 /** 170 * Throws {@link UnsupportedOperationException}. 171 * @throws UnsupportedOperationException As this 172 * implementation does not support this feature. 173 */ 174 @Override 175 public int getLoginTimeout() { 176 throw new UnsupportedOperationException("Login timeout is not supported."); 177 } 178 179 /** 180 * Throws {@link UnsupportedOperationException}. 181 * @throws UnsupportedOperationException As this 182 * implementation does not support this feature. 183 */ 184 @Override 185 public void setLoginTimeout(int seconds) { 186 throw new UnsupportedOperationException("Login timeout is not supported."); 187 } 188 189 /** 190 * Sets my log writer. 191 * @see DataSource#setLogWriter 192 */ 193 @Override 194 public void setLogWriter(PrintWriter out) { 195 _logWriter = out; 196 } 197 198 /** My log writer. */ 199 private PrintWriter _logWriter = null; 200 201 private final ObjectPool<C> _pool; 202 203 protected ObjectPool<C> getPool() { 204 return _pool; 205 } 206 207 /** 208 * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a 209 * closed connection cannot be used anymore. 210 * @since 2.0 211 */ 212 private class PoolGuardConnectionWrapper<D extends Connection> 213 extends DelegatingConnection<D> { 214 215 PoolGuardConnectionWrapper(D delegate) { 216 super(delegate); 217 } 218 219 /** 220 * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate() 221 */ 222 @Override 223 public D getDelegate() { 224 if (isAccessToUnderlyingConnectionAllowed()) { 225 return super.getDelegate(); 226 } 227 return null; 228 } 229 230 /** 231 * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate() 232 */ 233 @Override 234 public Connection getInnermostDelegate() { 235 if (isAccessToUnderlyingConnectionAllowed()) { 236 return super.getInnermostDelegate(); 237 } 238 return null; 239 } 240 241 @Override 242 public void close() throws SQLException { 243 if (getDelegateInternal() != null) { 244 super.close(); 245 super.setDelegate(null); 246 } 247 } 248 249 @Override 250 public boolean isClosed() throws SQLException { 251 if (getDelegateInternal() == null) { 252 return true; 253 } 254 return super.isClosed(); 255 } 256 } 257}