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