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