0001: /*
0002: Copyright (C) 2003 Know Gate S.L. All rights reserved.
0003: C/Oņa, 107 1š2 28050 Madrid (Spain)
0004:
0005: Redistribution and use in source and binary forms, with or without
0006: modification, are permitted provided that the following conditions
0007: are met:
0008:
0009: 1. Redistributions of source code must retain the above copyright
0010: notice, this list of conditions and the following disclaimer.
0011:
0012: 2. The end-user documentation included with the redistribution,
0013: if any, must include the following acknowledgment:
0014: "This product includes software parts from hipergate
0015: (http://www.hipergate.org/)."
0016: Alternately, this acknowledgment may appear in the software itself,
0017: if and wherever such third-party acknowledgments normally appear.
0018:
0019: 3. The name hipergate must not be used to endorse or promote products
0020: derived from this software without prior written permission.
0021: Products derived from this software may not be called hipergate,
0022: nor may hipergate appear in their name, without prior written
0023: permission.
0024:
0025: This library is distributed in the hope that it will be useful,
0026: but WITHOUT ANY WARRANTY; without even the implied warranty of
0027: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0028:
0029: You should have received a copy of hipergate License with this code;
0030: if not, visit http://www.hipergate.org or mail to info@hipergate.org
0031: */
0032:
0033: package com.knowgate.jdc;
0034:
0035: import java.util.HashMap;
0036: import java.util.Iterator;
0037: import java.util.LinkedList;
0038: import java.util.ListIterator;
0039: import java.util.Set;
0040: import java.util.Vector;
0041: import java.util.Enumeration;
0042: import java.util.Date;
0043: import java.util.ConcurrentModificationException;
0044:
0045: import java.sql.DriverManager;
0046: import java.sql.Connection;
0047: import java.sql.CallableStatement;
0048: import java.sql.PreparedStatement;
0049: import java.sql.SQLException;
0050: import java.sql.Timestamp;
0051: import java.sql.Types.*;
0052:
0053: import com.knowgate.debug.DebugFile;
0054:
0055: /**
0056: * <p>Connection pool daemon thread</p>
0057: * This thread scans a ConnectionPool every given interval calling
0058: * ConnectionPool.reapConnections() for closing unused connections.
0059: */
0060:
0061: final class ConnectionReaper extends Thread {
0062:
0063: /**
0064: * Reference to reaped connection pool
0065: */
0066: private JDCConnectionPool pool;
0067:
0068: /**
0069: * Used to stop the Connection reaper thread
0070: */
0071: private boolean keepruning;
0072:
0073: /**
0074: * Connection Reaper call interval (default = 5 mins)
0075: */
0076: private long delay = 300000l;
0077:
0078: /**
0079: * <p>Constructor</p>
0080: * @param forpool JDCConnectionPool
0081: */
0082: ConnectionReaper(JDCConnectionPool forpool) {
0083: pool = forpool;
0084: keepruning = true;
0085: try {
0086: checkAccess();
0087: setDaemon(true);
0088: setPriority(MIN_PRIORITY);
0089: } catch (SecurityException ignore) {
0090: }
0091: }
0092:
0093: /**
0094: * Get connection reaper call interval
0095: * @return long Number of miliseconds between reaper calls
0096: * @since 3.0
0097: */
0098: public long getDelay() {
0099: return delay;
0100: }
0101:
0102: /**
0103: * <p>Set connection reaper call interval</p>
0104: * The default value is 5 minutes
0105: * @param lDelay long Number of miliseconds between reaper calls
0106: * @throws IllegalArgumentException if lDelay is less than 1000
0107: * @since 3.0
0108: */
0109: public void setDelay(long lDelay) throws IllegalArgumentException {
0110: if (lDelay < 1000l)
0111: throw new IllegalArgumentException(
0112: "ConnectionReaper delay cannot be smaller than 1000 miliseconds");
0113: delay = lDelay;
0114: }
0115:
0116: public void halt() {
0117: keepruning = false;
0118: }
0119:
0120: /**
0121: * Reap connections every n-minutes
0122: */
0123: public void run() {
0124: while (keepruning) {
0125: try {
0126: sleep(delay);
0127: } catch (InterruptedException e) {
0128: }
0129: pool.reapConnections();
0130: } // wend
0131: } // run
0132: } // ConnectionReaper
0133:
0134: // ---------------------------------------------------------
0135:
0136: /**
0137: * <p>JDBC Connection Pool</p>
0138: * <p>Implementation of a standard JDBC connection pool.</p>
0139: * @version 2.2
0140: */
0141:
0142: public final class JDCConnectionPool {
0143:
0144: private Object binding;
0145: private Vector connections;
0146: private int openconns;
0147: private HashMap callers;
0148: private String url, user, password;
0149: private ConnectionReaper reaper;
0150: private LinkedList errorlog;
0151:
0152: /**
0153: * Staled connection threshold (10 minutes)
0154: * The maximum time that any SQL single statement may last.
0155: */
0156: private long timeout = 600000l;
0157:
0158: /**
0159: * Soft limit for maximum open connections
0160: */
0161: private int poolsize = 32;
0162:
0163: /**
0164: * Hard absolute limit for maximum open connections
0165: */
0166: private int hardlimit = 100;
0167:
0168: // ---------------------------------------------------------
0169:
0170: /**
0171: * Constructor
0172: * By default, maximum pool size is set to 32,
0173: * maximum opened connections is 100,
0174: * login timeout is 20 seconds,
0175: * connection timeout is 5 minutes.
0176: * @param url JDBC URL string
0177: * @param user Database user
0178: * @param password Password for user
0179: */
0180:
0181: public JDCConnectionPool(String url, String user, String password) {
0182:
0183: binding = null;
0184:
0185: if (null == url)
0186: throw new IllegalArgumentException(
0187: "JDCConnectionPool : url cannot be null");
0188:
0189: if (url.length() == 0)
0190: throw new IllegalArgumentException(
0191: "JDCConnectionPool : url value not set");
0192:
0193: this .url = url;
0194: this .user = user;
0195: this .password = password;
0196: this .openconns = 0;
0197:
0198: DriverManager.setLoginTimeout(20); // default login timeout = 20 seconds
0199:
0200: connections = new Vector(poolsize <= hardlimit ? poolsize
0201: : hardlimit);
0202: reaper = new ConnectionReaper(this );
0203: reaper.start();
0204:
0205: if (DebugFile.trace)
0206: callers = new HashMap(1023);
0207:
0208: errorlog = new LinkedList();
0209: }
0210:
0211: // ---------------------------------------------------------
0212:
0213: /**
0214: * <p>Constructor</p>
0215: * This method sets a default login timeout of 20 seconds
0216: * @param bind DBBind owner of the connection pool (may be <b>null</b>)
0217: * @param url JDBC URL string
0218: * @param user Database user
0219: * @param password Password for user
0220: * @param maxpoolsize Maximum pool size
0221: * @param maxconnections Maximum opened connections
0222: * @param connectiontimeout Maximum time that a
0223: * @param logintimeout Maximum time, in seconds, to wait for connection
0224: * @since v2.2
0225: */
0226:
0227: public JDCConnectionPool(Object bind, String url, String user,
0228: String password, int maxpoolsize, int maxconnections,
0229: int logintimeout, long connectiontimeout) {
0230:
0231: binding = bind;
0232:
0233: if (null == url)
0234: throw new IllegalArgumentException(
0235: "JDCConnectionPool : url cannot be null");
0236:
0237: if (url.length() == 0)
0238: throw new IllegalArgumentException(
0239: "JDCConnectionPool : url value not set");
0240:
0241: if (maxpoolsize < 1)
0242: throw new IllegalArgumentException(
0243: "maxpoolsize must be greater than or equal to 1");
0244:
0245: if (maxconnections < 1)
0246: throw new IllegalArgumentException(
0247: "maxpoolsize must be greater than or equal to 1");
0248:
0249: if (maxpoolsize > maxconnections)
0250: throw new IllegalArgumentException(
0251: "maxpoolsize must be less than or equal to maxconnections");
0252:
0253: this .url = url;
0254: this .user = user;
0255: this .password = password;
0256: this .openconns = 0;
0257: this .poolsize = maxpoolsize;
0258: this .hardlimit = maxconnections;
0259: this .timeout = connectiontimeout;
0260:
0261: DriverManager.setLoginTimeout(logintimeout);
0262:
0263: connections = new Vector(poolsize <= hardlimit ? poolsize
0264: : hardlimit);
0265: reaper = new ConnectionReaper(this );
0266: reaper.start();
0267:
0268: if (DebugFile.trace)
0269: callers = new HashMap(1023);
0270:
0271: errorlog = new LinkedList();
0272: }
0273:
0274: /**
0275: * <p>Constructor</p>
0276: * This method sets a default login timeout of 20 seconds
0277: * @param bind DBBind owner of the connection pool (may be <b>null</b>)
0278: * @param url JDBC URL string
0279: * @param user Database user
0280: * @param password Password for user
0281: * @param maxpoolsize Maximum pool size
0282: * @param maxconnections Maximum opened connections
0283: * @param logintimeout Maximum time, in seconds, to wait for connection
0284: * @since v2.2
0285: */
0286:
0287: public JDCConnectionPool(Object bind, String url, String user,
0288: String password, int maxpoolsize, int maxconnections,
0289: int logintimeout) {
0290: this (bind, url, user, password, maxpoolsize, maxconnections,
0291: logintimeout, 60000l);
0292: }
0293:
0294: // ---------------------------------------------------------
0295:
0296: /**
0297: * <p>Constructor</p>
0298: * This method sets a default login timeout of 20 seconds
0299: * @param bind DBBind owner of the connection pool (may be <b>null</b>)
0300: * @param url JDBC URL string
0301: * @param user Database user
0302: * @param password Password for user
0303: * @param maxpoolsize Maximum pool size (Default 32)
0304: * @param maxconnections Maximum opened connections (Default 100)
0305: */
0306: public JDCConnectionPool(Object bind, String url, String user,
0307: String password, int maxpoolsize, int maxconnections) {
0308: this (bind, url, user, password, maxpoolsize, maxconnections, 20);
0309: }
0310:
0311: // ---------------------------------------------------------
0312:
0313: /**
0314: * <p>Constructor</p>
0315: * @param url JDBC URL string
0316: * @param user Database user
0317: * @param password Password for user
0318: * @param maxpoolsize Maximum pool size (Default 32)
0319: * @param maxconnections Maximum opened connections (Default 100)
0320: * @param logintimeout Maximum time, in seconds, to wait for connection
0321: * @since v2.2
0322: */
0323:
0324: public JDCConnectionPool(String url, String user, String password,
0325: int maxpoolsize, int maxconnections, int logintimeout) {
0326:
0327: binding = null;
0328:
0329: if (null == url)
0330: throw new IllegalArgumentException(
0331: "JDCConnectionPool : url cannot be null");
0332:
0333: if (url.length() == 0)
0334: throw new IllegalArgumentException(
0335: "JDCConnectionPool : url value not set");
0336:
0337: if (maxpoolsize < 1)
0338: throw new IllegalArgumentException(
0339: "maxpoolsize must be greater than or equal to 1");
0340:
0341: if (maxconnections < 1)
0342: throw new IllegalArgumentException(
0343: "maxpoolsize must be greater than or equal to 1");
0344:
0345: if (maxpoolsize > maxconnections)
0346: throw new IllegalArgumentException(
0347: "maxpoolsize must be less than or equal to maxconnections");
0348:
0349: this .url = url;
0350: this .user = user;
0351: this .password = password;
0352: this .openconns = 0;
0353: this .poolsize = maxpoolsize;
0354: this .hardlimit = maxconnections;
0355:
0356: DriverManager.setLoginTimeout(logintimeout);
0357:
0358: connections = new Vector(poolsize);
0359: reaper = new ConnectionReaper(this );
0360: reaper.start();
0361:
0362: if (DebugFile.trace)
0363: callers = new HashMap(1023);
0364:
0365: errorlog = new LinkedList();
0366: }
0367:
0368: // ---------------------------------------------------------
0369:
0370: /**
0371: * <p>Constructor</p>
0372: * This method sets a default login timeout of 20 seconds
0373: * @param url JDBC URL string
0374: * @param user Database user
0375: * @param password Password for user
0376: * @param maxpoolsize Maximum pool size (Default 32)
0377: * @param maxconnections Maximum opened connections (Default 100)
0378: */
0379:
0380: // ---------------------------------------------------------
0381: public JDCConnectionPool(String url, String user, String password,
0382: int maxpoolsize, int maxconnections) {
0383: this (url, user, password, maxpoolsize, maxconnections, 20);
0384: }
0385:
0386: // ---------------------------------------------------------
0387:
0388: private synchronized void modifyMap(String sCaller, int iAction)
0389: throws NullPointerException {
0390:
0391: if (null == callers)
0392: callers = new HashMap(1023);
0393:
0394: if (callers.containsKey(sCaller)) {
0395: Integer iRefCount = new Integer(((Integer) callers
0396: .get(sCaller)).intValue()
0397: + iAction);
0398: callers.remove(sCaller);
0399: callers.put(sCaller, iRefCount);
0400: DebugFile.writeln(" " + sCaller + " reference count is "
0401: + iRefCount.toString());
0402: } else {
0403: if (1 == iAction) {
0404: callers.put(sCaller, new Integer(1));
0405: DebugFile.writeln(" " + sCaller
0406: + " reference count is 1");
0407: } else {
0408: DebugFile
0409: .writeln(" ERROR: JDCConnectionPool get/close connection mismatch for "
0410: + sCaller);
0411: }
0412: }
0413: } // modifyMap
0414:
0415: // ---------------------------------------------------------
0416:
0417: /**
0418: * Close all connections and stop connection reaper
0419: */
0420: public void close() {
0421: if (DebugFile.trace) {
0422: DebugFile.writeln("Begin ConnectionPool.close()");
0423: DebugFile.incIdent();
0424: }
0425:
0426: reaper.halt();
0427:
0428: closeConnections();
0429:
0430: if (DebugFile.trace) {
0431: DebugFile.decIdent();
0432: DebugFile.writeln("End ConnectionPool.close()");
0433: }
0434: } // close
0435:
0436: // ---------------------------------------------------------
0437:
0438: /**
0439: * <p>Get prefered open connections limit</p>
0440: * <p>Additional connections beyond PoolSize may be opened but they
0441: * will closed inmediately after use and not pooled.<br>The default value is 32.</p>
0442: * @return open connections soft limit
0443: */
0444: public int getPoolSize() {
0445: return poolsize;
0446: }
0447:
0448: // ---------------------------------------------------------
0449:
0450: /**
0451: * <p>Set prefered open connections limit</p>
0452: * <p>Additional connections beyond PoolSize may be opened but they
0453: * will closed inmediately after use and not pooled.<br>The default value is 32.<br>
0454: * Connections not being used can only be in the pool for a maximum of five minutes.<br>
0455: * After a connection is not used for over 5 minutes it will be closed so the actual
0456: * pool size will eventually go down to zero after a period of inactivity.</p>
0457: * @param iPoolSize Maximum pooled connections
0458: */
0459:
0460: public void setPoolSize(int iPoolSize) {
0461:
0462: if (iPoolSize > hardlimit)
0463: throw new IllegalArgumentException(
0464: "prefered pool size must be less than or equal to max pool size ");
0465:
0466: reapConnections();
0467: poolsize = iPoolSize;
0468: }
0469:
0470: // ---------------------------------------------------------
0471:
0472: /**
0473: * <p>Set maximum concurrent open connections limit</p>
0474: * The default value is 100.<br>
0475: * If iMaxConnections is set to zero then the connection pool is effectively
0476: * turned off and no pooling occurs.
0477: * @param iMaxConnections Absolute maximum for opened connections
0478: */
0479: public void setMaxPoolSize(int iMaxConnections) {
0480:
0481: if (iMaxConnections == 0) {
0482: reapConnections();
0483: poolsize = hardlimit = 0;
0484: } else {
0485: if (iMaxConnections < poolsize)
0486: throw new IllegalArgumentException(
0487: "max pool size must be greater than or equal to prefered pool size ");
0488:
0489: reapConnections();
0490: hardlimit = iMaxConnections;
0491: }
0492: }
0493:
0494: // ---------------------------------------------------------
0495:
0496: /**
0497: * <p>Absolute maximum allowed for concurrent opened connections.</p>
0498: * The default value is 100.
0499: */
0500: public int getMaxPoolSize() {
0501: return hardlimit;
0502: }
0503:
0504: /**
0505: * <p>Get staled connection threshold</p>
0506: * The default value is 600000ms (10 mins.)<br>
0507: * This implies that all database operations done using connections
0508: * obtained from the pool must be completed before 10 minutes or else
0509: * they can be closed by the connection reaper before their normal finish.
0510: * @return The maximum amount of time in miliseconds that a JDCConnection
0511: * can be opened and not used before considering it staled.
0512: */
0513: public long getTimeout() {
0514: return timeout;
0515: }
0516:
0517: // ---------------------------------------------------------
0518:
0519: /**
0520: * <p>Set staled connection threshold</p>
0521: * @param miliseconds The maximum amount of time in miliseconds that a JDCConnection
0522: * can be opened and not used before considering it staled.<BR>
0523: * Default value is 600000ms (10 mins.) Minimum value is 1000.
0524: * @throws IllegalArgumentException If miliseconds<1000
0525: */
0526:
0527: public void setTimeout(long miliseconds)
0528: throws IllegalArgumentException {
0529:
0530: if (miliseconds < 1000l)
0531: throw new IllegalArgumentException(
0532: "Connection timeout must be at least 1000 miliseconds");
0533:
0534: timeout = miliseconds;
0535: }
0536:
0537: /**
0538: * Delay betwwen connection reaper executions
0539: * @return long Number of miliseconds
0540: * @since 3.0
0541: */
0542: public long getReaperDaemonDelay() {
0543: return reaper.getDelay();
0544: }
0545:
0546: /**
0547: * Set delay betwwen connection reaper executions (default value is 5 mins)
0548: * @param lDelayMs long Miliseconds
0549: * @throws IllegalArgumentException if lDelayMs is less than 1000
0550: * @since 3.0
0551: */
0552: public void setReaperDaemonDelay(long lDelayMs)
0553: throws IllegalArgumentException {
0554: reaper.setDelay(lDelayMs);
0555: }
0556:
0557: // ---------------------------------------------------------
0558:
0559: /**
0560: * Close and remove one connection from the pool
0561: * @param conn Connection to close
0562: */
0563:
0564: private synchronized void removeConnection(JDCConnection conn) {
0565: boolean bClosed;
0566: String sCaller = "";
0567:
0568: try {
0569: if (DebugFile.trace)
0570: logConnection(conn, "removeConnection", "RDBC", null);
0571:
0572: sCaller = conn.getName();
0573: if (!conn.isClosed())
0574: conn.getConnection().close();
0575: conn.expireLease();
0576: if (DebugFile.trace && (null != sCaller))
0577: modifyMap(sCaller, -1);
0578: bClosed = true;
0579: } catch (SQLException e) {
0580: bClosed = false;
0581:
0582: if (errorlog.size() > 100)
0583: errorlog.removeFirst();
0584: errorlog.addLast(new Date().toString() + " " + sCaller
0585: + " Connection.close() " + e.getMessage());
0586:
0587: if (DebugFile.trace)
0588: DebugFile
0589: .writeln("SQLException at JDCConnectionPool.removeConnection() : "
0590: + e.getMessage());
0591: }
0592:
0593: if (bClosed) {
0594: if (DebugFile.trace)
0595: DebugFile.writeln("connections.removeElement("
0596: + String.valueOf(openconns) + ")");
0597: connections.removeElement(conn);
0598: openconns--;
0599: }
0600: } // removeConnection()
0601:
0602: // ---------------------------------------------------------
0603:
0604: /**
0605: * Called from the connection reaper daemon thread every n-minutes for maintaining the pool clean
0606: */
0607:
0608: synchronized void reapConnections() {
0609:
0610: if (DebugFile.trace) {
0611: DebugFile
0612: .writeln("Begin JDCConnectionPool.reapConnections()");
0613: DebugFile.incIdent();
0614: }
0615:
0616: long stale = System.currentTimeMillis() - timeout;
0617: Enumeration connlist = connections.elements();
0618: JDCConnection conn;
0619:
0620: while ((connlist != null) && (connlist.hasMoreElements())) {
0621: conn = (JDCConnection) connlist.nextElement();
0622:
0623: // Remove each connection that is not in use or
0624: // is stalled for more than maximun usage timeout (default 10 mins)
0625: if (!conn.inUse())
0626: removeConnection(conn);
0627: else if (stale > conn.getLastUse()) {
0628: if (DebugFile.trace)
0629: DebugFile.writeln("Connection " + conn.getName()
0630: + " was staled since "
0631: + new Date(conn.getLastUse()).toString());
0632: if (errorlog.size() > 100)
0633: errorlog.removeFirst();
0634: errorlog.addLast(new Date().toString() + " Connection "
0635: + conn.getName() + " was staled since "
0636: + new Date(conn.getLastUse()).toString());
0637: removeConnection(conn);
0638: }
0639: } // wend
0640:
0641: if (DebugFile.trace) {
0642: DebugFile.decIdent();
0643: DebugFile
0644: .writeln("End JDCConnectionPool.reapConnections() : "
0645: + new Date().toString());
0646: }
0647: } // reapConnections()
0648:
0649: // ---------------------------------------------------------
0650:
0651: /**
0652: * Close all connections from the pool regardless of their current state
0653: */
0654:
0655: public void closeConnections() {
0656:
0657: Enumeration connlist;
0658:
0659: if (DebugFile.trace) {
0660: DebugFile
0661: .writeln("Begin JDCConnectionPool.closeConnections()");
0662: DebugFile.incIdent();
0663: }
0664:
0665: connlist = connections.elements();
0666:
0667: if (connlist != null) {
0668: while (connlist.hasMoreElements()) {
0669: removeConnection((JDCConnection) connlist.nextElement());
0670: } // wend
0671: } // fi ()
0672:
0673: if (DebugFile.trace)
0674: callers.clear();
0675:
0676: connections.clear();
0677:
0678: if (DebugFile.trace) {
0679: DebugFile.decIdent();
0680: DebugFile
0681: .writeln("End JDCConnectionPool.closeConnections() : "
0682: + String.valueOf(openconns));
0683: }
0684:
0685: openconns = 0;
0686: } // closeConnections()
0687:
0688: // ---------------------------------------------------------
0689:
0690: /**
0691: * Close connections from the pool not used for a longer time
0692: * @return Count of staled connections closed
0693: */
0694:
0695: public int closeStaledConnections() {
0696:
0697: JDCConnection conn;
0698: Enumeration connlist;
0699:
0700: if (DebugFile.trace) {
0701: DebugFile
0702: .writeln("Begin JDCConnectionPool.closeStaledConnections()");
0703: DebugFile.incIdent();
0704: }
0705:
0706: int staled = 0;
0707: final long stale = System.currentTimeMillis() - timeout;
0708:
0709: connlist = connections.elements();
0710:
0711: if (connlist != null) {
0712: while (connlist.hasMoreElements()) {
0713: conn = (JDCConnection) connlist.nextElement();
0714: if (stale > conn.getLastUse()) {
0715: staled++;
0716: removeConnection(conn);
0717: }
0718: } // wend
0719: } // fi ()
0720:
0721: if (DebugFile.trace) {
0722: DebugFile.decIdent();
0723: DebugFile
0724: .writeln("End JDCConnectionPool.closeStaledConnections() : "
0725: + String.valueOf(staled));
0726: }
0727:
0728: return staled;
0729: } // closeStaledConnections()
0730:
0731: // ---------------------------------------------------------
0732:
0733: private static void logConnection(JDCConnection conn, String sName,
0734: String cOpCode, String sParams) {
0735:
0736: PreparedStatement oStmt = null;
0737: JDCConnection oLogConn = null;
0738:
0739: if (DebugFile.trace) {
0740: com.knowgate.dataobjs.DBAudit.log(JDCConnection.IdClass,
0741: cOpCode, "", sName, "", 0, "", sParams, "");
0742: }
0743:
0744: } // logConnection
0745:
0746: // ---------------------------------------------------------
0747:
0748: /**
0749: * Get an array with references to all pooled connections
0750: */
0751: public synchronized JDCConnection[] getAllConnections() {
0752: int iConnections = connections.size();
0753: JDCConnection[] aConnections = new JDCConnection[iConnections];
0754:
0755: for (int c = 0; c < iConnections; c++)
0756: aConnections[c] = (JDCConnection) connections.get(c);
0757:
0758: return aConnections;
0759: } // getAllConnections
0760:
0761: // ---------------------------------------------------------
0762:
0763: /**
0764: * Get the DBbind object owner of this conenction pool
0765: * @return DBBind instance or <b>null</b> if this connection pool has no owner
0766: */
0767: public Object getDatabaseBinding() {
0768: return binding;
0769: }
0770:
0771: // ---------------------------------------------------------
0772:
0773: /**
0774: * Get a connection from the pool
0775: * @param sCaller This is just an information parameter used for open/closed
0776: * mismatch tracking and other benchmarking and statistical purposes.
0777: * @return Opened JDCConnection
0778: * @throws SQLException If getMaxPoolSize() opened connections is reached an
0779: * SQLException with SQLState="08004" will be raised upon calling getConnection().<br>
0780: * <b>Microsoft SQL Server</b>: Connection reuse requires that SelectMethod=cursor was
0781: * specified at connection string.
0782: */
0783:
0784: public synchronized JDCConnection getConnection(String sCaller)
0785: throws SQLException {
0786:
0787: int i, s;
0788: JDCConnection j;
0789: Connection c;
0790:
0791: if (DebugFile.trace) {
0792: DebugFile.writeln("Begin JDCConnectionPool.getConnection("
0793: + (sCaller != null ? sCaller : "") + ")");
0794: DebugFile.incIdent();
0795: }
0796:
0797: if (hardlimit == 0) {
0798: // If hardlimit==0 Then connection pool is turned off so return a connection
0799: // directly from the DriverManager
0800:
0801: c = DriverManager.getConnection(url, user, password);
0802: j = new JDCConnection(c, null);
0803: } else {
0804:
0805: j = null;
0806:
0807: s = connections.size();
0808: for (i = 0; i < s; i++) {
0809: j = (JDCConnection) connections.elementAt(i);
0810: if (j.lease(sCaller)) {
0811: if (DebugFile.trace) {
0812: DebugFile
0813: .writeln(" JDCConnectionPool hit for ("
0814: + url
0815: + ", ...) on pooled connection #"
0816: + String.valueOf(i));
0817: if (sCaller != null)
0818: logConnection(j, sCaller, "ODBC", "hit");
0819: }
0820: break;
0821: } else
0822: j = null;
0823: } // endfor
0824:
0825: if (null == j) {
0826: if (openconns == hardlimit) {
0827: if (DebugFile.trace)
0828: DebugFile.decIdent();
0829: throw new SQLException("Maximum number of "
0830: + String.valueOf(hardlimit)
0831: + " concurrent connections exceeded",
0832: "08004");
0833: }
0834:
0835: if (DebugFile.trace)
0836: DebugFile.writeln(" DriverManager.getConnection("
0837: + url + ", ...)");
0838:
0839: c = DriverManager.getConnection(url, user, password);
0840:
0841: if (null != c) {
0842: j = new JDCConnection(c, this );
0843: j.lease(sCaller);
0844:
0845: if (DebugFile.trace) {
0846: DebugFile
0847: .writeln(" JDCConnectionPool miss for ("
0848: + url + ", ...)");
0849: if (sCaller != null)
0850: logConnection(j, sCaller, "ODBC", "miss");
0851: }
0852:
0853: connections.addElement(j);
0854: c = null;
0855: } else {
0856: if (DebugFile.trace)
0857: DebugFile
0858: .writeln("JDCConnectionPool.getConnection() DriverManager.getConnection() returned null value");
0859: j = null;
0860: }
0861:
0862: if (null != j)
0863: openconns++;
0864: } // endif (null==j)
0865:
0866: if (DebugFile.trace) {
0867: if (sCaller != null)
0868: modifyMap(sCaller, 1);
0869: } // DebugFile()
0870: } // fi (hardlimit==0)
0871:
0872: if (DebugFile.trace) {
0873: DebugFile.decIdent();
0874: DebugFile.writeln("End JDCConnectionPool.getConnection()");
0875: } // DebugFile()
0876:
0877: return j;
0878: } // getConnection()
0879:
0880: // ---------------------------------------------------------
0881:
0882: /**
0883: * Get conenction for a server process identifier
0884: * @param sPId String Operating system process identifier at server side
0885: * @return JDCConnection or <b>null</b> if no connection for such pid was found
0886: * @throws SQLException
0887: * @since 2.2
0888: */
0889: public JDCConnection getConnectionForPId(String sPId)
0890: throws SQLException {
0891: String pid;
0892: JDCConnection conn;
0893: Enumeration connlist = connections.elements();
0894: if (connlist != null) {
0895: while (connlist.hasMoreElements()) {
0896: conn = (JDCConnection) connlist.nextElement();
0897: try {
0898: pid = conn.pid();
0899: } catch (Exception ignore) {
0900: pid = null;
0901: }
0902: if (sPId.equals(pid))
0903: return conn;
0904: } // wend
0905: } // fi ()
0906: return null;
0907: } // getConnectionForPId
0908:
0909: // ---------------------------------------------------------
0910:
0911: /**
0912: * Return a connection to the pool
0913: * @param conn JDCConnection returned to the pool
0914: */
0915:
0916: public synchronized void returnConnection(JDCConnection conn) {
0917: if (DebugFile.trace)
0918: DebugFile
0919: .writeln("JDCConnectionPool.returnConnection([JDCConnection])");
0920: conn.expireLease();
0921: } // returnConnection()
0922:
0923: // ---------------------------------------------------------
0924:
0925: /**
0926: * Return a connection to the pool
0927: * @param conn JDCConnection returned to the pool
0928: * @param sCaller Must be the same String passed as parameter at getConnection()
0929: */
0930:
0931: public synchronized void returnConnection(JDCConnection conn,
0932: String sCaller) {
0933:
0934: if (DebugFile.trace) {
0935: DebugFile
0936: .writeln("JDCConnectionPool.returnConnection([JDCConnection], "
0937: + sCaller + ")");
0938: if (sCaller != null)
0939: logConnection(conn, sCaller, "CDBC", null);
0940: }
0941:
0942: if (DebugFile.trace)
0943: DebugFile.writeln("JDCConnection.expireLease()");
0944:
0945: conn.expireLease();
0946:
0947: if (DebugFile.trace && (null != sCaller))
0948: modifyMap(sCaller, -1);
0949: }
0950:
0951: // ---------------------------------------------------------
0952:
0953: /**
0954: * @return Actual connection pool size
0955: */
0956:
0957: public synchronized int size() {
0958: return openconns;
0959: }
0960:
0961: /**
0962: * Get information of current activity at database to which this pool is connected
0963: * @return JDCActivityInfo
0964: * @throws SQLException
0965: * @since 3.0
0966: */
0967: public JDCActivityInfo getActivityInfo() throws SQLException {
0968: JDCActivityInfo oInfo;
0969: try {
0970: oInfo = new JDCActivityInfo(this );
0971: } catch (Exception xcpt) {
0972: throw new SQLException(xcpt.getMessage());
0973: }
0974: return oInfo;
0975: }
0976:
0977: // ---------------------------------------------------------
0978:
0979: /**
0980: * Human readable usage statistics
0981: * @return Connection pool usage statistics string
0982: * @throws ConcurrentModificationException If pool is modified while iterating
0983: * throught connection collection
0984: */
0985: public String dumpStatistics()
0986: throws ConcurrentModificationException {
0987: String sDump;
0988: String sPId;
0989: Object sKey;
0990: Object iVal;
0991: int iConnOrdinal, iStaled;
0992: long stale = System.currentTimeMillis() - timeout;
0993:
0994: if (DebugFile.trace) {
0995: DebugFile
0996: .writeln("Begin JDCConnectionPool.dumpStatistics()");
0997: DebugFile.incIdent();
0998: }
0999:
1000: Enumeration connlist = connections.elements();
1001: JDCConnection conn;
1002:
1003: sDump = "Maximum Pool Size=" + String.valueOf(poolsize) + "\n";
1004: sDump += "Maximum Connections=" + String.valueOf(hardlimit)
1005: + "\n";
1006: sDump += "Connection Timeout=" + String.valueOf(timeout)
1007: + " ms\n";
1008: sDump += "Reaper Daemon Delay="
1009: + String.valueOf(getReaperDaemonDelay()) + " ms\n";
1010: sDump += "\n";
1011:
1012: iStaled = iConnOrdinal = 0;
1013:
1014: if (connlist != null) {
1015: while (connlist.hasMoreElements()) {
1016: conn = (JDCConnection) connlist.nextElement();
1017:
1018: if (stale > conn.getLastUse())
1019: iStaled++;
1020:
1021: try {
1022: sPId = conn.pid();
1023: } catch (Exception ignore) {
1024: sPId = null;
1025: }
1026:
1027: sDump += "#"
1028: + String.valueOf(++iConnOrdinal)
1029: + (conn.inUse() ? " in use, " : " vacant, ")
1030: + (stale > conn.getLastUse() ? " staled,"
1031: : " ready,")
1032: + (conn.validate() ? "validate=yes"
1033: : " validate=no") + ", last use="
1034: + new Date(conn.getLastUse()).toString()
1035: + ", caller=" + conn.getName()
1036: + (sPId == null ? "" : " pid=" + sPId) + "\n";
1037: }
1038: } // fi ()
1039:
1040: sDump += "\n";
1041:
1042: if (DebugFile.trace) {
1043: Iterator oCallersIterator = callers.keySet().iterator();
1044:
1045: while (oCallersIterator.hasNext()) {
1046: sKey = oCallersIterator.next();
1047: iVal = callers.get(sKey);
1048: sDump += sKey + " , " + iVal.toString()
1049: + " named open connections\n";
1050: }
1051: sDump += "\n\n";
1052: } // fi (DebugFile.trace)
1053:
1054: sDump += String.valueOf(iStaled) + " staled connections\n";
1055:
1056: sDump += "Actual pool size " + String.valueOf(size()) + "\n\n";
1057:
1058: try {
1059: JDCProcessInfo[] oPinfo = getActivityInfo().processesInfo();
1060: if (oPinfo != null) {
1061: sDump += "Activity information:\n";
1062: for (int p = 0; p < oPinfo.length; p++) {
1063: sDump += "user " + oPinfo[p].getUserName()
1064: + " running process "
1065: + oPinfo[p].getProcessId();
1066: conn = getConnectionForPId(oPinfo[p].getProcessId());
1067: if (conn != null) {
1068: sDump += " on connection " + conn.getName();
1069: }
1070: if (oPinfo[p].getQueryText().length() > 0) {
1071: sDump += " for query "
1072: + oPinfo[p].getQueryText();
1073: }
1074: if (oPinfo[p].getQueryStart() != null) {
1075: sDump += " since "
1076: + oPinfo[p].getQueryStart().toString();
1077: }
1078: sDump += "\n";
1079: } // next
1080: }
1081: } catch (Exception xcpt) {
1082: sDump += xcpt.getClass().getName()
1083: + " trying to get activity information "
1084: + xcpt.getMessage() + "\n";
1085: }
1086:
1087: sDump += "\n";
1088:
1089: if (errorlog.size() > 0) {
1090: sDump += "Fatal error log:\n";
1091: ListIterator oErrIterator = errorlog.listIterator();
1092: while (oErrIterator.hasNext())
1093: sDump += oErrIterator.next() + "\n";
1094: } // fi
1095:
1096: if (DebugFile.trace) {
1097: DebugFile.decIdent();
1098: DebugFile.writeln("End JDCConnectionPool.dumpStatistics()");
1099: }
1100:
1101: return sDump;
1102: } // dumpStatistics
1103:
1104: } // JDCConnectionPool
|