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