0001: /**
0002: * EasyBeans
0003: * Copyright (C) 2006 Bull S.A.S.
0004: * Contact: easybeans@ow2.org
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation; either
0009: * version 2.1 of the License, or any later version.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU Lesser General Public
0017: * License along with this library; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
0019: * USA
0020: *
0021: * --------------------------------------------------------------------------
0022: * $Id: ConnectionManager.java 1970 2007-10-16 11:49:25Z benoitf $
0023: * --------------------------------------------------------------------------
0024: */package org.ow2.easybeans.component.jdbcpool;
0025:
0026: import java.io.PrintWriter;
0027: import java.sql.Connection;
0028: import java.sql.DriverManager;
0029: import java.sql.SQLException;
0030: import java.util.HashMap;
0031: import java.util.Iterator;
0032: import java.util.LinkedList;
0033: import java.util.Map;
0034: import java.util.TreeSet;
0035:
0036: import javax.naming.NamingException;
0037: import javax.naming.Reference;
0038: import javax.naming.Referenceable;
0039: import javax.naming.StringRefAddr;
0040: import javax.sql.ConnectionEvent;
0041: import javax.sql.ConnectionEventListener;
0042: import javax.sql.DataSource;
0043: import javax.sql.XAConnection;
0044: import javax.sql.XADataSource;
0045: import javax.transaction.RollbackException;
0046: import javax.transaction.SystemException;
0047: import javax.transaction.Transaction;
0048: import javax.transaction.TransactionManager;
0049: import javax.transaction.xa.XAResource;
0050:
0051: import org.ow2.util.log.Log;
0052: import org.ow2.util.log.LogFactory;
0053:
0054: /**
0055: * DataSource implementation. Manage a pool of connections.
0056: * @author Philippe Durieux
0057: * @author Florent Benoit
0058: */
0059: public class ConnectionManager implements DataSource, XADataSource,
0060: Referenceable, ConnectionEventListener {
0061:
0062: /**
0063: * Logger.
0064: */
0065: private Log logger = LogFactory.getLog(ConnectionManager.class);
0066:
0067: /**
0068: * Milliseconds.
0069: */
0070: private static final long MILLI = 1000L;
0071:
0072: /**
0073: * One minute in milliseconds.
0074: */
0075: private static final long ONE_MIN_MILLI = 60L * MILLI;
0076:
0077: /**
0078: * Default timeout.
0079: */
0080: private static final int DEFAULT_TIMEOUT = 60;
0081:
0082: /**
0083: * Default timeout for waiters (10s).
0084: */
0085: private static final long WAITER_TIMEOUT = 10 * MILLI;
0086:
0087: /**
0088: * Max waiters (by default).
0089: */
0090: private static final int DEFAULT_MAX_WAITERS = 1000;
0091:
0092: /**
0093: * Default prepare statement.
0094: */
0095: private static final int DEFAULT_PSTMT = 12;
0096:
0097: /**
0098: * Default sampling period.
0099: */
0100: private static final int DEFAULT_SAMPLING = 60;
0101:
0102: /**
0103: * List of all datasources.
0104: */
0105: private static Map<String, ConnectionManager> cmList = new HashMap<String, ConnectionManager>();
0106:
0107: /**
0108: * Transaction manager.
0109: */
0110: private TransactionManager tm = null;
0111:
0112: /**
0113: * List of JManagedConnection not currently used. This avoids closing and
0114: * reopening physical connections. We try to keep a minimum of minConPool
0115: * elements here.
0116: */
0117: private TreeSet<JManagedConnection> freeList = new TreeSet<JManagedConnection>();
0118:
0119: /**
0120: * Total list of JManagedConnection physically opened.
0121: */
0122: private LinkedList<JManagedConnection> mcList = new LinkedList<JManagedConnection>();
0123:
0124: /**
0125: * This HashMap gives the JManagedConnection from its transaction Requests
0126: * with same tx get always the same connection.
0127: */
0128: private Map<Transaction, JManagedConnection> tx2mc = new HashMap<Transaction, JManagedConnection>();
0129:
0130: /**
0131: * Login timeout (DataSource impl).
0132: */
0133: private int loginTimeout = DEFAULT_TIMEOUT;
0134:
0135: /**
0136: * PrintWriter used logging (DataSource impl).
0137: */
0138: private PrintWriter log = null;
0139:
0140: /**
0141: * Constructor for Factory.
0142: */
0143: public ConnectionManager() {
0144:
0145: }
0146:
0147: /**
0148: * Gets the ConnectionManager matching the DataSource name.
0149: * @param dsname datasource name.
0150: * @return a connection manager impl.
0151: */
0152: public static ConnectionManager getConnectionManager(
0153: final String dsname) {
0154: ConnectionManager cm = cmList.get(dsname);
0155: return cm;
0156: }
0157:
0158: /**
0159: * Datasource name.
0160: */
0161: private String dSName = null;
0162:
0163: /**
0164: * @return Jndi name of the datasource
0165: */
0166: public String getDSName() {
0167: return dSName;
0168: }
0169:
0170: /**
0171: * @param s Jndi name for the datasource
0172: */
0173: public void setDSName(final String s) {
0174: dSName = s;
0175: // Add it to the list
0176: cmList.put(s, this );
0177: }
0178:
0179: /**
0180: * @serial datasource name
0181: */
0182: private String dataSourceName;
0183:
0184: /**
0185: * Gets the name of the datasource.
0186: * @return the name of the datasource
0187: */
0188: public String getDatasourceName() {
0189: return dataSourceName;
0190: }
0191:
0192: /**
0193: * Sets the name of the datasource.
0194: * @param dataSourceName the name of the datasource
0195: */
0196: public void setDatasourceName(final String dataSourceName) {
0197: this .dataSourceName = dataSourceName;
0198: }
0199:
0200: /**
0201: * url for database.
0202: */
0203: private String url = null;
0204:
0205: /**
0206: * @return the url used to get the connection.
0207: */
0208: public String getUrl() {
0209: return url;
0210: }
0211:
0212: /**
0213: * Sets the url to get connections.
0214: * @param url the url for JDBC connections.
0215: */
0216: public void setUrl(final String url) {
0217: this .url = url;
0218: }
0219:
0220: /**
0221: * JDBC driver Class.
0222: */
0223: private String className = null;
0224:
0225: /**
0226: * @return the JDBC driver class name.
0227: */
0228: public String getClassName() {
0229: return className;
0230: }
0231:
0232: /**
0233: * Sets the driver class for JDBC.
0234: * @param className the name of the JDBC driver
0235: * @throws ClassNotFoundException if driver is not found
0236: */
0237: public void setClassName(final String className)
0238: throws ClassNotFoundException {
0239: this .className = className;
0240:
0241: // Loads standard JDBC driver and keeps it loaded (via driverClass)
0242: logger.debug("Load JDBC driver {0}", className);
0243: try {
0244: Class.forName(className);
0245: } catch (java.lang.ClassNotFoundException e) {
0246: logger.error("Cannot load JDBC driver", e);
0247: throw e;
0248: }
0249: }
0250:
0251: /**
0252: * default user.
0253: */
0254: private String userName = null;
0255:
0256: /**
0257: * @return the user used for getting connections.
0258: */
0259: public String getUserName() {
0260: return userName;
0261: }
0262:
0263: /**
0264: * Sets the user for getting connections.
0265: * @param userName the name of the user.
0266: */
0267: public void setUserName(final String userName) {
0268: this .userName = userName;
0269: }
0270:
0271: /**
0272: * default passwd.
0273: */
0274: private String password = null;
0275:
0276: /**
0277: * @return the password used for connections.
0278: */
0279: public String getPassword() {
0280: return password;
0281: }
0282:
0283: /**
0284: * Sets the password used to get connections.
0285: * @param password the password value.
0286: */
0287: public void setPassword(final String password) {
0288: this .password = password;
0289: }
0290:
0291: /**
0292: * Isolation level for JDBC.
0293: */
0294: private int isolationLevel = -1;
0295:
0296: /**
0297: * Isolation level (but String format).
0298: */
0299: private String isolationStr = null;
0300:
0301: /**
0302: * Sets the transaction isolation level of the connections.
0303: * @param level the level of isolation.
0304: */
0305: public void setTransactionIsolation(final String level) {
0306: if (level.equals("serializable")) {
0307: isolationLevel = Connection.TRANSACTION_SERIALIZABLE;
0308: } else if (level.equals("none")) {
0309: isolationLevel = Connection.TRANSACTION_NONE;
0310: } else if (level.equals("read_committed")) {
0311: isolationLevel = Connection.TRANSACTION_READ_COMMITTED;
0312: } else if (level.equals("read_uncommitted")) {
0313: isolationLevel = Connection.TRANSACTION_READ_UNCOMMITTED;
0314: } else if (level.equals("repeatable_read")) {
0315: isolationLevel = Connection.TRANSACTION_REPEATABLE_READ;
0316: } else {
0317: isolationStr = "default";
0318: return;
0319: }
0320: isolationStr = level;
0321: }
0322:
0323: /**
0324: * Gets the transaction isolation level.
0325: * @return transaction isolation level.
0326: */
0327: public String getTransactionIsolation() {
0328: return isolationStr;
0329: }
0330:
0331: /**
0332: * count max waiters during current period.
0333: */
0334: private int waiterCount = 0;
0335:
0336: /**
0337: * count max waiting time during current period.
0338: */
0339: private long waitingTime = 0;
0340:
0341: /**
0342: * count max busy connection during current period.
0343: */
0344: private int busyMax = 0;
0345:
0346: /**
0347: * count min busy connection during current period.
0348: */
0349: private int busyMin = 0;
0350:
0351: /**
0352: * High Value for no limit for the connection pool.
0353: */
0354: private static final int NO_LIMIT = 99999;
0355:
0356: /**
0357: * Nb of milliseconds in a day.
0358: */
0359: private static final long ONE_DAY = 1440L * 60L * 1000L;
0360:
0361: /**
0362: * max number of remove at once in the freelist We avoid removing too much
0363: * mcs at once for perf reasons.
0364: */
0365: private static final int MAX_REMOVE_FREELIST = 10;
0366:
0367: /**
0368: * minimum size of the connection pool.
0369: */
0370: private int poolMin = 0;
0371:
0372: /**
0373: * @return min pool size.
0374: */
0375: public int getPoolMin() {
0376: return poolMin;
0377: }
0378:
0379: /**
0380: * @param min minimum connection pool size to be set.
0381: */
0382: public synchronized void setPoolMin(final int min) {
0383: if (poolMin != min) {
0384: poolMin = min;
0385: adjust();
0386: }
0387: }
0388:
0389: /**
0390: * maximum size of the connection pool. default value is "NO LIMIT".
0391: */
0392: private int poolMax = NO_LIMIT;
0393:
0394: /**
0395: * @return actual max pool size
0396: */
0397: public int getPoolMax() {
0398: return poolMax;
0399: }
0400:
0401: /**
0402: * @param max max pool size. -1 means "no limit".
0403: */
0404: public synchronized void setPoolMax(final int max) {
0405: if (poolMax != max) {
0406: if (max < 0 || max > NO_LIMIT) {
0407: if (currentWaiters > 0) {
0408: notify();
0409: }
0410: poolMax = NO_LIMIT;
0411: } else {
0412: if (currentWaiters > 0 && poolMax < max) {
0413: notify();
0414: }
0415: poolMax = max;
0416: adjust();
0417: }
0418: }
0419: }
0420:
0421: /**
0422: * Max age of a Connection in milliseconds. When the time is elapsed, the
0423: * connection will be closed. This avoids keeping connections open too long
0424: * for nothing.
0425: */
0426: private long maxAge = ONE_DAY;
0427:
0428: /**
0429: * Same value in mns.
0430: */
0431: private int maxAgeMn;
0432:
0433: /**
0434: * @return max age for connections (in mm).
0435: */
0436: public int getMaxAge() {
0437: return maxAgeMn;
0438: }
0439:
0440: /**
0441: * @return max age for connections (in millisec).
0442: */
0443: public long getMaxAgeMilli() {
0444: return maxAge;
0445: }
0446:
0447: /**
0448: * @param mn max age of connection in minutes.
0449: */
0450: public void setMaxAge(final int mn) {
0451: maxAgeMn = mn;
0452: // set times in milliseconds
0453: maxAge = mn * ONE_MIN_MILLI;
0454: }
0455:
0456: /**
0457: * max open time for a connection, in millisec.
0458: */
0459: private long maxOpenTime = ONE_DAY;
0460:
0461: /**
0462: * Same value in mn.
0463: */
0464: private int maxOpenTimeMn;
0465:
0466: /**
0467: * @return max age for connections (in mns).
0468: */
0469: public int getMaxOpenTime() {
0470: return maxOpenTimeMn;
0471: }
0472:
0473: /**
0474: * @return max age for connections (in millisecs).
0475: */
0476: public long getMaxOpenTimeMilli() {
0477: return maxOpenTime;
0478: }
0479:
0480: /**
0481: * @param mn max time of open connection in minutes.
0482: */
0483: public void setMaxOpenTime(final int mn) {
0484: maxOpenTimeMn = mn;
0485: // set times in milliseconds
0486: maxOpenTime = mn * ONE_MIN_MILLI;
0487: }
0488:
0489: /**
0490: * max nb of milliseconds to wait for a connection when pool is empty.
0491: */
0492: private long waiterTimeout = WAITER_TIMEOUT;
0493:
0494: /**
0495: * @return waiter timeout in seconds.
0496: */
0497: public int getMaxWaitTime() {
0498: return (int) (waiterTimeout / MILLI);
0499: }
0500:
0501: /**
0502: * @param sec max time to wait for a connection, in seconds.
0503: */
0504: public void setMaxWaitTime(final int sec) {
0505: waiterTimeout = sec * MILLI;
0506: }
0507:
0508: /**
0509: * max nb of waiters allowed to wait for a Connection.
0510: */
0511: private int maxWaiters = DEFAULT_MAX_WAITERS;
0512:
0513: /**
0514: * @return max nb of waiters
0515: */
0516: public int getMaxWaiters() {
0517: return maxWaiters;
0518: }
0519:
0520: /**
0521: * @param nb max nb of waiters
0522: */
0523: public void setMaxWaiters(final int nb) {
0524: maxWaiters = nb;
0525: }
0526:
0527: /**
0528: * sampling period in sec.
0529: */
0530: private int samplingPeriod = DEFAULT_SAMPLING; // default sampling period
0531:
0532: /**
0533: * @return sampling period in sec.
0534: */
0535: public int getSamplingPeriod() {
0536: return samplingPeriod;
0537: }
0538:
0539: /**
0540: * @param sec sampling period in sec.
0541: */
0542: public void setSamplingPeriod(final int sec) {
0543: if (sec > 0) {
0544: samplingPeriod = sec;
0545: }
0546: }
0547:
0548: /**
0549: * Level of checking on connections when got from the pool. this avoids
0550: * reusing bad connections because too old, for example when database was
0551: * restarted... 0 = no checking 1 = check that still physically opened. 2 =
0552: * try a null statement.
0553: */
0554: private int checkLevel = 0; // default = 0
0555:
0556: /**
0557: * @return connection checking level
0558: */
0559: public int getCheckLevel() {
0560: return checkLevel;
0561: }
0562:
0563: /**
0564: * @param level jdbc connection checking level (0, 1, or 2)
0565: */
0566: public void setCheckLevel(final int level) {
0567: checkLevel = level;
0568: }
0569:
0570: /**
0571: * PreparedStatement pool size per managed connection.
0572: */
0573: private int pstmtMax = DEFAULT_PSTMT;
0574:
0575: /**
0576: * @return PreparedStatement cache size.
0577: */
0578: public int getPstmtMax() {
0579: return pstmtMax;
0580: }
0581:
0582: /**
0583: * @param nb PreparedStatement cache size.
0584: */
0585: public void setPstmtMax(final int nb) {
0586: pstmtMax = nb;
0587: // Set the value in each connection.
0588: for (Iterator i = mcList.iterator(); i.hasNext();) {
0589: JManagedConnection mc = (JManagedConnection) i.next();
0590: mc.setPstmtMax(pstmtMax);
0591: }
0592: }
0593:
0594: /**
0595: * test statement used when checkLevel = 2.
0596: */
0597: private String testStatement;
0598:
0599: /**
0600: * @return test statement used when checkLevel = 2.
0601: */
0602: public String getTestStatement() {
0603: return testStatement;
0604: }
0605:
0606: /**
0607: * @param s test statement
0608: */
0609: public void setTestStatement(final String s) {
0610: testStatement = s;
0611: }
0612:
0613: /**
0614: * Configure the Connection pool. Called by the Container at init.
0615: * Configuration can be set in datasource.properties files.
0616: * @param connchecklevel JDBC connection checking level
0617: * @param connmaxage JDBC connection maximum age
0618: * @param maxopentime JDBC connection maximum open time
0619: * @param connteststmt SQL query for test statement
0620: * @param pstmtmax prepare statement pool size per managed connection
0621: * @param minconpool Min size for the connection pool
0622: * @param maxconpool Max size for the connection pool
0623: * @param maxwaittime Max time to wait for a connection (in seconds)
0624: * @param maxwaiters Max nb of waiters for a connection
0625: * @param samplingperiod sampling period in sec.
0626: */
0627: @SuppressWarnings("boxing")
0628: public void poolConfigure(final String connchecklevel,
0629: final String connmaxage, final String maxopentime,
0630: final String connteststmt, final String pstmtmax,
0631: final String minconpool, final String maxconpool,
0632: final String maxwaittime, final String maxwaiters,
0633: final String samplingperiod) {
0634:
0635: // Configure pool
0636: setCheckLevel((new Integer(connchecklevel)).intValue());
0637: // set con max age BEFORE min/max pool size.
0638: setMaxAge((new Integer(connmaxage)).intValue());
0639: setMaxOpenTime((new Integer(maxopentime)).intValue());
0640: setTestStatement(connteststmt);
0641: setPstmtMax((new Integer(pstmtmax)).intValue());
0642: setPoolMin((new Integer(minconpool)).intValue());
0643: setPoolMax((new Integer(maxconpool)).intValue());
0644: setMaxWaitTime((new Integer(maxwaittime)).intValue());
0645: setMaxWaiters((new Integer(maxwaiters)).intValue());
0646: setSamplingPeriod((new Integer(samplingperiod)).intValue());
0647: if (logger.isDebugEnabled()) {
0648: logger.debug("ConnectionManager configured with:");
0649: logger
0650: .debug(" jdbcConnCheckLevel = {0}",
0651: connchecklevel);
0652: logger.debug(" jdbcConnMaxAge = {0}", connmaxage);
0653: logger.debug(" jdbcMaxOpenTime = {0}", maxopentime);
0654: logger.debug(" jdbcTestStmt = {0}", connteststmt);
0655: logger.debug(" jdbcPstmtMax = {0}", pstmtmax);
0656: logger.debug(" minConPool = {0}", getPoolMin());
0657: logger.debug(" maxConPool = {0}", getPoolMax());
0658: logger.debug(" maxWaitTime = {0}",
0659: getMaxWaitTime());
0660: logger.debug(" maxWaiters = {0}",
0661: getMaxWaiters());
0662: logger.debug(" samplingPeriod = {0}",
0663: getSamplingPeriod());
0664: }
0665: }
0666:
0667: /**
0668: * maximum nb of busy connections in last sampling period.
0669: */
0670: private int busyMaxRecent = 0;
0671:
0672: /**
0673: * @return maximum nb of busy connections in last sampling period.
0674: */
0675: public int getBusyMaxRecent() {
0676: return busyMaxRecent;
0677: }
0678:
0679: /**
0680: * minimum nb of busy connections in last sampling period.
0681: */
0682: private int busyMinRecent = 0;
0683:
0684: /**
0685: * @return minimum nb of busy connections in last sampling period.
0686: */
0687: public int getBusyMinRecent() {
0688: return busyMinRecent;
0689: }
0690:
0691: /**
0692: * nb of threads waiting for a Connection.
0693: */
0694: private int currentWaiters = 0;
0695:
0696: /**
0697: * @return current number of connection waiters.
0698: */
0699: public int getCurrentWaiters() {
0700: return currentWaiters;
0701: }
0702:
0703: /**
0704: * total number of opened physical connections since the datasource
0705: * creation.
0706: */
0707: private int openedCount = 0;
0708:
0709: /**
0710: * @return int number of physical jdbc connection opened.
0711: */
0712: public int getOpenedCount() {
0713: return openedCount;
0714: }
0715:
0716: /**
0717: * total nb of physical connection failures.
0718: */
0719: private int connectionFailures = 0;
0720:
0721: /**
0722: * @return int number of xa connection failures on open.
0723: */
0724: public int getConnectionFailures() {
0725: return connectionFailures;
0726: }
0727:
0728: /**
0729: * total nb of connection leaks. A connection leak occurs when the caller
0730: * never issues a close method on the connection.
0731: */
0732: private int connectionLeaks = 0;
0733:
0734: /**
0735: * @return int number of connection leaks.
0736: */
0737: public int getConnectionLeaks() {
0738: return connectionLeaks;
0739: }
0740:
0741: /**
0742: * total number of opened connections since the datasource creation.
0743: */
0744: private int servedOpen = 0;
0745:
0746: /**
0747: * @return int number of xa connection served.
0748: */
0749: public int getServedOpen() {
0750: return servedOpen;
0751: }
0752:
0753: /**
0754: * total nb of open connection failures because waiter overflow.
0755: */
0756: private int rejectedFull = 0;
0757:
0758: /**
0759: * @return int number of open calls that were rejected due to waiter
0760: * overflow.
0761: */
0762: public int getRejectedFull() {
0763: return rejectedFull;
0764: }
0765:
0766: /**
0767: * total nb of open connection failures because timeout.
0768: */
0769: private int rejectedTimeout = 0;
0770:
0771: /**
0772: * @return int number of open calls that were rejected by timeout.
0773: */
0774: public int getRejectedTimeout() {
0775: return rejectedTimeout;
0776: }
0777:
0778: /**
0779: * total nb of open connection failures for any other reason.
0780: */
0781: private int rejectedOther = 0;
0782:
0783: /**
0784: * @return int number of open calls that were rejected.
0785: */
0786: public int getRejectedOther() {
0787: return rejectedOther;
0788: }
0789:
0790: /**
0791: * @return int number of open calls that were rejected.
0792: */
0793: public int getRejectedOpen() {
0794: return rejectedFull + rejectedTimeout + rejectedOther;
0795: }
0796:
0797: /**
0798: * maximum nb of waiters since datasource creation.
0799: */
0800: private int waitersHigh = 0;
0801:
0802: /**
0803: * @return maximum nb of waiters since the datasource creation.
0804: */
0805: public int getWaitersHigh() {
0806: return waitersHigh;
0807: }
0808:
0809: /**
0810: * maximum nb of waiters in last sampling period.
0811: */
0812: private int waitersHighRecent = 0;
0813:
0814: /**
0815: * @return maximum nb of waiters in last sampling period.
0816: */
0817: public int getWaitersHighRecent() {
0818: return waitersHighRecent;
0819: }
0820:
0821: /**
0822: * total nb of waiters since datasource creation.
0823: */
0824: private int totalWaiterCount = 0;
0825:
0826: /**
0827: * @return total nb of waiters since the datasource creation.
0828: */
0829: public int getWaiterCount() {
0830: return totalWaiterCount;
0831: }
0832:
0833: /**
0834: * total waiting time in milliseconds.
0835: */
0836: private long totalWaitingTime = 0;
0837:
0838: /**
0839: * @return total waiting time since the datasource creation.
0840: */
0841: public long getWaitingTime() {
0842: return totalWaitingTime;
0843: }
0844:
0845: /**
0846: * max waiting time in milliseconds.
0847: */
0848: private long waitingHigh = 0;
0849:
0850: /**
0851: * @return max waiting time since the datasource creation.
0852: */
0853: public long getWaitingHigh() {
0854: return waitingHigh;
0855: }
0856:
0857: /**
0858: * max waiting time in milliseconds in last sampling period.
0859: */
0860: private long waitingHighRecent = 0;
0861:
0862: /**
0863: * @return max waiting time in last sampling period.
0864: */
0865: public long getWaitingHighRecent() {
0866: return waitingHighRecent;
0867: }
0868:
0869: /**
0870: * {@inheritDoc}
0871: */
0872: public int getLoginTimeout() throws SQLException {
0873: return loginTimeout;
0874: }
0875:
0876: /**
0877: * {@inheritDoc}
0878: */
0879: public void setLoginTimeout(final int seconds) throws SQLException {
0880: loginTimeout = seconds;
0881: }
0882:
0883: /**
0884: * {@inheritDoc}
0885: */
0886: public PrintWriter getLogWriter() throws SQLException {
0887: return log;
0888: }
0889:
0890: /**
0891: * {@inheritDoc}
0892: */
0893: public void setLogWriter(final PrintWriter out) throws SQLException {
0894: log = out;
0895: }
0896:
0897: /**
0898: * {@inheritDoc}
0899: */
0900: public Connection getConnection() throws SQLException {
0901: return getConnection(userName, password);
0902: }
0903:
0904: /**
0905: * Attempts to establish a connection with the data source that this
0906: * DataSource object represents. - comes from the javax.sql.DataSource
0907: * interface
0908: * @param username - the database user on whose behalf the connection is
0909: * being made
0910: * @param password - the user's password
0911: * @return a connection to the data source
0912: * @throws SQLException - if a database access error occurs
0913: */
0914: public Connection getConnection(final String username,
0915: final String password) throws SQLException {
0916: JManagedConnection mc = null;
0917:
0918: // Get the current Transaction
0919: Transaction tx = null;
0920: try {
0921: tx = tm.getTransaction();
0922: } catch (NullPointerException n) {
0923: // current is null: we are not in EasyBeans Server.
0924: logger
0925: .error("ConnectionManager: should not be used outside a EasyBeans Server");
0926: } catch (SystemException e) {
0927: logger.error("ConnectionManager: getTransaction failed", e);
0928: }
0929: logger.debug("Tx = {0}", tx);
0930:
0931: // Get a JManagedConnection in the pool for this user
0932: mc = openConnection(username, tx);
0933: Connection ret = mc.getConnection();
0934:
0935: // Enlist XAResource if we are actually in a transaction
0936: if (tx != null) {
0937: if (mc.getOpenCount() == 1) { // Only if first/only thread
0938: try {
0939: logger.debug("enlist XAResource on {0}", tx);
0940: tx.enlistResource(mc.getXAResource());
0941: ret.setAutoCommit(false);
0942: } catch (RollbackException e) {
0943: // Although tx has been marked to be rolled back,
0944: // XAResource has been correctly enlisted.
0945: logger
0946: .warn(
0947: "XAResource enlisted, but tx is marked rollback",
0948: e);
0949: } catch (IllegalStateException e) {
0950: // In case tx is committed, no need to register resource!
0951: ret.setAutoCommit(true);
0952: } catch (Exception e) {
0953: logger.error("Cannot enlist XAResource", e);
0954: logger
0955: .error("Connection will not be enlisted in a transaction");
0956: // should return connection in the pool XXX
0957: throw new SQLException("Cannot enlist XAResource");
0958: }
0959: }
0960: } else {
0961: ret.setAutoCommit(true); // in case we do not start a Tx
0962: }
0963:
0964: // return a Connection object
0965: return ret;
0966: }
0967:
0968: /**
0969: * Attempts to establish a physical database connection that can be
0970: * used in a distributed transaction.
0971: *
0972: * @return an <code>XAConnection</code> object, which represents a
0973: * physical connection to a data source, that can be used in
0974: * a distributed transaction
0975: * @exception SQLException if a database access error occurs
0976: */
0977: public XAConnection getXAConnection() throws SQLException {
0978: return getXAConnection(userName, password);
0979: }
0980:
0981: /**
0982: * Attempts to establish a physical database connection, using the given
0983: * user name and password. The connection that is returned is one that can
0984: * be used in a distributed transaction - comes from the
0985: * javax.sql.XADataSource interface
0986: * @param user - the database user on whose behalf the connection is being
0987: * made
0988: * @param passwd - the user's password
0989: * @return an XAConnection object, which represents a physical connection to
0990: * a data source, that can be used in a distributed transaction
0991: * @throws SQLException - if a database access error occurs
0992: */
0993: @SuppressWarnings("boxing")
0994: public XAConnection getXAConnection(final String user,
0995: final String passwd) throws SQLException {
0996: // Create the actual connection in the std driver
0997: Connection conn = null;
0998: try {
0999: if (user.length() == 0) {
1000: conn = DriverManager.getConnection(url);
1001: logger.debug(" * New Connection on {0}", url);
1002: } else {
1003: // Accept password of zero length.
1004: conn = DriverManager.getConnection(url, user, passwd);
1005: logger.debug(
1006: " * New Connection on {0} for user {1}",
1007: url, user);
1008: }
1009: } catch (SQLException e) {
1010: logger.error("Could not get Connection on {0}", url, e);
1011: throw new SQLException("Could not get Connection on url : "
1012: + url + " for user : " + user + " inner exception"
1013: + e.getMessage());
1014: }
1015:
1016: // Attempt to set the transaction isolation level
1017: // Depending on the underlaying database, this may not succeed.
1018: if (isolationLevel != -1) {
1019: try {
1020: logger.debug("set transaction isolation to {0}",
1021: isolationLevel);
1022: conn.setTransactionIsolation(isolationLevel);
1023: } catch (SQLException e) {
1024: String ilstr = "?";
1025: switch (isolationLevel) {
1026: case Connection.TRANSACTION_SERIALIZABLE:
1027: ilstr = "SERIALIZABLE";
1028: break;
1029: case Connection.TRANSACTION_NONE:
1030: ilstr = "NONE";
1031: break;
1032: case Connection.TRANSACTION_READ_COMMITTED:
1033: ilstr = "READ_COMMITTED";
1034: break;
1035: case Connection.TRANSACTION_READ_UNCOMMITTED:
1036: ilstr = "READ_UNCOMMITTED";
1037: break;
1038: case Connection.TRANSACTION_REPEATABLE_READ:
1039: ilstr = "REPEATABLE_READ";
1040: break;
1041: default:
1042: throw new SQLException("Invalid isolation level '"
1043: + ilstr + "'.");
1044: }
1045: logger
1046: .error(
1047: "Cannot set transaction isolation to {0} for this DataSource url {1}",
1048: ilstr, url, e);
1049: isolationLevel = -1;
1050: }
1051: }
1052:
1053: // Create the JManagedConnection object
1054: JManagedConnection mc = new JManagedConnection(conn, this );
1055:
1056: // return the XAConnection
1057: return mc;
1058: }
1059:
1060: // -----------------------------------------------------------------
1061: // Referenceable Implementation
1062: // -----------------------------------------------------------------
1063:
1064: /**
1065: * Retrieves the Reference of this object. Used at binding time by JNDI to
1066: * build a reference on this object.
1067: * @return The non-null Reference of this object.
1068: * @exception NamingException If a naming exception was encountered while
1069: * retrieving the reference.
1070: */
1071: public Reference getReference() throws NamingException {
1072:
1073: Reference ref = new Reference(this .getClass().getName(),
1074: DataSourceFactory.class.getName(), null);
1075: // These values are used by ObjectFactory (see DataSourceFactory.java)
1076: ref.add(new StringRefAddr("datasource.name", getDSName()));
1077: ref.add(new StringRefAddr("datasource.url", getUrl()));
1078: ref.add(new StringRefAddr("datasource.classname",
1079: getClassName()));
1080: ref
1081: .add(new StringRefAddr("datasource.username",
1082: getUserName()));
1083: ref
1084: .add(new StringRefAddr("datasource.password",
1085: getPassword()));
1086: ref.add(new StringRefAddr("datasource.isolationlevel",
1087: getTransactionIsolation()));
1088: Integer checklevel = new Integer(getCheckLevel());
1089: ref.add(new StringRefAddr("connchecklevel", checklevel
1090: .toString()));
1091: Integer maxage = new Integer(getMaxAge());
1092: ref.add(new StringRefAddr("connmaxage", maxage.toString()));
1093: Integer maxopentime = new Integer(getMaxOpenTime());
1094: ref
1095: .add(new StringRefAddr("maxopentime", maxopentime
1096: .toString()));
1097: ref.add(new StringRefAddr("connteststmt", getTestStatement()));
1098: Integer pstmtmax = new Integer(getPstmtMax());
1099: ref.add(new StringRefAddr("pstmtmax", pstmtmax.toString()));
1100: Integer minpool = new Integer(getPoolMin());
1101: ref.add(new StringRefAddr("minconpool", minpool.toString()));
1102: Integer maxpool = new Integer(getPoolMax());
1103: ref.add(new StringRefAddr("maxconpool", maxpool.toString()));
1104: Integer maxwaittime = new Integer(getMaxWaitTime());
1105: ref
1106: .add(new StringRefAddr("maxwaittime", maxwaittime
1107: .toString()));
1108: Integer maxwaiters = new Integer(getMaxWaiters());
1109: ref.add(new StringRefAddr("maxwaiters", maxwaiters.toString()));
1110: Integer samplingperiod = new Integer(getSamplingPeriod());
1111: ref.add(new StringRefAddr("samplingperiod", samplingperiod
1112: .toString()));
1113: return ref;
1114: }
1115:
1116: /**
1117: * Notifies this <code>ConnectionEventListener</code> that
1118: * the application has called the method <code>close</code> on its
1119: * representation of a pooled connection.
1120: *
1121: * @param event an event object describing the source of
1122: * the event
1123: */
1124: public void connectionClosed(final ConnectionEvent event) {
1125: JManagedConnection mc = (JManagedConnection) event.getSource();
1126: closeConnection(mc, XAResource.TMSUCCESS);
1127: }
1128:
1129: /**
1130: * Notifies this <code>ConnectionEventListener</code> that
1131: * a fatal error has occurred and the pooled connection can
1132: * no longer be used. The driver makes this notification just
1133: * before it throws the application the <code>SQLException</code>
1134: * contained in the given <code>ConnectionEvent</code> object.
1135: *
1136: * @param event an event object describing the source of
1137: * the event and containing the <code>SQLException</code> that the
1138: * driver is about to throw
1139: */
1140: @SuppressWarnings("boxing")
1141: public void connectionErrorOccurred(final ConnectionEvent event) {
1142:
1143: JManagedConnection mc = (JManagedConnection) event.getSource();
1144: logger.debug("mc= {0}", mc.getIdentifier());
1145:
1146: // remove it from the list of open connections for this thread
1147: // only if it was opened outside a tx.
1148: closeConnection(mc, XAResource.TMFAIL);
1149: }
1150:
1151: /**
1152: * @return int number of xa connection
1153: */
1154: public int getCurrentOpened() {
1155: return mcList.size();
1156: }
1157:
1158: /**
1159: * @return int number of busy xa connection.
1160: */
1161: public int getCurrentBusy() {
1162: return mcList.size() - freeList.size();
1163: }
1164:
1165: /**
1166: * compute current min/max busyConnections.
1167: */
1168: public void recomputeBusy() {
1169: int busy = getCurrentBusy();
1170: if (busyMax < busy) {
1171: busyMax = busy;
1172: }
1173: if (busyMin > busy) {
1174: busyMin = busy;
1175: }
1176: }
1177:
1178: /**
1179: * @return int number of xa connection reserved for tx.
1180: */
1181: public int getCurrentInTx() {
1182: return tx2mc.size();
1183: }
1184:
1185: /**
1186: * make samples with some monitoring values.
1187: */
1188: public synchronized void sampling() {
1189: waitingHighRecent = waitingTime;
1190: if (waitingHigh < waitingTime) {
1191: waitingHigh = waitingTime;
1192: }
1193: waitingTime = 0;
1194:
1195: waitersHighRecent = waiterCount;
1196: if (waitersHigh < waiterCount) {
1197: waitersHigh = waiterCount;
1198: }
1199: waiterCount = 0;
1200:
1201: busyMaxRecent = busyMax;
1202: busyMax = getCurrentBusy();
1203: busyMinRecent = busyMin;
1204: busyMin = getCurrentBusy();
1205: }
1206:
1207: /**
1208: * Adjust the pool size, according to poolMax and poolMin values. Also
1209: * remove old connections in the freeList.
1210: */
1211: @SuppressWarnings("boxing")
1212: public synchronized void adjust() {
1213: logger.debug(dSName);
1214:
1215: // Remove max aged elements in freelist
1216: // - Not more than MAX_REMOVE_FREELIST
1217: // - Don't reduce pool size less than poolMin
1218: int count = mcList.size() - poolMin;
1219: // In case count is null, a new connection will be
1220: // recreated just after
1221: if (count >= 0) {
1222: if (count > MAX_REMOVE_FREELIST) {
1223: count = MAX_REMOVE_FREELIST;
1224: }
1225: for (Iterator i = freeList.iterator(); i.hasNext();) {
1226: JManagedConnection mc = (JManagedConnection) i.next();
1227: if (mc.isAged()) {
1228: logger.debug("remove a timed out connection");
1229: i.remove();
1230: destroyItem(mc);
1231: count--;
1232: if (count <= 0) {
1233: break;
1234: }
1235: }
1236: }
1237: }
1238: recomputeBusy();
1239:
1240: // Close (physically) connections lost (opened for too long time)
1241: for (Iterator i = mcList.iterator(); i.hasNext();) {
1242: JManagedConnection mc = (JManagedConnection) i.next();
1243: if (mc.inactive()) {
1244: if (logger.isWarnEnabled()) {
1245: logger.warn(
1246: "close a timed out open connection {0}", mc
1247: .getIdentifier());
1248: }
1249: i.remove();
1250: // destroy mc
1251: mc.remove();
1252: connectionLeaks++;
1253: // Notify 1 thread waiting for a Connection.
1254: if (currentWaiters > 0) {
1255: notify();
1256: }
1257: }
1258: }
1259:
1260: // Shrink the pool in case of max pool size
1261: // This occurs when max pool size has been reduced by admin console.
1262: if (poolMax != NO_LIMIT) {
1263: while (freeList.size() > poolMin && mcList.size() > poolMax) {
1264: JManagedConnection mc = freeList.first();
1265: freeList.remove(mc);
1266: destroyItem(mc);
1267: }
1268: }
1269: recomputeBusy();
1270:
1271: // Recreate more Connections while poolMin is not reached
1272: while (mcList.size() < poolMin) {
1273: JManagedConnection mc = null;
1274: try {
1275: mc = (JManagedConnection) getXAConnection();
1276: openedCount++;
1277: } catch (SQLException e) {
1278: throw new IllegalStateException("Could not create "
1279: + poolMin + " mcs in the pool : ", e);
1280: }
1281: // tx = null. Assumes maxage already configured.
1282: freeList.add(mc);
1283: mcList.add(mc);
1284: mc.addConnectionEventListener(this );
1285: }
1286: }
1287:
1288: /**
1289: * Lookup connection in the pool for this user/tx.
1290: * @param user user name
1291: * @param tx Transaction the connection is involved
1292: * @return a free JManagedConnection (never null)
1293: * @throws SQLException Cannot open a connection because the pool's max size
1294: * is reached
1295: */
1296: @SuppressWarnings("boxing")
1297: public synchronized JManagedConnection openConnection(
1298: final String user, final Transaction tx)
1299: throws SQLException {
1300: JManagedConnection mc = null;
1301: // If a Connection exists already for this tx, just return it.
1302: // If no transaction, never reuse a connection already used.
1303: if (tx != null) {
1304: mc = tx2mc.get(tx);
1305: if (mc != null) {
1306: logger.debug("Reuse a Connection for same tx");
1307: mc.hold();
1308: servedOpen++;
1309: return mc;
1310: }
1311: }
1312: // Loop until a valid mc is found
1313: long timetowait = waiterTimeout;
1314: long starttime = 0;
1315: while (mc == null) {
1316: // try to find an mc in the free list
1317: if (freeList.isEmpty()) {
1318: // In case we have reached the maximum limit of the pool,
1319: // we must wait until a connection is released.
1320: if (mcList.size() >= poolMax) {
1321: boolean stoplooping = true;
1322: // If a timeout has been specified, wait, unless maxWaiters
1323: // is reached.
1324: if (timetowait > 0) {
1325: if (currentWaiters < maxWaiters) {
1326: currentWaiters++;
1327: // Store the maximum concurrent waiters
1328: if (waiterCount < currentWaiters) {
1329: waiterCount = currentWaiters;
1330: }
1331: if (starttime == 0) {
1332: starttime = System.currentTimeMillis();
1333: logger
1334: .debug(
1335: "Wait for a free Connection, {0}",
1336: mcList.size());
1337: }
1338: try {
1339: wait(timetowait);
1340: } catch (InterruptedException ign) {
1341: logger.warn("Interrupted");
1342: } finally {
1343: currentWaiters--;
1344: }
1345: long stoptime = System.currentTimeMillis();
1346: long stillwaited = stoptime - starttime;
1347: timetowait = waiterTimeout - stillwaited;
1348: stoplooping = (timetowait <= 0);
1349: if (stoplooping) {
1350: // We have been waked up by the timeout.
1351: totalWaiterCount++;
1352: totalWaitingTime += stillwaited;
1353: if (waitingTime < stillwaited) {
1354: waitingTime = stillwaited;
1355: }
1356: } else {
1357: if (!freeList.isEmpty()
1358: || mcList.size() < poolMax) {
1359: // We have been notified by a connection
1360: // released.
1361: logger.debug("Notified after {0}",
1362: stillwaited);
1363: totalWaiterCount++;
1364: totalWaitingTime += stillwaited;
1365: if (waitingTime < stillwaited) {
1366: waitingTime = stillwaited;
1367: }
1368: }
1369: continue;
1370: }
1371: }
1372: }
1373: if (stoplooping && freeList.isEmpty()
1374: && mcList.size() >= poolMax) {
1375: if (starttime > 0) {
1376: rejectedTimeout++;
1377: logger
1378: .warn("Cannot create a Connection - timeout");
1379: } else {
1380: rejectedFull++;
1381: logger.warn("Cannot create a Connection");
1382: }
1383: throw new SQLException(
1384: "No more connections in "
1385: + getDatasourceName());
1386: }
1387: continue;
1388: }
1389: logger
1390: .debug("empty free list: Create a new Connection");
1391: try {
1392: // create a new XA Connection
1393: mc = (JManagedConnection) getXAConnection();
1394: openedCount++;
1395: } catch (SQLException e) {
1396: connectionFailures++;
1397: rejectedOther++;
1398: logger.warn("Cannot create new Connection for tx",
1399: e);
1400: throw e;
1401: }
1402: // Register the connection manager as a ConnectionEventListener
1403: mc.addConnectionEventListener(this );
1404: mcList.add(mc);
1405: } else {
1406: mc = freeList.last();
1407: freeList.remove(mc);
1408: // Check the connection before reusing it
1409: if (checkLevel > 0) {
1410: try {
1411: JConnection conn = (JConnection) mc
1412: .getConnection();
1413: if (conn.isPhysicallyClosed()) {
1414: logger
1415: .warn("The JDBC connection has been closed!");
1416: destroyItem(mc);
1417: starttime = 0;
1418: mc = null;
1419: continue;
1420: }
1421: if (checkLevel > 1) {
1422: java.sql.Statement stmt = conn
1423: .createStatement();
1424: stmt.execute(testStatement);
1425: stmt.close();
1426: }
1427: } catch (Exception e) {
1428: logger.error("DataSource "
1429: + getDatasourceName()
1430: + " error: removing invalid mc", e);
1431: destroyItem(mc);
1432: starttime = 0;
1433: mc = null;
1434: continue;
1435: }
1436: }
1437: }
1438: }
1439: recomputeBusy();
1440: mc.setTx(tx);
1441: if (tx == null) {
1442: logger.debug("Got a Connection - no TX: ");
1443: } else {
1444: logger.debug("Got a Connection for TX: ");
1445: // register synchronization
1446: try {
1447: tx.registerSynchronization(mc);
1448: tx2mc.put(tx, mc); // only if registerSynchronization was OK.
1449: } catch (javax.transaction.RollbackException e) {
1450: // / optimization is probably possible at this point
1451: logger
1452: .warn(
1453: "DataSource "
1454: + getDatasourceName()
1455: + " error: Pool mc registered, but tx is rollback only",
1456: e);
1457: } catch (javax.transaction.SystemException e) {
1458: logger
1459: .error(
1460: "DataSource "
1461: + getDatasourceName()
1462: + " error in pool: system exception from transaction manager ",
1463: e);
1464: } catch (IllegalStateException e) {
1465: // In case transaction has already committed, do as if no tx.
1466: logger.warn("Got a Connection - committed TX: ", e);
1467: mc.setTx(null);
1468: }
1469: }
1470: mc.hold();
1471: servedOpen++;
1472: return mc;
1473: }
1474:
1475: /**
1476: * The transaction has committed (or rolled back). We can return its
1477: * connections to the pool of available connections.
1478: * @param tx the non null transaction
1479: */
1480: public synchronized void freeConnections(final Transaction tx) {
1481: logger.debug("free connection for Tx = " + tx);
1482: JManagedConnection mc = tx2mc.remove(tx);
1483: if (mc == null) {
1484: logger.error("pool: no connection found to free for Tx = "
1485: + tx);
1486: return;
1487: }
1488: mc.setTx(null);
1489: if (mc.isOpen()) {
1490: // Connection not yet closed (but committed).
1491: logger.debug("Connection not closed by caller");
1492: return;
1493: }
1494: freeItem(mc);
1495: }
1496:
1497: /**
1498: * Close all connections in the pool, when server is shut down.
1499: */
1500: public synchronized void closeAllConnection() {
1501: // Close physically all connections
1502: Iterator it = mcList.iterator();
1503: try {
1504: while (it.hasNext()) {
1505: JManagedConnection mc = (JManagedConnection) it.next();
1506: mc.close();
1507: }
1508: } catch (java.sql.SQLException e) {
1509: logger.error("Error while closing a Connection:", e);
1510: }
1511: }
1512:
1513: // -----------------------------------------------------------------------
1514: // private methods
1515: // -----------------------------------------------------------------------
1516:
1517: /**
1518: * Mark a specific Connection in the pool as closed. If it is no longer
1519: * associated to a Tx, we can free it.
1520: * @param mc XAConnection being closed
1521: * @param flag TMSUCCESS (normal close) or TMFAIL (error) or null if error.
1522: * @return false if has not be closed (still in use)
1523: */
1524: private boolean closeConnection(final JManagedConnection mc,
1525: final int flag) {
1526: // The connection will be available only if not associated
1527: // to a transaction. Else, it will be reusable only for the
1528: // same transaction.
1529: if (!mc.release()) {
1530: return false;
1531: }
1532: if (mc.getTx() != null) {
1533: logger.debug("keep connection for same tx");
1534: } else {
1535: freeItem(mc);
1536: }
1537:
1538: // delist Resource if in transaction
1539: Transaction tx = null;
1540: try {
1541: tx = tm.getTransaction();
1542: } catch (NullPointerException n) {
1543: // current is null: we are not in EasyBeans Server.
1544: logger
1545: .error(
1546: "Pool: should not be used outside a EasyBeans Server",
1547: n);
1548: } catch (SystemException e) {
1549: logger.error("Pool: getTransaction failed:", e);
1550: }
1551: if (tx != null && mc.isClosed()) {
1552: try {
1553: tx.delistResource(mc.getXAResource(), flag);
1554: } catch (Exception e) {
1555: logger.error(
1556: "Pool: Exception while delisting resource:", e);
1557: }
1558: }
1559: return true;
1560: }
1561:
1562: /**
1563: * Free item and return it in the free list.
1564: * @param item The item to be freed
1565: */
1566: @SuppressWarnings("boxing")
1567: private synchronized void freeItem(final JManagedConnection item) {
1568: // Add it to the free list
1569: // Even if maxage is reached, because we avoids going under min pool
1570: // size.
1571: // PoolKeeper will manage aged connections.
1572: freeList.add(item);
1573: if (logger.isDebugEnabled()) {
1574: logger.debug("item added to freeList: "
1575: + item.getIdentifier());
1576: }
1577:
1578: // Notify 1 thread waiting for a Connection.
1579: if (currentWaiters > 0) {
1580: notify();
1581: }
1582: recomputeBusy();
1583: }
1584:
1585: /**
1586: * Destroy an mc because connection closed or error occured.
1587: * @param mc The mc to be destroyed
1588: */
1589: private synchronized void destroyItem(final JManagedConnection mc) {
1590: mcList.remove(mc);
1591: mc.remove();
1592: // Notify 1 thread waiting for a Connection.
1593: if (currentWaiters > 0) {
1594: notify();
1595: }
1596: recomputeBusy();
1597: }
1598:
1599: /**
1600: * Check on a connection the test statement.
1601: * @param testStatement the statement to use for test
1602: * @return the test statement if the test succeeded, an error message
1603: * otherwise
1604: * @throws SQLException If an error occured when trying to test (not due to
1605: * the test itself, but to other preliminary or post operation).
1606: */
1607: public String checkConnection(final String testStatement)
1608: throws SQLException {
1609: String noError = testStatement;
1610: JManagedConnection mc = null;
1611: boolean jmcCreated = false;
1612: if (!freeList.isEmpty()) {
1613: // find a connection to test in the freeList
1614: Iterator it = freeList.iterator();
1615: while (it.hasNext()) {
1616: mc = (JManagedConnection) it.next();
1617: try {
1618: JConnection conn = (JConnection) mc.getConnection();
1619: if (!conn.isPhysicallyClosed()) {
1620: // ok, we found a connection we can use to test
1621: logger
1622: .debug("Use a free JManagedConnection to test with "
1623: + testStatement);
1624: break;
1625: }
1626: mc = null;
1627: } catch (SQLException e) {
1628: // Can't use this connection to test
1629: mc = null;
1630: }
1631: }
1632: }
1633: if (mc == null) {
1634: // try to create mc Connection
1635: logger.debug("Create a JManagedConnection to test with "
1636: + testStatement);
1637: Connection conn = null;
1638: try {
1639: conn = DriverManager.getConnection(url, userName,
1640: password);
1641: } catch (SQLException e) {
1642: logger.error(
1643: "Could not get Connection on " + url + ":", e);
1644: }
1645: mc = new JManagedConnection(conn, this );
1646: jmcCreated = true;
1647: }
1648: if (mc != null) {
1649: // Do the test on a the free connection or the created connection
1650: JConnection conn = (JConnection) mc.getConnection();
1651: java.sql.Statement stmt = conn.createStatement();
1652: try {
1653: stmt.execute(testStatement);
1654: } catch (SQLException e) {
1655: // The test fails
1656: return e.getMessage();
1657: }
1658: stmt.close();
1659: if (jmcCreated) {
1660: mc.close();
1661: }
1662: }
1663: return noError;
1664: }
1665:
1666: /**
1667: * Sets the transaction managed used by the connections.
1668: * @param tm the transaction manager.
1669: */
1670: protected void setTm(final TransactionManager tm) {
1671: this.tm = tm;
1672: }
1673:
1674: }
|