0001: /**
0002: * Copyright (C) 2001 Yasna.com. All rights reserved.
0003: *
0004: * ===================================================================
0005: * The Apache Software License, Version 1.1
0006: *
0007: * Redistribution and use in source and binary forms, with or without
0008: * modification, are permitted provided that the following conditions
0009: * are met:
0010: *
0011: * 1. Redistributions of source code must retain the above copyright
0012: * notice, this list of conditions and the following disclaimer.
0013: *
0014: * 2. Redistributions in binary form must reproduce the above copyright
0015: * notice, this list of conditions and the following disclaimer in
0016: * the documentation and/or other materials provided with the
0017: * distribution.
0018: *
0019: * 3. The end-user documentation included with the redistribution,
0020: * if any, must include the following acknowledgment:
0021: * "This product includes software developed by
0022: * Yasna.com (http://www.yasna.com)."
0023: * Alternately, this acknowledgment may appear in the software itself,
0024: * if and wherever such third-party acknowledgments normally appear.
0025: *
0026: * 4. The names "Yazd" and "Yasna.com" must not be used to
0027: * endorse or promote products derived from this software without
0028: * prior written permission. For written permission, please
0029: * contact yazd@yasna.com.
0030: *
0031: * 5. Products derived from this software may not be called "Yazd",
0032: * nor may "Yazd" appear in their name, without prior written
0033: * permission of Yasna.com.
0034: *
0035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0038: * DISCLAIMED. IN NO EVENT SHALL YASNA.COM OR
0039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0046: * SUCH DAMAGE.
0047: * ====================================================================
0048: *
0049: * This software consists of voluntary contributions made by many
0050: * individuals on behalf of Yasna.com. For more information
0051: * on Yasna.com, please see <http://www.yasna.com>.
0052: */
0053:
0054: /**
0055: * Copyright (C) 2000 CoolServlets.com. All rights reserved.
0056: *
0057: * ===================================================================
0058: * The Apache Software License, Version 1.1
0059: *
0060: * Redistribution and use in source and binary forms, with or without
0061: * modification, are permitted provided that the following conditions
0062: * are met:
0063: *
0064: * 1. Redistributions of source code must retain the above copyright
0065: * notice, this list of conditions and the following disclaimer.
0066: *
0067: * 2. Redistributions in binary form must reproduce the above copyright
0068: * notice, this list of conditions and the following disclaimer in
0069: * the documentation and/or other materials provided with the
0070: * distribution.
0071: *
0072: * 3. The end-user documentation included with the redistribution,
0073: * if any, must include the following acknowledgment:
0074: * "This product includes software developed by
0075: * CoolServlets.com (http://www.coolservlets.com)."
0076: * Alternately, this acknowledgment may appear in the software itself,
0077: * if and wherever such third-party acknowledgments normally appear.
0078: *
0079: * 4. The names "Jive" and "CoolServlets.com" must not be used to
0080: * endorse or promote products derived from this software without
0081: * prior written permission. For written permission, please
0082: * contact webmaster@coolservlets.com.
0083: *
0084: * 5. Products derived from this software may not be called "Jive",
0085: * nor may "Jive" appear in their name, without prior written
0086: * permission of CoolServlets.com.
0087: *
0088: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0089: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0090: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0091: * DISCLAIMED. IN NO EVENT SHALL COOLSERVLETS.COM OR
0092: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0093: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0094: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0095: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0096: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0097: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0098: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0099: * SUCH DAMAGE.
0100: * ====================================================================
0101: *
0102: * This software consists of voluntary contributions made by many
0103: * individuals on behalf of CoolServlets.com. For more information
0104: * on CoolServlets.com, please see <http://www.coolservlets.com>.
0105: */package com.Yasna.forum.database;
0106:
0107: import java.sql.*;
0108: import java.util.*;
0109: import java.io.*;
0110: import java.text.*;
0111: import java.util.Date;
0112: import com.Yasna.forum.*;
0113:
0114: /**
0115: * Default Yazd connection provider. It uses the excellent connection pool
0116: * available from http://www.javaexchange.com. This connection provider is a
0117: * a good choice unless you can use a container-managed one.
0118: */
0119: public class DbConnectionDefaultPool extends DbConnectionProvider {
0120:
0121: private static final String NAME = "Default Connection Pool";
0122: private static final String DESCRIPTION = "The default connection provider "
0123: + "that uses the connection pool from javaexchange.com. It works with "
0124: + "almost any database setup, is customizable, and offers good performance. "
0125: + "Use this connection provider unless you have your own or can use a "
0126: + "container managed connection pool.";
0127: private static final String AUTHOR = "Yazd.Yasna.com";
0128: private static final int MAJOR_VERSION = 1;
0129: private static final int MINOR_VERSION = 0;
0130: private static final boolean POOLED = true;
0131:
0132: private ConnectionPool connectionPool = null;
0133: private Properties props;
0134: private Properties propDescriptions;
0135:
0136: private Object initLock = new Object();
0137:
0138: public DbConnectionDefaultPool() {
0139: //this.manager = manager;
0140: props = new Properties();
0141: propDescriptions = new Properties();
0142: //Initialize all property values
0143: initializeProperties();
0144: //Load any existing property values
0145: loadProperties();
0146: }
0147:
0148: /**
0149: * Returns a database connection.
0150: */
0151: public Connection getConnection() {
0152: if (connectionPool == null) {
0153: //block until the init has been done
0154: synchronized (initLock) {
0155: //if still null, something has gone wrong
0156: if (connectionPool == null) {
0157: System.err
0158: .println("Warning: DbConnectionDefaultPool.getConnection() was "
0159: + "called when the internal pool has not been initialized.");
0160: return null;
0161: }
0162: }
0163: }
0164: return new ConnectionWrapper(connectionPool.getConnection(),
0165: connectionPool);
0166: }
0167:
0168: /**
0169: * Starts the pool.
0170: */
0171: protected void start() {
0172: //acquire lock so that no connections can be returned.
0173: synchronized (initLock) {
0174: //Get properties
0175: String driver = props.getProperty("driver");
0176: String server = props.getProperty("server");
0177: String username = props.getProperty("username");
0178: String password = props.getProperty("password");
0179: int minConnections = 0, maxConnections = 0;
0180: double connectionTimeout = 0.0;
0181: try {
0182: minConnections = Integer.parseInt(props
0183: .getProperty("minConnections"));
0184: maxConnections = Integer.parseInt(props
0185: .getProperty("maxConnections"));
0186: connectionTimeout = Double.parseDouble(props
0187: .getProperty("connectionTimeout"));
0188: } catch (Exception e) {
0189: System.err
0190: .println("Error: could not parse default pool properties. "
0191: + "Make sure the values exist and are correct.");
0192: e.printStackTrace();
0193: return;
0194: }
0195: String logPath = props.getProperty("logPath");
0196:
0197: try {
0198: connectionPool = new ConnectionPool(driver, server,
0199: username, password, minConnections,
0200: maxConnections, logPath, connectionTimeout);
0201: } catch (IOException ioe) {
0202: System.err
0203: .println("Error starting DbConnectionDefaultPool: "
0204: + ioe);
0205: ioe.printStackTrace();
0206: }
0207: }
0208: }
0209:
0210: /**
0211: * Restarts the pool to take into account any property changes.
0212: */
0213: protected void restart() {
0214: //Kill off pool.
0215: destroy();
0216: //Reload properties.
0217: loadProperties();
0218: //Start a new pool.
0219: start();
0220: }
0221:
0222: /**
0223: * Destroys the connection pool.
0224: */
0225: protected void destroy() {
0226: if (connectionPool != null) {
0227: try {
0228: connectionPool.destroy(1);
0229: } catch (Exception e) {
0230: e.printStackTrace();
0231: }
0232: }
0233: //Release reference to connectionPool
0234: connectionPool = null;
0235: }
0236:
0237: /**
0238: * Returns the value of a property of the connection provider.
0239: *
0240: * @param name the name of the property.
0241: * @returns the value of the property.
0242: */
0243: public String getProperty(String name) {
0244: return (String) props.get(name);
0245: }
0246:
0247: /**
0248: * Returns the description of a property of the connection provider.
0249: *
0250: * @param name the name of the property.
0251: * @return the description of the property.
0252: */
0253: public String getPropertyDescription(String name) {
0254: return (String) propDescriptions.get(name);
0255: }
0256:
0257: /**
0258: * Returns an enumeration of the property names for the connection provider.
0259: */
0260: public Enumeration propertyNames() {
0261: return props.propertyNames();
0262: }
0263:
0264: /**
0265: * Sets a property of the connection provider. Each provider has a set number
0266: * of properties that are determined by the author. Trying to set a non-
0267: * existant property will result in an IllegalArgumentException.
0268: *
0269: * @param name the name of the property to set.
0270: * @param value the new value for the property.
0271: *
0272: */
0273: public void setProperty(String name, String value) {
0274: props.put(name, value);
0275: saveProperties();
0276: }
0277:
0278: /**
0279: * Give default values to all the properties and descriptions.
0280: */
0281: private void initializeProperties() {
0282: props.put("driver", "");
0283: props.put("server", "");
0284: props.put("username", "");
0285: props.put("password", "");
0286: props.put("minConnections", "");
0287: props.put("maxConnections", "");
0288: props.put("logPath", "");
0289: props.put("connectionTimeout", "");
0290:
0291: propDescriptions.put("driver",
0292: "JDBC driver. e.g. 'oracle.jdbc.driver.OracleDriver'");
0293: propDescriptions
0294: .put("server",
0295: "JDBC connect string. e.g. 'jdbc:oracle:thin:@203.92.21.109:1526:orcl'");
0296: propDescriptions.put("username",
0297: "Database username. e.g. 'Scott'");
0298: propDescriptions.put("password",
0299: "Database password. e.g. 'Tiger'");
0300: propDescriptions
0301: .put(
0302: "minConnections",
0303: "Minimum # of connections to start with in pool. Three is the recommended minimum");
0304: propDescriptions
0305: .put(
0306: "maxConnections",
0307: "Maximum # of connections in dynamic pool. Fifteen should give good performance for an average load.");
0308: propDescriptions
0309: .put("logPath",
0310: "Absolute path name for log file. e.g. 'c:\\logs\\yazdDbLog.log'");
0311: propDescriptions.put("connectionTimeout",
0312: "Time in days between connection resets. e.g. '.5'");
0313: }
0314:
0315: /**
0316: * Load whatever properties that already exist.
0317: */
0318: private void loadProperties() {
0319: String driver = PropertyManager
0320: .getProperty("DbConnectionDefaultPool.driver");
0321: String server = PropertyManager
0322: .getProperty("DbConnectionDefaultPool.server");
0323: String username = PropertyManager
0324: .getProperty("DbConnectionDefaultPool.username");
0325: String password = PropertyManager
0326: .getProperty("DbConnectionDefaultPool.password");
0327: String minConnections = PropertyManager
0328: .getProperty("DbConnectionDefaultPool.minConnections");
0329: String maxConnections = PropertyManager
0330: .getProperty("DbConnectionDefaultPool.maxConnections");
0331: String logPath = PropertyManager
0332: .getProperty("DbConnectionDefaultPool.logPath");
0333: String connectionTimeout = PropertyManager
0334: .getProperty("DbConnectionDefaultPool.connectionTimeout");
0335:
0336: if (driver != null) {
0337: props.setProperty("driver", driver);
0338: }
0339: if (server != null) {
0340: props.setProperty("server", server);
0341: }
0342: if (username != null) {
0343: props.setProperty("username", username);
0344: }
0345: if (password != null) {
0346: props.setProperty("password", password);
0347: }
0348: if (minConnections != null) {
0349: props.setProperty("minConnections", minConnections);
0350: }
0351: if (maxConnections != null) {
0352: props.setProperty("maxConnections", maxConnections);
0353: }
0354: if (logPath != null) {
0355: props.setProperty("logPath", logPath);
0356: }
0357: if (connectionTimeout != null) {
0358: props.setProperty("connectionTimeout", connectionTimeout);
0359: }
0360: }
0361:
0362: private void saveProperties() {
0363: PropertyManager.setProperty("DbConnectionDefaultPool.driver",
0364: props.getProperty("driver"));
0365: PropertyManager.setProperty("DbConnectionDefaultPool.server",
0366: props.getProperty("server"));
0367: PropertyManager.setProperty("DbConnectionDefaultPool.username",
0368: props.getProperty("username"));
0369: PropertyManager.setProperty("DbConnectionDefaultPool.password",
0370: props.getProperty("password"));
0371: PropertyManager.setProperty(
0372: "DbConnectionDefaultPool.minConnections", props
0373: .getProperty("minConnections"));
0374: PropertyManager.setProperty(
0375: "DbConnectionDefaultPool.maxConnections", props
0376: .getProperty("maxConnections"));
0377: PropertyManager.setProperty("DbConnectionDefaultPool.logPath",
0378: props.getProperty("logPath"));
0379: PropertyManager.setProperty(
0380: "DbConnectionDefaultPool.connectionTimeout", props
0381: .getProperty("connectionTimeout"));
0382: }
0383:
0384: /**
0385: * DbConnectionBroker
0386: * @version 1.0.11 12/7/99
0387: * @author Marc A. Mnich
0388: *
0389: * ----------------------------------------
0390: * Modified June 18, 2000 by Matt Tucker
0391: * Changes:
0392: * - New package name, class name to make it nice to embed as
0393: * an internal class.
0394: * - Source code reformatting.
0395: * - Added more error handling code in constructor, createConn method
0396: * so that more information is given to Yazd users.
0397: * DbConnectionBroker rules! Download it from javaexchange.com
0398: * ----------------------------------------
0399: *
0400: * DbConnectionBroker
0401: * A servlet-based broker for database connections.
0402: * Creates and manages a pool of database connections.
0403: * @version 1.0.11 12/7/99
0404: * @author Marc A. Mnich
0405: */
0406: private class ConnectionPool implements Runnable {
0407: private Thread runner;
0408:
0409: private Connection[] connPool;
0410: private int[] connStatus;
0411:
0412: private long[] connLockTime, connCreateDate;
0413: private String[] connID;
0414: private String dbDriver, dbServer, dbLogin, dbPassword,
0415: logFileString;
0416: private int currConnections, connLast, minConns, maxConns,
0417: maxConnMSec;
0418:
0419: //available: set to false on destroy, checked by getConnection()
0420: private boolean available = true;
0421:
0422: private PrintWriter log;
0423: private SQLWarning currSQLWarning;
0424: private String pid;
0425:
0426: /**
0427: * Creates a new Connection Broker<br>
0428: * dbDriver: JDBC driver. e.g. 'oracle.jdbc.driver.OracleDriver'<br>
0429: * dbServer: JDBC connect string. e.g. 'jdbc:oracle:thin:@203.92.21.109:1526:orcl'<br>
0430: * dbLogin: Database login name. e.g. 'Scott'<br>
0431: * dbPassword: Database password. e.g. 'Tiger'<br>
0432: * minConns: Minimum number of connections to start with.<br>
0433: * maxConns: Maximum number of connections in dynamic pool.<br>
0434: * logFileString: Absolute path name for log file. e.g. 'c:\temp\mylog.log' <br>
0435: * maxConnTime: Time in days between connection resets. (Reset does a basic cleanup)<br>
0436: */
0437: public ConnectionPool(String dbDriver, String dbServer,
0438: String dbLogin, String dbPassword, int minConns,
0439: int maxConns, String logFileString, double maxConnTime)
0440: throws IOException {
0441: connPool = new Connection[maxConns];
0442: connStatus = new int[maxConns];
0443: connLockTime = new long[maxConns];
0444: connCreateDate = new long[maxConns];
0445: connID = new String[maxConns];
0446: currConnections = minConns;
0447: this .maxConns = maxConns;
0448: this .dbDriver = dbDriver;
0449: this .dbServer = dbServer;
0450: this .dbLogin = dbLogin;
0451: this .dbPassword = dbPassword;
0452: this .logFileString = logFileString;
0453: maxConnMSec = (int) (maxConnTime * 86400000.0); //86400 sec/day
0454: if (maxConnMSec < 30000) { // Recycle no less than 30 seconds.
0455: maxConnMSec = 30000;
0456: }
0457:
0458: try {
0459: log = new PrintWriter(new FileOutputStream(
0460: logFileString), true);
0461: // Can't open the requested file. Open the default file.
0462: } catch (IOException e1) {
0463: System.err
0464: .println("Warning: DbConnectionDefaultPool could not open \""
0465: + logFileString
0466: + "\" to write log to. Make sure that your Java "
0467: + "process has permission to write to the file and that the directory exists.");
0468: try {
0469: log = new PrintWriter(new FileOutputStream("DCB_"
0470: + System.currentTimeMillis() + ".log"),
0471: true);
0472: } catch (IOException e2) {
0473: throw new IOException("Can't open any log file");
0474: }
0475: }
0476:
0477: // Write the pid file (used to clean up dead/broken connection)
0478: SimpleDateFormat formatter = new SimpleDateFormat(
0479: "yyyy.MM.dd G 'at' hh:mm:ss a zzz");
0480: java.util.Date nowc = new java.util.Date();
0481: pid = formatter.format(nowc);
0482:
0483: BufferedWriter pidout = new BufferedWriter(new FileWriter(
0484: logFileString + "pid"));
0485: pidout.write(pid);
0486: pidout.close();
0487:
0488: log.println("Starting ConnectionPool:");
0489: log.println("dbDriver = " + dbDriver);
0490: log.println("dbServer = " + dbServer);
0491: log.println("dbLogin = " + dbLogin);
0492: log.println("log file = " + logFileString);
0493: log.println("minconnections = " + minConns);
0494: log.println("maxconnections = " + maxConns);
0495: log.println("Total refresh interval = " + maxConnTime
0496: + " days");
0497: log.println("-----------------------------------------");
0498:
0499: // Initialize the pool of connections with the mininum connections:
0500: // Problems creating connections may be caused during reboot when the
0501: // servlet is started before the database is ready. Handle this
0502: // by waiting and trying again. The loop allows 5 minutes for
0503: // db reboot.
0504: boolean connectionsSucceeded = false;
0505: int dbLoop = 20;
0506:
0507: try {
0508: for (int i = 1; i < dbLoop; i++) {
0509: try {
0510: for (int j = 0; j < currConnections; j++) {
0511: createConn(j);
0512: }
0513: connectionsSucceeded = true;
0514: break;
0515: } catch (SQLException e) {
0516: log
0517: .println("--->Attempt ("
0518: + String.valueOf(i)
0519: + " of "
0520: + String.valueOf(dbLoop)
0521: + ") failed to create new connections set at startup: ");
0522: log.println(" " + e);
0523: log
0524: .println(" Will try again in 15 seconds...");
0525: try {
0526: Thread.sleep(15000);
0527: } catch (InterruptedException e1) {
0528: }
0529: }
0530: }
0531: if (!connectionsSucceeded) { // All attempts at connecting to db exhausted
0532: log
0533: .println("\r\nAll attempts at connecting to Database exhausted");
0534: throw new IOException();
0535: }
0536: } catch (Exception e) {
0537: e.printStackTrace();
0538: throw new IOException();
0539: }
0540:
0541: // Fire up the background housekeeping thread
0542:
0543: runner = new Thread(this );
0544: runner.start();
0545:
0546: } //End ConnectionPool()
0547:
0548: /**
0549: * Housekeeping thread. Runs in the background with low CPU overhead.
0550: * Connections are checked for warnings and closure and are periodically
0551: * restarted.
0552: * This thread is a catchall for corrupted
0553: * connections and prevents the buildup of open cursors. (Open cursors
0554: * result when the application fails to close a Statement).
0555: * This method acts as fault tolerance for bad connection/statement programming.
0556: */
0557: public void run() {
0558: boolean forever = true;
0559: Statement stmt = null;
0560: String currCatalog = null;
0561:
0562: while (forever) {
0563: // Make sure the log file is the one this instance opened
0564: // If not, clean it up!
0565: try {
0566: BufferedReader in = new BufferedReader(
0567: new FileReader(logFileString + "pid"));
0568: String curr_pid = in.readLine();
0569: if (curr_pid.equals(pid)) {
0570: //log.println("They match = " + curr_pid);
0571: } else {
0572: //log.println("No match = " + curr_pid);
0573: log.close();
0574:
0575: // Close all connections silently - they are definitely dead.
0576: for (int i = 0; i < currConnections; i++) {
0577: try {
0578: connPool[i].close();
0579: } catch (SQLException e1) {
0580: } // ignore
0581: }
0582: // Returning from the run() method kills the thread
0583: return;
0584: }
0585: in.close();
0586: } catch (IOException e1) {
0587: log.println("Can't read the file for pid info: "
0588: + logFileString + "pid");
0589: }
0590:
0591: // Get any Warnings on connections and print to event file
0592: for (int i = 0; i < currConnections; i++) {
0593: try {
0594: currSQLWarning = connPool[i].getWarnings();
0595: if (currSQLWarning != null) {
0596: log.println("Warnings on connection "
0597: + String.valueOf(i) + " "
0598: + currSQLWarning);
0599: connPool[i].clearWarnings();
0600: }
0601: } catch (SQLException e) {
0602: log.println("Cannot access Warnings: " + e);
0603: }
0604: }
0605:
0606: for (int i = 0; i < currConnections; i++) { // Do for each connection
0607: long age = System.currentTimeMillis()
0608: - connCreateDate[i];
0609:
0610: synchronized (connStatus) {
0611: if (connStatus[i] > 0) { // In use, catch it next time!
0612: continue;
0613: }
0614: connStatus[i] = 2; // Take offline (2 indicates housekeeping lock)
0615: }
0616:
0617: try { // Test the connection with createStatement call
0618: if (age > maxConnMSec) { // Force a reset at the max conn time
0619: throw new SQLException();
0620: }
0621:
0622: stmt = connPool[i].createStatement();
0623: connStatus[i] = 0; // Connection is O.K.
0624: //log.println("Connection confirmed for conn = " +
0625: // String.valueOf(i));
0626:
0627: // Some DBs return an object even if DB is shut down
0628: if (connPool[i].isClosed()) {
0629: throw new SQLException();
0630: }
0631: // Connection has a problem, restart it
0632: } catch (SQLException e) {
0633: try {
0634: log.println(new Date().toString()
0635: + " ***** Recycling connection "
0636: + String.valueOf(i) + ":");
0637:
0638: connPool[i].close();
0639: createConn(i);
0640: } catch (SQLException e1) {
0641: log.println("Failed: " + e1);
0642: connStatus[i] = 0; // Can't open, try again next time
0643: }
0644: } finally {
0645: try {
0646: if (stmt != null) {
0647: stmt.close();
0648: }
0649: } catch (SQLException e1) {
0650: }
0651: ;
0652: }
0653: }
0654:
0655: try {
0656: Thread.sleep(20000);
0657: } // Wait 20 seconds for next cycle
0658: catch (InterruptedException e) {
0659: // Returning from the run method sets the internal
0660: // flag referenced by Thread.isAlive() to false.
0661: // This is required because we don't use stop() to
0662: // shutdown this thread.
0663: return;
0664: }
0665: }
0666: } // End run
0667:
0668: /**
0669: * This method hands out the connections in round-robin order.
0670: * This prevents a faulty connection from locking
0671: * up an application entirely. A browser 'refresh' will
0672: * get the next connection while the faulty
0673: * connection is cleaned up by the housekeeping thread.
0674: *
0675: * If the min number of threads are ever exhausted, new
0676: * threads are added up the the max thread count.
0677: * Finally, if all threads are in use, this method waits
0678: * 2 seconds and tries again, up to ten times. After that, it
0679: * returns a null.
0680: */
0681: public Connection getConnection() {
0682:
0683: Connection conn = null;
0684:
0685: if (available) {
0686: boolean gotOne = false;
0687:
0688: for (int outerloop = 1; outerloop <= 10; outerloop++) {
0689:
0690: try {
0691: int loop = 0;
0692: int roundRobin = connLast + 1;
0693: if (roundRobin >= currConnections)
0694: roundRobin = 0;
0695:
0696: do {
0697: synchronized (connStatus) {
0698: if ((connStatus[roundRobin] < 1)
0699: && (!connPool[roundRobin]
0700: .isClosed())) {
0701: conn = connPool[roundRobin];
0702: connStatus[roundRobin] = 1;
0703: connLockTime[roundRobin] = System
0704: .currentTimeMillis();
0705: connLast = roundRobin;
0706: gotOne = true;
0707: break;
0708: } else {
0709: loop++;
0710: roundRobin++;
0711: if (roundRobin >= currConnections)
0712: roundRobin = 0;
0713: }
0714: }
0715: } while ((gotOne == false)
0716: && (loop < currConnections));
0717: } catch (SQLException e1) {
0718: }
0719:
0720: if (gotOne) {
0721: break;
0722: } else {
0723: synchronized (this ) { // Add new connections to the pool
0724: if (currConnections < maxConns) {
0725: try {
0726: createConn(currConnections);
0727: currConnections++;
0728: } catch (SQLException e) {
0729: log
0730: .println("Unable to create new connection: "
0731: + e);
0732: }
0733: }
0734: }
0735:
0736: try {
0737: Thread.sleep(2000);
0738: } catch (InterruptedException e) {
0739: }
0740: log
0741: .println("-----> Connections Exhausted! Will wait and try "
0742: + "again in loop "
0743: + String.valueOf(outerloop));
0744: }
0745: } // End of try 10 times loop
0746:
0747: } else {
0748: log
0749: .println("Unsuccessful getConnection() request during destroy()");
0750: } // End if(available)
0751:
0752: return conn;
0753: }
0754:
0755: /**
0756: * Returns the local JDBC ID for a connection.
0757: */
0758: public int idOfConnection(Connection conn) {
0759: int match;
0760: String tag;
0761:
0762: try {
0763: tag = conn.toString();
0764: } catch (NullPointerException e1) {
0765: tag = "none";
0766: }
0767:
0768: match = -1;
0769:
0770: for (int i = 0; i < currConnections; i++) {
0771: if (connID[i].equals(tag)) {
0772: match = i;
0773: break;
0774: }
0775: }
0776: return match;
0777: }
0778:
0779: /**
0780: * Frees a connection. Replaces connection back into the main pool for
0781: * reuse.
0782: */
0783: public String freeConnection(Connection conn) {
0784: String res = "";
0785:
0786: int this conn = idOfConnection(conn);
0787: if (this conn >= 0) {
0788: connStatus[this conn] = 0;
0789: res = "freed " + conn.toString();
0790: //log.println("Freed connection " + String.valueOf(thisconn) +
0791: // " normal exit: ");
0792: } else {
0793: log.println("----> Could not free connection!!!");
0794: }
0795:
0796: return res;
0797: }
0798:
0799: /**
0800: * Returns the age of a connection -- the time since it was handed out to
0801: * an application.
0802: */
0803: public long getAge(Connection conn) { // Returns the age of the connection in millisec.
0804: int this conn = idOfConnection(conn);
0805: return System.currentTimeMillis() - connLockTime[this conn];
0806: }
0807:
0808: private void createConn(int i) throws SQLException {
0809: Date now = new Date();
0810: try {
0811: Class.forName(dbDriver);
0812: connPool[i] = DriverManager.getConnection(dbServer,
0813: dbLogin, dbPassword);
0814: connStatus[i] = 0;
0815: connID[i] = connPool[i].toString();
0816: connLockTime[i] = 0;
0817: connCreateDate[i] = now.getTime();
0818:
0819: log.println(now.toString() + " Opening connection "
0820: + String.valueOf(i) + " "
0821: + connPool[i].toString() + ":");
0822: } catch (ClassNotFoundException e2) {
0823: e2.printStackTrace();
0824: throw new SQLException(e2.getMessage());
0825: }
0826: }
0827:
0828: /**
0829: * Shuts down the housekeeping thread and closes all connections
0830: * in the pool. Call this method from the destroy() method of the servlet.
0831: */
0832:
0833: /**
0834: * Multi-phase shutdown. having following sequence:
0835: * <OL>
0836: * <LI><code>getConnection()</code> will refuse to return connections.
0837: * <LI>The housekeeping thread is shut down.<br>
0838: * Up to the time of <code>millis</code> milliseconds after shutdown of
0839: * the housekeeping thread, <code>freeConnection()</code> can still be
0840: * called to return used connections.
0841: * <LI>After <code>millis</code> milliseconds after the shutdown of the
0842: * housekeeping thread, all connections in the pool are closed.
0843: * <LI>If any connections were in use while being closed then a
0844: * <code>SQLException</code> is thrown.
0845: * <LI>The log is closed.
0846: * </OL><br>
0847: * Call this method from a servlet destroy() method.
0848: *
0849: * @param millis the time to wait in milliseconds.
0850: * @exception SQLException if connections were in use after
0851: * <code>millis</code>.
0852: */
0853: public void destroy(int millis) throws SQLException {
0854:
0855: // Checking for invalid negative arguments is not necessary,
0856: // Thread.join() does this already in runner.join().
0857:
0858: // Stop issuing connections
0859: available = false;
0860:
0861: // Shut down the background housekeeping thread
0862: runner.interrupt();
0863:
0864: // Wait until the housekeeping thread has died.
0865: try {
0866: runner.join(millis);
0867: } catch (InterruptedException e) {
0868: } // ignore
0869:
0870: // The housekeeping thread could still be running
0871: // (e.g. if millis is too small). This case is ignored.
0872: // At worst, this method will throw an exception with the
0873: // clear indication that the timeout was too short.
0874:
0875: long startTime = System.currentTimeMillis();
0876:
0877: // Wait for freeConnection() to return any connections
0878: // that are still used at this time.
0879: int useCount;
0880: while ((useCount = getUseCount()) > 0
0881: && System.currentTimeMillis() - startTime <= millis) {
0882: try {
0883: Thread.sleep(500);
0884: } catch (InterruptedException e) {
0885: } // ignore
0886: }
0887:
0888: // Close all connections, whether safe or not
0889: for (int i = 0; i < currConnections; i++) {
0890: try {
0891: connPool[i].close();
0892: } catch (SQLException e1) {
0893: log.println("Cannot close connections on Destroy");
0894: }
0895: }
0896:
0897: if (useCount > 0) {
0898: //bt-test successful
0899: String msg = "Unsafe shutdown: Had to close "
0900: + useCount + " active DB connections after "
0901: + millis + "ms";
0902: log.println(msg);
0903: // Close all open files
0904: log.close();
0905: // Throwing following Exception is essential because servlet authors
0906: // are likely to have their own error logging requirements.
0907: throw new SQLException(msg);
0908: }
0909:
0910: // Close all open files
0911: log.close();
0912:
0913: }//End destroy()
0914:
0915: /**
0916: * Less safe shutdown. Uses default timeout value.
0917: * This method simply calls the <code>destroy()</code> method
0918: * with a <code>millis</code>
0919: * value of 10000 (10 seconds) and ignores <code>SQLException</code>
0920: * thrown by that method.
0921: * @see #destroy(int)
0922: */
0923: public void destroy() {
0924: try {
0925: destroy(10000);
0926: } catch (SQLException e) {
0927: }
0928: }
0929:
0930: /**
0931: * Returns the number of connections in use.
0932: */
0933: // This method could be reduced to return a counter that is
0934: // maintained by all methods that update connStatus.
0935: // However, it is more efficient to do it this way because:
0936: // Updating the counter would put an additional burden on the most
0937: // frequently used methods; in comparison, this method is
0938: // rarely used (although essential).
0939: public int getUseCount() {
0940: int useCount = 0;
0941: synchronized (connStatus) {
0942: for (int i = 0; i < currConnections; i++) {
0943: if (connStatus[i] > 0) { // In use
0944: useCount++;
0945: }
0946: }
0947: }
0948: return useCount;
0949: }//End getUseCount()
0950:
0951: /**
0952: * Returns the number of connections in the dynamic pool.
0953: */
0954: public int getSize() {
0955: return currConnections;
0956: }//End getSize()
0957:
0958: } // End class
0959:
0960: /**
0961: * An implementation of the Connection interface that wraps an underlying
0962: * Connection object. It releases the connection back to a connection pool
0963: * when Connection.close() is called.
0964: */
0965: public class ConnectionWrapper implements Connection {
0966:
0967: private Connection connection;
0968: private ConnectionPool connectionPool;
0969:
0970: public ConnectionWrapper(Connection connection,
0971: ConnectionPool connectionPool) {
0972: this .connection = connection;
0973: this .connectionPool = connectionPool;
0974: }
0975:
0976: /**
0977: * Instead of closing the underlying connection, we simply release
0978: * it back into the pool.
0979: */
0980: public void close() throws SQLException {
0981: connectionPool.freeConnection(this .connection);
0982: //Release object references. Any further method calls on the
0983: //connection will fail.
0984: connection = null;
0985: connectionPool = null;
0986: }
0987:
0988: public String toString() {
0989: if (connection != null) {
0990: return connection.toString();
0991: } else {
0992: return "CoolServlets connection wrapper";
0993: }
0994: }
0995:
0996: public Statement createStatement() throws SQLException {
0997: return connection.createStatement();
0998: }
0999:
1000: public void setHoldability(int holdability) throws SQLException {
1001: connection.setHoldability(holdability);
1002: }
1003:
1004: public int getHoldability() throws SQLException {
1005: return connection.getHoldability();
1006: }
1007:
1008: public Savepoint setSavepoint() throws SQLException {
1009: return connection.setSavepoint();
1010: }
1011:
1012: public Savepoint setSavepoint(String name) throws SQLException {
1013: return connection.setSavepoint(name);
1014: }
1015:
1016: public void rollback(Savepoint savepoint) throws SQLException {
1017: connection.rollback(savepoint);
1018: }
1019:
1020: public PreparedStatement prepareStatement(String sql)
1021: throws SQLException {
1022: return connection.prepareStatement(sql);
1023: }
1024:
1025: public CallableStatement prepareCall(String sql)
1026: throws SQLException {
1027: return connection.prepareCall(sql);
1028: }
1029:
1030: public String nativeSQL(String sql) throws SQLException {
1031: return connection.nativeSQL(sql);
1032: }
1033:
1034: public void setAutoCommit(boolean autoCommit)
1035: throws SQLException {
1036: connection.setAutoCommit(autoCommit);
1037: }
1038:
1039: public boolean getAutoCommit() throws SQLException {
1040: return connection.getAutoCommit();
1041: }
1042:
1043: public void commit() throws SQLException {
1044: connection.commit();
1045: }
1046:
1047: public void rollback() throws SQLException {
1048: connection.rollback();
1049: }
1050:
1051: public boolean isClosed() throws SQLException {
1052: return connection.isClosed();
1053: }
1054:
1055: public DatabaseMetaData getMetaData() throws SQLException {
1056: return connection.getMetaData();
1057: }
1058:
1059: public void setReadOnly(boolean readOnly) throws SQLException {
1060: connection.setReadOnly(readOnly);
1061: }
1062:
1063: public boolean isReadOnly() throws SQLException {
1064: return connection.isReadOnly();
1065: }
1066:
1067: public void setCatalog(String catalog) throws SQLException {
1068: connection.setCatalog(catalog);
1069: }
1070:
1071: public String getCatalog() throws SQLException {
1072: return connection.getCatalog();
1073: }
1074:
1075: public void setTransactionIsolation(int level)
1076: throws SQLException {
1077: connection.setTransactionIsolation(level);
1078: }
1079:
1080: public int getTransactionIsolation() throws SQLException {
1081: return connection.getTransactionIsolation();
1082: }
1083:
1084: public SQLWarning getWarnings() throws SQLException {
1085: return connection.getWarnings();
1086: }
1087:
1088: public void clearWarnings() throws SQLException {
1089: connection.clearWarnings();
1090: }
1091:
1092: public void releaseSavepoint(Savepoint savepoint)
1093: throws SQLException {
1094: connection.releaseSavepoint(savepoint);
1095: }
1096:
1097: public Statement createStatement(int resultSetType,
1098: int resultSetConcurrency) throws SQLException {
1099: return connection.createStatement(resultSetType,
1100: resultSetConcurrency);
1101: }
1102:
1103: public Statement createStatement(int resultSetType,
1104: int resultSetConcurrency, int resultSetHoldability)
1105: throws SQLException {
1106: return connection.createStatement(resultSetType,
1107: resultSetConcurrency, resultSetHoldability);
1108: }
1109:
1110: public PreparedStatement prepareStatement(String sql,
1111: int resultSetType, int resultSetConcurrency)
1112: throws SQLException {
1113: return connection.prepareStatement(sql, resultSetType,
1114: resultSetConcurrency);
1115: }
1116:
1117: public CallableStatement prepareCall(String sql,
1118: int resultSetType, int resultSetConcurrency)
1119: throws SQLException {
1120: return connection.prepareCall(sql, resultSetType,
1121: resultSetConcurrency);
1122: }
1123:
1124: public Map getTypeMap() throws SQLException {
1125: return connection.getTypeMap();
1126: }
1127:
1128: public PreparedStatement prepareStatement(String sql,
1129: String[] columnNames) throws SQLException {
1130: return connection.prepareStatement(sql, columnNames);
1131: }
1132:
1133: public PreparedStatement prepareStatement(String sql,
1134: int[] columnIndexes) throws SQLException {
1135: return connection.prepareStatement(sql, columnIndexes);
1136: }
1137:
1138: public PreparedStatement prepareStatement(String sql,
1139: int autoGeneratedKeys) throws SQLException {
1140: return connection.prepareStatement(sql, autoGeneratedKeys);
1141: }
1142:
1143: public PreparedStatement prepareStatement(String sql,
1144: int resultSetType, int resultSetConcurrency,
1145: int resultSetHoldability) throws SQLException {
1146: return connection.prepareStatement(sql, resultSetType,
1147: resultSetConcurrency, resultSetHoldability);
1148: }
1149:
1150: public CallableStatement prepareCall(String sql,
1151: int resultSetType, int resultSetConcurrency,
1152: int resultSetHoldability) throws SQLException {
1153: return connection.prepareCall(sql, resultSetType,
1154: resultSetConcurrency, resultSetHoldability);
1155: }
1156:
1157: public void setTypeMap(Map map) throws SQLException {
1158: connection.setTypeMap(map);
1159: }
1160:
1161: }
1162: }
|