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