001: package com.jat.integration.db.connectionpool;
002:
003: import java.sql.Connection;
004: import java.sql.DriverManager;
005: import java.sql.SQLException;
006: import java.util.Hashtable;
007: import java.util.LinkedList;
008:
009: import com.jat.core.config.Config;
010: import com.jat.core.log.LogManager;
011: import com.jat.integration.IntegrationException;
012: import com.jat.integration.db.GenericDatabaseDataSource;
013:
014: /**
015: * <p>Title: JAT</p>
016: * <p>Description: This class implements a database connection pool accessing through JDBC.
017: * </p>
018: * <p><b>Configuration:</b><br/>
019: * In the data source name section put the following parameters:
020: * <ul>
021: * <li><b>driver</b> the JDBC driver class</li>
022: * <li><b>URL</b> the JDBC URL to access to your database</li>
023: * <li><b>min_connections</b> the minimun number if connection in the pool</li>
024: * <li><b>max_connections</b> the maximum number if connection in the pool (use -1 to set unlimited)</li>
025: * <li><b>timeout</b> is the maximum time (in milliseconds) the caller wait for a connection</li>
026: * <li>query definition (see query configuration in {@link com.jat.integration.db.GenericDatabaseDataSource} for details)</li>
027: * </ul>
028: * <i>Example:</i>
029: * <blockquote>
030: * [<i>myDataSourceName</i>]<br/>
031: * driver = <i>sun.jdbc.odbc.JdbcOdbcDriver</i><br/>
032: * url = <i>jdbc:odbc:MyDatabase</i><br/>
033: * min_connections = 2<br/>
034: * max_connections = 10<br/>
035: * timeout = 20000<br/>
036: * query1.name = <i>myQueryName</i><br/>
037: * query1.value = <i>select * from dual</i>
038: * </blockquote>
039: * </p>
040: * <p>Copyright: Copyright (c) 2004 -2005 Stefano Fratini (stefano.fratini@gmail.com)</p>
041: * <p>Distributed under the terms of the GNU Lesser General Public License, v2.1 or later</p>
042: * @author stf
043: * @version 1.2
044: * @since 1.2
045: * @see com.jat.integration.DataSource
046: * @see com.jat.integration.db.GenericDatabaseDataSource
047: */
048:
049: public class ConnectionPoolDataSource extends GenericDatabaseDataSource {
050:
051: public final static String CONFIG_MIN_CONNECTIONS = "min_connections";
052: public final static String CONFIG_MAX_CONNECTIONS = "max_connections";
053: public final static String CONFIG_TIMEOUT = "timeout";
054: public final static String CONFIG_DRIVER = "driver";
055: public final static String CONFIG_URL = "url";
056:
057: protected void initDataSource()
058: throws com.jat.integration.IntegrationException {
059: LogManager.sendDebug(this .getClass().getName()
060: + "::initDataSource: start");
061: try {
062: this .minConnections = Integer.parseInt(Config.getCurrent()
063: .getValue(this .getName(), CONFIG_MIN_CONNECTIONS));
064: LogManager.sendDebug(this .getClass().getName()
065: + "::initDataSource: Data source '"
066: + this .getName() + "': minConnections: "
067: + minConnections);
068: this .maxConnections = Integer.parseInt(Config.getCurrent()
069: .getValue(this .getName(), CONFIG_MAX_CONNECTIONS));
070: LogManager.sendDebug(this .getClass().getName()
071: + "::initDataSource: Data source '"
072: + this .getName() + "': maxConnections: "
073: + maxConnections);
074: this .timeOut = Integer.parseInt(Config.getCurrent()
075: .getValue(this .getName(), CONFIG_TIMEOUT));
076: LogManager.sendDebug(this .getClass().getName()
077: + "::initDataSource: Data source '"
078: + this .getName() + "': timeout: " + timeOut);
079: this .url = Config.getCurrent().getValue(this .getName(),
080: CONFIG_URL);
081: LogManager.sendDebug(this .getClass().getName()
082: + "::initDataSource: Data source '"
083: + this .getName() + "': url: " + url);
084: this .driver = Config.getCurrent().getValue(this .getName(),
085: CONFIG_DRIVER);
086: LogManager.sendDebug(this .getClass().getName()
087: + "::initDataSource: Data source '"
088: + this .getName() + "': driver: " + driver);
089: Class.forName(driver);
090:
091: mFreeConnections = new LinkedList();
092: mPendingRequests = new LinkedList();
093:
094: for (int i = 0; i < this .minConnections; i++) {
095: mFreeConnections.add(getNewConnection());
096: }
097: LogManager.sendDebug(this .getClass().getName()
098: + "::initDataSource: Data source '"
099: + this .getName() + "': inited: " + this .toString());
100: } catch (ClassNotFoundException cnfex) {
101: LogManager.sendError(this .getClass().getName()
102: + "::initDataSource: Data source '"
103: + this .getName() + "': Cannot load driver '"
104: + driver + "': " + cnfex);
105: throw new IntegrationException(this .getClass().getName()
106: + "::initDataSource: Data source '"
107: + this .getName() + "': Cannot load driver '"
108: + driver + "': " + cnfex);
109: } catch (Exception ex) {
110: LogManager.sendError(this .getClass().getName()
111: + "::initDataSource: Data source '"
112: + this .getName() + "': exception: " + ex);
113: throw new IntegrationException(this .getClass().getName()
114: + "::initDataSource: Data source '"
115: + this .getName() + "':exception: " + ex);
116: } finally {
117: }
118: LogManager.sendDebug(this .getClass().getName()
119: + "::initDataSource: end");
120: }
121:
122: protected Hashtable putInitProperties(Hashtable hash)
123: throws Exception {
124: hash.put(CONFIG_DRIVER, this .driver);
125: hash.put(CONFIG_URL, this .url);
126: hash.put(CONFIG_MIN_CONNECTIONS, "" + this .minConnections);
127: hash.put(CONFIG_MAX_CONNECTIONS, "" + this .maxConnections);
128: hash.put(CONFIG_TIMEOUT, "" + this .timeOut);
129: return hash;
130: }
131:
132: protected Connection getConnection() throws SQLException {
133: ConnectionWrapper con = getFreeConnection();
134: if (con == null) {
135: Request request = new Request();
136: long time = System.currentTimeMillis();
137: try {
138: mPendingRequests.addLast(request);
139: LogManager
140: .sendWarning(this .getClass().getName()
141: + "::getConnection: no free connections, request ("
142: + request + ") waiting... "
143: + this .toString());
144: if (request.connection == null) {
145: synchronized (request) {
146: try {
147: request.wait(timeOut);
148: } catch (InterruptedException ex) {
149: }
150: }
151: }
152: } finally {
153: if (request != null)
154: mPendingRequests.remove(request);
155: }
156: long now = System.currentTimeMillis() - time;
157: LogManager.sendDebug(this .getClass().getName()
158: + "::getConnection: request (" + request
159: + ") wake up after " + now + " milliseconds. "
160: + this .toString());
161: con = request.connection;
162: if (con == null)
163: throw new SQLException(
164: "No connection available from connection pool");
165: }
166: return (Connection) con;
167: }
168:
169: private synchronized ConnectionWrapper getFreeConnection()
170: throws SQLException {
171: ConnectionWrapper con = null;
172: if (mFreeConnections.size() > 0) {
173: con = (ConnectionWrapper) mFreeConnections.removeFirst();
174: LogManager.sendDebug(this .getClass().getName()
175: + "::getFreeConnection: returned free connection ("
176: + con.toString() + "): " + this .toString());
177: } else {
178: if (connectionCount < maxConnections
179: || maxConnections == -1) {
180: con = this .getNewConnection();
181: LogManager
182: .sendDebug(this .getClass().getName()
183: + "::getFreeConnection: returned new connection ("
184: + con.toString() + "): "
185: + this .toString());
186: } else
187: LogManager
188: .sendDebug(this .getClass().getName()
189: + "::getFreeConnection: no connection returned becouse pool is full: "
190: + this .toString());
191: }
192: return con;
193: }
194:
195: private ConnectionWrapper getNewConnection() throws SQLException {
196: ConnectionWrapper con = new ConnectionWrapper(this ,
197: DriverManager.getConnection(this .url));
198: connectionCount++;
199: return con;
200:
201: }
202:
203: public synchronized void releaseConnection(
204: ConnectionWrapper connectionWrapper) {
205: try {
206: connectionWrapper.clearWarnings();
207: } catch (SQLException ex) {
208: LogManager
209: .sendDebug(this .getClass().getName()
210: + "::releaseConnection: exception clearing wornings: "
211: + ex);
212: }
213: if (mPendingRequests.size() > 0) {
214: Request request = (Request) mPendingRequests.removeFirst();
215: synchronized (request) {
216: request.connection = connectionWrapper;
217: request.notify();
218: }
219: LogManager.sendDebug(this .getClass().getName()
220: + "::releaseConnection: connection ("
221: + connectionWrapper.toString()
222: + ") passed to pending request (" + request + "): "
223: + this .toString());
224: } else if (this .mFreeConnections.size() >= this .minConnections) {
225: connectionWrapper.destroy();
226: this .connectionCount--;
227: } else {
228: mFreeConnections.addLast(connectionWrapper);
229: LogManager.sendDebug(this .getClass().getName()
230: + "::releaseConnection: connection ("
231: + connectionWrapper.toString() + ") freed: "
232: + this .toString());
233: }
234: }
235:
236: public String toString() {
237: String ret = this .getClass().getName() + "[ConnectionCount="
238: + connectionCount + "]";
239: ret += "[FreeConnections=" + mFreeConnections.size() + "]";
240: ret += "[PendingRequests=" + mPendingRequests.size() + "]";
241: ret += "[MinConnections=" + minConnections + "]";
242: ret += "[MaxConnections=" + maxConnections + "]";
243: ret += "[TimeOut=" + timeOut + "]";
244: return ret;
245: }
246:
247: private int minConnections;
248: private int maxConnections;
249: private long timeOut;
250: private String url;
251: private String driver;
252: private int connectionCount = 0;
253: private LinkedList mFreeConnections;
254: private LinkedList mPendingRequests;
255:
256: class Request {
257: ConnectionWrapper connection = null;
258: }
259: }
|