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