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 $Revision: 1592119 $ $Date: 2014-05-02 16:32:43 -0700 (Fri, 02 May 2014) $ 044 * @since 2.0 045 */ 046public class PoolingDataSource<C extends Connection> implements DataSource { 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 * Returns the value of the accessToUnderlyingConnectionAllowed property. 075 * 076 * @return true if access to the underlying is allowed, false otherwise. 077 */ 078 public boolean isAccessToUnderlyingConnectionAllowed() { 079 return this.accessToUnderlyingConnectionAllowed; 080 } 081 082 /** 083 * Sets the value of the accessToUnderlyingConnectionAllowed property. 084 * It controls if the PoolGuard allows access to the underlying connection. 085 * (Default: false) 086 * 087 * @param allow Access to the underlying connection is granted when true. 088 */ 089 public void setAccessToUnderlyingConnectionAllowed(boolean allow) { 090 this.accessToUnderlyingConnectionAllowed = allow; 091 } 092 093 /* JDBC_4_ANT_KEY_BEGIN */ 094 @Override 095 public boolean isWrapperFor(Class<?> iface) throws SQLException { 096 return false; 097 } 098 099 @Override 100 public <T> T unwrap(Class<T> iface) throws SQLException { 101 throw new SQLException("PoolingDataSource is not a wrapper."); 102 } 103 /* JDBC_4_ANT_KEY_END */ 104 105 @Override 106 public Logger getParentLogger() throws SQLFeatureNotSupportedException { 107 throw new SQLFeatureNotSupportedException(); 108 } 109 110 //--- DataSource methods ----------------------------------------- 111 112 /** 113 * Return a {@link java.sql.Connection} from my pool, 114 * according to the contract specified by {@link ObjectPool#borrowObject}. 115 */ 116 @Override 117 public Connection getConnection() throws SQLException { 118 try { 119 C conn = _pool.borrowObject(); 120 if (conn == null) { 121 return null; 122 } 123 return new PoolGuardConnectionWrapper<>(conn); 124 } catch(SQLException e) { 125 throw e; 126 } catch(NoSuchElementException e) { 127 throw new SQLException("Cannot get a connection, pool error " + e.getMessage(), e); 128 } catch(RuntimeException e) { 129 throw e; 130 } catch(Exception e) { 131 throw new SQLException("Cannot get a connection, general error", e); 132 } 133 } 134 135 /** 136 * Throws {@link UnsupportedOperationException} 137 * @throws UnsupportedOperationException 138 */ 139 @Override 140 public Connection getConnection(String uname, String passwd) throws SQLException { 141 throw new UnsupportedOperationException(); 142 } 143 144 /** 145 * Returns my log writer. 146 * @return my log writer 147 * @see DataSource#getLogWriter 148 */ 149 @Override 150 public PrintWriter getLogWriter() { 151 return _logWriter; 152 } 153 154 /** 155 * Throws {@link UnsupportedOperationException}. 156 * @throws UnsupportedOperationException As this 157 * implementation does not support this feature. 158 */ 159 @Override 160 public int getLoginTimeout() { 161 throw new UnsupportedOperationException("Login timeout is not supported."); 162 } 163 164 /** 165 * Throws {@link UnsupportedOperationException}. 166 * @throws UnsupportedOperationException As this 167 * implementation does not support this feature. 168 */ 169 @Override 170 public void setLoginTimeout(int seconds) { 171 throw new UnsupportedOperationException("Login timeout is not supported."); 172 } 173 174 /** 175 * Sets my log writer. 176 * @see DataSource#setLogWriter 177 */ 178 @Override 179 public void setLogWriter(PrintWriter out) { 180 _logWriter = out; 181 } 182 183 /** My log writer. */ 184 private PrintWriter _logWriter = null; 185 186 private final ObjectPool<C> _pool; 187 188 protected ObjectPool<C> getPool() { 189 return _pool; 190 } 191 192 /** 193 * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a 194 * closed connection cannot be used anymore. 195 * @since 2.0 196 */ 197 private class PoolGuardConnectionWrapper<D extends Connection> 198 extends DelegatingConnection<D> { 199 200 PoolGuardConnectionWrapper(D delegate) { 201 super(delegate); 202 } 203 204 /** 205 * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate() 206 */ 207 @Override 208 public D getDelegate() { 209 if (isAccessToUnderlyingConnectionAllowed()) { 210 return super.getDelegate(); 211 } 212 return null; 213 } 214 215 /** 216 * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate() 217 */ 218 @Override 219 public Connection getInnermostDelegate() { 220 if (isAccessToUnderlyingConnectionAllowed()) { 221 return super.getInnermostDelegate(); 222 } 223 return null; 224 } 225 226 @Override 227 public void close() throws SQLException { 228 if (getDelegateInternal() != null) { 229 super.close(); 230 super.setDelegate(null); 231 } 232 } 233 234 @Override 235 public boolean isClosed() throws SQLException { 236 if (getDelegateInternal() == null) { 237 return true; 238 } 239 return super.isClosed(); 240 } 241 } 242}