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