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