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.Objects; 025import java.util.concurrent.atomic.AtomicLong; 026 027import javax.management.ObjectName; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.apache.commons.pool2.KeyedObjectPool; 032import org.apache.commons.pool2.ObjectPool; 033import org.apache.commons.pool2.PooledObject; 034import org.apache.commons.pool2.PooledObjectFactory; 035import org.apache.commons.pool2.impl.DefaultPooledObject; 036import org.apache.commons.pool2.impl.GenericKeyedObjectPool; 037import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; 038 039/** 040 * A {@link PooledObjectFactory} that creates {@link PoolableConnection}s. 041 * 042 * @since 2.0 043 */ 044public class PoolableConnectionFactory implements PooledObjectFactory<PoolableConnection> { 045 046 private static final Log log = LogFactory.getLog(PoolableConnectionFactory.class); 047 048 /** 049 * Internal constant to indicate the level is not set. 050 */ 051 static final int UNKNOWN_TRANSACTION_ISOLATION = -1; 052 053 private final ConnectionFactory connectionFactory; 054 055 private final ObjectName dataSourceJmxObjectName; 056 057 private volatile String validationQuery; 058 059 private volatile int validationQueryTimeoutSeconds = -1; 060 061 private Collection<String> connectionInitSqls; 062 063 private Collection<String> disconnectionSqlCodes; 064 065 private boolean fastFailValidation = true; 066 067 private volatile ObjectPool<PoolableConnection> pool; 068 069 private Boolean defaultReadOnly; 070 071 private Boolean defaultAutoCommit; 072 073 private boolean autoCommitOnReturn = true; 074 075 private boolean rollbackOnReturn = true; 076 077 private int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION; 078 079 private String defaultCatalog; 080 081 private String defaultSchema; 082 083 private boolean cacheState; 084 085 private boolean poolStatements; 086 087 private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY; 088 089 private long maxConnLifetimeMillis = -1; 090 091 private final AtomicLong connectionIndex = new AtomicLong(0); 092 093 private Integer defaultQueryTimeoutSeconds; 094 095 /** 096 * Creates a new {@code PoolableConnectionFactory}. 097 * 098 * @param connFactory 099 * the {@link ConnectionFactory} from which to obtain base {@link Connection}s 100 * @param dataSourceJmxObjectName 101 * The JMX object name, may be null. 102 */ 103 public PoolableConnectionFactory(final ConnectionFactory connFactory, final ObjectName dataSourceJmxObjectName) { 104 this.connectionFactory = connFactory; 105 this.dataSourceJmxObjectName = dataSourceJmxObjectName; 106 } 107 108 @Override 109 public void activateObject(final PooledObject<PoolableConnection> p) throws Exception { 110 111 validateLifetime(p); 112 113 final PoolableConnection conn = p.getObject(); 114 conn.activate(); 115 116 if (defaultAutoCommit != null && conn.getAutoCommit() != defaultAutoCommit.booleanValue()) { 117 conn.setAutoCommit(defaultAutoCommit.booleanValue()); 118 } 119 if (defaultTransactionIsolation != UNKNOWN_TRANSACTION_ISOLATION 120 && conn.getTransactionIsolation() != defaultTransactionIsolation) { 121 conn.setTransactionIsolation(defaultTransactionIsolation); 122 } 123 if (defaultReadOnly != null && conn.isReadOnly() != defaultReadOnly.booleanValue()) { 124 conn.setReadOnly(defaultReadOnly.booleanValue()); 125 } 126 if (defaultCatalog != null && !defaultCatalog.equals(conn.getCatalog())) { 127 conn.setCatalog(defaultCatalog); 128 } 129 if (defaultSchema != null && !defaultSchema.equals(Jdbc41Bridge.getSchema(conn))) { 130 Jdbc41Bridge.setSchema(conn, defaultSchema); 131 } 132 conn.setDefaultQueryTimeout(defaultQueryTimeoutSeconds); 133 } 134 135 @Override 136 public void destroyObject(final PooledObject<PoolableConnection> p) throws Exception { 137 p.getObject().reallyClose(); 138 } 139 140 /** 141 * @return The cache state. 142 * @since Made public in 2.6.0. 143 */ 144 public boolean getCacheState() { 145 return cacheState; 146 } 147 148 /** 149 * @return The connection factory. 150 * @since Made public in 2.6.0. 151 */ 152 public ConnectionFactory getConnectionFactory() { 153 return connectionFactory; 154 } 155 156 protected AtomicLong getConnectionIndex() { 157 return connectionIndex; 158 } 159 160 /** 161 * @return The collection of initialization SQL statements. 162 * @since 2.6.0 163 */ 164 public Collection<String> getConnectionInitSqls() { 165 return connectionInitSqls; 166 } 167 168 /** 169 * @return The data source JMX ObjectName 170 * @since Made public in 2.6.0. 171 */ 172 public ObjectName getDataSourceJmxName() { 173 return dataSourceJmxObjectName; 174 } 175 176 /** 177 * @return The data source JMS ObjectName. 178 * @since 2.6.0 179 */ 180 public ObjectName getDataSourceJmxObjectName() { 181 return dataSourceJmxObjectName; 182 } 183 184 /** 185 * @return Default auto-commit value. 186 * @since 2.6.0 187 */ 188 public Boolean getDefaultAutoCommit() { 189 return defaultAutoCommit; 190 } 191 192 /** 193 * @return Default catalog. 194 * @since 2.6.0 195 */ 196 public String getDefaultCatalog() { 197 return defaultCatalog; 198 } 199 200 /** 201 * @return Default query timeout in seconds. 202 */ 203 public Integer getDefaultQueryTimeout() { 204 return defaultQueryTimeoutSeconds; 205 } 206 207 /** 208 * @return Default query timeout in seconds. 209 * @since 2.6.0 210 */ 211 public Integer getDefaultQueryTimeoutSeconds() { 212 return defaultQueryTimeoutSeconds; 213 } 214 215 /** 216 * @return Default read-only-value. 217 * @since 2.6.0 218 */ 219 public Boolean getDefaultReadOnly() { 220 return defaultReadOnly; 221 } 222 223 /** 224 * @return Default schema. 225 * @since 2.6.0 226 */ 227 public String getDefaultSchema() { 228 return defaultSchema; 229 } 230 231 /** 232 * @return Default transaction isolation. 233 * @since 2.6.0 234 */ 235 public int getDefaultTransactionIsolation() { 236 return defaultTransactionIsolation; 237 } 238 239 /** 240 * SQL_STATE codes considered to signal fatal conditions. 241 * <p> 242 * Overrides the defaults in {@link Utils#DISCONNECTION_SQL_CODES} (plus anything starting with 243 * {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}). If this property is non-null and {@link #isFastFailValidation()} is 244 * {@code true}, whenever connections created by this factory generate exceptions with SQL_STATE codes in this list, 245 * they will be marked as "fatally disconnected" and subsequent validations will fail fast (no attempt at isValid or 246 * validation query). 247 * </p> 248 * <p> 249 * If {@link #isFastFailValidation()} is {@code false} setting this property has no effect. 250 * </p> 251 * 252 * @return SQL_STATE codes overriding defaults 253 * @since 2.1 254 */ 255 public Collection<String> getDisconnectionSqlCodes() { 256 return disconnectionSqlCodes; 257 } 258 259 /** 260 * @return Maximum connection lifetime in milliseconds. 261 * @since 2.6.0 262 */ 263 public long getMaxConnLifetimeMillis() { 264 return maxConnLifetimeMillis; 265 } 266 267 protected int getMaxOpenPreparedStatements() { 268 return maxOpenPreparedStatements; 269 } 270 271 /** 272 * Returns the {@link ObjectPool} in which {@link Connection}s are pooled. 273 * 274 * @return the connection pool 275 */ 276 public synchronized ObjectPool<PoolableConnection> getPool() { 277 return pool; 278 } 279 280 /** 281 * @return Whether to pool statements. 282 * @since Made public in 2.6.0. 283 */ 284 public boolean getPoolStatements() { 285 return poolStatements; 286 } 287 /** 288 * @return Validation query. 289 * @since 2.6.0 290 */ 291 public String getValidationQuery() { 292 return validationQuery; 293 } 294 /** 295 * @return Validation query timeout in seconds. 296 * @since 2.6.0 297 */ 298 public int getValidationQueryTimeoutSeconds() { 299 return validationQueryTimeoutSeconds; 300 } 301 protected void initializeConnection(final Connection conn) throws SQLException { 302 final Collection<String> sqls = connectionInitSqls; 303 if (conn.isClosed()) { 304 throw new SQLException("initializeConnection: connection closed"); 305 } 306 if (null != sqls) { 307 try (Statement stmt = conn.createStatement()) { 308 for (final String sql : sqls) { 309 Objects.requireNonNull(sql, "null connectionInitSqls element"); 310 stmt.execute(sql); 311 } 312 } 313 } 314 } 315 316 /** 317 * @return Whether to auto-commit on return. 318 * @since 2.6.0 319 */ 320 public boolean isAutoCommitOnReturn() { 321 return autoCommitOnReturn; 322 } 323 324 /** 325 * @return Whether to auto-commit on return. 326 * @deprecated Use {@link #isAutoCommitOnReturn()}. 327 */ 328 @Deprecated 329 public boolean isEnableAutoCommitOnReturn() { 330 return autoCommitOnReturn; 331 } 332 333 /** 334 * True means that validation will fail immediately for connections that have previously thrown SQLExceptions with 335 * SQL_STATE indicating fatal disconnection errors. 336 * 337 * @return true if connections created by this factory will fast fail validation. 338 * @see #setDisconnectionSqlCodes(Collection) 339 * @since 2.1 340 * @since 2.5.0 Defaults to true, previous versions defaulted to false. 341 */ 342 public boolean isFastFailValidation() { 343 return fastFailValidation; 344 } 345 346 /** 347 * @return Whether to rollback on return. 348 */ 349 public boolean isRollbackOnReturn() { 350 return rollbackOnReturn; 351 } 352 353 @Override 354 public PooledObject<PoolableConnection> makeObject() throws Exception { 355 Connection conn = connectionFactory.createConnection(); 356 if (conn == null) { 357 throw new IllegalStateException("Connection factory returned null from createConnection"); 358 } 359 try { 360 initializeConnection(conn); 361 } catch (final SQLException sqle) { 362 // Make sure the connection is closed 363 try { 364 conn.close(); 365 } catch (final SQLException ignore) { 366 // ignore 367 } 368 // Rethrow original exception so it is visible to caller 369 throw sqle; 370 } 371 372 final long connIndex = connectionIndex.getAndIncrement(); 373 374 if (poolStatements) { 375 conn = new PoolingConnection(conn); 376 final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>(); 377 config.setMaxTotalPerKey(-1); 378 config.setBlockWhenExhausted(false); 379 config.setMaxWaitMillis(0); 380 config.setMaxIdlePerKey(1); 381 config.setMaxTotal(maxOpenPreparedStatements); 382 if (dataSourceJmxObjectName != null) { 383 final StringBuilder base = new StringBuilder(dataSourceJmxObjectName.toString()); 384 base.append(Constants.JMX_CONNECTION_BASE_EXT); 385 base.append(Long.toString(connIndex)); 386 config.setJmxNameBase(base.toString()); 387 config.setJmxNamePrefix(Constants.JMX_STATEMENT_POOL_PREFIX); 388 } else { 389 config.setJmxEnabled(false); 390 } 391 final PoolingConnection poolingConn = (PoolingConnection) conn; 392 final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>( 393 poolingConn, config); 394 poolingConn.setStatementPool(stmtPool); 395 poolingConn.setCacheState(cacheState); 396 } 397 398 // Register this connection with JMX 399 ObjectName connJmxName; 400 if (dataSourceJmxObjectName == null) { 401 connJmxName = null; 402 } else { 403 connJmxName = new ObjectName( 404 dataSourceJmxObjectName.toString() + Constants.JMX_CONNECTION_BASE_EXT + connIndex); 405 } 406 407 final PoolableConnection pc = new PoolableConnection(conn, pool, connJmxName, disconnectionSqlCodes, 408 fastFailValidation); 409 pc.setCacheState(cacheState); 410 411 return new DefaultPooledObject<>(pc); 412 } 413 414 @Override 415 public void passivateObject(final PooledObject<PoolableConnection> p) throws Exception { 416 417 validateLifetime(p); 418 419 final PoolableConnection conn = p.getObject(); 420 Boolean connAutoCommit = null; 421 if (rollbackOnReturn) { 422 connAutoCommit = Boolean.valueOf(conn.getAutoCommit()); 423 if (!connAutoCommit.booleanValue() && !conn.isReadOnly()) { 424 conn.rollback(); 425 } 426 } 427 428 conn.clearWarnings(); 429 430 // DBCP-97 / DBCP-399 / DBCP-351 Idle connections in the pool should 431 // have autoCommit enabled 432 if (autoCommitOnReturn) { 433 if (connAutoCommit == null) { 434 connAutoCommit = Boolean.valueOf(conn.getAutoCommit()); 435 } 436 if (!connAutoCommit.booleanValue()) { 437 conn.setAutoCommit(true); 438 } 439 } 440 441 conn.passivate(); 442 } 443 444 public void setAutoCommitOnReturn(final boolean autoCommitOnReturn) { 445 this.autoCommitOnReturn = autoCommitOnReturn; 446 } 447 448 public void setCacheState(final boolean cacheState) { 449 this.cacheState = cacheState; 450 } 451 452 /** 453 * Sets the SQL statements I use to initialize newly created {@link Connection}s. Using {@code null} turns off 454 * connection initialization. 455 * 456 * @param connectionInitSqls 457 * SQL statement to initialize {@link Connection}s. 458 */ 459 public void setConnectionInitSql(final Collection<String> connectionInitSqls) { 460 this.connectionInitSqls = connectionInitSqls; 461 } 462 463 /** 464 * Sets the default "auto commit" setting for borrowed {@link Connection}s 465 * 466 * @param defaultAutoCommit 467 * the default "auto commit" setting for borrowed {@link Connection}s 468 */ 469 public void setDefaultAutoCommit(final Boolean defaultAutoCommit) { 470 this.defaultAutoCommit = defaultAutoCommit; 471 } 472 473 /** 474 * Sets the default "catalog" setting for borrowed {@link Connection}s 475 * 476 * @param defaultCatalog 477 * the default "catalog" setting for borrowed {@link Connection}s 478 */ 479 public void setDefaultCatalog(final String defaultCatalog) { 480 this.defaultCatalog = defaultCatalog; 481 } 482 483 public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) { 484 this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds; 485 } 486 /** 487 * Sets the default "read only" setting for borrowed {@link Connection}s 488 * 489 * @param defaultReadOnly 490 * the default "read only" setting for borrowed {@link Connection}s 491 */ 492 public void setDefaultReadOnly(final Boolean defaultReadOnly) { 493 this.defaultReadOnly = defaultReadOnly; 494 } 495 496 /** 497 * Sets the default "schema" setting for borrowed {@link Connection}s 498 * 499 * @param defaultSchema 500 * the default "schema" setting for borrowed {@link Connection}s 501 * @since 2.5.0 502 */ 503 public void setDefaultSchema(final String defaultSchema) { 504 this.defaultSchema = defaultSchema; 505 } 506 507 /** 508 * Sets the default "Transaction Isolation" setting for borrowed {@link Connection}s 509 * 510 * @param defaultTransactionIsolation 511 * the default "Transaction Isolation" setting for returned {@link Connection}s 512 */ 513 public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) { 514 this.defaultTransactionIsolation = defaultTransactionIsolation; 515 } 516 517 /** 518 * @param disconnectionSqlCodes 519 * The disconnection SQL codes. 520 * @see #getDisconnectionSqlCodes() 521 * @since 2.1 522 */ 523 public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) { 524 this.disconnectionSqlCodes = disconnectionSqlCodes; 525 } 526 527 /** 528 * @param autoCommitOnReturn Whether to auto-commit on return. 529 * @deprecated Use {@link #setAutoCommitOnReturn(boolean)}. 530 */ 531 @Deprecated 532 public void setEnableAutoCommitOnReturn(final boolean autoCommitOnReturn) { 533 this.autoCommitOnReturn = autoCommitOnReturn; 534 } 535 536 /** 537 * @see #isFastFailValidation() 538 * @param fastFailValidation 539 * true means connections created by this factory will fast fail validation 540 * @since 2.1 541 */ 542 public void setFastFailValidation(final boolean fastFailValidation) { 543 this.fastFailValidation = fastFailValidation; 544 } 545 546 /** 547 * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation, 548 * passivation and validation. A value of zero or less indicates an infinite lifetime. The default value is -1. 549 * 550 * @param maxConnLifetimeMillis 551 * The maximum lifetime in milliseconds. 552 */ 553 public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) { 554 this.maxConnLifetimeMillis = maxConnLifetimeMillis; 555 } 556 557 /** 558 * Sets the maximum number of open prepared statements. 559 * 560 * @param maxOpenPreparedStatements 561 * The maximum number of open prepared statements. 562 */ 563 public void setMaxOpenPreparedStatements(final int maxOpenPreparedStatements) { 564 this.maxOpenPreparedStatements = maxOpenPreparedStatements; 565 } 566 567 /** 568 * Deprecated due to typo in method name. 569 * 570 * @param maxOpenPreparedStatements 571 * The maximum number of open prepared statements. 572 * @deprecated Use {@link #setMaxOpenPreparedStatements(int)}. 573 */ 574 @Deprecated // Due to typo in method name. 575 public void setMaxOpenPrepatedStatements(final int maxOpenPreparedStatements) { 576 setMaxOpenPreparedStatements(maxOpenPreparedStatements); 577 } 578 579 /** 580 * Sets the {@link ObjectPool} in which to pool {@link Connection}s. 581 * 582 * @param pool 583 * the {@link ObjectPool} in which to pool those {@link Connection}s 584 */ 585 public synchronized void setPool(final ObjectPool<PoolableConnection> pool) { 586 if (null != this.pool && pool != this.pool) { 587 try { 588 this.pool.close(); 589 } catch (final Exception e) { 590 // ignored !?! 591 } 592 } 593 this.pool = pool; 594 } 595 596 public void setPoolStatements(final boolean poolStatements) { 597 this.poolStatements = poolStatements; 598 } 599 600 public void setRollbackOnReturn(final boolean rollbackOnReturn) { 601 this.rollbackOnReturn = rollbackOnReturn; 602 } 603 604 /** 605 * Sets the query I use to {@link #validateObject validate} {@link Connection}s. Should return at least one row. If 606 * not specified, {@link Connection#isValid(int)} will be used to validate connections. 607 * 608 * @param validationQuery 609 * a query to use to {@link #validateObject validate} {@link Connection}s. 610 */ 611 public void setValidationQuery(final String validationQuery) { 612 this.validationQuery = validationQuery; 613 } 614 615 /** 616 * Sets the validation query timeout, the amount of time, in seconds, that connection validation will wait for a 617 * response from the database when executing a validation query. Use a value less than or equal to 0 for no timeout. 618 * 619 * @param validationQueryTimeoutSeconds 620 * new validation query timeout value in seconds 621 */ 622 public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) { 623 this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds; 624 } 625 626 public void validateConnection(final PoolableConnection conn) throws SQLException { 627 if (conn.isClosed()) { 628 throw new SQLException("validateConnection: connection closed"); 629 } 630 conn.validate(validationQuery, validationQueryTimeoutSeconds); 631 } 632 633 private void validateLifetime(final PooledObject<PoolableConnection> p) throws Exception { 634 if (maxConnLifetimeMillis > 0) { 635 final long lifetime = System.currentTimeMillis() - p.getCreateTime(); 636 if (lifetime > maxConnLifetimeMillis) { 637 throw new LifetimeExceededException(Utils.getMessage("connectionFactory.lifetimeExceeded", 638 Long.valueOf(lifetime), Long.valueOf(maxConnLifetimeMillis))); 639 } 640 } 641 } 642 643 @Override 644 public boolean validateObject(final PooledObject<PoolableConnection> p) { 645 try { 646 validateLifetime(p); 647 648 validateConnection(p.getObject()); 649 return true; 650 } catch (final Exception e) { 651 if (log.isDebugEnabled()) { 652 log.debug(Utils.getMessage("poolableConnectionFactory.validateObject.fail"), e); 653 } 654 return false; 655 } 656 } 657}