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