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 */ 017 018package org.apache.commons.dbcp2; 019 020import java.sql.Connection; 021import java.sql.SQLException; 022import java.sql.Statement; 023import java.util.Collection; 024import java.util.concurrent.atomic.AtomicLong; 025 026import javax.management.ObjectName; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030import org.apache.commons.pool2.KeyedObjectPool; 031import org.apache.commons.pool2.ObjectPool; 032import org.apache.commons.pool2.PooledObject; 033import org.apache.commons.pool2.PooledObjectFactory; 034import org.apache.commons.pool2.impl.GenericKeyedObjectPool; 035import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; 036import org.apache.commons.pool2.impl.DefaultPooledObject; 037 038/** 039 * A {@link PooledObjectFactory} that creates 040 * {@link PoolableConnection}s. 041 * 042 * @author Rodney Waldhoff 043 * @author Glenn L. Nielsen 044 * @author James House 045 * @author Dirk Verbeeck 046 * @version $Id: PoolableConnectionFactory.java 1659452 2015-02-13 04:00:39Z psteitz $ 047 * @since 2.0 048 */ 049public class PoolableConnectionFactory 050 implements PooledObjectFactory<PoolableConnection> { 051 052 private static final Log log = 053 LogFactory.getLog(PoolableConnectionFactory.class); 054 055 /** 056 * Create a new {@code PoolableConnectionFactory}. 057 * @param connFactory the {@link ConnectionFactory} from which to obtain 058 * base {@link Connection}s 059 */ 060 public PoolableConnectionFactory(ConnectionFactory connFactory, 061 ObjectName dataSourceJmxName) { 062 _connFactory = connFactory; 063 this.dataSourceJmxName = dataSourceJmxName; 064 } 065 066 /** 067 * Sets the query I use to {@link #validateObject validate} {@link Connection}s. 068 * Should return at least one row. If not specified, 069 * {@link Connection#isValid(int)} will be used to validate connections. 070 * 071 * @param validationQuery a query to use to {@link #validateObject validate} {@link Connection}s. 072 */ 073 public void setValidationQuery(String validationQuery) { 074 _validationQuery = validationQuery; 075 } 076 077 /** 078 * Sets the validation query timeout, the amount of time, in seconds, that 079 * connection validation will wait for a response from the database when 080 * executing a validation query. Use a value less than or equal to 0 for 081 * no timeout. 082 * 083 * @param timeout new validation query timeout value in seconds 084 */ 085 public void setValidationQueryTimeout(int timeout) { 086 _validationQueryTimeout = timeout; 087 } 088 089 /** 090 * Sets the SQL statements I use to initialize newly created {@link Connection}s. 091 * Using {@code null} turns off connection initialization. 092 * @param connectionInitSqls SQL statement to initialize {@link Connection}s. 093 */ 094 public void setConnectionInitSql(Collection<String> connectionInitSqls) { 095 _connectionInitSqls = connectionInitSqls; 096 } 097 098 /** 099 * Sets the {@link ObjectPool} in which to pool {@link Connection}s. 100 * @param pool the {@link ObjectPool} in which to pool those {@link Connection}s 101 */ 102 public synchronized void setPool(ObjectPool<PoolableConnection> pool) { 103 if(null != _pool && pool != _pool) { 104 try { 105 _pool.close(); 106 } catch(Exception e) { 107 // ignored !?! 108 } 109 } 110 _pool = pool; 111 } 112 113 /** 114 * Returns the {@link ObjectPool} in which {@link Connection}s are pooled. 115 * @return the connection pool 116 */ 117 public synchronized ObjectPool<PoolableConnection> getPool() { 118 return _pool; 119 } 120 121 /** 122 * Sets the default "read only" setting for borrowed {@link Connection}s 123 * @param defaultReadOnly the default "read only" setting for borrowed {@link Connection}s 124 */ 125 public void setDefaultReadOnly(Boolean defaultReadOnly) { 126 _defaultReadOnly = defaultReadOnly; 127 } 128 129 /** 130 * Sets the default "auto commit" setting for borrowed {@link Connection}s 131 * @param defaultAutoCommit the default "auto commit" setting for borrowed {@link Connection}s 132 */ 133 public void setDefaultAutoCommit(Boolean defaultAutoCommit) { 134 _defaultAutoCommit = defaultAutoCommit; 135 } 136 137 /** 138 * Sets the default "Transaction Isolation" setting for borrowed {@link Connection}s 139 * @param defaultTransactionIsolation the default "Transaction Isolation" setting for returned {@link Connection}s 140 */ 141 public void setDefaultTransactionIsolation(int defaultTransactionIsolation) { 142 _defaultTransactionIsolation = defaultTransactionIsolation; 143 } 144 145 /** 146 * Sets the default "catalog" setting for borrowed {@link Connection}s 147 * @param defaultCatalog the default "catalog" setting for borrowed {@link Connection}s 148 */ 149 public void setDefaultCatalog(String defaultCatalog) { 150 _defaultCatalog = defaultCatalog; 151 } 152 153 public void setCacheState(boolean cacheState) { 154 this._cacheState = cacheState; 155 } 156 157 public void setPoolStatements(boolean poolStatements) { 158 this.poolStatements = poolStatements; 159 } 160 161 public void setMaxOpenPrepatedStatements(int maxOpenPreparedStatements) { 162 this.maxOpenPreparedStatements = maxOpenPreparedStatements; 163 } 164 165 /** 166 * Sets the maximum lifetime in milliseconds of a connection after which the 167 * connection will always fail activation, passivation and validation. A 168 * value of zero or less indicates an infinite lifetime. The default value 169 * is -1. 170 */ 171 public void setMaxConnLifetimeMillis(long maxConnLifetimeMillis) { 172 this.maxConnLifetimeMillis = maxConnLifetimeMillis; 173 } 174 175 176 public boolean isEnableAutoCommitOnReturn() { 177 return enableAutoCommitOnReturn; 178 } 179 180 public void setEnableAutoCommitOnReturn(boolean enableAutoCommitOnReturn) { 181 this.enableAutoCommitOnReturn = enableAutoCommitOnReturn; 182 } 183 184 185 public boolean isRollbackOnReturn() { 186 return rollbackOnReturn; 187 } 188 189 public void setRollbackOnReturn(boolean rollbackOnReturn) { 190 this.rollbackOnReturn = rollbackOnReturn; 191 } 192 193 public Integer getDefaultQueryTimeout() { 194 return defaultQueryTimeout; 195 } 196 197 public void setDefaultQueryTimeout(Integer defaultQueryTimeout) { 198 this.defaultQueryTimeout = defaultQueryTimeout; 199 } 200 201 /** 202 * SQL_STATE codes considered to signal fatal conditions. 203 * <p> 204 * Overrides the defaults in {@link Utils#DISCONNECTION_SQL_CODES} 205 * (plus anything starting with {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}). 206 * If this property is non-null and {@link #isFastFailValidation()} is 207 * {@code true}, whenever connections created by this factory generate exceptions 208 * with SQL_STATE codes in this list, they will be marked as "fatally disconnected" 209 * and subsequent validations will fail fast (no attempt at isValid or validation 210 * query).</p> 211 * <p> 212 * If {@link #isFastFailValidation()} is {@code false} setting this property has no 213 * effect.</p> 214 * 215 * @return SQL_STATE codes overriding defaults 216 * @since 2.1 217 */ 218 public Collection<String> getDisconnectionSqlCodes() { 219 return _disconnectionSqlCodes; 220 } 221 222 /** 223 * @see #getDisconnectionSqlCodes() 224 * @param disconnectionSqlCodes 225 * @since 2.1 226 */ 227 public void setDisconnectionSqlCodes(Collection<String> disconnectionSqlCodes) { 228 _disconnectionSqlCodes = disconnectionSqlCodes; 229 } 230 231 /** 232 * True means that validation will fail immediately for connections that 233 * have previously thrown SQLExceptions with SQL_STATE indicating fatal 234 * disconnection errors. 235 * 236 * @return true if connections created by this factory will fast fail validation. 237 * @see #setDisconnectionSqlCodes(Collection) 238 * @since 2.1 239 */ 240 public boolean isFastFailValidation() { 241 return _fastFailValidation; 242 } 243 244 /** 245 * @see #isFastFailValidation() 246 * @param fastFailValidation true means connections created by this factory will 247 * fast fail validation 248 * @since 2.1 249 */ 250 public void setFastFailValidation(boolean fastFailValidation) { 251 _fastFailValidation = fastFailValidation; 252 } 253 254 @Override 255 public PooledObject<PoolableConnection> makeObject() throws Exception { 256 Connection conn = _connFactory.createConnection(); 257 if (conn == null) { 258 throw new IllegalStateException("Connection factory returned null from createConnection"); 259 } 260 try { 261 initializeConnection(conn); 262 } catch (SQLException sqle) { 263 // Make sure the connection is closed 264 try { 265 conn.close(); 266 } catch (SQLException ignore) { 267 // ignore 268 } 269 // Rethrow original exception so it is visible to caller 270 throw sqle; 271 } 272 273 long connIndex = connectionIndex.getAndIncrement(); 274 275 if(poolStatements) { 276 conn = new PoolingConnection(conn); 277 GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig(); 278 config.setMaxTotalPerKey(-1); 279 config.setBlockWhenExhausted(false); 280 config.setMaxWaitMillis(0); 281 config.setMaxIdlePerKey(1); 282 config.setMaxTotal(maxOpenPreparedStatements); 283 if (dataSourceJmxName != null) { 284 StringBuilder base = new StringBuilder(dataSourceJmxName.toString()); 285 base.append(Constants.JMX_CONNECTION_BASE_EXT); 286 base.append(Long.toString(connIndex)); 287 config.setJmxNameBase(base.toString()); 288 config.setJmxNamePrefix(Constants.JMX_STATEMENT_POOL_PREFIX); 289 } else { 290 config.setJmxEnabled(false); 291 } 292 KeyedObjectPool<PStmtKey,DelegatingPreparedStatement> stmtPool = 293 new GenericKeyedObjectPool<>((PoolingConnection)conn, config); 294 ((PoolingConnection)conn).setStatementPool(stmtPool); 295 ((PoolingConnection) conn).setCacheState(_cacheState); 296 } 297 298 // Register this connection with JMX 299 ObjectName connJmxName; 300 if (dataSourceJmxName == null) { 301 connJmxName = null; 302 } else { 303 connJmxName = new ObjectName(dataSourceJmxName.toString() + 304 Constants.JMX_CONNECTION_BASE_EXT + connIndex); 305 } 306 307 PoolableConnection pc = new PoolableConnection(conn,_pool, connJmxName, 308 _disconnectionSqlCodes, _fastFailValidation); 309 310 return new DefaultPooledObject<>(pc); 311 } 312 313 protected void initializeConnection(Connection conn) throws SQLException { 314 Collection<String> sqls = _connectionInitSqls; 315 if(conn.isClosed()) { 316 throw new SQLException("initializeConnection: connection closed"); 317 } 318 if(null != sqls) { 319 try (Statement stmt = conn.createStatement();) { 320 for (String sql : sqls) { 321 if (sql == null) { 322 throw new NullPointerException( 323 "null connectionInitSqls element"); 324 } 325 stmt.execute(sql); 326 } 327 } 328 } 329 } 330 331 @Override 332 public void destroyObject(PooledObject<PoolableConnection> p) 333 throws Exception { 334 p.getObject().reallyClose(); 335 } 336 337 @Override 338 public boolean validateObject(PooledObject<PoolableConnection> p) { 339 try { 340 validateLifetime(p); 341 342 validateConnection(p.getObject()); 343 return true; 344 } catch (Exception e) { 345 if (log.isDebugEnabled()) { 346 log.debug(Utils.getMessage( 347 "poolableConnectionFactory.validateObject.fail"), e); 348 } 349 return false; 350 } 351 } 352 353 public void validateConnection(PoolableConnection conn) throws SQLException { 354 if(conn.isClosed()) { 355 throw new SQLException("validateConnection: connection closed"); 356 } 357 conn.validate(_validationQuery, _validationQueryTimeout); 358 } 359 360 @Override 361 public void passivateObject(PooledObject<PoolableConnection> p) 362 throws Exception { 363 364 validateLifetime(p); 365 366 PoolableConnection conn = p.getObject(); 367 Boolean connAutoCommit = null; 368 if (rollbackOnReturn) { 369 connAutoCommit = Boolean.valueOf(conn.getAutoCommit()); 370 if(!connAutoCommit.booleanValue() && !conn.isReadOnly()) { 371 conn.rollback(); 372 } 373 } 374 375 conn.clearWarnings(); 376 377 // DBCP-97 / DBCP-399 / DBCP-351 Idle connections in the pool should 378 // have autoCommit enabled 379 if (enableAutoCommitOnReturn) { 380 if (connAutoCommit == null) { 381 connAutoCommit = Boolean.valueOf(conn.getAutoCommit()); 382 } 383 if(!connAutoCommit.booleanValue()) { 384 conn.setAutoCommit(true); 385 } 386 } 387 388 conn.passivate(); 389 } 390 391 @Override 392 public void activateObject(PooledObject<PoolableConnection> p) 393 throws Exception { 394 395 validateLifetime(p); 396 397 PoolableConnection conn = p.getObject(); 398 conn.activate(); 399 400 if (_defaultAutoCommit != null && 401 conn.getAutoCommit() != _defaultAutoCommit.booleanValue()) { 402 conn.setAutoCommit(_defaultAutoCommit.booleanValue()); 403 } 404 if (_defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION && 405 conn.getTransactionIsolation() != _defaultTransactionIsolation) { 406 conn.setTransactionIsolation(_defaultTransactionIsolation); 407 } 408 if (_defaultReadOnly != null && 409 conn.isReadOnly() != _defaultReadOnly.booleanValue()) { 410 conn.setReadOnly(_defaultReadOnly.booleanValue()); 411 } 412 if (_defaultCatalog != null && 413 !_defaultCatalog.equals(conn.getCatalog())) { 414 conn.setCatalog(_defaultCatalog); 415 } 416 conn.setDefaultQueryTimeout(defaultQueryTimeout); 417 } 418 419 private void validateLifetime(PooledObject<PoolableConnection> p) 420 throws Exception { 421 if (maxConnLifetimeMillis > 0) { 422 long lifetime = System.currentTimeMillis() - p.getCreateTime(); 423 if (lifetime > maxConnLifetimeMillis) { 424 throw new LifetimeExceededException(Utils.getMessage( 425 "connectionFactory.lifetimeExceeded", 426 Long.valueOf(lifetime), 427 Long.valueOf(maxConnLifetimeMillis))); 428 } 429 } 430 } 431 432 protected ConnectionFactory getConnectionFactory() { 433 return _connFactory; 434 } 435 436 protected boolean getPoolStatements() { 437 return poolStatements; 438 } 439 440 protected int getMaxOpenPreparedStatements() { 441 return maxOpenPreparedStatements; 442 } 443 444 protected boolean getCacheState() { 445 return _cacheState; 446 } 447 448 protected ObjectName getDataSourceJmxName() { 449 return dataSourceJmxName; 450 } 451 452 protected AtomicLong getConnectionIndex() { 453 return connectionIndex; 454 } 455 456 private final ConnectionFactory _connFactory; 457 private final ObjectName dataSourceJmxName; 458 private volatile String _validationQuery = null; 459 private volatile int _validationQueryTimeout = -1; 460 private Collection<String> _connectionInitSqls = null; 461 private Collection<String> _disconnectionSqlCodes = null; 462 private boolean _fastFailValidation = false; 463 private volatile ObjectPool<PoolableConnection> _pool = null; 464 private Boolean _defaultReadOnly = null; 465 private Boolean _defaultAutoCommit = null; 466 private boolean enableAutoCommitOnReturn = true; 467 private boolean rollbackOnReturn = true; 468 private int _defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION; 469 private String _defaultCatalog; 470 private boolean _cacheState; 471 private boolean poolStatements = false; 472 private int maxOpenPreparedStatements = 473 GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY; 474 private long maxConnLifetimeMillis = -1; 475 private final AtomicLong connectionIndex = new AtomicLong(0); 476 private Integer defaultQueryTimeout = null; 477 478 /** 479 * Internal constant to indicate the level is not set. 480 */ 481 static final int UNKNOWN_TRANSACTIONISOLATION = -1; 482}