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
18 package org.apache.commons.dbcp;
19
20 import java.io.PrintWriter;
21 import java.util.Properties;
22 import java.sql.Connection;
23 import java.sql.Driver;
24 import java.sql.DriverManager;
25 import java.sql.SQLException;
26 import javax.sql.DataSource;
27
28 import org.apache.commons.pool.impl.GenericKeyedObjectPool;
29 import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory;
30 import org.apache.commons.pool.impl.GenericObjectPool;
31
32
33 /**
34 * <p>Basic implementation of <code>javax.sql.DataSource</code> that is
35 * configured via JavaBeans properties. This is not the only way to
36 * combine the <em>commons-dbcp</em> and <em>commons-pool</em> packages,
37 * but provides a "one stop shopping" solution for basic requirements.</p>
38 *
39 * @author Glenn L. Nielsen
40 * @author Craig R. McClanahan
41 * @author Dirk Verbeeck
42 * @version $Revision: 506087 $ $Date: 2007-02-11 11:37:43 -0700 (Sun, 11 Feb 2007) $
43 */
44 public class BasicDataSource implements DataSource {
45
46 // ------------------------------------------------------------- Properties
47
48 /**
49 * The default auto-commit state of connections created by this pool.
50 */
51 protected boolean defaultAutoCommit = true;
52
53 /**
54 * Returns the default auto-commit property.
55 *
56 * @return true if default auto-commit is enabled
57 */
58 public synchronized boolean getDefaultAutoCommit() {
59 return this.defaultAutoCommit;
60 }
61
62 /**
63 * <p>Sets default auto-commit state of connections returned by this
64 * datasource.</p>
65 * <p>
66 * Note: this method currently has no effect once the pool has been
67 * initialized. The pool is initialized the first time one of the
68 * following methods is invoked: <code>getConnection, setLogwriter,
69 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
70 *
71 * @param defaultAutoCommit default auto-commit value
72 */
73 public synchronized void setDefaultAutoCommit(boolean defaultAutoCommit) {
74 this.defaultAutoCommit = defaultAutoCommit;
75 this.restartNeeded = true;
76 }
77
78
79 /**
80 * The default read-only state of connections created by this pool.
81 */
82 protected Boolean defaultReadOnly = null;
83
84 /**
85 * Returns the default readOnly property.
86 *
87 * @return true if connections are readOnly by default
88 */
89 public synchronized boolean getDefaultReadOnly() {
90 if (this.defaultReadOnly != null) {
91 return this.defaultReadOnly.booleanValue();
92 }
93 return false;
94 }
95
96 /**
97 * <p>Sets defaultReadonly property.</p>
98 * <p>
99 * Note: this method currently has no effect once the pool has been
100 * initialized. The pool is initialized the first time one of the
101 * following methods is invoked: <code>getConnection, setLogwriter,
102 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
103 *
104 * @param defaultReadOnly default read-only value
105 */
106 public synchronized void setDefaultReadOnly(boolean defaultReadOnly) {
107 this.defaultReadOnly = defaultReadOnly ? Boolean.TRUE : Boolean.FALSE;
108 this.restartNeeded = true;
109 }
110
111 /**
112 * The default TransactionIsolation state of connections created by this pool.
113 */
114 protected int defaultTransactionIsolation = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
115
116 /**
117 * Returns the default transaction isolation state of returned connections.
118 *
119 * @return the default value for transaction isolation state
120 * @see Connection#getTransactionIsolation
121 */
122 public synchronized int getDefaultTransactionIsolation() {
123 return this.defaultTransactionIsolation;
124 }
125
126 /**
127 * <p>Sets the default transaction isolation state for returned
128 * connections.</p>
129 * <p>
130 * Note: this method currently has no effect once the pool has been
131 * initialized. The pool is initialized the first time one of the
132 * following methods is invoked: <code>getConnection, setLogwriter,
133 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
134 *
135 * @param defaultTransactionIsolation the default transaction isolation
136 * state
137 * @see Connection#getTransactionIsolation
138 */
139 public synchronized void setDefaultTransactionIsolation(int defaultTransactionIsolation) {
140 this.defaultTransactionIsolation = defaultTransactionIsolation;
141 this.restartNeeded = true;
142 }
143
144
145 /**
146 * The default "catalog" of connections created by this pool.
147 */
148 protected String defaultCatalog = null;
149
150 /**
151 * Returns the default catalog.
152 *
153 * @return the default catalog
154 */
155 public synchronized String getDefaultCatalog() {
156 return this.defaultCatalog;
157 }
158
159 /**
160 * <p>Sets the default catalog.</p>
161 * <p>
162 * Note: this method currently has no effect once the pool has been
163 * initialized. The pool is initialized the first time one of the
164 * following methods is invoked: <code>getConnection, setLogwriter,
165 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
166 *
167 * @param defaultCatalog the default catalog
168 */
169 public synchronized void setDefaultCatalog(String defaultCatalog) {
170 if ((defaultCatalog != null) && (defaultCatalog.trim().length() > 0)) {
171 this.defaultCatalog = defaultCatalog;
172 }
173 else {
174 this.defaultCatalog = null;
175 }
176 this.restartNeeded = true;
177 }
178
179
180 /**
181 * The fully qualified Java class name of the JDBC driver to be used.
182 */
183 protected String driverClassName = null;
184
185 /**
186 * Returns the jdbc driver class name.
187 *
188 * @return the jdbc driver class name
189 */
190 public synchronized String getDriverClassName() {
191 return this.driverClassName;
192 }
193
194 /**
195 * <p>Sets the jdbc driver class name.</p>
196 * <p>
197 * Note: this method currently has no effect once the pool has been
198 * initialized. The pool is initialized the first time one of the
199 * following methods is invoked: <code>getConnection, setLogwriter,
200 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
201 *
202 * @param driverClassName the class name of the jdbc driver
203 */
204 public synchronized void setDriverClassName(String driverClassName) {
205 if ((driverClassName != null) && (driverClassName.trim().length() > 0)) {
206 this.driverClassName = driverClassName;
207 }
208 else {
209 this.driverClassName = null;
210 }
211 this.restartNeeded = true;
212 }
213
214
215 /**
216 * The maximum number of active connections that can be allocated from
217 * this pool at the same time, or non-positive for no limit.
218 */
219 protected int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
220
221 /**
222 * <p>Returns the maximum number of active connections that can be
223 * allocated at the same time.
224 * </p>
225 * <p>A non-positive number means that there is no limit.</p>
226 *
227 * @return the maximum number of active connections
228 */
229 public synchronized int getMaxActive() {
230 return this.maxActive;
231 }
232
233 /**
234 * Sets the maximum number of active connections that can be
235 * allocated at the same time.
236 *
237 * @param maxActive the new value for maxActive
238 * @see #getMaxActive()
239 */
240 public synchronized void setMaxActive(int maxActive) {
241 this.maxActive = maxActive;
242 if (connectionPool != null) {
243 connectionPool.setMaxActive(maxActive);
244 }
245 }
246
247 /**
248 * The maximum number of connections that can remain idle in the
249 * pool, without extra ones being released, or negative for no limit.
250 */
251 protected int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
252
253 /**
254 * <p>Returns the maximum number of connections that can remain idle in the
255 * pool.
256 * </p>
257 * <p>A negative value indicates that there is no limit</p>
258 *
259 * @return the maximum number of idle connections
260 */
261 public synchronized int getMaxIdle() {
262 return this.maxIdle;
263 }
264
265 /**
266 * Sets the maximum number of connections that can remail idle in the
267 * pool.
268 *
269 * @see #getMaxIdle()
270 * @param maxIdle the new value for maxIdle
271 */
272 public synchronized void setMaxIdle(int maxIdle) {
273 this.maxIdle = maxIdle;
274 if (connectionPool != null) {
275 connectionPool.setMaxIdle(maxIdle);
276 }
277 }
278
279 /**
280 * The minimum number of active connections that can remain idle in the
281 * pool, without extra ones being created, or 0 to create none.
282 */
283 protected int minIdle = GenericObjectPool.DEFAULT_MIN_IDLE;
284
285 /**
286 * Returns the minimum number of idle connections in the pool
287 *
288 * @return the minimum number of idle connections
289 * @see GenericObjectPool#getMinIdle()
290 */
291 public synchronized int getMinIdle() {
292 return this.minIdle;
293 }
294
295 /**
296 * Sets the minimum number of idle connections in the pool.
297 *
298 * @param minIdle the new value for minIdle
299 * @see GenericObjectPool#setMinIdle(int)
300 */
301 public synchronized void setMinIdle(int minIdle) {
302 this.minIdle = minIdle;
303 if (connectionPool != null) {
304 connectionPool.setMinIdle(minIdle);
305 }
306 }
307
308 /**
309 * The initial number of connections that are created when the pool
310 * is started.
311 *
312 * @since 1.2
313 */
314 protected int initialSize = 0;
315
316 /**
317 * Returns the initial size of the connection pool.
318 *
319 * @return the number of connections created when the pool is initialized
320 */
321 public synchronized int getInitialSize() {
322 return this.initialSize;
323 }
324
325 /**
326 * <p>Sets the initial size of the connection pool.</p>
327 * <p>
328 * Note: this method currently has no effect once the pool has been
329 * initialized. The pool is initialized the first time one of the
330 * following methods is invoked: <code>getConnection, setLogwriter,
331 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
332 *
333 * @param initialSize the number of connections created when the pool
334 * is initialized
335 */
336 public synchronized void setInitialSize(int initialSize) {
337 this.initialSize = initialSize;
338 this.restartNeeded = true;
339 }
340
341 /**
342 * The maximum number of milliseconds that the pool will wait (when there
343 * are no available connections) for a connection to be returned before
344 * throwing an exception, or -1 to wait indefinitely.
345 */
346 protected long maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;
347
348 /**
349 * <p>Returns the maximum number of milliseconds that the pool will wait
350 * for a connection to be returned before throwing an exception.
351 * </p>
352 * <p>Returns -1 if the pool is set to wait indefinitely.</p>
353 *
354 * @return the maxWait property value
355 */
356 public synchronized long getMaxWait() {
357 return this.maxWait;
358 }
359
360 /**
361 * Sets the maxWait property.
362 *
363 * @param maxWait the new value for maxWait
364 * @see #getMaxWait()
365 */
366 public synchronized void setMaxWait(long maxWait) {
367 this.maxWait = maxWait;
368 if (connectionPool != null) {
369 connectionPool.setMaxWait(maxWait);
370 }
371 }
372
373 /**
374 * Prepared statement pooling for this pool.
375 */
376 protected boolean poolPreparedStatements = false;
377
378 /**
379 * Returns true if we are pooling statements.
380 *
381 * @return true if prepared statements are pooled
382 */
383 public synchronized boolean isPoolPreparedStatements() {
384 return this.poolPreparedStatements;
385 }
386
387 /**
388 * <p>Sets whether to pool statements or not.</p>
389 * <p>
390 * Note: this method currently has no effect once the pool has been
391 * initialized. The pool is initialized the first time one of the
392 * following methods is invoked: <code>getConnection, setLogwriter,
393 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
394 *
395 * @param poolingStatements pooling on or off
396 */
397 public synchronized void setPoolPreparedStatements(boolean poolingStatements) {
398 this.poolPreparedStatements = poolingStatements;
399 this.restartNeeded = true;
400 }
401
402 /**
403 * The maximum number of open statements that can be allocated from
404 * the statement pool at the same time, or non-positive for no limit. Since
405 * a connection usually only uses one or two statements at a time, this is
406 * mostly used to help detect resource leaks.
407 */
408 protected int maxOpenPreparedStatements = GenericKeyedObjectPool.DEFAULT_MAX_TOTAL;
409
410 /**
411 * Gets the value of the {@link #maxOpenPreparedStatements} property.
412 *
413 * @return the maximum number of open statements
414 * @see #maxOpenPreparedStatements
415 */
416 public synchronized int getMaxOpenPreparedStatements() {
417 return this.maxOpenPreparedStatements;
418 }
419
420 /**
421 * <p>Sets the value of the {@link #maxOpenPreparedStatements}
422 * property.</p>
423 * <p>
424 * Note: this method currently has no effect once the pool has been
425 * initialized. The pool is initialized the first time one of the
426 * following methods is invoked: <code>getConnection, setLogwriter,
427 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
428 *
429 * @param maxOpenStatements the new maximum number of prepared statements
430 * @see #maxOpenPreparedStatements
431 */
432 public synchronized void setMaxOpenPreparedStatements(int maxOpenStatements) {
433 this.maxOpenPreparedStatements = maxOpenStatements;
434 this.restartNeeded = true;
435 }
436
437 /**
438 * The indication of whether objects will be validated before being
439 * borrowed from the pool. If the object fails to validate, it will be
440 * dropped from the pool, and we will attempt to borrow another.
441 */
442 protected boolean testOnBorrow = true;
443
444 /**
445 * Returns the {@link #testOnBorrow} property.
446 *
447 * @return true if objects are validated before being borrowed from the
448 * pool
449 *
450 * @see #testOnBorrow
451 */
452 public synchronized boolean getTestOnBorrow() {
453 return this.testOnBorrow;
454 }
455
456 /**
457 * Sets the {@link #testOnBorrow} property. This property determines
458 * whether or not the pool will validate objects before they are borrowed
459 * from the pool. For a <code>true</code> value to have any effect, the
460 * <code>validationQuery</code> property must be set to a non-null string.
461 *
462 * @param testOnBorrow new value for testOnBorrow property
463 */
464 public synchronized void setTestOnBorrow(boolean testOnBorrow) {
465 this.testOnBorrow = testOnBorrow;
466 if (connectionPool != null) {
467 connectionPool.setTestOnBorrow(testOnBorrow);
468 }
469 }
470
471 /**
472 * The indication of whether objects will be validated before being
473 * returned to the pool.
474 */
475 protected boolean testOnReturn = false;
476
477 /**
478 * Returns the value of the {@link #testOnReturn} property.
479 *
480 * @return true if objects are validated before being returned to the
481 * pool
482 * @see #testOnReturn
483 */
484 public synchronized boolean getTestOnReturn() {
485 return this.testOnReturn;
486 }
487
488 /**
489 * Sets the <code>testOnReturn</code> property. This property determines
490 * whether or not the pool will validate objects before they are returned
491 * to the pool. For a <code>true</code> value to have any effect, the
492 * <code>validationQuery</code> property must be set to a non-null string.
493 *
494 * @param testOnReturn new value for testOnReturn property
495 */
496 public synchronized void setTestOnReturn(boolean testOnReturn) {
497 this.testOnReturn = testOnReturn;
498 if (connectionPool != null) {
499 connectionPool.setTestOnReturn(testOnReturn);
500 }
501 }
502
503 /**
504 * The number of milliseconds to sleep between runs of the idle object
505 * evictor thread. When non-positive, no idle object evictor thread will
506 * be run.
507 */
508 protected long timeBetweenEvictionRunsMillis =
509 GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
510
511 /**
512 * Returns the value of the {@link #timeBetweenEvictionRunsMillis}
513 * property.
514 *
515 * @return the time (in miliseconds) between evictor runs
516 * @see #timeBetweenEvictionRunsMillis
517 */
518 public synchronized long getTimeBetweenEvictionRunsMillis() {
519 return this.timeBetweenEvictionRunsMillis;
520 }
521
522 /**
523 * Sets the {@link #timeBetweenEvictionRunsMillis} property.
524 *
525 * @param timeBetweenEvictionRunsMillis the new time between evictor runs
526 * @see #timeBetweenEvictionRunsMillis
527 */
528 public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
529 this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
530 if (connectionPool != null) {
531 connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
532 }
533 }
534
535 /**
536 * The number of objects to examine during each run of the idle object
537 * evictor thread (if any).
538 */
539 protected int numTestsPerEvictionRun =
540 GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
541
542 /**
543 * Returns the value of the {@link #numTestsPerEvictionRun} property.
544 *
545 * @return the number of objects to examine during idle object evictor
546 * runs
547 * @see #numTestsPerEvictionRun
548 */
549 public synchronized int getNumTestsPerEvictionRun() {
550 return this.numTestsPerEvictionRun;
551 }
552
553 /**
554 * Sets the value of the {@link #numTestsPerEvictionRun} property.
555 *
556 * @param numTestsPerEvictionRun the new {@link #numTestsPerEvictionRun}
557 * value
558 * @see #numTestsPerEvictionRun
559 */
560 public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
561 this.numTestsPerEvictionRun = numTestsPerEvictionRun;
562 if (connectionPool != null) {
563 connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
564 }
565 }
566
567 /**
568 * The minimum amount of time an object may sit idle in the pool before it
569 * is eligable for eviction by the idle object evictor (if any).
570 */
571 protected long minEvictableIdleTimeMillis =
572 GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
573
574 /**
575 * Returns the {@link #minEvictableIdleTimeMillis} property.
576 *
577 * @return the value of the {@link #minEvictableIdleTimeMillis} property
578 * @see #minEvictableIdleTimeMillis
579 */
580 public synchronized long getMinEvictableIdleTimeMillis() {
581 return this.minEvictableIdleTimeMillis;
582 }
583
584 /**
585 * Sets the {@link #minEvictableIdleTimeMillis} property.
586 *
587 * @param minEvictableIdleTimeMillis the minimum amount of time an object
588 * may sit idle in the pool
589 * @see #minEvictableIdleTimeMillis
590 */
591 public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
592 this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
593 if (connectionPool != null) {
594 connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
595 }
596 }
597
598 /**
599 * The indication of whether objects will be validated by the idle object
600 * evictor (if any). If an object fails to validate, it will be dropped
601 * from the pool.
602 */
603 protected boolean testWhileIdle = false;
604
605 /**
606 * Returns the value of the {@link #testWhileIdle} property.
607 *
608 * @return true if objects examined by the idle object evictor are
609 * validated
610 * @see #testWhileIdle
611 */
612 public synchronized boolean getTestWhileIdle() {
613 return this.testWhileIdle;
614 }
615
616 /**
617 * Sets the <code>testWhileIdle</code> property. This property determines
618 * whether or not the idle object evictor will validate connections. For a
619 * <code>true</code> value to have any effect, the
620 * <code>validationQuery</code> property must be set to a non-null string.
621 *
622 * @param testWhileIdle new value for testWhileIdle property
623 */
624 public synchronized void setTestWhileIdle(boolean testWhileIdle) {
625 this.testWhileIdle = testWhileIdle;
626 if (connectionPool != null) {
627 connectionPool.setTestWhileIdle(testWhileIdle);
628 }
629 }
630
631 /**
632 * [Read Only] The current number of active connections that have been
633 * allocated from this data source.
634 *
635 * @return the current number of active connections
636 */
637 public synchronized int getNumActive() {
638 if (connectionPool != null) {
639 return connectionPool.getNumActive();
640 } else {
641 return 0;
642 }
643 }
644
645
646 /**
647 * [Read Only] The current number of idle connections that are waiting
648 * to be allocated from this data source.
649 *
650 * @return the current number of idle connections
651 */
652 public synchronized int getNumIdle() {
653 if (connectionPool != null) {
654 return connectionPool.getNumIdle();
655 } else {
656 return 0;
657 }
658 }
659
660 /**
661 * The connection password to be passed to our JDBC driver to establish
662 * a connection.
663 */
664 protected String password = null;
665
666 /**
667 * Returns the password passed to the JDBC driver to establish connections.
668 *
669 * @return the connection password
670 */
671 public synchronized String getPassword() {
672 return this.password;
673 }
674
675 /**
676 * <p>Sets the {@link #password}.</p>
677 * <p>
678 * Note: this method currently has no effect once the pool has been
679 * initialized. The pool is initialized the first time one of the
680 * following methods is invoked: <code>getConnection, setLogwriter,
681 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
682 *
683 * @param password new value for the password
684 */
685 public synchronized void setPassword(String password) {
686 this.password = password;
687 this.restartNeeded = true;
688 }
689
690 /**
691 * The connection URL to be passed to our JDBC driver to establish
692 * a connection.
693 */
694 protected String url = null;
695
696 /**
697 * Returns the JDBC connection {@link #url} property.
698 *
699 * @return the {@link #url} passed to the JDBC driver to establish
700 * connections
701 */
702 public synchronized String getUrl() {
703 return this.url;
704 }
705
706 /**
707 * <p>Sets the {@link #url}.</p>
708 * <p>
709 * Note: this method currently has no effect once the pool has been
710 * initialized. The pool is initialized the first time one of the
711 * following methods is invoked: <code>getConnection, setLogwriter,
712 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
713 *
714 * @param url the new value for the JDBC connection url
715 */
716 public synchronized void setUrl(String url) {
717 this.url = url;
718 this.restartNeeded = true;
719 }
720
721 /**
722 * The connection username to be passed to our JDBC driver to
723 * establish a connection.
724 */
725 protected String username = null;
726
727 /**
728 * Returns the JDBC connection {@link #username} property.
729 *
730 * @return the {@link #username} passed to the JDBC driver to establish
731 * connections
732 */
733 public synchronized String getUsername() {
734 return this.username;
735 }
736
737 /**
738 * <p>Sets the {@link #username}.</p>
739 * <p>
740 * Note: this method currently has no effect once the pool has been
741 * initialized. The pool is initialized the first time one of the
742 * following methods is invoked: <code>getConnection, setLogwriter,
743 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
744 *
745 * @param username the new value for the JDBC connection username
746 */
747 public synchronized void setUsername(String username) {
748 this.username = username;
749 this.restartNeeded = true;
750 }
751
752 /**
753 * The SQL query that will be used to validate connections from this pool
754 * before returning them to the caller. If specified, this query
755 * <strong>MUST</strong> be an SQL SELECT statement that returns at least
756 * one row.
757 */
758 protected String validationQuery = null;
759
760 /**
761 * Returns the validation query used to validate connections before
762 * returning them.
763 *
764 * @return the SQL validation query
765 * @see #validationQuery
766 */
767 public synchronized String getValidationQuery() {
768 return this.validationQuery;
769 }
770
771 /**
772 * <p>Sets the {@link #validationQuery}.</p>
773 * <p>
774 * Note: this method currently has no effect once the pool has been
775 * initialized. The pool is initialized the first time one of the
776 * following methods is invoked: <code>getConnection, setLogwriter,
777 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
778 *
779 * @param validationQuery the new value for the validation query
780 */
781 public synchronized void setValidationQuery(String validationQuery) {
782 if ((validationQuery != null) && (validationQuery.trim().length() > 0)) {
783 this.validationQuery = validationQuery;
784 } else {
785 this.validationQuery = null;
786 }
787 this.restartNeeded = true;
788 }
789
790 /**
791 * Controls access to the underlying connection.
792 */
793 private boolean accessToUnderlyingConnectionAllowed = false;
794
795 /**
796 * Returns the value of the accessToUnderlyingConnectionAllowed property.
797 *
798 * @return true if access to the underlying connection is allowed, false
799 * otherwise.
800 */
801 public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
802 return this.accessToUnderlyingConnectionAllowed;
803 }
804
805 /**
806 * <p>Sets the value of the accessToUnderlyingConnectionAllowed property.
807 * It controls if the PoolGuard allows access to the underlying connection.
808 * (Default: false)</p>
809 * <p>
810 * Note: this method currently has no effect once the pool has been
811 * initialized. The pool is initialized the first time one of the
812 * following methods is invoked: <code>getConnection, setLogwriter,
813 * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
814 *
815 * @param allow Access to the underlying connection is granted when true.
816 */
817 public synchronized void setAccessToUnderlyingConnectionAllowed(boolean allow) {
818 this.accessToUnderlyingConnectionAllowed = allow;
819 this.restartNeeded = true;
820 }
821
822 // ----------------------------------------------------- Instance Variables
823
824 // TODO: review & make isRestartNeeded() public, restartNeeded protected
825
826 /**
827 * A property setter has been invoked that will require the connection
828 * pool to be re-initialized. Currently, restart is not triggered, so
829 * this property has no effect.
830 */
831 private boolean restartNeeded = false;
832
833 /**
834 * Returns whether or not a restart is needed.
835 *
836 * Note: restart is not currently triggered by property changes.
837 *
838 * @return true if a restart is needed
839 */
840 private synchronized boolean isRestartNeeded() {
841 return restartNeeded;
842 }
843
844 /**
845 * The object pool that internally manages our connections.
846 */
847 protected GenericObjectPool connectionPool = null;
848
849 /**
850 * The connection properties that will be sent to our JDBC driver when
851 * establishing new connections. <strong>NOTE</strong> - The "user" and
852 * "password" properties will be passed explicitly, so they do not need
853 * to be included here.
854 */
855 protected Properties connectionProperties = new Properties();
856
857 /**
858 * The data source we will use to manage connections. This object should
859 * be acquired <strong>ONLY</strong> by calls to the
860 * <code>createDataSource()</code> method.
861 */
862 protected DataSource dataSource = null;
863
864 /**
865 * The PrintWriter to which log messages should be directed.
866 */
867 protected PrintWriter logWriter = new PrintWriter(System.out);
868
869
870 // ----------------------------------------------------- DataSource Methods
871
872
873 /**
874 * Create (if necessary) and return a connection to the database.
875 *
876 * @throws SQLException if a database access error occurs
877 * @return a database connection
878 */
879 public Connection getConnection() throws SQLException {
880 return createDataSource().getConnection();
881 }
882
883
884 /**
885 * <strong>BasicDataSource does NOT support this method.
886 * </strong>
887 *
888 * @param username Database user on whose behalf the Connection
889 * is being made
890 * @param password The database user's password
891 *
892 * @throws UnsupportedOperationException
893 * @throws SQLException if a database access error occurs
894 * @return nothing - always throws UnsupportedOperationException
895 */
896 public Connection getConnection(String username, String password) throws SQLException {
897 // This method isn't supported by the PoolingDataSource returned by
898 // the createDataSource
899 throw new UnsupportedOperationException("Not supported by BasicDataSource");
900 // return createDataSource().getConnection(username, password);
901 }
902
903
904 /**
905 * <p>Returns the login timeout (in seconds) for connecting to the database.
906 * </p>
907 * <p>Calls {@link #createDataSource()}, so has the side effect
908 * of initializing the connection pool.</p>
909 *
910 * @throws SQLException if a database access error occurs
911 * @throws UnsupportedOperationException If the DataSource implementation
912 * does not support the login timeout feature.
913 * @return login timeout in seconds
914 */
915 public int getLoginTimeout() throws SQLException {
916 return createDataSource().getLoginTimeout();
917 }
918
919
920 /**
921 * <p>Returns the log writer being used by this data source.</p>
922 * <p>
923 * Calls {@link #createDataSource()}, so has the side effect
924 * of initializing the connection pool.</p>
925 *
926 * @throws SQLException if a database access error occurs
927 * @return log writer in use
928 */
929 public PrintWriter getLogWriter() throws SQLException {
930 return createDataSource().getLogWriter();
931 }
932
933
934 /**
935 * <p>Set the login timeout (in seconds) for connecting to the
936 * database.</p>
937 * <p>
938 * Calls {@link #createDataSource()}, so has the side effect
939 * of initializing the connection pool.</p>
940 *
941 * @param loginTimeout The new login timeout, or zero for no timeout
942 * @throws SQLException if a database access error occurs
943 */
944 public void setLoginTimeout(int loginTimeout) throws SQLException {
945 createDataSource().setLoginTimeout(loginTimeout);
946 }
947
948
949 /**
950 * <p>Sets the log writer being used by this data source.</p>
951 * <p>
952 * Calls {@link #createDataSource()}, so has the side effect
953 * of initializing the connection pool.</p>
954 *
955 * @param logWriter The new log writer
956 * @throws SQLException if a database access error occurs
957 */
958 public void setLogWriter(PrintWriter logWriter) throws SQLException {
959 createDataSource().setLogWriter(logWriter);
960 this.logWriter = logWriter;
961 }
962
963 private AbandonedConfig abandonedConfig;
964
965 /**
966 * Flag to remove abandoned connections if they exceed the
967 * removeAbandonedTimout.
968 *
969 * Set to true or false, default false.
970 * If set to true a connection is considered abandoned and eligible
971 * for removal if it has been idle longer than the removeAbandonedTimeout.
972 * Setting this to true can recover db connections from poorly written
973 * applications which fail to close a connection.
974 * @deprecated
975 */
976 public boolean getRemoveAbandoned() {
977 if (abandonedConfig != null) {
978 return abandonedConfig.getRemoveAbandoned();
979 }
980 return false;
981 }
982
983 /**
984 * @deprecated
985 * @param removeAbandoned new removeAbandoned property value
986 */
987 public void setRemoveAbandoned(boolean removeAbandoned) {
988 if (abandonedConfig == null) {
989 abandonedConfig = new AbandonedConfig();
990 }
991 abandonedConfig.setRemoveAbandoned(removeAbandoned);
992 this.restartNeeded = true;
993 }
994
995 /**
996 * Timeout in seconds before an abandoned connection can be removed.
997 *
998 * Defaults to 300 seconds.
999 * @return abandoned connection timeout
1000 * @deprecated
1001 */
1002 public int getRemoveAbandonedTimeout() {
1003 if (abandonedConfig != null) {
1004 return abandonedConfig.getRemoveAbandonedTimeout();
1005 }
1006 return 300;
1007 }
1008
1009 /**
1010 * @deprecated
1011 * @param removeAbandonedTimeout new removeAbandonedTimeout value
1012 */
1013 public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {
1014 if (abandonedConfig == null) {
1015 abandonedConfig = new AbandonedConfig();
1016 }
1017 abandonedConfig.setRemoveAbandonedTimeout(removeAbandonedTimeout);
1018 this.restartNeeded = true;
1019 }
1020
1021 /**
1022 * <p>Flag to log stack traces for application code which abandoned
1023 * a Statement or Connection.
1024 * </p>
1025 * <p>Defaults to false.
1026 * </p>
1027 * <p>Logging of abandoned Statements and Connections adds overhead
1028 * for every Connection open or new Statement because a stack
1029 * trace has to be generated. </p>
1030 *
1031 * @deprecated
1032 */
1033 public boolean getLogAbandoned() {
1034 if (abandonedConfig != null) {
1035 return abandonedConfig.getLogAbandoned();
1036 }
1037 return false;
1038 }
1039
1040 /**
1041 * @deprecated
1042 * @param logAbandoned new logAbandoned property value
1043 */
1044 public void setLogAbandoned(boolean logAbandoned) {
1045 if (abandonedConfig == null) {
1046 abandonedConfig = new AbandonedConfig();
1047 }
1048 abandonedConfig.setLogAbandoned(logAbandoned);
1049 this.restartNeeded = true;
1050 }
1051
1052 // --------------------------------------------------------- Public Methods
1053
1054 /**
1055 * Add a custom connection property to the set that will be passed to our
1056 * JDBC driver. This <strong>MUST</strong> be called before the first
1057 * connection is retrieved (along with all the other configuration
1058 * property setters). Calls to this method after the connection pool
1059 * has been initialized have no effect.
1060 *
1061 * @param name Name of the custom connection property
1062 * @param value Value of the custom connection property
1063 */
1064 public void addConnectionProperty(String name, String value) {
1065 connectionProperties.put(name, value);
1066 this.restartNeeded = true;
1067 }
1068
1069 /**
1070 * Remove a custom connection property.
1071 *
1072 * @param name Name of the custom connection property to remove
1073 * @see #addConnectionProperty(String, String)
1074 */
1075 public void removeConnectionProperty(String name) {
1076 connectionProperties.remove(name);
1077 this.restartNeeded = true;
1078 }
1079
1080 /**
1081 * Close and release all connections that are currently stored in the
1082 * connection pool associated with our data source.
1083 *
1084 * @throws SQLException if a database error occurs
1085 */
1086 public synchronized void close() throws SQLException {
1087 GenericObjectPool oldpool = connectionPool;
1088 connectionPool = null;
1089 dataSource = null;
1090 try {
1091 if (oldpool != null) {
1092 oldpool.close();
1093 }
1094 } catch(SQLException e) {
1095 throw e;
1096 } catch(RuntimeException e) {
1097 throw e;
1098 } catch(Exception e) {
1099 throw new SQLNestedException("Cannot close connection pool", e);
1100 }
1101 }
1102
1103
1104 // ------------------------------------------------------ Protected Methods
1105
1106
1107 /**
1108 * <p>Create (if necessary) and return the internal data source we are
1109 * using to manage our connections.</p>
1110 *
1111 * <p><strong>IMPLEMENTATION NOTE</strong> - It is tempting to use the
1112 * "double checked locking" idiom in an attempt to avoid synchronizing
1113 * on every single call to this method. However, this idiom fails to
1114 * work correctly in the face of some optimizations that are legal for
1115 * a JVM to perform.</p>
1116 *
1117 * @throws SQLException if the object pool cannot be created.
1118 */
1119 protected synchronized DataSource createDataSource()
1120 throws SQLException {
1121
1122 // Return the pool if we have already created it
1123 if (dataSource != null) {
1124 return (dataSource);
1125 }
1126
1127 // Load the JDBC driver class
1128 if (driverClassName != null) {
1129 try {
1130 Class.forName(driverClassName);
1131 } catch (Throwable t) {
1132 String message = "Cannot load JDBC driver class '" +
1133 driverClassName + "'";
1134 logWriter.println(message);
1135 t.printStackTrace(logWriter);
1136 throw new SQLNestedException(message, t);
1137 }
1138 }
1139
1140 // Create a JDBC driver instance
1141 Driver driver = null;
1142 try {
1143 driver = DriverManager.getDriver(url);
1144 } catch (Throwable t) {
1145 String message = "Cannot create JDBC driver of class '" +
1146 (driverClassName != null ? driverClassName : "") +
1147 "' for connect URL '" + url + "'";
1148 logWriter.println(message);
1149 t.printStackTrace(logWriter);
1150 throw new SQLNestedException(message, t);
1151 }
1152
1153 // Can't test without a validationQuery
1154 if (validationQuery == null) {
1155 setTestOnBorrow(false);
1156 setTestOnReturn(false);
1157 setTestWhileIdle(false);
1158 }
1159
1160 // Create an object pool to contain our active connections
1161 if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned())) {
1162 connectionPool = new AbandonedObjectPool(null,abandonedConfig);
1163 }
1164 else {
1165 connectionPool = new GenericObjectPool();
1166 }
1167 connectionPool.setMaxActive(maxActive);
1168 connectionPool.setMaxIdle(maxIdle);
1169 connectionPool.setMinIdle(minIdle);
1170 connectionPool.setMaxWait(maxWait);
1171 connectionPool.setTestOnBorrow(testOnBorrow);
1172 connectionPool.setTestOnReturn(testOnReturn);
1173 connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
1174 connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
1175 connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
1176 connectionPool.setTestWhileIdle(testWhileIdle);
1177
1178 // Set up statement pool, if desired
1179 GenericKeyedObjectPoolFactory statementPoolFactory = null;
1180 if (isPoolPreparedStatements()) {
1181 statementPoolFactory = new GenericKeyedObjectPoolFactory(null,
1182 -1, // unlimited maxActive (per key)
1183 GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL,
1184 0, // maxWait
1185 1, // maxIdle (per key)
1186 maxOpenPreparedStatements);
1187 }
1188
1189 // Set up the driver connection factory we will use
1190 if (username != null) {
1191 connectionProperties.put("user", username);
1192 } else {
1193 log("DBCP DataSource configured without a 'username'");
1194 }
1195
1196 if (password != null) {
1197 connectionProperties.put("password", password);
1198 } else {
1199 log("DBCP DataSource configured without a 'password'");
1200 }
1201
1202 DriverConnectionFactory driverConnectionFactory =
1203 new DriverConnectionFactory(driver, url, connectionProperties);
1204
1205 // Set up the poolable connection factory we will use
1206 PoolableConnectionFactory connectionFactory = null;
1207 try {
1208 connectionFactory =
1209 new PoolableConnectionFactory(driverConnectionFactory,
1210 connectionPool,
1211 statementPoolFactory,
1212 validationQuery,
1213 defaultReadOnly,
1214 defaultAutoCommit,
1215 defaultTransactionIsolation,
1216 defaultCatalog,
1217 abandonedConfig);
1218 if (connectionFactory == null) {
1219 throw new SQLException("Cannot create PoolableConnectionFactory");
1220 }
1221 validateConnectionFactory(connectionFactory);
1222 } catch (RuntimeException e) {
1223 throw e;
1224 } catch (Exception e) {
1225 throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
1226 }
1227
1228 // Create and return the pooling data source to manage the connections
1229 dataSource = new PoolingDataSource(connectionPool);
1230 ((PoolingDataSource) dataSource).setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
1231 dataSource.setLogWriter(logWriter);
1232
1233 try {
1234 for (int i = 0 ; i < initialSize ; i++) {
1235 connectionPool.addObject();
1236 }
1237 } catch (Exception e) {
1238 throw new SQLNestedException("Error preloading the connection pool", e);
1239 }
1240
1241 return dataSource;
1242 }
1243
1244 private static void validateConnectionFactory(PoolableConnectionFactory connectionFactory) throws Exception {
1245 Connection conn = null;
1246 try {
1247 conn = (Connection) connectionFactory.makeObject();
1248 connectionFactory.activateObject(conn);
1249 connectionFactory.validateConnection(conn);
1250 connectionFactory.passivateObject(conn);
1251 }
1252 finally {
1253 connectionFactory.destroyObject(conn);
1254 }
1255 }
1256
1257 /**
1258 * Not used currently
1259 */
1260 private void restart() {
1261 try {
1262 close();
1263 } catch (SQLException e) {
1264 log("Could not restart DataSource, cause: " + e.getMessage());
1265 }
1266 }
1267
1268 private void log(String message) {
1269 if (logWriter != null) {
1270 logWriter.println(message);
1271 }
1272 }
1273 }