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 018 package org.apache.commons.dbcp.datasources; 019 020 import java.io.Serializable; 021 import java.io.PrintWriter; 022 import java.sql.Connection; 023 import java.sql.SQLException; 024 import java.util.NoSuchElementException; 025 import java.util.Properties; 026 027 import javax.naming.Context; 028 import javax.naming.InitialContext; 029 import javax.naming.NamingException; 030 import javax.naming.Reference; 031 import javax.naming.StringRefAddr; 032 import javax.naming.Referenceable; 033 import javax.sql.ConnectionPoolDataSource; 034 import javax.sql.DataSource; 035 import javax.sql.PooledConnection; 036 037 import org.apache.commons.dbcp.SQLNestedException; 038 import org.apache.commons.pool.impl.GenericObjectPool; 039 040 /** 041 * <p>The base class for <code>SharedPoolDataSource</code> and 042 * <code>PerUserPoolDataSource</code>. Many of the configuration properties 043 * are shared and defined here. This class is declared public in order 044 * to allow particular usage with commons-beanutils; do not make direct 045 * use of it outside of commons-dbcp. 046 * </p> 047 * 048 * <p> 049 * A J2EE container will normally provide some method of initializing the 050 * <code>DataSource</code> whose attributes are presented 051 * as bean getters/setters and then deploying it via JNDI. It is then 052 * available to an application as a source of pooled logical connections to 053 * the database. The pool needs a source of physical connections. This 054 * source is in the form of a <code>ConnectionPoolDataSource</code> that 055 * can be specified via the {@link #setDataSourceName(String)} used to 056 * lookup the source via JNDI. 057 * </p> 058 * 059 * <p> 060 * Although normally used within a JNDI environment, A DataSource 061 * can be instantiated and initialized as any bean. In this case the 062 * <code>ConnectionPoolDataSource</code> will likely be instantiated in 063 * a similar manner. This class allows the physical source of connections 064 * to be attached directly to this pool using the 065 * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method. 066 * </p> 067 * 068 * <p> 069 * The dbcp package contains an adapter, 070 * {@link org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS}, 071 * that can be used to allow the use of <code>DataSource</code>'s based on this 072 * class with jdbc driver implementations that do not supply a 073 * <code>ConnectionPoolDataSource</code>, but still 074 * provide a {@link java.sql.Driver} implementation. 075 * </p> 076 * 077 * <p> 078 * The <a href="package-summary.html">package documentation</a> contains an 079 * example using Apache Tomcat and JNDI and it also contains a non-JNDI example. 080 * </p> 081 * 082 * @author John D. McNally 083 * @version $Revision: 892307 $ $Date: 2013-12-31 23:27:28 +0000 (Tue, 31 Dec 2013) $ 084 */ 085 public abstract class InstanceKeyDataSource 086 implements DataSource, Referenceable, Serializable { 087 private static final long serialVersionUID = -4243533936955098795L; 088 private static final String GET_CONNECTION_CALLED 089 = "A Connection was already requested from this source, " 090 + "further initialization is not allowed."; 091 private static final String BAD_TRANSACTION_ISOLATION 092 = "The requested TransactionIsolation level is invalid."; 093 /** 094 * Internal constant to indicate the level is not set. 095 */ 096 protected static final int UNKNOWN_TRANSACTIONISOLATION = -1; 097 098 /** Guards property setters - once true, setters throw IllegalStateException */ 099 private volatile boolean getConnectionCalled = false; 100 101 /** Underlying source of PooledConnections */ 102 private ConnectionPoolDataSource dataSource = null; 103 104 /** DataSource Name used to find the ConnectionPoolDataSource */ 105 private String dataSourceName = null; 106 107 // Default connection properties 108 private boolean defaultAutoCommit = false; 109 private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION; 110 private boolean defaultReadOnly = false; 111 112 /** Description */ 113 private String description = null; 114 115 /** Environment that may be used to set up a jndi initial context. */ 116 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 // Pool properties 125 private boolean _testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW; 126 private boolean _testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN; 127 private int _timeBetweenEvictionRunsMillis = (int) 128 Math.min(Integer.MAX_VALUE, 129 GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS); 130 private int _numTestsPerEvictionRun = 131 GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; 132 private int _minEvictableIdleTimeMillis = (int) 133 Math.min(Integer.MAX_VALUE, 134 GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS); 135 private boolean _testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE; 136 private String validationQuery = null; 137 private boolean rollbackAfterValidation = false; 138 139 /** true iff one of the setters for testOnBorrow, testOnReturn, testWhileIdle has been called. */ 140 private boolean testPositionSet = false; 141 142 /** Instance key */ 143 protected String instanceKey = null; 144 145 /** 146 * Default no-arg constructor for Serialization 147 */ 148 public InstanceKeyDataSource() { 149 defaultAutoCommit = true; 150 } 151 152 /** 153 * Throws an IllegalStateException, if a PooledConnection has already 154 * been requested. 155 */ 156 protected void assertInitializationAllowed() 157 throws IllegalStateException { 158 if (getConnectionCalled) { 159 throw new IllegalStateException(GET_CONNECTION_CALLED); 160 } 161 } 162 163 /** 164 * Close the connection pool being maintained by this datasource. 165 */ 166 public abstract void close() throws Exception; 167 168 protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey); 169 170 /* 171 public boolean isWrapperFor(Class<?> iface) throws SQLException { 172 return false; 173 } 174 175 public <T> T unwrap(Class<T> iface) throws SQLException { 176 throw new SQLException("InstanceKeyDataSource is not a wrapper."); 177 } 178 */ 179 180 // ------------------------------------------------------------------- 181 // Properties 182 183 /** 184 * Get the value of connectionPoolDataSource. This method will return 185 * null, if the backing datasource is being accessed via jndi. 186 * 187 * @return value of connectionPoolDataSource. 188 */ 189 public ConnectionPoolDataSource getConnectionPoolDataSource() { 190 return dataSource; 191 } 192 193 /** 194 * Set the backend ConnectionPoolDataSource. This property should not be 195 * set if using jndi to access the datasource. 196 * 197 * @param v Value to assign to connectionPoolDataSource. 198 */ 199 public void setConnectionPoolDataSource(ConnectionPoolDataSource v) { 200 assertInitializationAllowed(); 201 if (dataSourceName != null) { 202 throw new IllegalStateException( 203 "Cannot set the DataSource, if JNDI is used."); 204 } 205 if (dataSource != null) 206 { 207 throw new IllegalStateException( 208 "The CPDS has already been set. It cannot be altered."); 209 } 210 dataSource = v; 211 instanceKey = InstanceKeyObjectFactory.registerNewInstance(this); 212 } 213 214 /** 215 * Get the name of the ConnectionPoolDataSource which backs this pool. 216 * This name is used to look up the datasource from a jndi service 217 * provider. 218 * 219 * @return value of dataSourceName. 220 */ 221 public String getDataSourceName() { 222 return dataSourceName; 223 } 224 225 /** 226 * Set the name of the ConnectionPoolDataSource which backs this pool. 227 * This name is used to look up the datasource from a jndi service 228 * provider. 229 * 230 * @param v Value to assign to dataSourceName. 231 */ 232 public void setDataSourceName(String v) { 233 assertInitializationAllowed(); 234 if (dataSource != null) { 235 throw new IllegalStateException( 236 "Cannot set the JNDI name for the DataSource, if already " + 237 "set using setConnectionPoolDataSource."); 238 } 239 if (dataSourceName != null) 240 { 241 throw new IllegalStateException( 242 "The DataSourceName has already been set. " + 243 "It cannot be altered."); 244 } 245 this.dataSourceName = v; 246 instanceKey = InstanceKeyObjectFactory.registerNewInstance(this); 247 } 248 249 /** 250 * Get the value of defaultAutoCommit, which defines the state of 251 * connections handed out from this pool. The value can be changed 252 * on the Connection using Connection.setAutoCommit(boolean). 253 * The default is true. 254 * 255 * @return value of defaultAutoCommit. 256 */ 257 public boolean isDefaultAutoCommit() { 258 return defaultAutoCommit; 259 } 260 261 /** 262 * Set the value of defaultAutoCommit, which defines the state of 263 * connections handed out from this pool. The value can be changed 264 * on the Connection using Connection.setAutoCommit(boolean). 265 * The default is true. 266 * 267 * @param v Value to assign to defaultAutoCommit. 268 */ 269 public void setDefaultAutoCommit(boolean v) { 270 assertInitializationAllowed(); 271 this.defaultAutoCommit = v; 272 } 273 274 /** 275 * Get the value of defaultReadOnly, which defines the state of 276 * connections handed out from this pool. The value can be changed 277 * on the Connection using Connection.setReadOnly(boolean). 278 * The default is false. 279 * 280 * @return value of defaultReadOnly. 281 */ 282 public boolean isDefaultReadOnly() { 283 return defaultReadOnly; 284 } 285 286 /** 287 * Set the value of defaultReadOnly, which defines the state of 288 * connections handed out from this pool. The value can be changed 289 * on the Connection using Connection.setReadOnly(boolean). 290 * The default is false. 291 * 292 * @param v Value to assign to defaultReadOnly. 293 */ 294 public void setDefaultReadOnly(boolean v) { 295 assertInitializationAllowed(); 296 this.defaultReadOnly = v; 297 } 298 299 /** 300 * Get the value of defaultTransactionIsolation, which defines the state of 301 * connections handed out from this pool. The value can be changed 302 * on the Connection using Connection.setTransactionIsolation(int). 303 * If this method returns -1, the default is JDBC driver dependent. 304 * 305 * @return value of defaultTransactionIsolation. 306 */ 307 public int getDefaultTransactionIsolation() { 308 return defaultTransactionIsolation; 309 } 310 311 /** 312 * Set the value of defaultTransactionIsolation, which defines the state of 313 * connections handed out from this pool. The value can be changed 314 * on the Connection using Connection.setTransactionIsolation(int). 315 * The default is JDBC driver dependent. 316 * 317 * @param v Value to assign to defaultTransactionIsolation 318 */ 319 public void setDefaultTransactionIsolation(int v) { 320 assertInitializationAllowed(); 321 switch (v) { 322 case Connection.TRANSACTION_NONE: 323 case Connection.TRANSACTION_READ_COMMITTED: 324 case Connection.TRANSACTION_READ_UNCOMMITTED: 325 case Connection.TRANSACTION_REPEATABLE_READ: 326 case Connection.TRANSACTION_SERIALIZABLE: 327 break; 328 default: 329 throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION); 330 } 331 this.defaultTransactionIsolation = v; 332 } 333 334 /** 335 * Get the description. This property is defined by jdbc as for use with 336 * GUI (or other) tools that might deploy the datasource. It serves no 337 * internal purpose. 338 * 339 * @return value of description. 340 */ 341 public String getDescription() { 342 return description; 343 } 344 345 /** 346 * Set the description. This property is defined by jdbc as for use with 347 * GUI (or other) tools that might deploy the datasource. It serves no 348 * internal purpose. 349 * 350 * @param v Value to assign to description. 351 */ 352 public void setDescription(String v) { 353 this.description = v; 354 } 355 356 /** 357 * Get the value of jndiEnvironment which is used when instantiating 358 * a jndi InitialContext. This InitialContext is used to locate the 359 * backend ConnectionPoolDataSource. 360 * 361 * @return value of jndiEnvironment. 362 */ 363 public String getJndiEnvironment(String key) { 364 String value = null; 365 if (jndiEnvironment != null) { 366 value = jndiEnvironment.getProperty(key); 367 } 368 return value; 369 } 370 371 /** 372 * Sets the value of the given JNDI environment property to be used when 373 * instantiating a JNDI InitialContext. This InitialContext is used to 374 * locate the backend ConnectionPoolDataSource. 375 * 376 * @param key the JNDI environment property to set. 377 * @param value the value assigned to specified JNDI environment property. 378 */ 379 public void setJndiEnvironment(String key, String value) { 380 if (jndiEnvironment == null) { 381 jndiEnvironment = new Properties(); 382 } 383 jndiEnvironment.setProperty(key, value); 384 } 385 386 /** 387 * Get the value of loginTimeout. 388 * @return value of loginTimeout. 389 */ 390 public int getLoginTimeout() { 391 return loginTimeout; 392 } 393 394 /** 395 * Set the value of loginTimeout. 396 * @param v Value to assign to loginTimeout. 397 */ 398 public void setLoginTimeout(int v) { 399 this.loginTimeout = v; 400 } 401 402 /** 403 * Get the value of logWriter. 404 * @return value of logWriter. 405 */ 406 public PrintWriter getLogWriter() { 407 if (logWriter == null) { 408 logWriter = new PrintWriter(System.out); 409 } 410 return logWriter; 411 } 412 413 /** 414 * Set the value of logWriter. 415 * @param v Value to assign to logWriter. 416 */ 417 public void setLogWriter(PrintWriter v) { 418 this.logWriter = v; 419 } 420 421 /** 422 * @see #getTestOnBorrow 423 */ 424 public final boolean isTestOnBorrow() { 425 return getTestOnBorrow(); 426 } 427 428 /** 429 * When <tt>true</tt>, objects will be 430 * {*link PoolableObjectFactory#validateObject validated} 431 * before being returned by the {*link #borrowObject} 432 * method. If the object fails to validate, 433 * it will be dropped from the pool, and we will attempt 434 * to borrow another. 435 * 436 * @see #setTestOnBorrow 437 */ 438 public boolean getTestOnBorrow() { 439 return _testOnBorrow; 440 } 441 442 /** 443 * When <tt>true</tt>, objects will be 444 * {*link PoolableObjectFactory#validateObject validated} 445 * before being returned by the {*link #borrowObject} 446 * method. If the object fails to validate, 447 * it will be dropped from the pool, and we will attempt 448 * to borrow another. For a <code>true</code> value to have any effect, 449 * the <code>validationQuery</code> property must be set to a non-null 450 * string. 451 * 452 * @see #getTestOnBorrow 453 */ 454 public void setTestOnBorrow(boolean testOnBorrow) { 455 assertInitializationAllowed(); 456 _testOnBorrow = testOnBorrow; 457 testPositionSet = true; 458 } 459 460 /** 461 * @see #getTestOnReturn 462 */ 463 public final boolean isTestOnReturn() { 464 return getTestOnReturn(); 465 } 466 467 /** 468 * When <tt>true</tt>, objects will be 469 * {*link PoolableObjectFactory#validateObject validated} 470 * before being returned to the pool within the 471 * {*link #returnObject}. 472 * 473 * @see #setTestOnReturn 474 */ 475 public boolean getTestOnReturn() { 476 return _testOnReturn; 477 } 478 479 /** 480 * When <tt>true</tt>, objects will be 481 * {*link PoolableObjectFactory#validateObject validated} 482 * before being returned to the pool within the 483 * {*link #returnObject}. For a <code>true</code> value to have any effect, 484 * the <code>validationQuery</code> property must be set to a non-null 485 * string. 486 * 487 * @see #getTestOnReturn 488 */ 489 public void setTestOnReturn(boolean testOnReturn) { 490 assertInitializationAllowed(); 491 _testOnReturn = testOnReturn; 492 testPositionSet = true; 493 } 494 495 /** 496 * Returns the number of milliseconds to sleep between runs of the 497 * idle object evictor thread. 498 * When non-positive, no idle object evictor thread will be 499 * run. 500 * 501 * @see #setTimeBetweenEvictionRunsMillis 502 */ 503 public int getTimeBetweenEvictionRunsMillis() { 504 return _timeBetweenEvictionRunsMillis; 505 } 506 507 /** 508 * Sets the number of milliseconds to sleep between runs of the 509 * idle object evictor thread. 510 * When non-positive, no idle object evictor thread will be 511 * run. 512 * 513 * @see #getTimeBetweenEvictionRunsMillis 514 */ 515 public void 516 setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) { 517 assertInitializationAllowed(); 518 _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; 519 } 520 521 /** 522 * Returns the number of objects to examine during each run of the 523 * idle object evictor thread (if any). 524 * 525 * @see #setNumTestsPerEvictionRun 526 * @see #setTimeBetweenEvictionRunsMillis 527 */ 528 public int getNumTestsPerEvictionRun() { 529 return _numTestsPerEvictionRun; 530 } 531 532 /** 533 * Sets the number of objects to examine during each run of the 534 * idle object evictor thread (if any). 535 * <p> 536 * When a negative value is supplied, <tt>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</tt> 537 * tests will be run. I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the 538 * idle objects will be tested per run. 539 * 540 * @see #getNumTestsPerEvictionRun 541 * @see #setTimeBetweenEvictionRunsMillis 542 */ 543 public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { 544 assertInitializationAllowed(); 545 _numTestsPerEvictionRun = numTestsPerEvictionRun; 546 } 547 548 /** 549 * Returns the minimum amount of time an object may sit idle in the pool 550 * before it is eligable for eviction by the idle object evictor 551 * (if any). 552 * 553 * @see #setMinEvictableIdleTimeMillis 554 * @see #setTimeBetweenEvictionRunsMillis 555 */ 556 public int getMinEvictableIdleTimeMillis() { 557 return _minEvictableIdleTimeMillis; 558 } 559 560 /** 561 * Sets the minimum amount of time an object may sit idle in the pool 562 * before it is eligable for eviction by the idle object evictor 563 * (if any). 564 * When non-positive, no objects will be evicted from the pool 565 * due to idle time alone. 566 * 567 * @see #getMinEvictableIdleTimeMillis 568 * @see #setTimeBetweenEvictionRunsMillis 569 */ 570 public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) { 571 assertInitializationAllowed(); 572 _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; 573 } 574 575 /** 576 * @see #getTestWhileIdle 577 */ 578 public final boolean isTestWhileIdle() { 579 return getTestWhileIdle(); 580 } 581 582 /** 583 * When <tt>true</tt>, objects will be 584 * {*link PoolableObjectFactory#validateObject validated} 585 * by the idle object evictor (if any). If an object 586 * fails to validate, it will be dropped from the pool. 587 * 588 * @see #setTestWhileIdle 589 * @see #setTimeBetweenEvictionRunsMillis 590 */ 591 public boolean getTestWhileIdle() { 592 return _testWhileIdle; 593 } 594 595 /** 596 * When <tt>true</tt>, objects will be 597 * {*link PoolableObjectFactory#validateObject validated} 598 * by the idle object evictor (if any). If an object 599 * fails to validate, it will be dropped from the pool. For a 600 * <code>true</code> value to have any effect, 601 * the <code>validationQuery</code> property must be set to a non-null 602 * string. 603 * 604 * @see #getTestWhileIdle 605 * @see #setTimeBetweenEvictionRunsMillis 606 */ 607 public void setTestWhileIdle(boolean testWhileIdle) { 608 assertInitializationAllowed(); 609 _testWhileIdle = testWhileIdle; 610 testPositionSet = true; 611 } 612 613 /** 614 * The SQL query that will be used to validate connections from this pool 615 * before returning them to the caller. If specified, this query 616 * <strong>MUST</strong> be an SQL SELECT statement that returns at least 617 * one row. 618 */ 619 public String getValidationQuery() { 620 return (this.validationQuery); 621 } 622 623 /** 624 * The SQL query that will be used to validate connections from this pool 625 * before returning them to the caller. If specified, this query 626 * <strong>MUST</strong> be an SQL SELECT statement that returns at least 627 * one row. If none of the properties, testOnBorow, testOnReturn, testWhileIdle 628 * have been previously set, calling this method sets testOnBorrow to true. 629 */ 630 public void setValidationQuery(String validationQuery) { 631 assertInitializationAllowed(); 632 this.validationQuery = validationQuery; 633 if (!testPositionSet) { 634 setTestOnBorrow(true); 635 } 636 } 637 638 /** 639 * Whether a rollback will be issued after executing the SQL query 640 * that will be used to validate connections from this pool 641 * before returning them to the caller. 642 * 643 * @return true if a rollback will be issued after executing the 644 * validation query 645 * @since 1.2.2 646 */ 647 public boolean isRollbackAfterValidation() { 648 return (this.rollbackAfterValidation); 649 } 650 651 /** 652 * Whether a rollback will be issued after executing the SQL query 653 * that will be used to validate connections from this pool 654 * before returning them to the caller. Default behavior is NOT 655 * to issue a rollback. The setting will only have an effect 656 * if a validation query is set 657 * 658 * @param rollbackAfterValidation new property value 659 * @since 1.2.2 660 */ 661 public void setRollbackAfterValidation(boolean rollbackAfterValidation) { 662 assertInitializationAllowed(); 663 this.rollbackAfterValidation = rollbackAfterValidation; 664 } 665 666 // ---------------------------------------------------------------------- 667 // Instrumentation Methods 668 669 // ---------------------------------------------------------------------- 670 // DataSource implementation 671 672 /** 673 * Attempt to establish a database connection. 674 */ 675 public Connection getConnection() throws SQLException { 676 return getConnection(null, null); 677 } 678 679 /** 680 * Attempt to retrieve a database connection using {@link #getPooledConnectionAndInfo(String, String)} 681 * with the provided username and password. The password on the {@link PooledConnectionAndInfo} 682 * instance returned by <code>getPooledConnectionAndInfo</code> is compared to the <code>password</code> 683 * parameter. If the comparison fails, a database connection using the supplied username and password 684 * is attempted. If the connection attempt fails, an SQLException is thrown, indicating that the given password 685 * did not match the password used to create the pooled connection. If the connection attempt succeeds, this 686 * means that the database password has been changed. In this case, the <code>PooledConnectionAndInfo</code> 687 * instance retrieved with the old password is destroyed and the <code>getPooledConnectionAndInfo</code> is 688 * repeatedly invoked until a <code>PooledConnectionAndInfo</code> instance with the new password is returned. 689 * 690 */ 691 public Connection getConnection(String username, String password) 692 throws SQLException { 693 if (instanceKey == null) { 694 throw new SQLException("Must set the ConnectionPoolDataSource " 695 + "through setDataSourceName or setConnectionPoolDataSource" 696 + " before calling getConnection."); 697 } 698 getConnectionCalled = true; 699 PooledConnectionAndInfo info = null; 700 try { 701 info = getPooledConnectionAndInfo(username, password); 702 } catch (NoSuchElementException e) { 703 closeDueToException(info); 704 throw new SQLNestedException("Cannot borrow connection from pool", e); 705 } catch (RuntimeException e) { 706 closeDueToException(info); 707 throw e; 708 } catch (SQLException e) { 709 closeDueToException(info); 710 throw e; 711 } catch (Exception e) { 712 closeDueToException(info); 713 throw new SQLNestedException("Cannot borrow connection from pool", e); 714 } 715 716 if (!(null == password ? null == info.getPassword() 717 : password.equals(info.getPassword()))) { // Password on PooledConnectionAndInfo does not match 718 try { // See if password has changed by attempting connection 719 testCPDS(username, password); 720 } catch (SQLException ex) { 721 // Password has not changed, so refuse client, but return connection to the pool 722 closeDueToException(info); 723 throw new SQLException("Given password did not match password used" 724 + " to create the PooledConnection."); 725 } catch (javax.naming.NamingException ne) { 726 throw (SQLException) new SQLException( 727 "NamingException encountered connecting to database").initCause(ne); 728 } 729 /* 730 * Password must have changed -> destroy connection and keep retrying until we get a new, good one, 731 * destroying any idle connections with the old passowrd as we pull them from the pool. 732 */ 733 final UserPassKey upkey = info.getUserPassKey(); 734 final PooledConnectionManager manager = getConnectionManager(upkey); 735 manager.invalidate(info.getPooledConnection()); // Destroy and remove from pool 736 manager.setPassword(upkey.getPassword()); // Reset the password on the factory if using CPDSConnectionFactory 737 info = null; 738 for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return 739 try { 740 info = getPooledConnectionAndInfo(username, password); 741 } catch (NoSuchElementException e) { 742 closeDueToException(info); 743 throw new SQLNestedException("Cannot borrow connection from pool", e); 744 } catch (RuntimeException e) { 745 closeDueToException(info); 746 throw e; 747 } catch (SQLException e) { 748 closeDueToException(info); 749 throw e; 750 } catch (Exception e) { 751 closeDueToException(info); 752 throw new SQLNestedException("Cannot borrow connection from pool", e); 753 } 754 if (info != null && password.equals(info.getPassword())) { 755 break; 756 } else { 757 if (info != null) { 758 manager.invalidate(info.getPooledConnection()); 759 } 760 info = null; 761 } 762 } 763 if (info == null) { 764 throw new SQLException("Cannot borrow connection from pool - password change failure."); 765 } 766 } 767 768 Connection con = info.getPooledConnection().getConnection(); 769 try { 770 setupDefaults(con, username); 771 con.clearWarnings(); 772 return con; 773 } catch (SQLException ex) { 774 try { 775 con.close(); 776 } catch (Exception exc) { 777 getLogWriter().println( 778 "ignoring exception during close: " + exc); 779 } 780 throw ex; 781 } 782 } 783 784 protected abstract PooledConnectionAndInfo 785 getPooledConnectionAndInfo(String username, String password) 786 throws SQLException; 787 788 protected abstract void setupDefaults(Connection con, String username) 789 throws SQLException; 790 791 792 private void closeDueToException(PooledConnectionAndInfo info) { 793 if (info != null) { 794 try { 795 info.getPooledConnection().getConnection().close(); 796 } catch (Exception e) { 797 // do not throw this exception because we are in the middle 798 // of handling another exception. But record it because 799 // it potentially leaks connections from the pool. 800 getLogWriter().println("[ERROR] Could not return connection to " 801 + "pool during exception handling. " + e.getMessage()); 802 } 803 } 804 } 805 806 protected ConnectionPoolDataSource 807 testCPDS(String username, String password) 808 throws javax.naming.NamingException, SQLException { 809 // The source of physical db connections 810 ConnectionPoolDataSource cpds = this.dataSource; 811 if (cpds == null) { 812 Context ctx = null; 813 if (jndiEnvironment == null) { 814 ctx = new InitialContext(); 815 } else { 816 ctx = new InitialContext(jndiEnvironment); 817 } 818 Object ds = ctx.lookup(dataSourceName); 819 if (ds instanceof ConnectionPoolDataSource) { 820 cpds = (ConnectionPoolDataSource) ds; 821 } else { 822 throw new SQLException("Illegal configuration: " 823 + "DataSource " + dataSourceName 824 + " (" + ds.getClass().getName() + ")" 825 + " doesn't implement javax.sql.ConnectionPoolDataSource"); 826 } 827 } 828 829 // try to get a connection with the supplied username/password 830 PooledConnection conn = null; 831 try { 832 if (username != null) { 833 conn = cpds.getPooledConnection(username, password); 834 } 835 else { 836 conn = cpds.getPooledConnection(); 837 } 838 if (conn == null) { 839 throw new SQLException( 840 "Cannot connect using the supplied username/password"); 841 } 842 } 843 finally { 844 if (conn != null) { 845 try { 846 conn.close(); 847 } 848 catch (SQLException e) { 849 // at least we could connect 850 } 851 } 852 } 853 return cpds; 854 } 855 856 protected byte whenExhaustedAction(int maxActive, int maxWait) { 857 byte whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_BLOCK; 858 if (maxActive <= 0) { 859 whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_GROW; 860 } else if (maxWait == 0) { 861 whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_FAIL; 862 } 863 return whenExhausted; 864 } 865 866 // ---------------------------------------------------------------------- 867 // Referenceable implementation 868 869 /** 870 * Retrieves the Reference of this object. 871 * <strong>Note:</strong> <code>InstanceKeyDataSource</code> subclasses 872 * should override this method. The implementaion included below 873 * is not robust and will be removed at the next major version DBCP 874 * release. 875 * 876 * @return The non-null Reference of this object. 877 * @exception NamingException If a naming exception was encountered 878 * while retrieving the reference. 879 */ 880 // TODO: Remove the implementation of this method at next major 881 // version release. 882 883 public Reference getReference() throws NamingException { 884 final String className = getClass().getName(); 885 final String factoryName = className + "Factory"; // XXX: not robust 886 Reference ref = new Reference(className, factoryName, null); 887 ref.add(new StringRefAddr("instanceKey", instanceKey)); 888 return ref; 889 } 890 }