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