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