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.cpdsadapter; 019 020import java.io.PrintWriter; 021import java.io.Serializable; 022import java.sql.DriverManager; 023import java.sql.SQLException; 024import java.sql.SQLFeatureNotSupportedException; 025import java.time.Duration; 026import java.util.Hashtable; 027import java.util.Properties; 028import java.util.logging.Logger; 029 030import javax.naming.Context; 031import javax.naming.Name; 032import javax.naming.NamingException; 033import javax.naming.RefAddr; 034import javax.naming.Reference; 035import javax.naming.Referenceable; 036import javax.naming.StringRefAddr; 037import javax.naming.spi.ObjectFactory; 038import javax.sql.ConnectionPoolDataSource; 039import javax.sql.PooledConnection; 040 041import org.apache.commons.dbcp2.BasicDataSource; 042import org.apache.commons.dbcp2.Constants; 043import org.apache.commons.dbcp2.DelegatingPreparedStatement; 044import org.apache.commons.dbcp2.PStmtKey; 045import org.apache.commons.dbcp2.Utils; 046import org.apache.commons.pool2.KeyedObjectPool; 047import org.apache.commons.pool2.impl.BaseObjectPoolConfig; 048import org.apache.commons.pool2.impl.GenericKeyedObjectPool; 049import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; 050 051/** 052 * <p> 053 * An adapter for JDBC drivers that do not include an implementation of {@link javax.sql.ConnectionPoolDataSource}, but 054 * still include a {@link java.sql.DriverManager} implementation. <code>ConnectionPoolDataSource</code>s are not used 055 * within general applications. They are used by <code>DataSource</code> implementations that pool 056 * <code>Connection</code>s, such as {@link org.apache.commons.dbcp2.datasources.SharedPoolDataSource}. A J2EE container 057 * will normally provide some method of initializing the <code>ConnectionPoolDataSource</code> whose attributes are 058 * presented as bean getters/setters and then deploying it via JNDI. It is then available as a source of physical 059 * connections to the database, when the pooling <code>DataSource</code> needs to create a new physical connection. 060 * </p> 061 * <p> 062 * Although normally used within a JNDI environment, the DriverAdapterCPDS can be instantiated and initialized as any 063 * bean and then attached directly to a pooling <code>DataSource</code>. <code>Jdbc2PoolDataSource</code> can use the 064 * <code>ConnectionPoolDataSource</code> with or without the use of JNDI. 065 * </p> 066 * <p> 067 * The DriverAdapterCPDS also provides <code>PreparedStatement</code> pooling which is not generally available in jdbc2 068 * <code>ConnectionPoolDataSource</code> implementation, but is addressed within the jdbc3 specification. The 069 * <code>PreparedStatement</code> pool in DriverAdapterCPDS has been in the dbcp package for some time, but it has not 070 * undergone extensive testing in the configuration used here. It should be considered experimental and can be toggled 071 * with the poolPreparedStatements attribute. 072 * </p> 073 * <p> 074 * The <a href="package-summary.html">package documentation</a> contains an example using catalina and JNDI. The 075 * <a href="../datasources/package-summary.html">datasources package documentation</a> shows how to use 076 * <code>DriverAdapterCPDS</code> as a source for <code>Jdbc2PoolDataSource</code> without the use of JNDI. 077 * </p> 078 * 079 * @since 2.0 080 */ 081public class DriverAdapterCPDS implements ConnectionPoolDataSource, Referenceable, Serializable, ObjectFactory { 082 083 private static final long serialVersionUID = -4820523787212147844L; 084 085 private static final String GET_CONNECTION_CALLED = "A PooledConnection was already requested from this source, " 086 + "further initialization is not allowed."; 087 088 static { 089 // Attempt to prevent deadlocks - see DBCP - 272 090 DriverManager.getDrivers(); 091 } 092 093 /** Description */ 094 private String description; 095 096 /** Url name */ 097 private String url; 098 099 /** User name */ 100 private String userName; 101 102 /** User password */ 103 private char[] userPassword; 104 105 /** Driver class name */ 106 private String driver; 107 108 /** Login TimeOut in seconds */ 109 private int loginTimeout; 110 111 /** Log stream. NOT USED */ 112 private transient PrintWriter logWriter; 113 114 // PreparedStatement pool properties 115 private boolean poolPreparedStatements; 116 private int maxIdle = 10; 117 private Duration durationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS; 118 private int numTestsPerEvictionRun = -1; 119 private Duration minEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME; 120 121 private int maxPreparedStatements = -1; 122 123 /** Whether or not getConnection has been called */ 124 private volatile boolean getConnectionCalled; 125 126 /** Connection properties passed to JDBC Driver */ 127 private Properties connectionProperties; 128 129 /** 130 * Controls access to the underlying connection 131 */ 132 private boolean accessToUnderlyingConnectionAllowed; 133 134 /** 135 * Default no-argument constructor for Serialization 136 */ 137 public DriverAdapterCPDS() { 138 } 139 140 /** 141 * Throws an IllegalStateException, if a PooledConnection has already been requested. 142 */ 143 private void assertInitializationAllowed() throws IllegalStateException { 144 if (getConnectionCalled) { 145 throw new IllegalStateException(GET_CONNECTION_CALLED); 146 } 147 } 148 149 private boolean getBooleanContentString(final RefAddr ra) { 150 return Boolean.parseBoolean(getStringContent(ra)); 151 } 152 153 /** 154 * Gets the connection properties passed to the JDBC driver. 155 * 156 * @return the JDBC connection properties used when creating connections. 157 */ 158 public Properties getConnectionProperties() { 159 return connectionProperties; 160 } 161 162 /** 163 * Gets the value of description. This property is here for use by the code which will deploy this data source. It 164 * is not used internally. 165 * 166 * @return value of description, may be null. 167 * @see #setDescription(String) 168 */ 169 public String getDescription() { 170 return description; 171 } 172 173 /** 174 * Gets the driver class name. 175 * 176 * @return value of driver. 177 */ 178 public String getDriver() { 179 return driver; 180 } 181 182 /** 183 * Gets the duration to sleep between runs of the idle object evictor thread. When non-positive, no 184 * idle object evictor thread will be run. 185 * 186 * @return the value of the evictor thread timer 187 * @see #setDurationBetweenEvictionRuns(Duration) 188 * @since 2.9.0 189 */ 190 public Duration getDurationBetweenEvictionRuns() { 191 return durationBetweenEvictionRuns; 192 } 193 194 private int getIntegerStringContent(final RefAddr ra) { 195 return Integer.parseInt(getStringContent(ra)); 196 } 197 198 /** 199 * Gets the maximum time in seconds that this data source can wait while attempting to connect to a database. NOT 200 * USED. 201 */ 202 @Override 203 public int getLoginTimeout() { 204 return loginTimeout; 205 } 206 207 /** 208 * Gets the log writer for this data source. NOT USED. 209 */ 210 @Override 211 public PrintWriter getLogWriter() { 212 return logWriter; 213 } 214 215 /** 216 * Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or 217 * negative for no limit. 218 * 219 * @return the value of maxIdle 220 */ 221 public int getMaxIdle() { 222 return maxIdle; 223 } 224 225 /** 226 * Gets the maximum number of prepared statements. 227 * 228 * @return maxPrepartedStatements value 229 */ 230 public int getMaxPreparedStatements() { 231 return maxPreparedStatements; 232 } 233 234 /** 235 * Gets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the 236 * idle object evictor (if any). 237 * 238 * @see #setMinEvictableIdleDuration 239 * @see #setDurationBetweenEvictionRuns 240 * @return the minimum amount of time a statement may sit idle in the pool. 241 * @since 2.9.0 242 */ 243 public Duration getMinEvictableIdleDuration() { 244 return minEvictableIdleDuration; 245 } 246 247 /** 248 * Gets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the 249 * idle object evictor (if any). 250 * 251 * @see #setMinEvictableIdleTimeMillis 252 * @see #setTimeBetweenEvictionRunsMillis 253 * @return the minimum amount of time a statement may sit idle in the pool. 254 * @deprecated USe {@link #getMinEvictableIdleDuration()}. 255 */ 256 @Deprecated 257 public int getMinEvictableIdleTimeMillis() { 258 return (int) minEvictableIdleDuration.toMillis(); 259 } 260 261 /** 262 * Gets the number of statements to examine during each run of the idle object evictor thread (if any.) 263 * 264 * @see #setNumTestsPerEvictionRun 265 * @see #setTimeBetweenEvictionRunsMillis 266 * @return the number of statements to examine during each run of the idle object evictor thread (if any.) 267 */ 268 public int getNumTestsPerEvictionRun() { 269 return numTestsPerEvictionRun; 270 } 271 272 /** 273 * Implements {@link ObjectFactory} to create an instance of this class 274 */ 275 @Override 276 public Object getObjectInstance(final Object refObj, final Name name, final Context context, 277 final Hashtable<?, ?> env) throws Exception { 278 // The spec says to return null if we can't create an instance 279 // of the reference 280 DriverAdapterCPDS cpds = null; 281 if (refObj instanceof Reference) { 282 final Reference ref = (Reference) refObj; 283 if (ref.getClassName().equals(getClass().getName())) { 284 RefAddr ra = ref.get("description"); 285 if (isNotEmpty(ra)) { 286 setDescription(getStringContent(ra)); 287 } 288 289 ra = ref.get("driver"); 290 if (isNotEmpty(ra)) { 291 setDriver(getStringContent(ra)); 292 } 293 ra = ref.get("url"); 294 if (isNotEmpty(ra)) { 295 setUrl(getStringContent(ra)); 296 } 297 ra = ref.get(Constants.KEY_USER); 298 if (isNotEmpty(ra)) { 299 setUser(getStringContent(ra)); 300 } 301 ra = ref.get(Constants.KEY_PASSWORD); 302 if (isNotEmpty(ra)) { 303 setPassword(getStringContent(ra)); 304 } 305 306 ra = ref.get("poolPreparedStatements"); 307 if (isNotEmpty(ra)) { 308 setPoolPreparedStatements(getBooleanContentString(ra)); 309 } 310 ra = ref.get("maxIdle"); 311 if (isNotEmpty(ra)) { 312 setMaxIdle(getIntegerStringContent(ra)); 313 } 314 315 ra = ref.get("timeBetweenEvictionRunsMillis"); 316 if (isNotEmpty(ra)) { 317 setTimeBetweenEvictionRunsMillis(getIntegerStringContent(ra)); 318 } 319 320 ra = ref.get("numTestsPerEvictionRun"); 321 if (isNotEmpty(ra)) { 322 setNumTestsPerEvictionRun(getIntegerStringContent(ra)); 323 } 324 325 ra = ref.get("minEvictableIdleTimeMillis"); 326 if (isNotEmpty(ra)) { 327 setMinEvictableIdleTimeMillis(getIntegerStringContent(ra)); 328 } 329 330 ra = ref.get("maxPreparedStatements"); 331 if (isNotEmpty(ra)) { 332 setMaxPreparedStatements(getIntegerStringContent(ra)); 333 } 334 335 ra = ref.get("accessToUnderlyingConnectionAllowed"); 336 if (isNotEmpty(ra)) { 337 setAccessToUnderlyingConnectionAllowed(getBooleanContentString(ra)); 338 } 339 340 cpds = this; 341 } 342 } 343 return cpds; 344 } 345 346 @Override 347 public Logger getParentLogger() throws SQLFeatureNotSupportedException { 348 throw new SQLFeatureNotSupportedException(); 349 } 350 351 /** 352 * Gets the value of password for the default user. 353 * 354 * @return value of password. 355 */ 356 public String getPassword() { 357 return Utils.toString(userPassword); 358 } 359 360 /** 361 * Gets the value of password for the default user. 362 * 363 * @return value of password. 364 * @since 2.4.0 365 */ 366 public char[] getPasswordCharArray() { 367 return userPassword == null ? null : userPassword.clone(); 368 } 369 370 /** 371 * Attempts to establish a database connection using the default user and password. 372 */ 373 @Override 374 public PooledConnection getPooledConnection() throws SQLException { 375 return getPooledConnection(getUser(), getPassword()); 376 } 377 378 /** 379 * Attempts to establish a database connection. 380 * 381 * @param pooledUserName name to be used for the connection 382 * @param pooledUserPassword password to be used fur the connection 383 */ 384 @Override 385 public PooledConnection getPooledConnection(final String pooledUserName, final String pooledUserPassword) 386 throws SQLException { 387 getConnectionCalled = true; 388 PooledConnectionImpl pooledConnection = null; 389 // Workaround for buggy WebLogic 5.1 class loader - ignore the exception upon first invocation. 390 try { 391 if (connectionProperties != null) { 392 update(connectionProperties, Constants.KEY_USER, pooledUserName); 393 update(connectionProperties, Constants.KEY_PASSWORD, pooledUserPassword); 394 pooledConnection = new PooledConnectionImpl( 395 DriverManager.getConnection(getUrl(), connectionProperties)); 396 } else { 397 pooledConnection = new PooledConnectionImpl( 398 DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword)); 399 } 400 pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); 401 } catch (final ClassCircularityError e) { 402 if (connectionProperties != null) { 403 pooledConnection = new PooledConnectionImpl( 404 DriverManager.getConnection(getUrl(), connectionProperties)); 405 } else { 406 pooledConnection = new PooledConnectionImpl( 407 DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword)); 408 } 409 pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); 410 } 411 KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = null; 412 if (isPoolPreparedStatements()) { 413 final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>(); 414 config.setMaxTotalPerKey(Integer.MAX_VALUE); 415 config.setBlockWhenExhausted(false); 416 config.setMaxWaitMillis(0); 417 config.setMaxIdlePerKey(getMaxIdle()); 418 if (getMaxPreparedStatements() <= 0) { 419 // Since there is no limit, create a prepared statement pool with an eviction thread; 420 // evictor settings are the same as the connection pool settings. 421 config.setTimeBetweenEvictionRuns(getDurationBetweenEvictionRuns()); 422 config.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun()); 423 config.setMinEvictableIdleTime(getMinEvictableIdleDuration()); 424 } else { 425 // Since there is a limit, create a prepared statement pool without an eviction thread; 426 // pool has LRU functionality so when the limit is reached, 15% of the pool is cleared. 427 // see org.apache.commons.pool2.impl.GenericKeyedObjectPool.clearOldest method 428 config.setMaxTotal(getMaxPreparedStatements()); 429 config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1)); 430 config.setNumTestsPerEvictionRun(0); 431 config.setMinEvictableIdleTime(Duration.ZERO); 432 } 433 stmtPool = new GenericKeyedObjectPool<>(pooledConnection, config); 434 pooledConnection.setStatementPool(stmtPool); 435 } 436 return pooledConnection; 437 } 438 439 /** 440 * Implements {@link Referenceable}. 441 */ 442 @Override 443 public Reference getReference() throws NamingException { 444 // this class implements its own factory 445 final String factory = getClass().getName(); 446 447 final Reference ref = new Reference(getClass().getName(), factory, null); 448 449 ref.add(new StringRefAddr("description", getDescription())); 450 ref.add(new StringRefAddr("driver", getDriver())); 451 ref.add(new StringRefAddr("loginTimeout", String.valueOf(getLoginTimeout()))); 452 ref.add(new StringRefAddr(Constants.KEY_PASSWORD, getPassword())); 453 ref.add(new StringRefAddr(Constants.KEY_USER, getUser())); 454 ref.add(new StringRefAddr("url", getUrl())); 455 456 ref.add(new StringRefAddr("poolPreparedStatements", String.valueOf(isPoolPreparedStatements()))); 457 ref.add(new StringRefAddr("maxIdle", String.valueOf(getMaxIdle()))); 458 ref.add(new StringRefAddr("numTestsPerEvictionRun", String.valueOf(getNumTestsPerEvictionRun()))); 459 ref.add(new StringRefAddr("maxPreparedStatements", String.valueOf(getMaxPreparedStatements()))); 460 // 461 // Pair of current and deprecated. 462 ref.add(new StringRefAddr("durationBetweenEvictionRuns", String.valueOf(getDurationBetweenEvictionRuns()))); 463 ref.add(new StringRefAddr("timeBetweenEvictionRunsMillis", String.valueOf(getTimeBetweenEvictionRunsMillis()))); 464 // 465 // Pair of current and deprecated. 466 ref.add(new StringRefAddr("minEvictableIdleDuration", String.valueOf(getMinEvictableIdleDuration()))); 467 ref.add(new StringRefAddr("minEvictableIdleTimeMillis", String.valueOf(getMinEvictableIdleTimeMillis()))); 468 469 return ref; 470 } 471 472 private String getStringContent(final RefAddr ra) { 473 return ra.getContent().toString(); 474 } 475 476 /** 477 * Gets the number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no 478 * idle object evictor thread will be run. 479 * 480 * @return the value of the evictor thread timer 481 * @see #setDurationBetweenEvictionRuns(Duration) 482 * @deprecated Use {@link #getDurationBetweenEvictionRuns()}. 483 */ 484 @Deprecated 485 public long getTimeBetweenEvictionRunsMillis() { 486 return durationBetweenEvictionRuns.toMillis(); 487 } 488 489 /** 490 * Gets the value of url used to locate the database for this datasource. 491 * 492 * @return value of url. 493 */ 494 public String getUrl() { 495 return url; 496 } 497 498 /** 499 * Gets the value of default user (login or user name). 500 * 501 * @return value of user. 502 */ 503 public String getUser() { 504 return userName; 505 } 506 507 /** 508 * Returns the value of the accessToUnderlyingConnectionAllowed property. 509 * 510 * @return true if access to the underlying is allowed, false otherwise. 511 */ 512 public synchronized boolean isAccessToUnderlyingConnectionAllowed() { 513 return this.accessToUnderlyingConnectionAllowed; 514 } 515 516 private boolean isNotEmpty(final RefAddr ra) { 517 return ra != null && ra.getContent() != null; 518 } 519 520 /** 521 * Whether to toggle the pooling of <code>PreparedStatement</code>s 522 * 523 * @return value of poolPreparedStatements. 524 */ 525 public boolean isPoolPreparedStatements() { 526 return poolPreparedStatements; 527 } 528 529 /** 530 * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to 531 * the underlying connection. (Default: false) 532 * 533 * @param allow Access to the underlying connection is granted when true. 534 */ 535 public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) { 536 this.accessToUnderlyingConnectionAllowed = allow; 537 } 538 539 /** 540 * Sets the connection properties passed to the JDBC driver. 541 * <p> 542 * If <code>props</code> contains "user" and/or "password" properties, the corresponding instance properties are 543 * set. If these properties are not present, they are filled in using {@link #getUser()}, {@link #getPassword()} 544 * when {@link #getPooledConnection()} is called, or using the actual parameters to the method call when 545 * {@link #getPooledConnection(String, String)} is called. Calls to {@link #setUser(String)} or 546 * {@link #setPassword(String)} overwrite the values of these properties if <code>connectionProperties</code> is not 547 * null. 548 * </p> 549 * 550 * @param props Connection properties to use when creating new connections. 551 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 552 */ 553 public void setConnectionProperties(final Properties props) { 554 assertInitializationAllowed(); 555 connectionProperties = props; 556 if (connectionProperties != null) { 557 if (connectionProperties.containsKey(Constants.KEY_USER)) { 558 setUser(connectionProperties.getProperty(Constants.KEY_USER)); 559 } 560 if (connectionProperties.containsKey(Constants.KEY_PASSWORD)) { 561 setPassword(connectionProperties.getProperty(Constants.KEY_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})</code> 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</code>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 #getTimeBetweenEvictionRunsMillis() 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 datasource. 746 * 747 * @param url Value to assign to url. 748 * @throws IllegalStateException if {@link #getPooledConnection()} has been called 749 */ 750 public void setUrl(final String url) { 751 assertInitializationAllowed(); 752 this.url = url; 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(", url="); 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(url); 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}