Source code: org/apache/commons/dbcp/BasicDataSource.java
1 /*
2 * Copyright 1999-2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.commons.dbcp;
18
19 import java.io.PrintWriter;
20 import java.util.Properties;
21 import java.sql.Connection;
22 import java.sql.Driver;
23 import java.sql.DriverManager;
24 import java.sql.SQLException;
25 import javax.sql.DataSource;
26
27 import org.apache.commons.pool.impl.GenericKeyedObjectPool;
28 import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory;
29 import org.apache.commons.pool.impl.GenericObjectPool;
30
31
32 /**
33 * <p>Basic implementation of <code>javax.sql.DataSource</code> that is
34 * configured via JavaBeans properties. This is not the only way to
35 * combine the <em>commons-dbcp</em> and <em>commons-pool</em> packages,
36 * but provides a "one stop shopping" solution for basic requirements.</p>
37 *
38 * @author Glenn L. Nielsen
39 * @author Craig R. McClanahan
40 * @author Dirk Verbeeck
41 * @version $Revision: 1.37 $ $Date: 2004/06/09 18:21:23 $
42 */
43 public class BasicDataSource implements DataSource {
44
45 // ------------------------------------------------------------- Properties
46
47 /**
48 * The default auto-commit state of connections created by this pool.
49 */
50 protected boolean defaultAutoCommit = true;
51
52 public synchronized boolean getDefaultAutoCommit() {
53 return this.defaultAutoCommit;
54 }
55
56 public synchronized void setDefaultAutoCommit(boolean defaultAutoCommit) {
57 this.defaultAutoCommit = defaultAutoCommit;
58 this.restartNeeded = true;
59 }
60
61
62 /**
63 * The default read-only state of connections created by this pool.
64 */
65 protected Boolean defaultReadOnly = null;
66
67 public synchronized boolean getDefaultReadOnly() {
68 if (this.defaultReadOnly != null) {
69 return this.defaultReadOnly.booleanValue();
70 }
71 return false;
72 }
73
74 public synchronized void setDefaultReadOnly(boolean defaultReadOnly) {
75 this.defaultReadOnly = defaultReadOnly ? Boolean.TRUE : Boolean.FALSE;
76 this.restartNeeded = true;
77 }
78
79 /**
80 * The default TransactionIsolation state of connections created by this pool.
81 */
82 protected int defaultTransactionIsolation = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
83
84 public synchronized int getDefaultTransactionIsolation() {
85 return this.defaultTransactionIsolation;
86 }
87
88 public synchronized void setDefaultTransactionIsolation(int defaultTransactionIsolation) {
89 this.defaultTransactionIsolation = defaultTransactionIsolation;
90 this.restartNeeded = true;
91 }
92
93
94 /**
95 * The default "catalog" of connections created by this pool.
96 */
97 protected String defaultCatalog = null;
98
99 public synchronized String getDefaultCatalog() {
100 return this.defaultCatalog;
101 }
102
103 public synchronized void setDefaultCatalog(String defaultCatalog) {
104 if ((defaultCatalog != null) && (defaultCatalog.trim().length() > 0)) {
105 this.defaultCatalog = defaultCatalog;
106 }
107 else {
108 this.defaultCatalog = null;
109 }
110 this.restartNeeded = true;
111 }
112
113
114 /**
115 * The fully qualified Java class name of the JDBC driver to be used.
116 */
117 protected String driverClassName = null;
118
119 public synchronized String getDriverClassName() {
120 return this.driverClassName;
121 }
122
123 public synchronized void setDriverClassName(String driverClassName) {
124 if ((driverClassName != null) && (driverClassName.trim().length() > 0)) {
125 this.driverClassName = driverClassName;
126 }
127 else {
128 this.driverClassName = null;
129 }
130 this.restartNeeded = true;
131 }
132
133
134 /**
135 * The maximum number of active connections that can be allocated from
136 * this pool at the same time, or zero for no limit.
137 */
138 protected int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
139
140 public synchronized int getMaxActive() {
141 return this.maxActive;
142 }
143
144 public synchronized void setMaxActive(int maxActive) {
145 this.maxActive = maxActive;
146 if (connectionPool != null) {
147 connectionPool.setMaxActive(maxActive);
148 }
149 }
150
151
152 /**
153 * The maximum number of active connections that can remain idle in the
154 * pool, without extra ones being released, or zero for no limit.
155 */
156 protected int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;;
157
158 public synchronized int getMaxIdle() {
159 return this.maxIdle;
160 }
161
162 public synchronized void setMaxIdle(int maxIdle) {
163 this.maxIdle = maxIdle;
164 if (connectionPool != null) {
165 connectionPool.setMaxIdle(maxIdle);
166 }
167 }
168
169 /**
170 * The minimum number of active connections that can remain idle in the
171 * pool, without extra ones being created, or 0 to create none.
172 */
173 protected int minIdle = GenericObjectPool.DEFAULT_MIN_IDLE;;
174
175 public synchronized int getMinIdle() {
176 return this.minIdle;
177 }
178
179 public synchronized void setMinIdle(int minIdle) {
180 this.minIdle = minIdle;
181 if (connectionPool != null) {
182 connectionPool.setMinIdle(minIdle);
183 }
184 }
185
186 /**
187 * The initial number of connections that are created when the pool
188 * is started.
189 * @since 1.2
190 */
191 protected int initialSize = 0;
192
193 public synchronized int getInitialSize() {
194 return this.initialSize;
195 }
196
197 public synchronized void setInitialSize(int initialSize) {
198 this.initialSize = initialSize;
199 this.restartNeeded = true;
200 }
201
202 /**
203 * The maximum number of milliseconds that the pool will wait (when there
204 * are no available connections) for a connection to be returned before
205 * throwing an exception, or -1 to wait indefinitely.
206 */
207 protected long maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;
208
209 public synchronized long getMaxWait() {
210 return this.maxWait;
211 }
212
213 public synchronized void setMaxWait(long maxWait) {
214 this.maxWait = maxWait;
215 if (connectionPool != null) {
216 connectionPool.setMaxWait(maxWait);
217 }
218 }
219
220 /**
221 * Prepared statement pooling for this pool.
222 */
223 protected boolean poolPreparedStatements = false;
224
225 /**
226 * Returns true if we are pooling statements.
227 * @return boolean
228 */
229 public synchronized boolean isPoolPreparedStatements() {
230 return this.poolPreparedStatements;
231 }
232
233 /**
234 * Sets whether to pool statements or not.
235 * @param poolPreparedStatements pooling on or off
236 */
237 public synchronized void setPoolPreparedStatements(boolean poolingStatements) {
238 this.poolPreparedStatements = poolingStatements;
239 this.restartNeeded = true;
240 }
241
242 /**
243 * The maximum number of open statements that can be allocated from
244 * the statement pool at the same time, or zero for no limit. Since
245 * a connection usually only uses one or two statements at a time, this is
246 * mostly used to help detect resource leaks.
247 */
248 protected int maxOpenPreparedStatements = GenericKeyedObjectPool.DEFAULT_MAX_TOTAL;
249
250 public synchronized int getMaxOpenPreparedStatements() {
251 return this.maxOpenPreparedStatements;
252 }
253
254 public synchronized void setMaxOpenPreparedStatements(int maxOpenStatements) {
255 this.maxOpenPreparedStatements = maxOpenStatements;
256 this.restartNeeded = true;
257 }
258
259 /**
260 * The indication of whether objects will be validated before being
261 * borrowed from the pool. If the object fails to validate, it will be
262 * dropped from the pool, and we will attempt to borrow another.
263 */
264 protected boolean testOnBorrow = true;
265
266 public synchronized boolean getTestOnBorrow() {
267 return this.testOnBorrow;
268 }
269
270 public synchronized void setTestOnBorrow(boolean testOnBorrow) {
271 this.testOnBorrow = testOnBorrow;
272 if (connectionPool != null) {
273 connectionPool.setTestOnBorrow(testOnBorrow);
274 }
275 }
276
277 /**
278 * The indication of whether objects will be validated before being
279 * returned to the pool.
280 */
281 protected boolean testOnReturn = false;
282
283 public synchronized boolean getTestOnReturn() {
284 return this.testOnReturn;
285 }
286
287 public synchronized void setTestOnReturn(boolean testOnReturn) {
288 this.testOnReturn = testOnReturn;
289 if (connectionPool != null) {
290 connectionPool.setTestOnReturn(testOnReturn);
291 }
292 }
293
294
295 /**
296 * The number of milliseconds to sleep between runs of the idle object
297 * evictor thread. When non-positive, no idle object evictor thread will
298 * be run.
299 */
300 protected long timeBetweenEvictionRunsMillis =
301 GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
302
303 public synchronized long getTimeBetweenEvictionRunsMillis() {
304 return this.timeBetweenEvictionRunsMillis;
305 }
306
307 public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
308 this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
309 if (connectionPool != null) {
310 connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
311 }
312 }
313
314
315 /**
316 * The number of objects to examine during each run of the idle object
317 * evictor thread (if any).
318 */
319 protected int numTestsPerEvictionRun =
320 GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
321
322 public synchronized int getNumTestsPerEvictionRun() {
323 return this.numTestsPerEvictionRun;
324 }
325
326 public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
327 this.numTestsPerEvictionRun = numTestsPerEvictionRun;
328 if (connectionPool != null) {
329 connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
330 }
331 }
332
333
334 /**
335 * The minimum amount of time an object may sit idle in the pool before it
336 * is eligable for eviction by the idle object evictor (if any).
337 */
338 protected long minEvictableIdleTimeMillis =
339 GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
340
341 public synchronized long getMinEvictableIdleTimeMillis() {
342 return this.minEvictableIdleTimeMillis;
343 }
344
345 public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
346 this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
347 if (connectionPool != null) {
348 connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
349 }
350 }
351
352 /**
353 * The indication of whether objects will be validated by the idle object
354 * evictor (if any). If an object fails to validate, it will be dropped
355 * from the pool.
356 */
357 protected boolean testWhileIdle = false;
358
359 public synchronized boolean getTestWhileIdle() {
360 return this.testWhileIdle;
361 }
362
363 public synchronized void setTestWhileIdle(boolean testWhileIdle) {
364 this.testWhileIdle = testWhileIdle;
365 if (connectionPool != null) {
366 connectionPool.setTestWhileIdle(testWhileIdle);
367 }
368 }
369
370 /**
371 * [Read Only] The current number of active connections that have been
372 * allocated from this data source.
373 */
374 public synchronized int getNumActive() {
375 if (connectionPool != null) {
376 return connectionPool.getNumActive();
377 } else {
378 return 0;
379 }
380 }
381
382
383 /**
384 * [Read Only] The current number of idle connections that are waiting
385 * to be allocated from this data source.
386 */
387 public synchronized int getNumIdle() {
388 if (connectionPool != null) {
389 return connectionPool.getNumIdle();
390 } else {
391 return 0;
392 }
393 }
394
395
396 /**
397 * The connection password to be passed to our JDBC driver to establish
398 * a connection.
399 */
400 protected String password = null;
401
402 public synchronized String getPassword() {
403 return this.password;
404 }
405
406 public synchronized void setPassword(String password) {
407 this.password = password;
408 this.restartNeeded = true;
409 }
410
411
412 /**
413 * The connection URL to be passed to our JDBC driver to establish
414 * a connection.
415 */
416 protected String url = null;
417
418 public synchronized String getUrl() {
419 return this.url;
420 }
421
422 public synchronized void setUrl(String url) {
423 this.url = url;
424 this.restartNeeded = true;
425 }
426
427
428 /**
429 * The connection username to be passed to our JDBC driver to
430 * establish a connection.
431 */
432 protected String username = null;
433
434 public synchronized String getUsername() {
435 return this.username;
436 }
437
438 public synchronized void setUsername(String username) {
439 this.username = username;
440 this.restartNeeded = true;
441 }
442
443
444 /**
445 * The SQL query that will be used to validate connections from this pool
446 * before returning them to the caller. If specified, this query
447 * <strong>MUST</strong> be an SQL SELECT statement that returns at least
448 * one row.
449 */
450 protected String validationQuery = null;
451
452 public synchronized String getValidationQuery() {
453 return this.validationQuery;
454 }
455
456 public synchronized void setValidationQuery(String validationQuery) {
457 if ((validationQuery != null) && (validationQuery.trim().length() > 0)) {
458 this.validationQuery = validationQuery;
459 } else {
460 this.validationQuery = null;
461 }
462 this.restartNeeded = true;
463 }
464
465 /**
466 * Controls access to the underlying connection
467 */
468 private boolean accessToUnderlyingConnectionAllowed = false;
469
470 /**
471 * Returns the value of the accessToUnderlyingConnectionAllowed property.
472 *
473 * @return true if access to the underlying is allowed, false otherwise.
474 */
475 public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
476 return this.accessToUnderlyingConnectionAllowed;
477 }
478
479 /**
480 * Sets the value of the accessToUnderlyingConnectionAllowed property.
481 * It controls if the PoolGuard allows access to the underlying connection.
482 * (Default: false)
483 *
484 * @param allow Access to the underlying connection is granted when true.
485 */
486 public synchronized void setAccessToUnderlyingConnectionAllowed(boolean allow) {
487 this.accessToUnderlyingConnectionAllowed = allow;
488 this.restartNeeded = true;
489 }
490
491 // ----------------------------------------------------- Instance Variables
492
493 // TODO: review & make isRestartNeeded() public, restartNeeded protected
494
495 private boolean restartNeeded = false;
496
497 /**
498 * Returns whether or not a restart is needed.
499 * @return true if a restart is needed
500 */
501 private synchronized boolean isRestartNeeded() {
502 return restartNeeded;
503 }
504
505 /**
506 * The object pool that internally manages our connections.
507 */
508 protected GenericObjectPool connectionPool = null;
509
510 /**
511 * The connection properties that will be sent to our JDBC driver when
512 * establishing new connections. <strong>NOTE</strong> - The "user" and
513 * "password" properties will be passed explicitly, so they do not need
514 * to be included here.
515 */
516 protected Properties connectionProperties = new Properties();
517
518 /**
519 * The data source we will use to manage connections. This object should
520 * be acquired <strong>ONLY</strong> by calls to the
521 * <code>createDataSource()</code> method.
522 */
523 protected DataSource dataSource = null;
524
525 /**
526 * The PrintWriter to which log messages should be directed.
527 */
528 protected PrintWriter logWriter = new PrintWriter(System.out);
529
530
531 // ----------------------------------------------------- DataSource Methods
532
533
534 /**
535 * Create (if necessary) and return a connection to the database.
536 *
537 * @exception SQLException if a database access error occurs
538 */
539 public Connection getConnection() throws SQLException {
540 return createDataSource().getConnection();
541 }
542
543
544 /**
545 * Create (if necessary) and return a connection to the database.
546 *
547 * @param username Database user on whose behalf the Connection
548 * is being made
549 * @param password The database user's password
550 *
551 * @exception SQLException if a database access error occurs
552 */
553 public Connection getConnection(String username, String password) throws SQLException {
554 return createDataSource().getConnection(username, password);
555 }
556
557
558 /**
559 * Return the login timeout (in seconds) for connecting to the database.
560 *
561 * @exception SQLException if a database access error occurs
562 */
563 public int getLoginTimeout() throws SQLException {
564 return createDataSource().getLoginTimeout();
565 }
566
567
568 /**
569 * Return the log writer being used by this data source.
570 *
571 * @exception SQLException if a database access error occurs
572 */
573 public PrintWriter getLogWriter() throws SQLException {
574 return createDataSource().getLogWriter();
575 }
576
577
578 /**
579 * Set the login timeout (in seconds) for connecting to the database.
580 *
581 * @param loginTimeout The new login timeout, or zero for no timeout
582 *
583 * @exception SQLException if a database access error occurs
584 */
585 public void setLoginTimeout(int loginTimeout) throws SQLException {
586 createDataSource().setLoginTimeout(loginTimeout);
587 }
588
589
590 /**
591 * Set the log writer being used by this data source.
592 *
593 * @param logWriter The new log writer
594 *
595 * @exception SQLException if a database access error occurs
596 */
597 public void setLogWriter(PrintWriter logWriter) throws SQLException {
598 createDataSource().setLogWriter(logWriter);
599 this.logWriter = logWriter;
600 }
601
602 private AbandonedConfig abandonedConfig;
603
604 /**
605 * Flag to remove abandoned connections if they exceed the
606 * removeAbandonedTimout.
607 *
608 * Set to true or false, default false.
609 * If set to true a connection is considered abandoned and eligible
610 * for removal if it has been idle longer than the removeAbandonedTimeout.
611 * Setting this to true can recover db connections from poorly written
612 * applications which fail to close a connection.
613 * @deprecated
614 */
615 public boolean getRemoveAbandoned() {
616 if (abandonedConfig != null) {
617 return abandonedConfig.getRemoveAbandoned();
618 }
619 return false;
620 }
621
622 /**
623 * @deprecated
624 * @param removeAbandoned
625 */
626 public void setRemoveAbandoned(boolean removeAbandoned) {
627 if (abandonedConfig == null) {
628 abandonedConfig = new AbandonedConfig();
629 }
630 abandonedConfig.setRemoveAbandoned(removeAbandoned);
631 this.restartNeeded = true;
632 }
633
634 /**
635 * Timeout in seconds before an abandoned connection can be removed.
636 *
637 * Defaults to 300 seconds.
638 * @deprecated
639 */
640 public int getRemoveAbandonedTimeout() {
641 if (abandonedConfig != null) {
642 return abandonedConfig.getRemoveAbandonedTimeout();
643 }
644 return 300;
645 }
646
647 /**
648 * @deprecated
649 * @param removeAbandonedTimeout
650 */
651 public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {
652 if (abandonedConfig == null) {
653 abandonedConfig = new AbandonedConfig();
654 }
655 abandonedConfig.setRemoveAbandonedTimeout(removeAbandonedTimeout);
656 this.restartNeeded = true;
657 }
658
659 /**
660 * Flag to log stack traces for application code which abandoned
661 * a Statement or Connection.
662 *
663 * Defaults to false.
664 *
665 * Logging of abandoned Statements and Connections adds overhead
666 * for every Connection open or new Statement because a stack
667 * trace has to be generated.
668 * @deprecated
669 */
670 public boolean getLogAbandoned() {
671 if (abandonedConfig != null) {
672 return abandonedConfig.getLogAbandoned();
673 }
674 return false;
675 }
676
677 /**
678 * @deprecated
679 * @param logAbandoned
680 */
681 public void setLogAbandoned(boolean logAbandoned) {
682 if (abandonedConfig == null) {
683 abandonedConfig = new AbandonedConfig();
684 }
685 abandonedConfig.setLogAbandoned(logAbandoned);
686 this.restartNeeded = true;
687 }
688
689 // --------------------------------------------------------- Public Methods
690
691 /**
692 * Add a custom connection property to the set that will be passed to our
693 * JDBC driver. This <strong>MUST</strong> be called before the first
694 * connection is retrieved (along with all the other configuration
695 * property setters).
696 *
697 * @param name Name of the custom connection property
698 * @param value Value of the custom connection property
699 */
700 public void addConnectionProperty(String name, String value) {
701 connectionProperties.put(name, value);
702 this.restartNeeded = true;
703 }
704
705 public void removeConnectionProperty(String name) {
706 connectionProperties.remove(name);
707 this.restartNeeded = true;
708 }
709
710 /**
711 * Close and release all connections that are currently stored in the
712 * connection pool associated with our data source.
713 *
714 * @exception SQLException if a database error occurs
715 */
716 public synchronized void close() throws SQLException {
717 GenericObjectPool oldpool = connectionPool;
718 connectionPool = null;
719 dataSource = null;
720 try {
721 if (oldpool != null) {
722 oldpool.close();
723 }
724 } catch(SQLException e) {
725 throw e;
726 } catch(RuntimeException e) {
727 throw e;
728 } catch(Exception e) {
729 throw new SQLNestedException("Cannot close connection pool", e);
730 }
731 }
732
733
734 // ------------------------------------------------------ Protected Methods
735
736
737 /**
738 * <p>Create (if necessary) and return the internal data source we are
739 * using to manage our connections.</p>
740 *
741 * <p><strong>IMPLEMENTATION NOTE</strong> - It is tempting to use the
742 * "double checked locking" idiom in an attempt to avoid synchronizing
743 * on every single call to this method. However, this idiom fails to
744 * work correctly in the face of some optimizations that are legal for
745 * a JVM to perform.</p>
746 *
747 * @exception SQLException if the object pool cannot be created.
748 */
749 protected synchronized DataSource createDataSource()
750 throws SQLException {
751
752 // Return the pool if we have already created it
753 if (dataSource != null) {
754 return (dataSource);
755 }
756
757 // Load the JDBC driver class
758 if (driverClassName != null) {
759 try {
760 Class.forName(driverClassName);
761 } catch (Throwable t) {
762 String message = "Cannot load JDBC driver class '" +
763 driverClassName + "'";
764 logWriter.println(message);
765 t.printStackTrace(logWriter);
766 throw new SQLNestedException(message, t);
767 }
768 }
769
770 // Create a JDBC driver instance
771 Driver driver = null;
772 try {
773 driver = DriverManager.getDriver(url);
774 } catch (Throwable t) {
775 String message = "Cannot create JDBC driver of class '" +
776 (driverClassName != null ? driverClassName : "") +
777 "' for connect URL '" + url + "'";
778 logWriter.println(message);
779 t.printStackTrace(logWriter);
780 throw new SQLNestedException(message, t);
781 }
782
783 // Can't test without a validationQuery
784 if (validationQuery == null) {
785 setTestOnBorrow(false);
786 setTestOnReturn(false);
787 setTestWhileIdle(false);
788 }
789
790 // Create an object pool to contain our active connections
791 if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned() == true)) {
792 connectionPool = new AbandonedObjectPool(null,abandonedConfig);
793 }
794 else {
795 connectionPool = new GenericObjectPool();
796 }
797 connectionPool.setMaxActive(maxActive);
798 connectionPool.setMaxIdle(maxIdle);
799 connectionPool.setMinIdle(minIdle);
800 connectionPool.setMaxWait(maxWait);
801 connectionPool.setTestOnBorrow(testOnBorrow);
802 connectionPool.setTestOnReturn(testOnReturn);
803 connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
804 connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
805 connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
806 connectionPool.setTestWhileIdle(testWhileIdle);
807
808 // Set up statement pool, if desired
809 GenericKeyedObjectPoolFactory statementPoolFactory = null;
810 if (isPoolPreparedStatements()) {
811 statementPoolFactory = new GenericKeyedObjectPoolFactory(null,
812 -1, // unlimited maxActive (per key)
813 GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL,
814 0, // maxWait
815 1, // maxIdle (per key)
816 maxOpenPreparedStatements);
817 }
818
819 // Set up the driver connection factory we will use
820 if (username != null) {
821 connectionProperties.put("user", username);
822 } else {
823 log("DBCP DataSource configured without a 'username'");
824 }
825
826 if (password != null) {
827 connectionProperties.put("password", password);
828 } else {
829 log("DBCP DataSource configured without a 'password'");
830 }
831
832 DriverConnectionFactory driverConnectionFactory =
833 new DriverConnectionFactory(driver, url, connectionProperties);
834
835 // Set up the poolable connection factory we will use
836 PoolableConnectionFactory connectionFactory = null;
837 try {
838 connectionFactory =
839 new PoolableConnectionFactory(driverConnectionFactory,
840 connectionPool,
841 statementPoolFactory,
842 validationQuery,
843 defaultReadOnly,
844 defaultAutoCommit,
845 defaultTransactionIsolation,
846 defaultCatalog,
847 abandonedConfig);
848 if (connectionFactory == null) {
849 throw new SQLException("Cannot create PoolableConnectionFactory");
850 }
851 validateConnectionFactory(connectionFactory);
852 } catch (RuntimeException e) {
853 throw e;
854 } catch (Exception e) {
855 throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
856 }
857
858 // Create and return the pooling data source to manage the connections
859 dataSource = new PoolingDataSource(connectionPool);
860 ((PoolingDataSource) dataSource).setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
861 dataSource.setLogWriter(logWriter);
862
863 try {
864 for (int i = 0 ; i < initialSize ; i++) {
865 connectionPool.addObject();
866 }
867 } catch (Exception e) {
868 throw new SQLNestedException("Error preloading the connection pool", e);
869 }
870
871 return dataSource;
872 }
873
874 private static void validateConnectionFactory(PoolableConnectionFactory connectionFactory) throws Exception {
875 Connection conn = null;
876 try {
877 conn = (Connection) connectionFactory.makeObject();
878 connectionFactory.activateObject(conn);
879 connectionFactory.validateConnection(conn);
880 connectionFactory.passivateObject(conn);
881 }
882 finally {
883 connectionFactory.destroyObject(conn);
884 }
885 }
886
887 private void restart() {
888 try {
889 close();
890 } catch (SQLException e) {
891 log("Could not restart DataSource, cause: " + e.getMessage());
892 }
893 }
894
895 private void log(String message) {
896 if (logWriter != null) {
897 logWriter.println(message);
898 }
899 }
900 }