0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.commons.dbcp;
0019:
0020: import java.io.PrintWriter;
0021: import java.util.Properties;
0022: import java.sql.Connection;
0023: import java.sql.Driver;
0024: import java.sql.DriverManager;
0025: import java.sql.SQLException;
0026: import javax.sql.DataSource;
0027:
0028: import org.apache.commons.pool.impl.GenericKeyedObjectPool;
0029: import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory;
0030: import org.apache.commons.pool.impl.GenericObjectPool;
0031:
0032: /**
0033: * <p>Basic implementation of <code>javax.sql.DataSource</code> that is
0034: * configured via JavaBeans properties. This is not the only way to
0035: * combine the <em>commons-dbcp</em> and <em>commons-pool</em> packages,
0036: * but provides a "one stop shopping" solution for basic requirements.</p>
0037: *
0038: * @author Glenn L. Nielsen
0039: * @author Craig R. McClanahan
0040: * @author Dirk Verbeeck
0041: * @version $Revision: 506087 $ $Date: 2007-02-11 11:37:43 -0700 (Sun, 11 Feb 2007) $
0042: */
0043: public class BasicDataSource implements DataSource {
0044:
0045: // ------------------------------------------------------------- Properties
0046:
0047: /**
0048: * The default auto-commit state of connections created by this pool.
0049: */
0050: protected boolean defaultAutoCommit = true;
0051:
0052: /**
0053: * Returns the default auto-commit property.
0054: *
0055: * @return true if default auto-commit is enabled
0056: */
0057: public synchronized boolean getDefaultAutoCommit() {
0058: return this .defaultAutoCommit;
0059: }
0060:
0061: /**
0062: * <p>Sets default auto-commit state of connections returned by this
0063: * datasource.</p>
0064: * <p>
0065: * Note: this method currently has no effect once the pool has been
0066: * initialized. The pool is initialized the first time one of the
0067: * following methods is invoked: <code>getConnection, setLogwriter,
0068: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0069: *
0070: * @param defaultAutoCommit default auto-commit value
0071: */
0072: public synchronized void setDefaultAutoCommit(
0073: boolean defaultAutoCommit) {
0074: this .defaultAutoCommit = defaultAutoCommit;
0075: this .restartNeeded = true;
0076: }
0077:
0078: /**
0079: * The default read-only state of connections created by this pool.
0080: */
0081: protected Boolean defaultReadOnly = null;
0082:
0083: /**
0084: * Returns the default readOnly property.
0085: *
0086: * @return true if connections are readOnly by default
0087: */
0088: public synchronized boolean getDefaultReadOnly() {
0089: if (this .defaultReadOnly != null) {
0090: return this .defaultReadOnly.booleanValue();
0091: }
0092: return false;
0093: }
0094:
0095: /**
0096: * <p>Sets defaultReadonly property.</p>
0097: * <p>
0098: * Note: this method currently has no effect once the pool has been
0099: * initialized. The pool is initialized the first time one of the
0100: * following methods is invoked: <code>getConnection, setLogwriter,
0101: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0102: *
0103: * @param defaultReadOnly default read-only value
0104: */
0105: public synchronized void setDefaultReadOnly(boolean defaultReadOnly) {
0106: this .defaultReadOnly = defaultReadOnly ? Boolean.TRUE
0107: : Boolean.FALSE;
0108: this .restartNeeded = true;
0109: }
0110:
0111: /**
0112: * The default TransactionIsolation state of connections created by this pool.
0113: */
0114: protected int defaultTransactionIsolation = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
0115:
0116: /**
0117: * Returns the default transaction isolation state of returned connections.
0118: *
0119: * @return the default value for transaction isolation state
0120: * @see Connection#getTransactionIsolation
0121: */
0122: public synchronized int getDefaultTransactionIsolation() {
0123: return this .defaultTransactionIsolation;
0124: }
0125:
0126: /**
0127: * <p>Sets the default transaction isolation state for returned
0128: * connections.</p>
0129: * <p>
0130: * Note: this method currently has no effect once the pool has been
0131: * initialized. The pool is initialized the first time one of the
0132: * following methods is invoked: <code>getConnection, setLogwriter,
0133: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0134: *
0135: * @param defaultTransactionIsolation the default transaction isolation
0136: * state
0137: * @see Connection#getTransactionIsolation
0138: */
0139: public synchronized void setDefaultTransactionIsolation(
0140: int defaultTransactionIsolation) {
0141: this .defaultTransactionIsolation = defaultTransactionIsolation;
0142: this .restartNeeded = true;
0143: }
0144:
0145: /**
0146: * The default "catalog" of connections created by this pool.
0147: */
0148: protected String defaultCatalog = null;
0149:
0150: /**
0151: * Returns the default catalog.
0152: *
0153: * @return the default catalog
0154: */
0155: public synchronized String getDefaultCatalog() {
0156: return this .defaultCatalog;
0157: }
0158:
0159: /**
0160: * <p>Sets the default catalog.</p>
0161: * <p>
0162: * Note: this method currently has no effect once the pool has been
0163: * initialized. The pool is initialized the first time one of the
0164: * following methods is invoked: <code>getConnection, setLogwriter,
0165: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0166: *
0167: * @param defaultCatalog the default catalog
0168: */
0169: public synchronized void setDefaultCatalog(String defaultCatalog) {
0170: if ((defaultCatalog != null)
0171: && (defaultCatalog.trim().length() > 0)) {
0172: this .defaultCatalog = defaultCatalog;
0173: } else {
0174: this .defaultCatalog = null;
0175: }
0176: this .restartNeeded = true;
0177: }
0178:
0179: /**
0180: * The fully qualified Java class name of the JDBC driver to be used.
0181: */
0182: protected String driverClassName = null;
0183:
0184: /**
0185: * Returns the jdbc driver class name.
0186: *
0187: * @return the jdbc driver class name
0188: */
0189: public synchronized String getDriverClassName() {
0190: return this .driverClassName;
0191: }
0192:
0193: /**
0194: * <p>Sets the jdbc driver class name.</p>
0195: * <p>
0196: * Note: this method currently has no effect once the pool has been
0197: * initialized. The pool is initialized the first time one of the
0198: * following methods is invoked: <code>getConnection, setLogwriter,
0199: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0200: *
0201: * @param driverClassName the class name of the jdbc driver
0202: */
0203: public synchronized void setDriverClassName(String driverClassName) {
0204: if ((driverClassName != null)
0205: && (driverClassName.trim().length() > 0)) {
0206: this .driverClassName = driverClassName;
0207: } else {
0208: this .driverClassName = null;
0209: }
0210: this .restartNeeded = true;
0211: }
0212:
0213: /**
0214: * The maximum number of active connections that can be allocated from
0215: * this pool at the same time, or non-positive for no limit.
0216: */
0217: protected int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
0218:
0219: /**
0220: * <p>Returns the maximum number of active connections that can be
0221: * allocated at the same time.
0222: * </p>
0223: * <p>A non-positive number means that there is no limit.</p>
0224: *
0225: * @return the maximum number of active connections
0226: */
0227: public synchronized int getMaxActive() {
0228: return this .maxActive;
0229: }
0230:
0231: /**
0232: * Sets the maximum number of active connections that can be
0233: * allocated at the same time.
0234: *
0235: * @param maxActive the new value for maxActive
0236: * @see #getMaxActive()
0237: */
0238: public synchronized void setMaxActive(int maxActive) {
0239: this .maxActive = maxActive;
0240: if (connectionPool != null) {
0241: connectionPool.setMaxActive(maxActive);
0242: }
0243: }
0244:
0245: /**
0246: * The maximum number of connections that can remain idle in the
0247: * pool, without extra ones being released, or negative for no limit.
0248: */
0249: protected int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
0250:
0251: /**
0252: * <p>Returns the maximum number of connections that can remain idle in the
0253: * pool.
0254: * </p>
0255: * <p>A negative value indicates that there is no limit</p>
0256: *
0257: * @return the maximum number of idle connections
0258: */
0259: public synchronized int getMaxIdle() {
0260: return this .maxIdle;
0261: }
0262:
0263: /**
0264: * Sets the maximum number of connections that can remail idle in the
0265: * pool.
0266: *
0267: * @see #getMaxIdle()
0268: * @param maxIdle the new value for maxIdle
0269: */
0270: public synchronized void setMaxIdle(int maxIdle) {
0271: this .maxIdle = maxIdle;
0272: if (connectionPool != null) {
0273: connectionPool.setMaxIdle(maxIdle);
0274: }
0275: }
0276:
0277: /**
0278: * The minimum number of active connections that can remain idle in the
0279: * pool, without extra ones being created, or 0 to create none.
0280: */
0281: protected int minIdle = GenericObjectPool.DEFAULT_MIN_IDLE;
0282:
0283: /**
0284: * Returns the minimum number of idle connections in the pool
0285: *
0286: * @return the minimum number of idle connections
0287: * @see GenericObjectPool#getMinIdle()
0288: */
0289: public synchronized int getMinIdle() {
0290: return this .minIdle;
0291: }
0292:
0293: /**
0294: * Sets the minimum number of idle connections in the pool.
0295: *
0296: * @param minIdle the new value for minIdle
0297: * @see GenericObjectPool#setMinIdle(int)
0298: */
0299: public synchronized void setMinIdle(int minIdle) {
0300: this .minIdle = minIdle;
0301: if (connectionPool != null) {
0302: connectionPool.setMinIdle(minIdle);
0303: }
0304: }
0305:
0306: /**
0307: * The initial number of connections that are created when the pool
0308: * is started.
0309: *
0310: * @since 1.2
0311: */
0312: protected int initialSize = 0;
0313:
0314: /**
0315: * Returns the initial size of the connection pool.
0316: *
0317: * @return the number of connections created when the pool is initialized
0318: */
0319: public synchronized int getInitialSize() {
0320: return this .initialSize;
0321: }
0322:
0323: /**
0324: * <p>Sets the initial size of the connection pool.</p>
0325: * <p>
0326: * Note: this method currently has no effect once the pool has been
0327: * initialized. The pool is initialized the first time one of the
0328: * following methods is invoked: <code>getConnection, setLogwriter,
0329: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0330: *
0331: * @param initialSize the number of connections created when the pool
0332: * is initialized
0333: */
0334: public synchronized void setInitialSize(int initialSize) {
0335: this .initialSize = initialSize;
0336: this .restartNeeded = true;
0337: }
0338:
0339: /**
0340: * The maximum number of milliseconds that the pool will wait (when there
0341: * are no available connections) for a connection to be returned before
0342: * throwing an exception, or -1 to wait indefinitely.
0343: */
0344: protected long maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;
0345:
0346: /**
0347: * <p>Returns the maximum number of milliseconds that the pool will wait
0348: * for a connection to be returned before throwing an exception.
0349: * </p>
0350: * <p>Returns -1 if the pool is set to wait indefinitely.</p>
0351: *
0352: * @return the maxWait property value
0353: */
0354: public synchronized long getMaxWait() {
0355: return this .maxWait;
0356: }
0357:
0358: /**
0359: * Sets the maxWait property.
0360: *
0361: * @param maxWait the new value for maxWait
0362: * @see #getMaxWait()
0363: */
0364: public synchronized void setMaxWait(long maxWait) {
0365: this .maxWait = maxWait;
0366: if (connectionPool != null) {
0367: connectionPool.setMaxWait(maxWait);
0368: }
0369: }
0370:
0371: /**
0372: * Prepared statement pooling for this pool.
0373: */
0374: protected boolean poolPreparedStatements = false;
0375:
0376: /**
0377: * Returns true if we are pooling statements.
0378: *
0379: * @return true if prepared statements are pooled
0380: */
0381: public synchronized boolean isPoolPreparedStatements() {
0382: return this .poolPreparedStatements;
0383: }
0384:
0385: /**
0386: * <p>Sets whether to pool statements or not.</p>
0387: * <p>
0388: * Note: this method currently has no effect once the pool has been
0389: * initialized. The pool is initialized the first time one of the
0390: * following methods is invoked: <code>getConnection, setLogwriter,
0391: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0392: *
0393: * @param poolingStatements pooling on or off
0394: */
0395: public synchronized void setPoolPreparedStatements(
0396: boolean poolingStatements) {
0397: this .poolPreparedStatements = poolingStatements;
0398: this .restartNeeded = true;
0399: }
0400:
0401: /**
0402: * The maximum number of open statements that can be allocated from
0403: * the statement pool at the same time, or non-positive for no limit. Since
0404: * a connection usually only uses one or two statements at a time, this is
0405: * mostly used to help detect resource leaks.
0406: */
0407: protected int maxOpenPreparedStatements = GenericKeyedObjectPool.DEFAULT_MAX_TOTAL;
0408:
0409: /**
0410: * Gets the value of the {@link #maxOpenPreparedStatements} property.
0411: *
0412: * @return the maximum number of open statements
0413: * @see #maxOpenPreparedStatements
0414: */
0415: public synchronized int getMaxOpenPreparedStatements() {
0416: return this .maxOpenPreparedStatements;
0417: }
0418:
0419: /**
0420: * <p>Sets the value of the {@link #maxOpenPreparedStatements}
0421: * property.</p>
0422: * <p>
0423: * Note: this method currently has no effect once the pool has been
0424: * initialized. The pool is initialized the first time one of the
0425: * following methods is invoked: <code>getConnection, setLogwriter,
0426: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0427: *
0428: * @param maxOpenStatements the new maximum number of prepared statements
0429: * @see #maxOpenPreparedStatements
0430: */
0431: public synchronized void setMaxOpenPreparedStatements(
0432: int maxOpenStatements) {
0433: this .maxOpenPreparedStatements = maxOpenStatements;
0434: this .restartNeeded = true;
0435: }
0436:
0437: /**
0438: * The indication of whether objects will be validated before being
0439: * borrowed from the pool. If the object fails to validate, it will be
0440: * dropped from the pool, and we will attempt to borrow another.
0441: */
0442: protected boolean testOnBorrow = true;
0443:
0444: /**
0445: * Returns the {@link #testOnBorrow} property.
0446: *
0447: * @return true if objects are validated before being borrowed from the
0448: * pool
0449: *
0450: * @see #testOnBorrow
0451: */
0452: public synchronized boolean getTestOnBorrow() {
0453: return this .testOnBorrow;
0454: }
0455:
0456: /**
0457: * Sets the {@link #testOnBorrow} property. This property determines
0458: * whether or not the pool will validate objects before they are borrowed
0459: * from the pool. For a <code>true</code> value to have any effect, the
0460: * <code>validationQuery</code> property must be set to a non-null string.
0461: *
0462: * @param testOnBorrow new value for testOnBorrow property
0463: */
0464: public synchronized void setTestOnBorrow(boolean testOnBorrow) {
0465: this .testOnBorrow = testOnBorrow;
0466: if (connectionPool != null) {
0467: connectionPool.setTestOnBorrow(testOnBorrow);
0468: }
0469: }
0470:
0471: /**
0472: * The indication of whether objects will be validated before being
0473: * returned to the pool.
0474: */
0475: protected boolean testOnReturn = false;
0476:
0477: /**
0478: * Returns the value of the {@link #testOnReturn} property.
0479: *
0480: * @return true if objects are validated before being returned to the
0481: * pool
0482: * @see #testOnReturn
0483: */
0484: public synchronized boolean getTestOnReturn() {
0485: return this .testOnReturn;
0486: }
0487:
0488: /**
0489: * Sets the <code>testOnReturn</code> property. This property determines
0490: * whether or not the pool will validate objects before they are returned
0491: * to the pool. For a <code>true</code> value to have any effect, the
0492: * <code>validationQuery</code> property must be set to a non-null string.
0493: *
0494: * @param testOnReturn new value for testOnReturn property
0495: */
0496: public synchronized void setTestOnReturn(boolean testOnReturn) {
0497: this .testOnReturn = testOnReturn;
0498: if (connectionPool != null) {
0499: connectionPool.setTestOnReturn(testOnReturn);
0500: }
0501: }
0502:
0503: /**
0504: * The number of milliseconds to sleep between runs of the idle object
0505: * evictor thread. When non-positive, no idle object evictor thread will
0506: * be run.
0507: */
0508: protected long timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
0509:
0510: /**
0511: * Returns the value of the {@link #timeBetweenEvictionRunsMillis}
0512: * property.
0513: *
0514: * @return the time (in miliseconds) between evictor runs
0515: * @see #timeBetweenEvictionRunsMillis
0516: */
0517: public synchronized long getTimeBetweenEvictionRunsMillis() {
0518: return this .timeBetweenEvictionRunsMillis;
0519: }
0520:
0521: /**
0522: * Sets the {@link #timeBetweenEvictionRunsMillis} property.
0523: *
0524: * @param timeBetweenEvictionRunsMillis the new time between evictor runs
0525: * @see #timeBetweenEvictionRunsMillis
0526: */
0527: public synchronized void setTimeBetweenEvictionRunsMillis(
0528: long timeBetweenEvictionRunsMillis) {
0529: this .timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
0530: if (connectionPool != null) {
0531: connectionPool
0532: .setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
0533: }
0534: }
0535:
0536: /**
0537: * The number of objects to examine during each run of the idle object
0538: * evictor thread (if any).
0539: */
0540: protected int numTestsPerEvictionRun = GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
0541:
0542: /**
0543: * Returns the value of the {@link #numTestsPerEvictionRun} property.
0544: *
0545: * @return the number of objects to examine during idle object evictor
0546: * runs
0547: * @see #numTestsPerEvictionRun
0548: */
0549: public synchronized int getNumTestsPerEvictionRun() {
0550: return this .numTestsPerEvictionRun;
0551: }
0552:
0553: /**
0554: * Sets the value of the {@link #numTestsPerEvictionRun} property.
0555: *
0556: * @param numTestsPerEvictionRun the new {@link #numTestsPerEvictionRun}
0557: * value
0558: * @see #numTestsPerEvictionRun
0559: */
0560: public synchronized void setNumTestsPerEvictionRun(
0561: int numTestsPerEvictionRun) {
0562: this .numTestsPerEvictionRun = numTestsPerEvictionRun;
0563: if (connectionPool != null) {
0564: connectionPool
0565: .setNumTestsPerEvictionRun(numTestsPerEvictionRun);
0566: }
0567: }
0568:
0569: /**
0570: * The minimum amount of time an object may sit idle in the pool before it
0571: * is eligable for eviction by the idle object evictor (if any).
0572: */
0573: protected long minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
0574:
0575: /**
0576: * Returns the {@link #minEvictableIdleTimeMillis} property.
0577: *
0578: * @return the value of the {@link #minEvictableIdleTimeMillis} property
0579: * @see #minEvictableIdleTimeMillis
0580: */
0581: public synchronized long getMinEvictableIdleTimeMillis() {
0582: return this .minEvictableIdleTimeMillis;
0583: }
0584:
0585: /**
0586: * Sets the {@link #minEvictableIdleTimeMillis} property.
0587: *
0588: * @param minEvictableIdleTimeMillis the minimum amount of time an object
0589: * may sit idle in the pool
0590: * @see #minEvictableIdleTimeMillis
0591: */
0592: public synchronized void setMinEvictableIdleTimeMillis(
0593: long minEvictableIdleTimeMillis) {
0594: this .minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
0595: if (connectionPool != null) {
0596: connectionPool
0597: .setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
0598: }
0599: }
0600:
0601: /**
0602: * The indication of whether objects will be validated by the idle object
0603: * evictor (if any). If an object fails to validate, it will be dropped
0604: * from the pool.
0605: */
0606: protected boolean testWhileIdle = false;
0607:
0608: /**
0609: * Returns the value of the {@link #testWhileIdle} property.
0610: *
0611: * @return true if objects examined by the idle object evictor are
0612: * validated
0613: * @see #testWhileIdle
0614: */
0615: public synchronized boolean getTestWhileIdle() {
0616: return this .testWhileIdle;
0617: }
0618:
0619: /**
0620: * Sets the <code>testWhileIdle</code> property. This property determines
0621: * whether or not the idle object evictor will validate connections. For a
0622: * <code>true</code> value to have any effect, the
0623: * <code>validationQuery</code> property must be set to a non-null string.
0624: *
0625: * @param testWhileIdle new value for testWhileIdle property
0626: */
0627: public synchronized void setTestWhileIdle(boolean testWhileIdle) {
0628: this .testWhileIdle = testWhileIdle;
0629: if (connectionPool != null) {
0630: connectionPool.setTestWhileIdle(testWhileIdle);
0631: }
0632: }
0633:
0634: /**
0635: * [Read Only] The current number of active connections that have been
0636: * allocated from this data source.
0637: *
0638: * @return the current number of active connections
0639: */
0640: public synchronized int getNumActive() {
0641: if (connectionPool != null) {
0642: return connectionPool.getNumActive();
0643: } else {
0644: return 0;
0645: }
0646: }
0647:
0648: /**
0649: * [Read Only] The current number of idle connections that are waiting
0650: * to be allocated from this data source.
0651: *
0652: * @return the current number of idle connections
0653: */
0654: public synchronized int getNumIdle() {
0655: if (connectionPool != null) {
0656: return connectionPool.getNumIdle();
0657: } else {
0658: return 0;
0659: }
0660: }
0661:
0662: /**
0663: * The connection password to be passed to our JDBC driver to establish
0664: * a connection.
0665: */
0666: protected String password = null;
0667:
0668: /**
0669: * Returns the password passed to the JDBC driver to establish connections.
0670: *
0671: * @return the connection password
0672: */
0673: public synchronized String getPassword() {
0674: return this .password;
0675: }
0676:
0677: /**
0678: * <p>Sets the {@link #password}.</p>
0679: * <p>
0680: * Note: this method currently has no effect once the pool has been
0681: * initialized. The pool is initialized the first time one of the
0682: * following methods is invoked: <code>getConnection, setLogwriter,
0683: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0684: *
0685: * @param password new value for the password
0686: */
0687: public synchronized void setPassword(String password) {
0688: this .password = password;
0689: this .restartNeeded = true;
0690: }
0691:
0692: /**
0693: * The connection URL to be passed to our JDBC driver to establish
0694: * a connection.
0695: */
0696: protected String url = null;
0697:
0698: /**
0699: * Returns the JDBC connection {@link #url} property.
0700: *
0701: * @return the {@link #url} passed to the JDBC driver to establish
0702: * connections
0703: */
0704: public synchronized String getUrl() {
0705: return this .url;
0706: }
0707:
0708: /**
0709: * <p>Sets the {@link #url}.</p>
0710: * <p>
0711: * Note: this method currently has no effect once the pool has been
0712: * initialized. The pool is initialized the first time one of the
0713: * following methods is invoked: <code>getConnection, setLogwriter,
0714: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0715: *
0716: * @param url the new value for the JDBC connection url
0717: */
0718: public synchronized void setUrl(String url) {
0719: this .url = url;
0720: this .restartNeeded = true;
0721: }
0722:
0723: /**
0724: * The connection username to be passed to our JDBC driver to
0725: * establish a connection.
0726: */
0727: protected String username = null;
0728:
0729: /**
0730: * Returns the JDBC connection {@link #username} property.
0731: *
0732: * @return the {@link #username} passed to the JDBC driver to establish
0733: * connections
0734: */
0735: public synchronized String getUsername() {
0736: return this .username;
0737: }
0738:
0739: /**
0740: * <p>Sets the {@link #username}.</p>
0741: * <p>
0742: * Note: this method currently has no effect once the pool has been
0743: * initialized. The pool is initialized the first time one of the
0744: * following methods is invoked: <code>getConnection, setLogwriter,
0745: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0746: *
0747: * @param username the new value for the JDBC connection username
0748: */
0749: public synchronized void setUsername(String username) {
0750: this .username = username;
0751: this .restartNeeded = true;
0752: }
0753:
0754: /**
0755: * The SQL query that will be used to validate connections from this pool
0756: * before returning them to the caller. If specified, this query
0757: * <strong>MUST</strong> be an SQL SELECT statement that returns at least
0758: * one row.
0759: */
0760: protected String validationQuery = null;
0761:
0762: /**
0763: * Returns the validation query used to validate connections before
0764: * returning them.
0765: *
0766: * @return the SQL validation query
0767: * @see #validationQuery
0768: */
0769: public synchronized String getValidationQuery() {
0770: return this .validationQuery;
0771: }
0772:
0773: /**
0774: * <p>Sets the {@link #validationQuery}.</p>
0775: * <p>
0776: * Note: this method currently has no effect once the pool has been
0777: * initialized. The pool is initialized the first time one of the
0778: * following methods is invoked: <code>getConnection, setLogwriter,
0779: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0780: *
0781: * @param validationQuery the new value for the validation query
0782: */
0783: public synchronized void setValidationQuery(String validationQuery) {
0784: if ((validationQuery != null)
0785: && (validationQuery.trim().length() > 0)) {
0786: this .validationQuery = validationQuery;
0787: } else {
0788: this .validationQuery = null;
0789: }
0790: this .restartNeeded = true;
0791: }
0792:
0793: /**
0794: * Controls access to the underlying connection.
0795: */
0796: private boolean accessToUnderlyingConnectionAllowed = false;
0797:
0798: /**
0799: * Returns the value of the accessToUnderlyingConnectionAllowed property.
0800: *
0801: * @return true if access to the underlying connection is allowed, false
0802: * otherwise.
0803: */
0804: public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
0805: return this .accessToUnderlyingConnectionAllowed;
0806: }
0807:
0808: /**
0809: * <p>Sets the value of the accessToUnderlyingConnectionAllowed property.
0810: * It controls if the PoolGuard allows access to the underlying connection.
0811: * (Default: false)</p>
0812: * <p>
0813: * Note: this method currently has no effect once the pool has been
0814: * initialized. The pool is initialized the first time one of the
0815: * following methods is invoked: <code>getConnection, setLogwriter,
0816: * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
0817: *
0818: * @param allow Access to the underlying connection is granted when true.
0819: */
0820: public synchronized void setAccessToUnderlyingConnectionAllowed(
0821: boolean allow) {
0822: this .accessToUnderlyingConnectionAllowed = allow;
0823: this .restartNeeded = true;
0824: }
0825:
0826: // ----------------------------------------------------- Instance Variables
0827:
0828: // TODO: review & make isRestartNeeded() public, restartNeeded protected
0829:
0830: /**
0831: * A property setter has been invoked that will require the connection
0832: * pool to be re-initialized. Currently, restart is not triggered, so
0833: * this property has no effect.
0834: */
0835: private boolean restartNeeded = false;
0836:
0837: /**
0838: * Returns whether or not a restart is needed.
0839: *
0840: * Note: restart is not currently triggered by property changes.
0841: *
0842: * @return true if a restart is needed
0843: */
0844: private synchronized boolean isRestartNeeded() {
0845: return restartNeeded;
0846: }
0847:
0848: /**
0849: * The object pool that internally manages our connections.
0850: */
0851: protected GenericObjectPool connectionPool = null;
0852:
0853: /**
0854: * The connection properties that will be sent to our JDBC driver when
0855: * establishing new connections. <strong>NOTE</strong> - The "user" and
0856: * "password" properties will be passed explicitly, so they do not need
0857: * to be included here.
0858: */
0859: protected Properties connectionProperties = new Properties();
0860:
0861: /**
0862: * The data source we will use to manage connections. This object should
0863: * be acquired <strong>ONLY</strong> by calls to the
0864: * <code>createDataSource()</code> method.
0865: */
0866: protected DataSource dataSource = null;
0867:
0868: /**
0869: * The PrintWriter to which log messages should be directed.
0870: */
0871: protected PrintWriter logWriter = new PrintWriter(System.out);
0872:
0873: // ----------------------------------------------------- DataSource Methods
0874:
0875: /**
0876: * Create (if necessary) and return a connection to the database.
0877: *
0878: * @throws SQLException if a database access error occurs
0879: * @return a database connection
0880: */
0881: public Connection getConnection() throws SQLException {
0882: return createDataSource().getConnection();
0883: }
0884:
0885: /**
0886: * <strong>BasicDataSource does NOT support this method.
0887: * </strong>
0888: *
0889: * @param username Database user on whose behalf the Connection
0890: * is being made
0891: * @param password The database user's password
0892: *
0893: * @throws UnsupportedOperationException
0894: * @throws SQLException if a database access error occurs
0895: * @return nothing - always throws UnsupportedOperationException
0896: */
0897: public Connection getConnection(String username, String password)
0898: throws SQLException {
0899: // This method isn't supported by the PoolingDataSource returned by
0900: // the createDataSource
0901: throw new UnsupportedOperationException(
0902: "Not supported by BasicDataSource");
0903: // return createDataSource().getConnection(username, password);
0904: }
0905:
0906: /**
0907: * <p>Returns the login timeout (in seconds) for connecting to the database.
0908: * </p>
0909: * <p>Calls {@link #createDataSource()}, so has the side effect
0910: * of initializing the connection pool.</p>
0911: *
0912: * @throws SQLException if a database access error occurs
0913: * @throws UnsupportedOperationException If the DataSource implementation
0914: * does not support the login timeout feature.
0915: * @return login timeout in seconds
0916: */
0917: public int getLoginTimeout() throws SQLException {
0918: return createDataSource().getLoginTimeout();
0919: }
0920:
0921: /**
0922: * <p>Returns the log writer being used by this data source.</p>
0923: * <p>
0924: * Calls {@link #createDataSource()}, so has the side effect
0925: * of initializing the connection pool.</p>
0926: *
0927: * @throws SQLException if a database access error occurs
0928: * @return log writer in use
0929: */
0930: public PrintWriter getLogWriter() throws SQLException {
0931: return createDataSource().getLogWriter();
0932: }
0933:
0934: /**
0935: * <p>Set the login timeout (in seconds) for connecting to the
0936: * database.</p>
0937: * <p>
0938: * Calls {@link #createDataSource()}, so has the side effect
0939: * of initializing the connection pool.</p>
0940: *
0941: * @param loginTimeout The new login timeout, or zero for no timeout
0942: * @throws SQLException if a database access error occurs
0943: */
0944: public void setLoginTimeout(int loginTimeout) throws SQLException {
0945: createDataSource().setLoginTimeout(loginTimeout);
0946: }
0947:
0948: /**
0949: * <p>Sets the log writer being used by this data source.</p>
0950: * <p>
0951: * Calls {@link #createDataSource()}, so has the side effect
0952: * of initializing the connection pool.</p>
0953: *
0954: * @param logWriter The new log writer
0955: * @throws SQLException if a database access error occurs
0956: */
0957: public void setLogWriter(PrintWriter logWriter) throws SQLException {
0958: createDataSource().setLogWriter(logWriter);
0959: this .logWriter = logWriter;
0960: }
0961:
0962: private AbandonedConfig abandonedConfig;
0963:
0964: /**
0965: * Flag to remove abandoned connections if they exceed the
0966: * removeAbandonedTimout.
0967: *
0968: * Set to true or false, default false.
0969: * If set to true a connection is considered abandoned and eligible
0970: * for removal if it has been idle longer than the removeAbandonedTimeout.
0971: * Setting this to true can recover db connections from poorly written
0972: * applications which fail to close a connection.
0973: * @deprecated
0974: */
0975: public boolean getRemoveAbandoned() {
0976: if (abandonedConfig != null) {
0977: return abandonedConfig.getRemoveAbandoned();
0978: }
0979: return false;
0980: }
0981:
0982: /**
0983: * @deprecated
0984: * @param removeAbandoned new removeAbandoned property value
0985: */
0986: public void setRemoveAbandoned(boolean removeAbandoned) {
0987: if (abandonedConfig == null) {
0988: abandonedConfig = new AbandonedConfig();
0989: }
0990: abandonedConfig.setRemoveAbandoned(removeAbandoned);
0991: this .restartNeeded = true;
0992: }
0993:
0994: /**
0995: * Timeout in seconds before an abandoned connection can be removed.
0996: *
0997: * Defaults to 300 seconds.
0998: * @return abandoned connection timeout
0999: * @deprecated
1000: */
1001: public int getRemoveAbandonedTimeout() {
1002: if (abandonedConfig != null) {
1003: return abandonedConfig.getRemoveAbandonedTimeout();
1004: }
1005: return 300;
1006: }
1007:
1008: /**
1009: * @deprecated
1010: * @param removeAbandonedTimeout new removeAbandonedTimeout value
1011: */
1012: public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {
1013: if (abandonedConfig == null) {
1014: abandonedConfig = new AbandonedConfig();
1015: }
1016: abandonedConfig
1017: .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(
1100: "Cannot close connection pool", e);
1101: }
1102: }
1103:
1104: // ------------------------------------------------------ Protected Methods
1105:
1106: /**
1107: * <p>Create (if necessary) and return the internal data source we are
1108: * using to manage our connections.</p>
1109: *
1110: * <p><strong>IMPLEMENTATION NOTE</strong> - It is tempting to use the
1111: * "double checked locking" idiom in an attempt to avoid synchronizing
1112: * on every single call to this method. However, this idiom fails to
1113: * work correctly in the face of some optimizations that are legal for
1114: * a JVM to perform.</p>
1115: *
1116: * @throws SQLException if the object pool cannot be created.
1117: */
1118: protected synchronized DataSource createDataSource()
1119: throws SQLException {
1120:
1121: // Return the pool if we have already created it
1122: if (dataSource != null) {
1123: return (dataSource);
1124: }
1125:
1126: // Load the JDBC driver class
1127: if (driverClassName != null) {
1128: try {
1129: Class.forName(driverClassName);
1130: } catch (Throwable t) {
1131: String message = "Cannot load JDBC driver class '"
1132: + driverClassName + "'";
1133: logWriter.println(message);
1134: t.printStackTrace(logWriter);
1135: throw new SQLNestedException(message, t);
1136: }
1137: }
1138:
1139: // Create a JDBC driver instance
1140: Driver driver = null;
1141: try {
1142: driver = DriverManager.getDriver(url);
1143: } catch (Throwable t) {
1144: String message = "Cannot create JDBC driver of class '"
1145: + (driverClassName != null ? driverClassName : "")
1146: + "' for connect URL '" + url + "'";
1147: logWriter.println(message);
1148: t.printStackTrace(logWriter);
1149: throw new SQLNestedException(message, t);
1150: }
1151:
1152: // Can't test without a validationQuery
1153: if (validationQuery == null) {
1154: setTestOnBorrow(false);
1155: setTestOnReturn(false);
1156: setTestWhileIdle(false);
1157: }
1158:
1159: // Create an object pool to contain our active connections
1160: if ((abandonedConfig != null)
1161: && (abandonedConfig.getRemoveAbandoned())) {
1162: connectionPool = new AbandonedObjectPool(null,
1163: abandonedConfig);
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
1174: .setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
1175: connectionPool
1176: .setNumTestsPerEvictionRun(numTestsPerEvictionRun);
1177: connectionPool
1178: .setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
1179: connectionPool.setTestWhileIdle(testWhileIdle);
1180:
1181: // Set up statement pool, if desired
1182: GenericKeyedObjectPoolFactory statementPoolFactory = null;
1183: if (isPoolPreparedStatements()) {
1184: statementPoolFactory = new GenericKeyedObjectPoolFactory(
1185: null, -1, // unlimited maxActive (per key)
1186: GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL, 0, // maxWait
1187: 1, // maxIdle (per key)
1188: maxOpenPreparedStatements);
1189: }
1190:
1191: // Set up the driver connection factory we will use
1192: if (username != null) {
1193: connectionProperties.put("user", username);
1194: } else {
1195: log("DBCP DataSource configured without a 'username'");
1196: }
1197:
1198: if (password != null) {
1199: connectionProperties.put("password", password);
1200: } else {
1201: log("DBCP DataSource configured without a 'password'");
1202: }
1203:
1204: DriverConnectionFactory driverConnectionFactory = new DriverConnectionFactory(
1205: driver, url, connectionProperties);
1206:
1207: // Set up the poolable connection factory we will use
1208: PoolableConnectionFactory connectionFactory = null;
1209: try {
1210: connectionFactory = new PoolableConnectionFactory(
1211: driverConnectionFactory, connectionPool,
1212: statementPoolFactory, validationQuery,
1213: defaultReadOnly, defaultAutoCommit,
1214: defaultTransactionIsolation, defaultCatalog,
1215: abandonedConfig);
1216: if (connectionFactory == null) {
1217: throw new SQLException(
1218: "Cannot create PoolableConnectionFactory");
1219: }
1220: validateConnectionFactory(connectionFactory);
1221: } catch (RuntimeException e) {
1222: throw e;
1223: } catch (Exception e) {
1224: throw new SQLNestedException(
1225: "Cannot create PoolableConnectionFactory ("
1226: + e.getMessage() + ")", e);
1227: }
1228:
1229: // Create and return the pooling data source to manage the connections
1230: dataSource = new PoolingDataSource(connectionPool);
1231: ((PoolingDataSource) dataSource)
1232: .setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
1233: dataSource.setLogWriter(logWriter);
1234:
1235: try {
1236: for (int i = 0; i < initialSize; i++) {
1237: connectionPool.addObject();
1238: }
1239: } catch (Exception e) {
1240: throw new SQLNestedException(
1241: "Error preloading the connection pool", e);
1242: }
1243:
1244: return dataSource;
1245: }
1246:
1247: private static void validateConnectionFactory(
1248: PoolableConnectionFactory connectionFactory)
1249: throws Exception {
1250: Connection conn = null;
1251: try {
1252: conn = (Connection) connectionFactory.makeObject();
1253: connectionFactory.activateObject(conn);
1254: connectionFactory.validateConnection(conn);
1255: connectionFactory.passivateObject(conn);
1256: } finally {
1257: connectionFactory.destroyObject(conn);
1258: }
1259: }
1260:
1261: /**
1262: * Not used currently
1263: */
1264: private void restart() {
1265: try {
1266: close();
1267: } catch (SQLException e) {
1268: log("Could not restart DataSource, cause: "
1269: + e.getMessage());
1270: }
1271: }
1272:
1273: private void log(String message) {
1274: if (logWriter != null) {
1275: logWriter.println(message);
1276: }
1277: }
1278: }
|