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 */ 017package org.apache.commons.dbcp2.cpdsadapter; 018 019import java.io.PrintWriter; 020import java.io.Serializable; 021import java.sql.DriverManager; 022import java.sql.SQLException; 023import java.sql.SQLFeatureNotSupportedException; 024import java.time.Duration; 025import java.util.Hashtable; 026import java.util.Properties; 027import java.util.logging.Logger; 028 029import javax.naming.Context; 030import javax.naming.Name; 031import javax.naming.NamingException; 032import javax.naming.RefAddr; 033import javax.naming.Reference; 034import javax.naming.Referenceable; 035import javax.naming.StringRefAddr; 036import javax.naming.spi.ObjectFactory; 037import javax.sql.ConnectionPoolDataSource; 038import javax.sql.PooledConnection; 039 040import org.apache.commons.dbcp2.Constants; 041import org.apache.commons.dbcp2.DelegatingPreparedStatement; 042import org.apache.commons.dbcp2.PStmtKey; 043import org.apache.commons.dbcp2.Utils; 044import org.apache.commons.pool2.KeyedObjectPool; 045import org.apache.commons.pool2.impl.BaseObjectPoolConfig; 046import org.apache.commons.pool2.impl.GenericKeyedObjectPool; 047import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; 048 049/** 050 * <p> 051 * An adapter for JDBC drivers that do not include an implementation of {@link javax.sql.ConnectionPoolDataSource}, but 052 * still include a {@link java.sql.DriverManager} implementation. {@code ConnectionPoolDataSource}s are not used 053 * within general applications. They are used by {@code DataSource} implementations that pool 054 * {@code Connection}s, such as {@link org.apache.commons.dbcp2.datasources.SharedPoolDataSource}. A J2EE container 055 * will normally provide some method of initializing the {@code ConnectionPoolDataSource} whose attributes are 056 * presented as bean getters/setters and then deploying it via JNDI. It is then available as a source of physical 057 * connections to the database, when the pooling {@code DataSource} needs to create a new physical connection. 058 * </p> 059 * <p> 060 * Although normally used within a JNDI environment, the DriverAdapterCPDS can be instantiated and initialized as any 061 * bean and then attached directly to a pooling {@code DataSource}. {@code Jdbc2PoolDataSource} can use the 062 * {@code ConnectionPoolDataSource} with or without the use of JNDI. 063 * </p> 064 * <p> 065 * The DriverAdapterCPDS also provides {@code PreparedStatement} pooling which is not generally available in jdbc2 066 * {@code ConnectionPoolDataSource} implementation, but is addressed within the jdbc3 specification. The 067 * {@code PreparedStatement} pool in DriverAdapterCPDS has been in the dbcp package for some time, but it has not 068 * undergone extensive testing in the configuration used here. It should be considered experimental and can be toggled 069 * with the poolPreparedStatements attribute. 070 * </p> 071 * <p> 072 * The <a href="package-summary.html">package documentation</a> contains an example using catalina and JNDI. The 073 * <a href="../datasources/package-summary.html">datasources package documentation</a> shows how to use 074 * {@code DriverAdapterCPDS} as a source for {@code Jdbc2PoolDataSource} without the use of JNDI. 075 * </p> 076 * 077 * @since 2.0 078 */ 079public class DriverAdapterCPDS implements ConnectionPoolDataSource, Referenceable, Serializable, ObjectFactory { 080 081 private static final long serialVersionUID = -4820523787212147844L; 082 083 private static final String GET_CONNECTION_CALLED = "A PooledConnection was already requested from this source, " 084 + "further initialization is not allowed."; 085 086 static { 087 // Attempt to prevent deadlocks - see DBCP - 272 088 DriverManager.getDrivers(); 089 } 090 091 /** Description */ 092 private String description; 093 094 /** Connection string */ 095 private String connectionString; 096 097 /** User name */ 098 private String userName; 099 100 /** User password */ 101 private char[] userPassword; 102 103 /** Driver class name */ 104 private String driver; 105 106 /** Login TimeOut in seconds */ 107 private int loginTimeout; 108 109 /** Log stream. NOT USED */ 110 private transient PrintWriter logWriter; 111 112 // PreparedStatement pool properties 113 private boolean poolPreparedStatements; 114 private int maxIdle = 10; 115 private Duration durationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS; 116 private int numTestsPerEvictionRun = -1; 117 private Duration minEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION; 118 119 private int maxPreparedStatements = -1; 120 121 /** Whether or not getConnection has been called */ 122 private volatile boolean getConnectionCalled; 123 124 /** Connection properties passed to JDBC Driver */ 125 private Properties connectionProperties; 126 127 /** 128 * Controls access to the underlying connection 129 */ 130 private boolean accessToUnderlyingConnectionAllowed; 131 132 /** 133 * Default no-argument constructor for Serialization 134 */ 135 public DriverAdapterCPDS() { 136 } 137 138 /** 139 * Throws an IllegalStateException, if a PooledConnection has already been requested. 140 */ 141 private void assertInitializationAllowed() throws IllegalStateException { 142 if (getConnectionCalled) { 143 throw new IllegalStateException(GET_CONNECTION_CALLED); 144 } 145 } 146 147 private boolean getBooleanContentString(final RefAddr ra) { 148 return Boolean.parseBoolean(getStringContent(ra)); 149 } 150 151 /** 152 * Gets the connection properties passed to the JDBC driver. 153 * 154 * @return the JDBC connection properties used when creating connections. 155 */ 156 public Properties getConnectionProperties() { 157 return connectionProperties; 158 } 159 160 /** 161 * Gets the value of description. This property is here for use by the code which will deploy this data source. It 162 * is not used internally. 163 * 164 * @return value of description, may be null. 165 * @see #setDescription(String) 166 */ 167 public String getDescription() { 168 return description; 169 } 170 171 /** 172 * Gets the driver class name. 173 * 174 * @return value of driver. 175 */ 176 public String getDriver() { 177 return driver; 178 } 179 180 /** 181 * Gets the duration to sleep between runs of the idle object evictor thread. When non-positive, no 182 * idle object evictor thread will be run. 183 * 184 * @return the value of the evictor thread timer 185 * @see #setDurationBetweenEvictionRuns(Duration) 186 * @since 2.9.0 187 */ 188 public Duration getDurationBetweenEvictionRuns() { 189 return durationBetweenEvictionRuns; 190 } 191 192 private int getIntegerStringContent(final RefAddr ra) { 193 return Integer.parseInt(getStringContent(ra)); 194 } 195 196 /** 197 * Gets the maximum time in seconds that this data source can wait while attempting to connect to a database. NOT 198 * USED. 199 */ 200 @Override 201 public int getLoginTimeout() { 202 return loginTimeout; 203 } 204 205 /** 206 * Gets the log writer for this data source. NOT USED. 207 */ 208 @Override 209 public PrintWriter getLogWriter() { 210 return logWriter; 211 } 212 213 /** 214 * Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or 215 * negative for no limit. 216 * 217 * @return the value of maxIdle 218 */ 219 public int getMaxIdle() { 220 return maxIdle; 221 } 222 223 /** 224 * Gets the maximum number of prepared statements. 225 * 226 * @return maxPrepartedStatements value 227 */ 228 public int getMaxPreparedStatements() { 229 return maxPreparedStatements; 230 } 231 232 /** 233 * Gets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the 234 * idle object evictor (if any). 235 * 236 * @see #setMinEvictableIdleDuration 237 * @see #setDurationBetweenEvictionRuns 238 * @return the minimum amount of time a statement may sit idle in the pool. 239 * @since 2.9.0 240 */ 241 public Duration getMinEvictableIdleDuration() { 242 return minEvictableIdleDuration; 243 } 244 245 /** 246 * Gets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the 247 * idle object evictor (if any). 248 * 249 * @see #setMinEvictableIdleTimeMillis 250 * @see #setTimeBetweenEvictionRunsMillis 251 * @return the minimum amount of time a statement may sit idle in the pool. 252 * @deprecated USe {@link #getMinEvictableIdleDuration()}. 253 */ 254 @Deprecated 255 public int getMinEvictableIdleTimeMillis() { 256 return (int) minEvictableIdleDuration.toMillis(); 257 } 258 259 /** 260 * Gets the number of statements to examine during each run of the idle object evictor thread (if any.) 261 * 262 * @see #setNumTestsPerEvictionRun 263 * @see #setTimeBetweenEvictionRunsMillis 264 * @return the number of statements to examine during each run of the idle object evictor thread (if any.) 265 */ 266 public int getNumTestsPerEvictionRun() { 267 return numTestsPerEvictionRun; 268 } 269 270 /** 271 * Implements {@link ObjectFactory} to create an instance of this class 272 */ 273 @Override 274 public Object getObjectInstance(final Object refObj, final Name name, final Context context, 275 final Hashtable<?, ?> env) throws ClassNotFoundException { 276 // The spec says to return null if we can't create an instance 277 // of the reference 278 DriverAdapterCPDS cpds = null; 279 if (refObj instanceof Reference) { 280 final Reference ref = (Reference) refObj; 281 if (ref.getClassName().equals(getClass().getName())) { 282 RefAddr ra = ref.get("description"); 283 if (isNotEmpty(ra)) { 284 setDescription(getStringContent(ra)); 285 } 286 287 ra = ref.get("driver"); 288 if (isNotEmpty(ra)) { 289 setDriver(getStringContent(ra)); 290 } 291 ra = ref.get("url"); 292 if (isNotEmpty(ra)) { 293 setUrl(getStringContent(ra)); 294 } 295 ra = ref.get(Constants.KEY_USER); 296 if (isNotEmpty(ra)) { 297 setUser(getStringContent(ra)); 298 } 299 ra = ref.get(Constants.KEY_PASSWORD); 300 if (isNotEmpty(ra)) { 301 setPassword(getStringContent(ra)); 302 } 303 304 ra = ref.get("poolPreparedStatements"); 305 if (isNotEmpty(ra)) { 306 setPoolPreparedStatements(getBooleanContentString(ra)); 307 } 308 ra = ref.get("maxIdle"); 309 if (isNotEmpty(ra)) { 310 setMaxIdle(getIntegerStringContent(ra)); 311 } 312 313 ra = ref.get("timeBetweenEvictionRunsMillis"); 314 if (isNotEmpty(ra)) { 315 setTimeBetweenEvictionRunsMillis(getIntegerStringContent(ra)); 316 } 317 318 ra = ref.get("numTestsPerEvictionRun"); 319 if (isNotEmpty(ra)) { 320 setNumTestsPerEvictionRun(getIntegerStringContent(ra)); 321 } 322 323 ra = ref.get("minEvictableIdleTimeMillis"); 324 if (isNotEmpty(ra)) { 325 setMinEvictableIdleTimeMillis(getIntegerStringContent(ra)); 326 } 327 328 ra = ref.get("maxPreparedStatements"); 329 if (isNotEmpty(ra)) { 330 setMaxPreparedStatements(getIntegerStringContent(ra)); 331 } 332 333 ra = ref.get("accessToUnderlyingConnectionAllowed"); 334 if (isNotEmpty(ra)) { 335 setAccessToUnderlyingConnectionAllowed(getBooleanContentString(ra)); 336 } 337 338 cpds = this; 339 } 340 } 341 return cpds; 342 } 343 344 @Override 345 public Logger getParentLogger() throws SQLFeatureNotSupportedException { 346 throw new SQLFeatureNotSupportedException(); 347 } 348 349 /** 350 * Gets the value of password for the default user. 351 * 352 * @return value of password. 353 */ 354 public String getPassword() { 355 return Utils.toString(userPassword); 356 } 357 358 /** 359 * Gets the value of password for the default user. 360 * 361 * @return value of password. 362 * @since 2.4.0 363 */ 364 public char[] getPasswordCharArray() { 365 return Utils.clone(userPassword); 366 } 367 368 /** 369 * Attempts to establish a database connection using the default user and password. 370 */ 371 @Override 372 public PooledConnection getPooledConnection() throws SQLException { 373 return getPooledConnection(getUser(), getPassword()); 374 } 375 376 /** 377 * Attempts to establish a database connection. 378 * 379 * @param pooledUserName name to be used for the connection 380 * @param pooledUserPassword password to be used fur the connection 381 */ 382 @Override 383 public PooledConnection getPooledConnection(final String pooledUserName, final String pooledUserPassword) 384 throws SQLException { 385 getConnectionCalled = true; 386 PooledConnectionImpl pooledConnection = null; 387 // Workaround for buggy WebLogic 5.1 class loader - ignore the exception upon first invocation. 388 try { 389 if (connectionProperties != null) { 390 update(connectionProperties, Constants.KEY_USER, pooledUserName); 391 update(connectionProperties, Constants.KEY_PASSWORD, pooledUserPassword); 392 pooledConnection = new PooledConnectionImpl( 393 DriverManager.getConnection(getUrl(), connectionProperties)); 394 } else { 395 pooledConnection = new PooledConnectionImpl( 396 DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword)); 397 } 398 pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); 399 } catch (final ClassCircularityError e) { 400 if (connectionProperties != null) { 401 pooledConnection = new PooledConnectionImpl( 402 DriverManager.getConnection(getUrl(), connectionProperties)); 403 } else { 404 pooledConnection = new PooledConnectionImpl( 405 DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword)); 406 } 407 pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); 408 } 409 KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = null; 410 if (isPoolPreparedStatements()) { 411 final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>(); 412 config.setMaxTotalPerKey(Integer.MAX_VALUE); 413 config.setBlockWhenExhausted(false); 414 config.setMaxWait(Duration.ZERO); 415 config.setMaxIdlePerKey(getMaxIdle()); 416 if (getMaxPreparedStatements() <= 0) { 417 // Since there is no limit, create a prepared statement pool with an eviction thread; 418 // evictor settings are the same as the connection pool settings. 419 config.setTimeBetweenEvictionRuns(getDurationBetweenEvictionRuns()); 420 config.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun()); 421 config.setMinEvictableIdleTime(getMinEvictableIdleDuration()); 422 } else { 423 // Since there is a limit, create a prepared statement pool without an eviction thread; 424 // pool has LRU functionality so when the limit is reached, 15% of the pool is cleared. 425 // see org.apache.commons.pool2.impl.GenericKeyedObjectPool.clearOldest method 426 config.setMaxTotal(getMaxPreparedStatements()); 427 config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1)); 428 config.setNumTestsPerEvictionRun(0); 429 config.setMinEvictableIdleTime(Duration.ZERO); 430 } 431 stmtPool = new GenericKeyedObjectPool<>(pooledConnection, config); 432 pooledConnection.setStatementPool(stmtPool); 433 } 434 return pooledConnection; 435 } 436 437 /** 438 * Implements {@link Referenceable}. 439 */ 440 @Override 441 public Reference getReference() throws NamingException { 442 // this class implements its own factory 443 final String factory = getClass().getName(); 444 445 final Reference ref = new Reference(getClass().getName(), factory, null); 446 447 ref.add(new StringRefAddr("description", getDescription())); 448 ref.add(new StringRefAddr("driver", getDriver())); 449 ref.add(new StringRefAddr("loginTimeout", String.valueOf(getLoginTimeout()))); 450 ref.add(new StringRefAddr(Constants.KEY_PASSWORD, getPassword())); 451 ref.add(new StringRefAddr(Constants.KEY_USER, getUser())); 452 ref.add(new StringRefAddr("url", getUrl())); 453 454 ref.add(new StringRefAddr("poolPreparedStatements", String.valueOf(isPoolPreparedStatements()))); 455 ref.add(new StringRefAddr("maxIdle", String.valueOf(getMaxIdle()))); 456 ref.add(new StringRefAddr("numTestsPerEvictionRun", String.valueOf(getNumTestsPerEvictionRun()))); 457 ref.add(new StringRefAddr("maxPreparedStatements", String.valueOf(getMaxPreparedStatements()))); 458 // 459 // Pair of current and deprecated. 460 ref.add(new StringRefAddr("durationBetweenEvictionRuns", String.valueOf(getDurationBetweenEvictionRuns()))); 461 ref.add(new StringRefAddr("timeBetweenEvictionRunsMillis", String.valueOf(getTimeBetweenEvictionRunsMillis()))); 462 // 463 // Pair of current and deprecated. 464 ref.add(new StringRefAddr("minEvictableIdleDuration", String.valueOf(getMinEvictableIdleDuration()))); 465 ref.add(new StringRefAddr("minEvictableIdleTimeMillis", String.valueOf(getMinEvictableIdleTimeMillis()))); 466 467 return ref; 468 } 469 470 private String getStringContent(final RefAddr ra) { 471 return ra.getContent().toString(); 472 } 473 474 /** 475 * Gets the number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no 476 * idle object evictor thread will be run. 477 * 478 * @return the value of the evictor thread timer 479 * @see #setDurationBetweenEvictionRuns(Duration) 480 * @deprecated Use {@link #getDurationBetweenEvictionRuns()}. 481 */ 482 @Deprecated 483 public long getTimeBetweenEvictionRunsMillis() { 484 return durationBetweenEvictionRuns.toMillis(); 485 } 486 487 /** 488 * Gets the value of connection string used to locate the database for this data source. 489 * 490 * @return value of connection string. 491 */ 492 public String getUrl() { 493 return connectionString; 494 } 495 496 /** 497 * Gets the value of default user (login or user name). 498 * 499 * @return value of user. 500 */ 501 public String getUser() { 502 return userName; 503 } 504 505 /** 506 * Returns the value of the accessToUnderlyingConnectionAllowed property. 507 * 508 * @return true if access to the underlying is allowed, false otherwise. 509 */ 510 public synchronized boolean isAccessToUnderlyingConnectionAllowed() { 511 return this.accessToUnderlyingConnectionAllowed; 512 } 513 514 private boolean isNotEmpty(final RefAddr ra) { 515 return ra != null && ra.getContent() != null; 516 } 517 518 /** 519 * Whether to toggle the pooling of {@code PreparedStatement}s 520 * 521 * @return value of poolPreparedStatements. 522 */ 523 public boolean isPoolPreparedStatements() { 524 return poolPreparedStatements; 525 } 526 527 /** 528 * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to 529 * the underlying connection. (Default: false) 530 * 531 * @param allow Access to the underlying connection is granted when true. 532 */ 533 public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) { 534 this.accessToUnderlyingConnectionAllowed = allow; 535 } 536 537 /** 538 * Sets the connection properties passed to the JDBC driver. 539 * <p> 540 * If {@code props} contains "user" and/or "password" properties, the corresponding instance properties are 541 * set. If these properties are not present, they are filled in using {@link #getUser()}, {@link #getPassword()} 542 * when {@link #getPooledConnection()} is called, or using the actual parameters to the method call when 543 * {@link #getPooledConnection(String, String)} is called. Calls to {@link #setUser(String)} or 544 * {@link #setPassword(String)} overwrite the values of these properties if {@code connectionProperties} is not 545 * null. 546 * </p> 547 * 548 * @param props Connection properties to use when creating new connections. 549 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 550 */ 551 public void setConnectionProperties(final Properties props) { 552 assertInitializationAllowed(); 553 connectionProperties = props; 554 if (connectionProperties != null) { 555 final String user = connectionProperties.getProperty(Constants.KEY_USER); 556 if (user != null) { 557 setUser(user); 558 } 559 final String password = connectionProperties.getProperty(Constants.KEY_PASSWORD); 560 if (password != null) { 561 setPassword(password); 562 } 563 } 564 } 565 566 /** 567 * Sets the value of description. This property is here for use by the code which will deploy this datasource. It is 568 * not used internally. 569 * 570 * @param description Value to assign to description. 571 */ 572 public void setDescription(final String description) { 573 this.description = description; 574 } 575 576 /** 577 * Sets the driver class name. Setting the driver class name cause the driver to be registered with the 578 * DriverManager. 579 * 580 * @param driver Value to assign to driver. 581 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 582 * @throws ClassNotFoundException if the class cannot be located 583 */ 584 public void setDriver(final String driver) throws ClassNotFoundException { 585 assertInitializationAllowed(); 586 this.driver = driver; 587 // make sure driver is registered 588 Class.forName(driver); 589 } 590 591 /** 592 * Sets the duration to sleep between runs of the idle object evictor thread. When non-positive, no 593 * idle object evictor thread will be run. 594 * 595 * @param durationBetweenEvictionRuns The duration to sleep between runs of the idle object evictor 596 * thread. When non-positive, no idle object evictor thread will be run. 597 * @see #getDurationBetweenEvictionRuns() 598 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 599 * @since 2.9.0 600 */ 601 public void setDurationBetweenEvictionRuns(final Duration durationBetweenEvictionRuns) { 602 assertInitializationAllowed(); 603 this.durationBetweenEvictionRuns = durationBetweenEvictionRuns; 604 } 605 606 /** 607 * Sets the maximum time in seconds that this data source will wait while attempting to connect to a database. NOT 608 * USED. 609 */ 610 @Override 611 public void setLoginTimeout(final int seconds) { 612 this.loginTimeout = seconds; 613 } 614 615 /** 616 * Sets the log writer for this data source. NOT USED. 617 */ 618 @Override 619 public void setLogWriter(final PrintWriter logWriter) { 620 this.logWriter = logWriter; 621 } 622 623 /** 624 * Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or 625 * negative for no limit. 626 * 627 * @param maxIdle The maximum number of statements that can remain idle 628 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 629 */ 630 public void setMaxIdle(final int maxIdle) { 631 assertInitializationAllowed(); 632 this.maxIdle = maxIdle; 633 } 634 635 /** 636 * Sets the maximum number of prepared statements. 637 * 638 * @param maxPreparedStatements the new maximum number of prepared statements 639 */ 640 public void setMaxPreparedStatements(final int maxPreparedStatements) { 641 this.maxPreparedStatements = maxPreparedStatements; 642 } 643 644 /** 645 * Sets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the 646 * idle object evictor (if any). When non-positive, no objects will be evicted from the pool due to idle time alone. 647 * 648 * @param minEvictableIdleDuration minimum time to set in milliseconds. 649 * @see #getMinEvictableIdleDuration() 650 * @see #setDurationBetweenEvictionRuns(Duration) 651 * @throws IllegalStateException if {@link #getPooledConnection()} has been called. 652 * @since 2.9.0 653 */ 654 public void setMinEvictableIdleDuration(final Duration minEvictableIdleDuration) { 655 assertInitializationAllowed(); 656 this.minEvictableIdleDuration = minEvictableIdleDuration; 657 } 658 659 /** 660 * Sets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the 661 * idle object evictor (if any). When non-positive, no objects will be evicted from the pool due to idle time alone. 662 * 663 * @param minEvictableIdleTimeMillis minimum time to set in milliseconds. 664 * @see #getMinEvictableIdleDuration() 665 * @see #setDurationBetweenEvictionRuns(Duration) 666 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 667 * @deprecated Use {@link #setMinEvictableIdleDuration(Duration)}. 668 */ 669 @Deprecated 670 public void setMinEvictableIdleTimeMillis(final int minEvictableIdleTimeMillis) { 671 assertInitializationAllowed(); 672 this.minEvictableIdleDuration = Duration.ofMillis(minEvictableIdleTimeMillis); 673 } 674 675 /** 676 * Sets the number of statements to examine during each run of the idle object evictor thread (if any). 677 * <p> 678 * When a negative value is supplied, 679 * {@code ceil({@link BasicDataSource#getNumIdle})/abs({@link #getNumTestsPerEvictionRun})} tests will be run. 680 * I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the idle objects will be tested per run. 681 * </p> 682 * 683 * @param numTestsPerEvictionRun number of statements to examine per run 684 * @see #getNumTestsPerEvictionRun() 685 * @see #setDurationBetweenEvictionRuns(Duration) 686 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 687 */ 688 public void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) { 689 assertInitializationAllowed(); 690 this.numTestsPerEvictionRun = numTestsPerEvictionRun; 691 } 692 693 /** 694 * Sets the value of password for the default user. 695 * 696 * @param userPassword Value to assign to password. 697 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 698 */ 699 public void setPassword(final char[] userPassword) { 700 assertInitializationAllowed(); 701 this.userPassword = Utils.clone(userPassword); 702 update(connectionProperties, Constants.KEY_PASSWORD, Utils.toString(this.userPassword)); 703 } 704 705 /** 706 * Sets the value of password for the default user. 707 * 708 * @param userPassword Value to assign to password. 709 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 710 */ 711 public void setPassword(final String userPassword) { 712 assertInitializationAllowed(); 713 this.userPassword = Utils.toCharArray(userPassword); 714 update(connectionProperties, Constants.KEY_PASSWORD, userPassword); 715 } 716 717 /** 718 * Whether to toggle the pooling of {@code PreparedStatement}s 719 * 720 * @param poolPreparedStatements true to pool statements. 721 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 722 */ 723 public void setPoolPreparedStatements(final boolean poolPreparedStatements) { 724 assertInitializationAllowed(); 725 this.poolPreparedStatements = poolPreparedStatements; 726 } 727 728 /** 729 * Sets the number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no 730 * idle object evictor thread will be run. 731 * 732 * @param timeBetweenEvictionRunsMillis The number of milliseconds to sleep between runs of the idle object evictor 733 * thread. When non-positive, no idle object evictor thread will be run. 734 * @see #getDurationBetweenEvictionRuns() 735 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 736 * @deprecated Use {@link #setDurationBetweenEvictionRuns(Duration)}. 737 */ 738 @Deprecated 739 public void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) { 740 assertInitializationAllowed(); 741 this.durationBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis); 742 } 743 744 /** 745 * Sets the value of URL string used to locate the database for this data source. 746 * 747 * @param connectionString Value to assign to connection string. 748 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 749 */ 750 public void setUrl(final String connectionString) { 751 assertInitializationAllowed(); 752 this.connectionString = connectionString; 753 } 754 755 /** 756 * Sets the value of default user (login or user name). 757 * 758 * @param userName Value to assign to user. 759 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 760 */ 761 public void setUser(final String userName) { 762 assertInitializationAllowed(); 763 this.userName = userName; 764 update(connectionProperties, Constants.KEY_USER, userName); 765 } 766 767 /** 768 * Does not print the userName and userPassword field nor the 'user' or 'password' in the connectionProperties. 769 * 770 * @since 2.6.0 771 */ 772 @Override 773 public synchronized String toString() { 774 final StringBuilder builder = new StringBuilder(super.toString()); 775 builder.append("[description="); 776 builder.append(description); 777 builder.append(", connectionString="); 778 // TODO What if the connection string contains a 'user' or 'password' query parameter but that connection string 779 // is not in a legal URL format? 780 builder.append(connectionString); 781 builder.append(", driver="); 782 builder.append(driver); 783 builder.append(", loginTimeout="); 784 builder.append(loginTimeout); 785 builder.append(", poolPreparedStatements="); 786 builder.append(poolPreparedStatements); 787 builder.append(", maxIdle="); 788 builder.append(maxIdle); 789 builder.append(", timeBetweenEvictionRunsMillis="); 790 builder.append(durationBetweenEvictionRuns); 791 builder.append(", numTestsPerEvictionRun="); 792 builder.append(numTestsPerEvictionRun); 793 builder.append(", minEvictableIdleTimeMillis="); 794 builder.append(minEvictableIdleDuration); 795 builder.append(", maxPreparedStatements="); 796 builder.append(maxPreparedStatements); 797 builder.append(", getConnectionCalled="); 798 builder.append(getConnectionCalled); 799 builder.append(", connectionProperties="); 800 builder.append(Utils.cloneWithoutCredentials(connectionProperties)); 801 builder.append(", accessToUnderlyingConnectionAllowed="); 802 builder.append(accessToUnderlyingConnectionAllowed); 803 builder.append("]"); 804 return builder.toString(); 805 } 806 807 private void update(final Properties properties, final String key, final String value) { 808 if (properties != null && key != null) { 809 if (value == null) { 810 properties.remove(key); 811 } else { 812 properties.setProperty(key, value); 813 } 814 } 815 } 816}