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.datasources; 019 020import java.io.OutputStreamWriter; 021import java.io.PrintWriter; 022import java.io.Serializable; 023import java.nio.charset.StandardCharsets; 024import java.sql.Connection; 025import java.sql.SQLException; 026import java.sql.SQLFeatureNotSupportedException; 027import java.util.NoSuchElementException; 028import java.util.Properties; 029import java.util.logging.Logger; 030 031import javax.naming.Context; 032import javax.naming.InitialContext; 033import javax.naming.Referenceable; 034import javax.sql.ConnectionPoolDataSource; 035import javax.sql.DataSource; 036import javax.sql.PooledConnection; 037 038import org.apache.commons.pool2.impl.BaseObjectPoolConfig; 039import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; 040 041/** 042 * <p>The base class for <code>SharedPoolDataSource</code> and 043 * <code>PerUserPoolDataSource</code>. Many of the configuration properties 044 * are shared and defined here. This class is declared public in order 045 * to allow particular usage with commons-beanutils; do not make direct 046 * use of it outside of <em>commons-dbcp2</em>. 047 * </p> 048 * 049 * <p> 050 * A J2EE container will normally provide some method of initializing the 051 * <code>DataSource</code> whose attributes are presented 052 * as bean getters/setters and then deploying it via JNDI. It is then 053 * available to an application as a source of pooled logical connections to 054 * the database. The pool needs a source of physical connections. This 055 * source is in the form of a <code>ConnectionPoolDataSource</code> that 056 * can be specified via the {@link #setDataSourceName(String)} used to 057 * lookup the source via JNDI. 058 * </p> 059 * 060 * <p> 061 * Although normally used within a JNDI environment, A DataSource 062 * can be instantiated and initialized as any bean. In this case the 063 * <code>ConnectionPoolDataSource</code> will likely be instantiated in 064 * a similar manner. This class allows the physical source of connections 065 * to be attached directly to this pool using the 066 * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method. 067 * </p> 068 * 069 * <p> 070 * The dbcp package contains an adapter, 071 * {@link org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS}, 072 * that can be used to allow the use of <code>DataSource</code>'s based on this 073 * class with JDBC driver implementations that do not supply a 074 * <code>ConnectionPoolDataSource</code>, but still 075 * provide a {@link java.sql.Driver} implementation. 076 * </p> 077 * 078 * <p> 079 * The <a href="package-summary.html">package documentation</a> contains an 080 * example using Apache Tomcat and JNDI and it also contains a non-JNDI example. 081 * </p> 082 * 083 * @author John D. McNally 084 * @since 2.0 085 */ 086public abstract class InstanceKeyDataSource 087 implements DataSource, Referenceable, Serializable, AutoCloseable { 088 089 private static final long serialVersionUID = -6819270431752240878L; 090 091 private static final String GET_CONNECTION_CALLED 092 = "A Connection was already requested from this source, " 093 + "further initialization is not allowed."; 094 private static final String BAD_TRANSACTION_ISOLATION 095 = "The requested TransactionIsolation level is invalid."; 096 097 /** 098 * Internal constant to indicate the level is not set. 099 */ 100 protected static final int UNKNOWN_TRANSACTIONISOLATION = -1; 101 102 /** Guards property setters - once true, setters throw IllegalStateException */ 103 private volatile boolean getConnectionCalled = false; 104 105 /** Underlying source of PooledConnections */ 106 private ConnectionPoolDataSource dataSource = null; 107 108 /** DataSource Name used to find the ConnectionPoolDataSource */ 109 private String dataSourceName = null; 110 111 /** Description */ 112 private String description = null; 113 114 /** Environment that may be used to set up a jndi initial context. */ 115 private Properties jndiEnvironment = null; 116 117 /** Login TimeOut in seconds */ 118 private int loginTimeout = 0; 119 120 /** Log stream */ 121 private PrintWriter logWriter = null; 122 123 /** Instance key */ 124 private String instanceKey = null; 125 126 // Pool properties 127 private boolean defaultBlockWhenExhausted = 128 BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED; 129 private String defaultEvictionPolicyClassName = 130 BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME; 131 private boolean defaultLifo = BaseObjectPoolConfig.DEFAULT_LIFO; 132 private int defaultMaxIdle = 133 GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY; 134 private int defaultMaxTotal = 135 GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; 136 private long defaultMaxWaitMillis = 137 BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS; 138 private long defaultMinEvictableIdleTimeMillis = 139 BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; 140 private int defaultMinIdle = 141 GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY; 142 private int defaultNumTestsPerEvictionRun = 143 BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; 144 private long defaultSoftMinEvictableIdleTimeMillis = 145 BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS; 146 private boolean defaultTestOnCreate = 147 BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE; 148 private boolean defaultTestOnBorrow = 149 BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW; 150 private boolean defaultTestOnReturn = 151 BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN; 152 private boolean defaultTestWhileIdle = 153 BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE; 154 private long defaultTimeBetweenEvictionRunsMillis = 155 BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; 156 157 // Connection factory properties 158 private String validationQuery = null; 159 private int validationQueryTimeout = -1; 160 private boolean rollbackAfterValidation = false; 161 private long maxConnLifetimeMillis = -1; 162 163 // Connection properties 164 private Boolean defaultAutoCommit = null; 165 private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION; 166 private Boolean defaultReadOnly = null; 167 168 169 /** 170 * Default no-arg constructor for Serialization 171 */ 172 public InstanceKeyDataSource() { 173 } 174 175 /** 176 * Throws an IllegalStateException, if a PooledConnection has already 177 * been requested. 178 */ 179 protected void assertInitializationAllowed() 180 throws IllegalStateException { 181 if (getConnectionCalled) { 182 throw new IllegalStateException(GET_CONNECTION_CALLED); 183 } 184 } 185 186 /** 187 * Close the connection pool being maintained by this datasource. 188 */ 189 @Override 190 public abstract void close() throws Exception; 191 192 protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey); 193 194 /* JDBC_4_ANT_KEY_BEGIN */ 195 @Override 196 public boolean isWrapperFor(final Class<?> iface) throws SQLException { 197 return false; 198 } 199 200 @Override 201 public <T> T unwrap(final Class<T> iface) throws SQLException { 202 throw new SQLException("InstanceKeyDataSource is not a wrapper."); 203 } 204 /* JDBC_4_ANT_KEY_END */ 205 206 @Override 207 public Logger getParentLogger() throws SQLFeatureNotSupportedException { 208 throw new SQLFeatureNotSupportedException(); 209 } 210 211 212 // ------------------------------------------------------------------- 213 // Properties 214 215 /** 216 * Gets the default value for 217 * {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per 218 * user pool. 219 */ 220 public boolean getDefaultBlockWhenExhausted() { 221 return this.defaultBlockWhenExhausted; 222 } 223 224 /** 225 * Sets the default value for 226 * {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per 227 * user pool. 228 */ 229 public void setDefaultBlockWhenExhausted(final boolean blockWhenExhausted) { 230 assertInitializationAllowed(); 231 this.defaultBlockWhenExhausted = blockWhenExhausted; 232 } 233 234 /** 235 * Gets the default value for 236 * {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for 237 * each per user pool. 238 */ 239 public String getDefaultEvictionPolicyClassName() { 240 return this.defaultEvictionPolicyClassName; 241 } 242 243 /** 244 * Sets the default value for 245 * {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for 246 * each per user pool. 247 */ 248 public void setDefaultEvictionPolicyClassName( 249 final String evictionPolicyClassName) { 250 assertInitializationAllowed(); 251 this.defaultEvictionPolicyClassName = evictionPolicyClassName; 252 } 253 254 /** 255 * Gets the default value for 256 * {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool. 257 */ 258 public boolean getDefaultLifo() { 259 return this.defaultLifo; 260 } 261 262 /** 263 * Sets the default value for 264 * {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool. 265 */ 266 public void setDefaultLifo(final boolean lifo) { 267 assertInitializationAllowed(); 268 this.defaultLifo = lifo; 269 } 270 271 /** 272 * Gets the default value for 273 * {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user 274 * pool. 275 */ 276 public int getDefaultMaxIdle() { 277 return this.defaultMaxIdle; 278 } 279 280 /** 281 * Sets the default value for 282 * {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user 283 * pool. 284 */ 285 public void setDefaultMaxIdle(final int maxIdle) { 286 assertInitializationAllowed(); 287 this.defaultMaxIdle = maxIdle; 288 } 289 290 /** 291 * Gets the default value for 292 * {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per 293 * user pool. 294 */ 295 public int getDefaultMaxTotal() { 296 return this.defaultMaxTotal; 297 } 298 299 /** 300 * Sets the default value for 301 * {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per 302 * user pool. 303 */ 304 public void setDefaultMaxTotal(final int maxTotal) { 305 assertInitializationAllowed(); 306 this.defaultMaxTotal = maxTotal; 307 } 308 309 /** 310 * Gets the default value for 311 * {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user 312 * pool. 313 */ 314 public long getDefaultMaxWaitMillis() { 315 return this.defaultMaxWaitMillis; 316 } 317 318 /** 319 * Sets the default value for 320 * {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user 321 * pool. 322 */ 323 public void setDefaultMaxWaitMillis(final long maxWaitMillis) { 324 assertInitializationAllowed(); 325 this.defaultMaxWaitMillis = maxWaitMillis; 326 } 327 328 /** 329 * Gets the default value for 330 * {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for 331 * each per user pool. 332 */ 333 public long getDefaultMinEvictableIdleTimeMillis() { 334 return this.defaultMinEvictableIdleTimeMillis; 335 } 336 337 /** 338 * Sets the default value for 339 * {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for 340 * each per user pool. 341 */ 342 public void setDefaultMinEvictableIdleTimeMillis( 343 final long minEvictableIdleTimeMillis) { 344 assertInitializationAllowed(); 345 this.defaultMinEvictableIdleTimeMillis = minEvictableIdleTimeMillis; 346 } 347 348 /** 349 * Gets the default value for 350 * {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user 351 * pool. 352 */ 353 public int getDefaultMinIdle() { 354 return this.defaultMinIdle; 355 } 356 357 /** 358 * Sets the default value for 359 * {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user 360 * pool. 361 */ 362 public void setDefaultMinIdle(final int minIdle) { 363 assertInitializationAllowed(); 364 this.defaultMinIdle = minIdle; 365 } 366 367 /** 368 * Gets the default value for 369 * {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each 370 * per user pool. 371 */ 372 public int getDefaultNumTestsPerEvictionRun() { 373 return this.defaultNumTestsPerEvictionRun; 374 } 375 376 /** 377 * Sets the default value for 378 * {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each 379 * per user pool. 380 */ 381 public void setDefaultNumTestsPerEvictionRun(final int numTestsPerEvictionRun) { 382 assertInitializationAllowed(); 383 this.defaultNumTestsPerEvictionRun = numTestsPerEvictionRun; 384 } 385 386 /** 387 * Gets the default value for 388 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each 389 * per user pool. 390 */ 391 public long getDefaultSoftMinEvictableIdleTimeMillis() { 392 return this.defaultSoftMinEvictableIdleTimeMillis; 393 } 394 395 /** 396 * Sets the default value for 397 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool. 398 */ 399 public void setDefaultSoftMinEvictableIdleTimeMillis( 400 final long softMinEvictableIdleTimeMillis) { 401 assertInitializationAllowed(); 402 this.defaultSoftMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; 403 } 404 405 /** 406 * Gets the default value for 407 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnCreate()} for each per user pool. 408 */ 409 public boolean getDefaultTestOnCreate() { 410 return this.defaultTestOnCreate; 411 } 412 413 /** 414 * Sets the default value for 415 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnCreate()} for each per user pool. 416 */ 417 public void setDefaultTestOnCreate(final boolean testOnCreate) { 418 assertInitializationAllowed(); 419 this.defaultTestOnCreate = testOnCreate; 420 } 421 422 /** 423 * Gets the default value for 424 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnBorrow()} for each per user pool. 425 */ 426 public boolean getDefaultTestOnBorrow() { 427 return this.defaultTestOnBorrow; 428 } 429 430 /** 431 * Sets the default value for 432 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnBorrow()} for each per user pool. 433 */ 434 public void setDefaultTestOnBorrow(final boolean testOnBorrow) { 435 assertInitializationAllowed(); 436 this.defaultTestOnBorrow = testOnBorrow; 437 } 438 439 /** 440 * Gets the default value for 441 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnReturn()} for each per user pool. 442 */ 443 public boolean getDefaultTestOnReturn() { 444 return this.defaultTestOnReturn; 445 } 446 447 /** 448 * Sets the default value for 449 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnReturn()} for each per user pool. 450 */ 451 public void setDefaultTestOnReturn(final boolean testOnReturn) { 452 assertInitializationAllowed(); 453 this.defaultTestOnReturn = testOnReturn; 454 } 455 456 /** 457 * Gets the default value for 458 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTestWhileIdle()} for each per user pool. 459 */ 460 public boolean getDefaultTestWhileIdle() { 461 return this.defaultTestWhileIdle; 462 } 463 464 /** 465 * Sets the default value for 466 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTestWhileIdle()} for each per user pool. 467 */ 468 public void setDefaultTestWhileIdle(final boolean testWhileIdle) { 469 assertInitializationAllowed(); 470 this.defaultTestWhileIdle = testWhileIdle; 471 } 472 473 /** 474 * Gets the default value for 475 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each 476 * per user pool. 477 */ 478 public long getDefaultTimeBetweenEvictionRunsMillis () { 479 return this.defaultTimeBetweenEvictionRunsMillis ; 480 } 481 482 /** 483 * Sets the default value for 484 * {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each 485 * per user pool. 486 */ 487 public void setDefaultTimeBetweenEvictionRunsMillis ( 488 final long timeBetweenEvictionRunsMillis ) { 489 assertInitializationAllowed(); 490 this.defaultTimeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis ; 491 } 492 493 /** 494 * Get the value of connectionPoolDataSource. This method will return 495 * null, if the backing datasource is being accessed via jndi. 496 * 497 * @return value of connectionPoolDataSource. 498 */ 499 public ConnectionPoolDataSource getConnectionPoolDataSource() { 500 return dataSource; 501 } 502 503 /** 504 * Set the backend ConnectionPoolDataSource. This property should not be 505 * set if using jndi to access the datasource. 506 * 507 * @param v Value to assign to connectionPoolDataSource. 508 */ 509 public void setConnectionPoolDataSource(final ConnectionPoolDataSource v) { 510 assertInitializationAllowed(); 511 if (dataSourceName != null) { 512 throw new IllegalStateException( 513 "Cannot set the DataSource, if JNDI is used."); 514 } 515 if (dataSource != null) 516 { 517 throw new IllegalStateException( 518 "The CPDS has already been set. It cannot be altered."); 519 } 520 dataSource = v; 521 instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this); 522 } 523 524 /** 525 * Get the name of the ConnectionPoolDataSource which backs this pool. 526 * This name is used to look up the datasource from a jndi service 527 * provider. 528 * 529 * @return value of dataSourceName. 530 */ 531 public String getDataSourceName() { 532 return dataSourceName; 533 } 534 535 /** 536 * Set the name of the ConnectionPoolDataSource which backs this pool. 537 * This name is used to look up the datasource from a jndi service 538 * provider. 539 * 540 * @param v Value to assign to dataSourceName. 541 */ 542 public void setDataSourceName(final String v) { 543 assertInitializationAllowed(); 544 if (dataSource != null) { 545 throw new IllegalStateException( 546 "Cannot set the JNDI name for the DataSource, if already " + 547 "set using setConnectionPoolDataSource."); 548 } 549 if (dataSourceName != null) 550 { 551 throw new IllegalStateException( 552 "The DataSourceName has already been set. " + 553 "It cannot be altered."); 554 } 555 this.dataSourceName = v; 556 instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this); 557 } 558 559 /** 560 * Get the value of defaultAutoCommit, which defines the state of 561 * connections handed out from this pool. The value can be changed 562 * on the Connection using Connection.setAutoCommit(boolean). 563 * The default is <code>null</code> which will use the default value for the 564 * drive. 565 * 566 * @return value of defaultAutoCommit. 567 */ 568 public Boolean isDefaultAutoCommit() { 569 return defaultAutoCommit; 570 } 571 572 /** 573 * Set the value of defaultAutoCommit, which defines the state of 574 * connections handed out from this pool. The value can be changed 575 * on the Connection using Connection.setAutoCommit(boolean). 576 * The default is <code>null</code> which will use the default value for the 577 * drive. 578 * 579 * @param v Value to assign to defaultAutoCommit. 580 */ 581 public void setDefaultAutoCommit(final Boolean v) { 582 assertInitializationAllowed(); 583 this.defaultAutoCommit = v; 584 } 585 586 /** 587 * Get the value of defaultReadOnly, which defines the state of 588 * connections handed out from this pool. The value can be changed 589 * on the Connection using Connection.setReadOnly(boolean). 590 * The default is <code>null</code> which will use the default value for the 591 * drive. 592 * 593 * @return value of defaultReadOnly. 594 */ 595 public Boolean isDefaultReadOnly() { 596 return defaultReadOnly; 597 } 598 599 /** 600 * Set the value of defaultReadOnly, which defines the state of 601 * connections handed out from this pool. The value can be changed 602 * on the Connection using Connection.setReadOnly(boolean). 603 * The default is <code>null</code> which will use the default value for the 604 * drive. 605 * 606 * @param v Value to assign to defaultReadOnly. 607 */ 608 public void setDefaultReadOnly(final Boolean v) { 609 assertInitializationAllowed(); 610 this.defaultReadOnly = v; 611 } 612 613 /** 614 * Get the value of defaultTransactionIsolation, which defines the state of 615 * connections handed out from this pool. The value can be changed 616 * on the Connection using Connection.setTransactionIsolation(int). 617 * If this method returns -1, the default is JDBC driver dependent. 618 * 619 * @return value of defaultTransactionIsolation. 620 */ 621 public int getDefaultTransactionIsolation() { 622 return defaultTransactionIsolation; 623 } 624 625 /** 626 * Set the value of defaultTransactionIsolation, which defines the state of 627 * connections handed out from this pool. The value can be changed 628 * on the Connection using Connection.setTransactionIsolation(int). 629 * The default is JDBC driver dependent. 630 * 631 * @param v Value to assign to defaultTransactionIsolation 632 */ 633 public void setDefaultTransactionIsolation(final int v) { 634 assertInitializationAllowed(); 635 switch (v) { 636 case Connection.TRANSACTION_NONE: 637 case Connection.TRANSACTION_READ_COMMITTED: 638 case Connection.TRANSACTION_READ_UNCOMMITTED: 639 case Connection.TRANSACTION_REPEATABLE_READ: 640 case Connection.TRANSACTION_SERIALIZABLE: 641 break; 642 default: 643 throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION); 644 } 645 this.defaultTransactionIsolation = v; 646 } 647 648 /** 649 * Get the description. This property is defined by JDBC as for use with 650 * GUI (or other) tools that might deploy the datasource. It serves no 651 * internal purpose. 652 * 653 * @return value of description. 654 */ 655 public String getDescription() { 656 return description; 657 } 658 659 /** 660 * Set the description. This property is defined by JDBC as for use with 661 * GUI (or other) tools that might deploy the datasource. It serves no 662 * internal purpose. 663 * 664 * @param v Value to assign to description. 665 */ 666 public void setDescription(final String v) { 667 this.description = v; 668 } 669 670 protected String getInstanceKey() { 671 return instanceKey; 672 } 673 674 /** 675 * Get the value of jndiEnvironment which is used when instantiating 676 * a jndi InitialContext. This InitialContext is used to locate the 677 * backend ConnectionPoolDataSource. 678 * 679 * @return value of jndiEnvironment. 680 */ 681 public String getJndiEnvironment(final String key) { 682 String value = null; 683 if (jndiEnvironment != null) { 684 value = jndiEnvironment.getProperty(key); 685 } 686 return value; 687 } 688 689 /** 690 * Sets the value of the given JNDI environment property to be used when 691 * instantiating a JNDI InitialContext. This InitialContext is used to 692 * locate the backend ConnectionPoolDataSource. 693 * 694 * @param key the JNDI environment property to set. 695 * @param value the value assigned to specified JNDI environment property. 696 */ 697 public void setJndiEnvironment(final String key, final String value) { 698 if (jndiEnvironment == null) { 699 jndiEnvironment = new Properties(); 700 } 701 jndiEnvironment.setProperty(key, value); 702 } 703 704 /** 705 * Sets the JNDI environment to be used when instantiating a JNDI 706 * InitialContext. This InitialContext is used to locate the backend 707 * ConnectionPoolDataSource. 708 * 709 * @param properties the JNDI environment property to set which will 710 * overwrite any current settings 711 */ 712 void setJndiEnvironment(final Properties properties) { 713 if (jndiEnvironment == null) { 714 jndiEnvironment = new Properties(); 715 } else { 716 jndiEnvironment.clear(); 717 } 718 jndiEnvironment.putAll(properties); 719 } 720 721 /** 722 * Get the value of loginTimeout. 723 * @return value of loginTimeout. 724 */ 725 @Override 726 public int getLoginTimeout() { 727 return loginTimeout; 728 } 729 730 /** 731 * Set the value of loginTimeout. 732 * @param v Value to assign to loginTimeout. 733 */ 734 @Override 735 public void setLoginTimeout(final int v) { 736 this.loginTimeout = v; 737 } 738 739 /** 740 * Get the value of logWriter. 741 * @return value of logWriter. 742 */ 743 @Override 744 public PrintWriter getLogWriter() { 745 if (logWriter == null) { 746 logWriter = new PrintWriter( 747 new OutputStreamWriter(System.out, StandardCharsets.UTF_8)); 748 } 749 return logWriter; 750 } 751 752 /** 753 * Set the value of logWriter. 754 * @param v Value to assign to logWriter. 755 */ 756 @Override 757 public void setLogWriter(final PrintWriter v) { 758 this.logWriter = v; 759 } 760 761 /** 762 * The SQL query that will be used to validate connections from this pool 763 * before returning them to the caller. If specified, this query 764 * <strong>MUST</strong> be an SQL SELECT statement that returns at least 765 * one row. If not specified, {@link Connection#isValid(int)} will be used 766 * to validate connections. 767 */ 768 public String getValidationQuery() { 769 return this.validationQuery; 770 } 771 772 /** 773 * The SQL query that will be used to validate connections from this pool 774 * before returning them to the caller. If specified, this query 775 * <strong>MUST</strong> be an SQL SELECT statement that returns at least 776 * one row. If not specified, connections will be validated using 777 * {@link Connection#isValid(int)}. 778 */ 779 public void setValidationQuery(final String validationQuery) { 780 assertInitializationAllowed(); 781 this.validationQuery = validationQuery; 782 } 783 784 /** 785 * Returns the timeout in seconds before the validation query fails. 786 */ 787 public int getValidationQueryTimeout() { 788 return validationQueryTimeout; 789 } 790 791 /** 792 * Sets the timeout in seconds before the validation query fails. 793 * 794 * @param validationQueryTimeout The new timeout in seconds 795 */ 796 public void setValidationQueryTimeout(final int validationQueryTimeout) { 797 this.validationQueryTimeout = validationQueryTimeout; 798 } 799 800 /** 801 * Whether a rollback will be issued after executing the SQL query 802 * that will be used to validate connections from this pool 803 * before returning them to the caller. 804 * 805 * @return true if a rollback will be issued after executing the 806 * validation query 807 */ 808 public boolean isRollbackAfterValidation() { 809 return this.rollbackAfterValidation; 810 } 811 812 /** 813 * Whether a rollback will be issued after executing the SQL query 814 * that will be used to validate connections from this pool 815 * before returning them to the caller. Default behavior is NOT 816 * to issue a rollback. The setting will only have an effect 817 * if a validation query is set 818 * 819 * @param rollbackAfterValidation new property value 820 */ 821 public void setRollbackAfterValidation(final boolean rollbackAfterValidation) { 822 assertInitializationAllowed(); 823 this.rollbackAfterValidation = rollbackAfterValidation; 824 } 825 826 /** 827 * Returns the maximum permitted lifetime of a connection in milliseconds. A 828 * value of zero or less indicates an infinite lifetime. 829 */ 830 public long getMaxConnLifetimeMillis() { 831 return maxConnLifetimeMillis; 832 } 833 834 /** 835 * <p>Sets the maximum permitted lifetime of a connection in 836 * milliseconds. A value of zero or less indicates an infinite lifetime.</p> 837 * <p> 838 * Note: this method currently has no effect once the pool has been 839 * initialized. The pool is initialized the first time one of the 840 * following methods is invoked: <code>getConnection, setLogwriter, 841 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> 842 */ 843 public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) { 844 this.maxConnLifetimeMillis = maxConnLifetimeMillis; 845 } 846 847 // ---------------------------------------------------------------------- 848 // Instrumentation Methods 849 850 // ---------------------------------------------------------------------- 851 // DataSource implementation 852 853 /** 854 * Attempt to establish a database connection. 855 */ 856 @Override 857 public Connection getConnection() throws SQLException { 858 return getConnection(null, null); 859 } 860 861 /** 862 * Attempt to retrieve a database connection using {@link #getPooledConnectionAndInfo(String, String)} 863 * with the provided username and password. The password on the {@link PooledConnectionAndInfo} 864 * instance returned by <code>getPooledConnectionAndInfo</code> is compared to the <code>password</code> 865 * parameter. If the comparison fails, a database connection using the supplied username and password 866 * is attempted. If the connection attempt fails, an SQLException is thrown, indicating that the given password 867 * did not match the password used to create the pooled connection. If the connection attempt succeeds, this 868 * means that the database password has been changed. In this case, the <code>PooledConnectionAndInfo</code> 869 * instance retrieved with the old password is destroyed and the <code>getPooledConnectionAndInfo</code> is 870 * repeatedly invoked until a <code>PooledConnectionAndInfo</code> instance with the new password is returned. 871 * 872 */ 873 @Override 874 public Connection getConnection(final String username, final String password) 875 throws SQLException { 876 if (instanceKey == null) { 877 throw new SQLException("Must set the ConnectionPoolDataSource " 878 + "through setDataSourceName or setConnectionPoolDataSource" 879 + " before calling getConnection."); 880 } 881 getConnectionCalled = true; 882 PooledConnectionAndInfo info = null; 883 try { 884 info = getPooledConnectionAndInfo(username, password); 885 } catch (final NoSuchElementException e) { 886 closeDueToException(info); 887 throw new SQLException("Cannot borrow connection from pool", e); 888 } catch (final RuntimeException e) { 889 closeDueToException(info); 890 throw e; 891 } catch (final SQLException e) { 892 closeDueToException(info); 893 throw e; 894 } catch (final Exception e) { 895 closeDueToException(info); 896 throw new SQLException("Cannot borrow connection from pool", e); 897 } 898 899 if (!(null == password ? null == info.getPassword() 900 : password.equals(info.getPassword()))) { // Password on PooledConnectionAndInfo does not match 901 try { // See if password has changed by attempting connection 902 testCPDS(username, password); 903 } catch (final SQLException ex) { 904 // Password has not changed, so refuse client, but return connection to the pool 905 closeDueToException(info); 906 throw new SQLException("Given password did not match password used" 907 + " to create the PooledConnection.", ex); 908 } catch (final javax.naming.NamingException ne) { 909 throw new SQLException( 910 "NamingException encountered connecting to database", ne); 911 } 912 /* 913 * Password must have changed -> destroy connection and keep retrying until we get a new, good one, 914 * destroying any idle connections with the old password as we pull them from the pool. 915 */ 916 final UserPassKey upkey = info.getUserPassKey(); 917 final PooledConnectionManager manager = getConnectionManager(upkey); 918 manager.invalidate(info.getPooledConnection()); // Destroy and remove from pool 919 manager.setPassword(upkey.getPassword()); // Reset the password on the factory if using CPDSConnectionFactory 920 info = null; 921 for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return 922 try { 923 info = getPooledConnectionAndInfo(username, password); 924 } catch (final NoSuchElementException e) { 925 closeDueToException(info); 926 throw new SQLException("Cannot borrow connection from pool", e); 927 } catch (final RuntimeException e) { 928 closeDueToException(info); 929 throw e; 930 } catch (final SQLException e) { 931 closeDueToException(info); 932 throw e; 933 } catch (final Exception e) { 934 closeDueToException(info); 935 throw new SQLException("Cannot borrow connection from pool", e); 936 } 937 if (info != null && password != null && password.equals(info.getPassword())) { 938 break; 939 } 940 if (info != null) { 941 manager.invalidate(info.getPooledConnection()); 942 } 943 info = null; 944 } 945 if (info == null) { 946 throw new SQLException("Cannot borrow connection from pool - password change failure."); 947 } 948 } 949 950 final Connection con = info.getPooledConnection().getConnection(); 951 try { 952 setupDefaults(con, username); 953 con.clearWarnings(); 954 return con; 955 } catch (final SQLException ex) { 956 try { 957 con.close(); 958 } catch (final Exception exc) { 959 getLogWriter().println( 960 "ignoring exception during close: " + exc); 961 } 962 throw ex; 963 } 964 } 965 966 protected abstract PooledConnectionAndInfo 967 getPooledConnectionAndInfo(String username, String password) 968 throws SQLException; 969 970 protected abstract void setupDefaults(Connection con, String username) 971 throws SQLException; 972 973 974 private void closeDueToException(final PooledConnectionAndInfo info) { 975 if (info != null) { 976 try { 977 info.getPooledConnection().getConnection().close(); 978 } catch (final Exception e) { 979 // do not throw this exception because we are in the middle 980 // of handling another exception. But record it because 981 // it potentially leaks connections from the pool. 982 getLogWriter().println("[ERROR] Could not return connection to " 983 + "pool during exception handling. " + e.getMessage()); 984 } 985 } 986 } 987 988 protected ConnectionPoolDataSource 989 testCPDS(final String username, final String password) 990 throws javax.naming.NamingException, SQLException { 991 // The source of physical db connections 992 ConnectionPoolDataSource cpds = this.dataSource; 993 if (cpds == null) { 994 Context ctx = null; 995 if (jndiEnvironment == null) { 996 ctx = new InitialContext(); 997 } else { 998 ctx = new InitialContext(jndiEnvironment); 999 } 1000 final Object ds = ctx.lookup(dataSourceName); 1001 if (ds instanceof ConnectionPoolDataSource) { 1002 cpds = (ConnectionPoolDataSource) ds; 1003 } else { 1004 throw new SQLException("Illegal configuration: " 1005 + "DataSource " + dataSourceName 1006 + " (" + ds.getClass().getName() + ")" 1007 + " doesn't implement javax.sql.ConnectionPoolDataSource"); 1008 } 1009 } 1010 1011 // try to get a connection with the supplied username/password 1012 PooledConnection conn = null; 1013 try { 1014 if (username != null) { 1015 conn = cpds.getPooledConnection(username, password); 1016 } 1017 else { 1018 conn = cpds.getPooledConnection(); 1019 } 1020 if (conn == null) { 1021 throw new SQLException( 1022 "Cannot connect using the supplied username/password"); 1023 } 1024 } 1025 finally { 1026 if (conn != null) { 1027 try { 1028 conn.close(); 1029 } 1030 catch (final SQLException e) { 1031 // at least we could connect 1032 } 1033 } 1034 } 1035 return cpds; 1036 } 1037}