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