FileDocCategorySizeDatePackage
ConnectionManager.javaAPI DocGlassfish v2 API82514Fri May 04 22:35:02 BST 2007com.sun.jdo.spi.persistence.support.sqlstore.connection

ConnectionManager.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * ConnectionManager.java
 *
 * Create on March 3, 2000
 */

package com.sun.jdo.spi.persistence.support.sqlstore.connection;

import com.sun.jdo.api.persistence.support.Transaction;
import com.sun.jdo.spi.persistence.utility.I18NHelper;
import com.sun.jdo.spi.persistence.utility.DoubleLinkedList;
import com.sun.jdo.spi.persistence.utility.Linkable;
import com.sun.jdo.spi.persistence.support.sqlstore.utility.StringScanner;
import com.sun.jdo.spi.persistence.utility.logging.Logger;
import com.sun.jdo.spi.persistence.support.sqlstore.LogHelperSQLStore;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.ResourceBundle;

/**
 * <P>This class represents a connection manager, which creates a
 * JDBC driver manager and manages
 * established database connections. This class lets you specify the following
 * settings for JDBC connections:
 * <UL>
 * <LI>JDBC driver type
 * <LI>data source name (JDBC URL)
 * <LI>user name
 * <LI>password
 * <LI>number of pooled connections (settable when running)
 * <LI>how often to try to get a connection after failing the first time and how long
 * to retry (settable when running)
 * </UL>
 * If you define a connection manager as a component, you can define
 * these settings when you partition the application instead of when you write the application.
 * <P>You can change only the following settings when the connection manager is
 * running:
 * <UL>
 * <LI> minimum and maximum number of pooled connections, although not whether pooling is
 * on or off (<code>setMinPool</code> and <code>setMaxPool</code>)
 * <LI> how often to try to get a connection after failing the first time and how
 * long to retry for a connection (<code>setMsWait</code> and <code>setMsInterval</code>)
 * </UL>
 * <P>You cannot set any other setting while the connection manager is running.
 * To change other settings, shut down the connection manager using the
 * <code>shutDown</code> method, then change
 * the settings. Then, start the connection manager
 * using the <code>startUp</code> method.
 * <P>If you use a connection manager to manage your database connections,
 * the connection manager can also extend a SynerJ transaction to include
 * JDBC database transactions. In other words, if the JDBC database
 * transactions occur within a SynerJ transaction, the JDBC database
 * transactions are committed or rolled back when the SynerJ transaction
 * committed or rolled back.
 * <P>You can set up your connection manager to manage database connections in
 * the following ways:
 * <UL>
 * <LI>Start a new connection every time you request a connection. This approach is
 * often referred to as <i>client-based security</i>, because the security
 * for the connection is based on a particular database client and can be
 * different for each client.
 * <LI>Maintain a pool of connections for a given user name and password,
 * and return one of these connections when you request a connection using the
 * getConnection method with no parameters.
 * This approach is often referred to as <i>application-based security</i>, because
 * the security information for the pool of connections is the same and is the
 * same for all clients of the application.
 * <LI>Maintain a pool of connections for a given user name and password
 * and start a new connection if you specify another user name and password
 * with the getConnection method.
 * This approach is a blend of client-based and application-based security.
 * </UL>
 * <P>You also have the choice of either defining the connection manager
 * as a service object typed as a ConnectionManager object, or
 * defining the connection manager by dynamically creating a
 * ConnectionManager object in your code. If you define the
 * connection manager as a service object, the SynerJ partitioning system
 * can help you determine how to define partitions that contain the
 * service object by being aware of where JDBC drivers are installed,
 * for example. If you define the ConnectionManager object
 * dynamically, then you need to keep such issues in mind when you
 * define the partitions whose code defines the ConnectionManager
 * objects; the partitioning system will not help you.
 *
 * <H4>Starting a New Database Connection for Each Request (Client-based Security)</H4>
 * <P>In this situation, the connection
 * manager establishes a new connection each time you request a connection.
 * By default, the connection manager does not establish and maintain a
 * pool of connections. In this case, you can leave the maximum and minimum
 * number of pooled connections set to 0.
 * Each time you need a connection to the database,
 * use the getConnection method.
 * You can use the default user name, password, and database URL for the current
 * connection manager by invoking the getConnection method with no parameters.
 * The default settings are specified in one of the ConnectionManager
 * constructors when you create the connection manager.
 * You can also specify a user name, password, and database URL on the
 * getConnection method.
 *
 * <H5>Example of Using ConnectionManager without Pooling</H4>
 * <PRE>
 * import java.sql.*;
 * import com.sun.jdo.api.persistence.support.*;
 * Connection con = myTransaction.getConnection();
 * Statement stmt = con.createStatement();
 * ResultSet rs = stmt.executeQuery("SELECT * FROM T1");
 * </PRE>
 *
 * <H4>Using a Pool of Database Connections (Application-based Security)</H4>
 * <P>When you create a connection manager using the
 * ConnectionManager class, you can have the connection manager
 * establish a pool of connections for a given user name and password
 * to allow a thread to using an existing connection instead of waiting
 * for a new database connection to be created.
 * <P>To create a connection manager with a pool of connections using
 * a service object:
 * <OL>
 * <LI>Define a service object of the class ConnectionManager.
 * <LI>In the Partition Workshop, set the component properties for
 * the component instance represented by the service
 * object to define the driver name and the default database URL,
 * user name, and password. You can also define the values for the
 * minimum connections and the maximum connections for the connection pool, as well
 * as for how often and how long to try to get a connection after an initial
 * failure to do so.
 * <P>If you set the values for the minimum and maximum connections to
 * 0, then no pool of connections is established. Any value greater
 * than 0 means that a pool of connections is established. The maximum
 * value must be equal to or greater than the value of the minimum
 * value. If you set the maximum and minimum as equal values, the
 * number of connections established for the pool will be constant.
 * <P>The connection manager establishes the minimum number of connections
 * when it starts up using the default database URL, user name, and password.
 * </OL>
 * <P>To get one of the pooled connections, you can use the
 * <CODE>getConnection</CODE>
 * method with no parameters. If all the existing connections are in
 * use, the connection manager establishes another connection with
 * the same database URL, user name, and password and adds it to the pool,
 * up to the specified maximum number of connections.
 * <P>When you have finished using a connection, you can use the
 * <CODE>close()</CODE> method to return the connection to the pool
 * of available connections.
 * The connection manager periodically checks its pool of connections,
 * and can reduce the number of established connections to the minimum
 * number, if enough connections are not in use.
 * At runtime, you can change the maximum and minimum number of connections,
 * because the ConnectionManager
 * is a component.
 *
 * <H4>Using a Pool of Connections and Starting New Connections</H4>
 * <P>You can have the connection manager establish and maintain a
 * pool of connections and have the connection manager establish new
 * connections on a request-by-request basis.
 *
 * <H5>Example of Using ConnectionManager with Pooling</H5>
 * <PRE>
 * import com.sun.jdo.api.persistence.support.*;
 * void getT1Data() {
 * 	// The following connection is from the connection pool.
 * 	Connection myConn = myTransaction.getConnection();
 * 	Statement myStatement = myConn.createStatement();
 * 	ResultSet myResults = myStatement.executeQuery(
 * 		"SELECT * FROM T1);
 * 	// Free the connection; it is returned to the connection pool.
 * 	myConn.close();
 *
 * 	// The connection manager creates a new connection for the
 * 	// following request.
 * 	Connection yourConn = myConnMgr.getConnection(
 * 		"data:oracle:thin:@CUSTOMERDB:1521:ORCL", "paul", "omni8");
 * 	Statement yourStatement = yourConn.createStatement();
 * 	ResultSet yourResults = yourStatement.executeQuery(
 * 	"SELECT Customer, Date, Amount FROM Orders");
 * 	.
 * 	.
 * 	.
 * }
 * </PRE>
 */
public class ConnectionManager {
    /**
     * Name of the driver; e.g. "oracle.jdbc.driver.OracleDriver"
     * @serial
     */
    private String driverName;

    /**
     * Expanded name of the driver
     * @serial
     */
    private transient String expandedDriverName;

    /**
     * Datasource url; e.g. "jdbc:oracle:oci7:@ABYSS_ORACLE"
     * @serial
     */
    private String url;

    /**
     * Expanded datasource url
     * @serial
     */
    private transient String expandedUrl;

    /**
     * DBMS Username.
     * @serial
     */
    private String userName;

    /**
     * Expanded user name
     * @serial
     */
    private transient String expandedUserName;

    /**
     * DBMS password.
     * @serial
     */
    private String password;

    /**
     * Expanded DBMS password.
     * @serial
     */
    private transient String expandedPassword;

    /**
     * The minimum size of the connection pool.
     * @serial
     */
    private int minPool;

    /**
     * The maximum size of the connection pool.
     * @serial
     */
    private int maxPool;

    /**
     * The current size of the connection pool.
     * @serial
     */
    private transient int poolSize;

    /**
     * True if connection pooling is enabled.
     * @serial
     */
    private transient boolean pooling;

    /**
     * The linked list of idle DB connections.
     * @serial
     */
    transient DoubleLinkedList freeList;

    /**
     * The linked list of in-use DB connections.
     * @serial
     */
    transient DoubleLinkedList busyList;

    /**
     * List of Connections associated with transactions, indexed by
     * transaction object (javax.transaction.Transaction).
     * @serial
     */
    private transient Hashtable xactConnections;

    /**
     * Flag that a shutdown to this ConnectionManager object is pending.
     * @serial
     */
    transient boolean shutDownPending;

    /**
     * Flag that specifies we are using default connection blocking.
     * @serial
     */
    private transient boolean connectionBlocking;

    /**
     * Millisecond time to wait between attempts to connect
     * to the database.
     * @serial
     */
    private int msInterval;

    /**
     * Millisecond time to block while attempting to connect
     * to the database.
     * @serial
     */
    private int msWait;

    //
    // Default number of milliseconds to block while retrying to get
    // a pooled connection to the database.
    //
    private static final int DEFAULT_RETRY_INTERVAL = 1000;

    /**
     * Indicates whether this ConnectionManager is properly initialized.
     * @serial
     */
    private transient boolean initialized;

    /**
     * Maximumn number of seconds this DataSource will wait while attempting to
     * connection to a database.
     */
    private int loginTimeout;

    /**
     * Free non-pooled connection to reduce time for getting a new connection.
     */
    private transient ConnectionImpl freeConn = null;

    
    /**
     * The logger
     */
    private static Logger logger = LogHelperSQLStore.getLogger();
    
    /**
     * I18N message handler
     */
    private final static ResourceBundle messages = I18NHelper.loadBundle(
            "com.sun.jdo.spi.persistence.support.sqlstore.Bundle", // NOI18N
            ConnectionManager.class.getClassLoader());


    //
    // SQL92 State Codes.
    //

    //
    // SQL92 "00000" Successful completion.
    //
    static final String SQL_SUCCESS = "00000"; // NOI18N

    //
    // SQL92 "01000" Warning.
    //
    static final String SQL_WARNING = "01000"; // NOI18N

    //
    // SQL92 "01001" Warning; cursor operation conflict.
    //
    static final String SQL_CURSOR_OP = "01001"; // NOI18N

    //
    // SQL92 "01002" Warning; disconnect error.
    //
    static final String SQL_DISCONNECT = "01002"; // NOI18N

    //
    // SQL92 "01003" Warning; null value eliminated in set function.
    //
    static final String SQL_NULL_ELIM = "01003"; // NOI18N

    //
    // SQL92 "01004" Warning; string date, right truncation.
    //
    static final String SQL_R_TRUNC = "01004"; // NOI18N

    //
    // SQL92 "01005" Warning; insufficient item descriptor areas.
    //
    static final String SQL_INSUFF_ITEM = "01005"; // NOI18N

    //
    // SQL92 "01006" Warning; privilege not revoked.
    //
    static final String SQL_NOT_REVOKED = "01006"; // NOI18N

    //
    // SQL92 "01007" Warning; privilege not granted.
    //
    static final String SQL_NOT_GRANTED = "01007"; // NOI18N

    //
    // SQL92 "01008" Warning; implicit zero-bit padding.
    //
    static final String SQL_ZERO_BIT_PAD = "01008"; // NOI18N

    //
    // SQL92 "01009" Warning; search condition too long for
    // information schema.
    //
    static final String SQL_COND_TOO_LONG = "01009"; // NOI18N

    //
    // SQL92 "0100A" Warning; query condition too long for
    // information schema.
    //
    static final String SQL_QUERY_TOO_LONG = "0100A"; // NOI18N

    //
    // SQL92 "02000" No data.
    //
    static final String SQL_NO_DATA = "02000"; // NOI18N

    //
    // SQL92 "07000" Dynamic SQL error.
    //
    static final String SQL_DYN_ERROR = "07000"; // NOI18N

    //
    // SQL92 "07001" Dynamic SQL error; using clause does not
    // match dynamic parameter specifications.
    //
    static final String SQL_USING_NO_PARAM = "07001"; // NOI18N

    //
    // SQL92 "07002" Dynamic SQL error; using clause does not
    // match target specifications.
    //
    static final String SQL_USING_NO_TARGET = "07002"; // NOI18N

    //
    // SQL92 "07003" Dynamic SQL error; cursor specification
    // cannot be executed.
    //
    static final String SQL_CURSOR_NOEXE = "07003"; // NOI18N

    //
    // SQL92 "07004" Dynamic SQL error; using clause
    // required for dynamic parameters.
    //
    static final String SQL_USING_REQ = "07004"; // NOI18N

    //
    // SQL92 "07005" Dynamic SQL error; prepared statement
    // not a cursor specification.
    //
    static final String SQL_PREP_NO_CURSOR = "07005"; // NOI18N

    //
    // SQL92 "07006" Dynamic SQL error; restricted datatype
    // attribute violation.
    //
    static final String SQL_RESTRIC_ATTR = "07006"; // NOI18N

    //
    // SQL92 "07007" Dynamic SQL error; using caluse required
    // for result fields.
    //
    static final String SQL_USING_RESULTS = "07007"; // NOI18N

    //
    // SQL92 "07008" Dynamic SQL error; invalid descriptor count.
    //
    static final String SQL_INVAL_DESC_CNT = "07008"; // NOI18N

    //
    // SQL92 "07009" Dynamic SQL error; invalid descriptor index.
    //
    static final String SQL_INVAL_DESC_IDX = "07009"; // NOI18N

    //
    // SQL92 "08000" Connection exception.
    //
    static final String SQL_CONN = "08000"; // NOI18N

    //
    // SQL92 "08001" Connection exception; SQL-client unable
    // to establish SQL-connection.
    //
    static final String SQL_CLIENT_NO_CONN = "08001"; // NOI18N

    //
    // SQL92 "08002" Connection exception; connection name
    // in use.
    //
    static final String SQL_CONN_IN_USE = "08002"; // NOI18N

    //
    // SQL92 "08003" Connection exception; connection does not exist.
    //
    static final String SQL_NO_CONN = "08003"; // NOI18N

    //
    // SQL92 "08004" Connection exception; SQL-server rejected
    // establishment of SQL-connection.
    //
    static final String SQL_REJECT_CONN = "08004"; // NOI18N

    //
    // SQL92 "08006" Connection exception; connection failure.
    //
    static final String SQL_CONN_FAIL = "08006"; // NOI18N

    //
    // SQL92 "08007" Connection exception; transaction resolution unknown.
    //
    static final String SQL_TRANS_UNK = "08007"; // NOI18N

    //
    // SQL92 "0A000" Feature not supported.
    //
    static final String SQL_NO_SUPPORT = "0A000"; // NOI18N

    //
    // SQL92 "0A001" Feature not supported; multiple
    // server transactions
    //
    static final String SQL_NO_SUPPORT_MULTI = "0A001"; // NOI18N

    //
    // SQL92 "21000" Cardinality violation.
    //
    static final String SQL_INVALID_VALUE = "21000"; // NOI18N

    //
    // SQL92 "22000" Data exception.
    //
    static final String SQL_DATA = "22000"; // NOI18N

    //
    // SQL92 "22001" Data exception; string data,
    // right trunctation.
    //
    static final String SQL_DATA_RTRUNC = "22001"; // NOI18N

    //
    // SQL92 "22002" Data exception; null value, no
    // indicator parameter.
    //
    static final String SQL_DATA_NULL = "22002"; // NOI18N

    //
    // SQL92 "22003" Data exception; numeric value out
    // of range.
    //
    static final String SQL_OUT_OF_RANGE = "22003"; // NOI18N

    //
    // SQL92 "22005" Data exception; error in assignment.
    //
    static final String SQL_DATA_EXCEPT = "22005"; // NOI18N

    //
    // SQL92 "22007" Data exception; invalid datetime format.
    //
    static final String SQL_DATETIME_FMT = "22007"; // NOI18N

    //
    // SQL92 "22008" Data exception; datetime field overflow.
    //
    static final String SQL_DATETIME_OVFLO = "22008"; // NOI18N

    //
    // SQL92 "22009" Data exception; invalid time zone
    // displacement value.
    //
    static final String SQL_TIMEZONE = "22009"; // NOI18N

    //
    // SQL92 "22011" Data exception; substring error.
    //
    static final String SQL_SUBSTR_ERROR = "22011"; // NOI18N

    //
    // SQL92 "22012" Data exception; division by zero.
    //
    static final String SQL_DIV_BY_ZERO = "22012"; // NOI18N

    //
    // SQL92 "22015" Data exception; interval field overflow.
    //
    static final String SQL_INTERVAL_OVFLO = "22015"; // NOI18N

    //
    // SQL92 "22018" Data exception; invalid character value
    // for cast.
    //
    static final String SQL_INVAL_CHAR_CAST = "22018"; // NOI18N

    //
    // SQL92 "22019" Data exception; invalid escape character.
    //
    static final String SQL_INVAL_ESCAPE_CHAR = "22019"; // NOI18N

    //
    // SQL92 "22021" Data exception; character not in repertoire.
    //
    static final String SQL_CHAR_NOT_REP = "22021"; // NOI18N

    //
    // SQL92 "22022" Data exception; indicator overflow.
    //
    static final String SQL_IND_OVERFLOW = "22022"; // NOI18N

    //
    // SQL92 "22023" Data exception; invalid parameter value.
    //
    static final String SQL_INVAL_PARAM_VALUE = "22023"; // NOI18N

    //
    // SQL92 "22024" Data exception; unterminated C string.
    //
    static final String SQL_UNTERM_C_STR = "22024"; // NOI18N

    //
    // SQL92 "22025" Data exception; invalid escape sequence.
    //
    static final String SQL_INVAL_ESCAPE_SEQ = "22025"; // NOI18N

    //
    // SQL92 "22026" Data exception; string data, length mismatch.
    //
    static final String SQL_STR_LEN_MISMATCH = "22026"; // NOI18N

    //
    // SQL92 "22027" Data exception; trim error.
    //
    static final String SQL_TRIM_ERROR = "22027"; // NOI18N

    //
    // SQL92 "23000" Integrity constraint violation.
    //
    static final String SQL_INTEG_CONSTRAINT = "23000"; // NOI18N

    //
    // SQL92 "24000" Invalid cursor state.
    //
    static final String SQL_INVAL_CURSOR_STATE = "24000"; // NOI18N

    //
    // SQL92 "25000" Invalid transaction state
    //
    static final String SQL_INVAL_TRANS_STATE = "25000"; // NOI18N

    //
    // SQL92 "26000" Invalid SQL statement name.
    //
    static final String SQL_INVAL_SQL_NAME = "26000"; // NOI18N

    //
    // SQL92 "28000" Invalid authorization specification.
    //
    static final String SQL_INVAL_AUTH = "28000"; // NOI18N

    //
    // SQL92 "2A000" Syntax error or access rule violation
    // in direct SQL statement.
    //
    static final String SQL_SYNTAX_DIRECT = "2A000"; // NOI18N

    //
    // SQL92 "2B000" Dependent privilege descriptors
    // still exist.
    //
    static final String SQL_DESC_EXIST = "2B000"; // NOI18N

    //
    // SQL92 "2C000" Invalid character set name.
    //
    static final String SQL_INVAL_CHAR_SET = "2C000"; // NOI18N

    //
    // SQL92 "2D000" Invalid transaction termination.
    //
    static final String SQL_INVAL_TRANS_TERM = "2D000"; // NOI18N

    //
    // SQL92 "2E000" Invalid connection name.
    //
    static final String SQL_INVAL_CONN_NAME = "2E000"; // NOI18N

    //
    // SQL92 "33000" Invalid SQL descriptor name
    //
    static final String SQL_INVAL_SQL_DESC_NAME = "33000"; // NOI18N

    //
    // SQL92 "34000" Invalid cursor name.
    //
    static final String SQL_INVAL_CURSOR_NAME = "34000"; // NOI18N

    //
    // SQL92 "35000" Invalid condition number
    //
    static final String SQL_INVAL_COND_NUM = "35000"; // NOI18N

    //
    // SQL92 "37000" Syntax error or access rule violation
    // in dynamic SQL statement.
    //
    static final String SQL_SYNTAX_DYNAMIC = "37000"; // NOI18N

    //
    // SQL92 "3C000" Ambiguous cursor name.
    //
    static final String SQL_AMBIG_CURSOR = "3C000"; // NOI18N

    //
    // SQL92 "3D000" Invalid catalog name.
    //
    static final String SQL_INVAL_CATALOG = "3D000"; // NOI18N

    //
    // SQL92 "3F000" Invalid schema name.
    //
    static final String SQL_INVAL_SCHEMA_NAME = "3F000"; // NOI18N

    //
    // SQL92 "40000" Transaction rollback.
    //
    static final String SQL_TRANS_ROLLBACK = "40000"; // NOI18N

    //
    // SQL92 "40001" Transaction rollback; serialization
    // failure.
    //
    static final String SQL_TRANS_SERIAL_FAIL = "40001"; // NOI18N

    //
    // SQL92 "40002" Transaction rollback; integrity
    // constraint violation.
    //
    static final String SQL_TRANS_INTEG = "40002"; // NOI18N

    //
    // SQL92 "40003" Transaction rollback; statement
    // completion unknown.
    //
    static final String SQL_TRANS_COMP_UNK = "40003"; // NOI18N

    //
    // SQL92 "42000" Syntax error or access rule violation.
    //
    static final String SQL_SYNTAX = "42000"; // NOI18N

    //
    // SQL92 "44000" With check option violation.
    //
    static final String SQL_CHECK_OPT = "44000"; // NOI18N

    //
    // SQL92 "HZ   " Remote Database Access.
    //
    static final String SQL_RMT_DB_ACCESS = "HZ   "; // NOI18N


    /**
     * Default constructor.
     * Creates a new connection manager and loads the generic JDBC driver.
     * You should typically use one of the other constructors, because
     * you cannot change JDBC drivers after the connection manager has
     * been created.
     */
    public ConnectionManager() {
        super();
        this.driverName = null;
        this.expandedDriverName = null;
        this.url = null;
        this.expandedUrl = null;
        this.userName = null;
        this.expandedUserName = null;
        this.password = null;
        this.expandedPassword = null;
        this.minPool = 0;
        this.maxPool = 0;
        this.busyList = null;
        this.freeList = null;
        this.poolSize = 0;
        this.pooling = false;
        this.xactConnections = null;
        this.shutDownPending = false;
        this.connectionBlocking = false;
        this.msWait = 0;
        this.msInterval = 0;
        this.busyList = null;
        this.xactConnections = null;
        this.initialized = false;
    }

    // --------------- Overloaded Constructors -----------------

    /**
     * Creates a new connection manager and loads the named
     * JDBC driver.
     * <p>
     * @param      driverName  name of JDBC driver.
     * @exception  ClassNotFoundException if the driver cannot be found.
     * @exception  SQLException  if a SQL error is encountered.
     */
    public ConnectionManager(String driverName)
            throws ClassNotFoundException, SQLException {
        this();
        try {
            this.setDriverName(driverName);
            startUp();
        } catch (SQLException se) {
            throw se;
        } catch (ClassNotFoundException e) {
            throw e;
        }
    }

    /**
     * Creates a new connection manager, loads the named JDBC driver, and
     * sets the default database URL for the new connection manager.
     * <p>
     * @param      driverName  the name of JDBC driver.
     * @param      url         the database URL for the data source.
     * @exception  ClassNotFoundException if the driver cannot be found.
     * @exception  SQLException  if a SQL error is encountered.
     */
    public ConnectionManager(String driverName, String url)
            throws ClassNotFoundException, SQLException {
        this();
        try {
            this.setDriverName(driverName);
            this.setURL(url);
            startUp();
        } catch (SQLException se) {
            throw se;
        }
    }

    /**
     * Creates a new connection manager, loads the named JDBC driver, and
     * sets the default database URL and user name for the new
     * connection manager.
     * @param      driverName  the name of JDBC driver.
     * @param      url         the default database URL for the data source.
     * @param      userName    the default user name for database connections.
     * @exception  ClassNotFoundException if the driver cannot be found.
     * @exception  SQLException  if a SQL error is encountered.
     */
    public ConnectionManager(String driverName, String url,
                             String userName) throws ClassNotFoundException, SQLException {
        this();
        try {
            this.setDriverName(driverName);
            this.setURL(url);
            this.setUserName(userName);
            startUp();
        } catch (SQLException se) {
            throw se;
        }
    }

    /**
     * Creates a new connection manager, loads the named JDBC driver, and
     * sets the default database URL, user name, and password for the new
     * connection manager.
     * @param	driverName  the name of JDBC driver.
     * @param   url         the default database URL for the data source.
     * @param   userName    the default user name for database connections.
     * @param	password		the default password for database connections.
     * @exception  ClassNotFoundException if the driver cannot be found.
     * @exception  SQLException  if a SQL error is encountered.
     */
    public ConnectionManager
            (
            String driverName,
            String url,
            String userName,
            String password
            ) throws ClassNotFoundException, SQLException {
        this();
        try {
            this.setDriverName(driverName);
            this.setURL(url);
            this.setUserName(userName);
            this.setPassword(password);
            startUp();
        } catch (SQLException se) {
            throw se;
        }
    }

    /**
     * Creates a new connection manager, loads the named JDBC driver, and sets
     * the default database URL, user name, password and minimum and maximum
     * connection pool sizes for the new
     * connection manager.
     * <P>If minPool and maxPool are 0, connection pooling is disabled.
     * If minPool is greater than 0 and maxPool is greater than or equal
     * to minPool, this constructor creates a connection pool containing
     * minPool connections.
     * @param	driverName	the name of JDBC driver.
     * @param   url			the default database URL for the data source.
     * @param   userName	the default user name for database connections.
     * @param	password	the default password for database connections.
     * @param	minPool		the default minimum size of the connection pool.
     * @param	maxPool		the default maximum size of the connection pool.
     * @exception  ClassNotFoundException if the driver cannot be found.
     * @exception  SQLException  if a SQL error is encountered or if the
     * specified  value of minPool is not less than or equal to the specified
     * value of maxPool.
     */
    public ConnectionManager
            (
            String driverName,
            String url,
            String userName,
            String password,
            int minPool,
            int maxPool
            ) throws ClassNotFoundException, SQLException {
        this();
        try {
            this.setDriverName(driverName);
            this.setURL(url);
            this.setUserName(userName);
            this.setPassword(password);
            this.setMinPool(minPool);
            this.setMaxPool(maxPool);
            startUp();
        } catch (SQLException se) {
            throw se;
        }
    }

    /**
     * Creates a new connection manager, loads the named JDBC driver, and defines
     * the default values for the database URL, user name, password, minimum and maximum
     * connection pool sizes, and the length of time to wait for a
     * database connection.
     * <P>If minPool and maxPool are 0, connection pooling is disabled.
     * If minPool is greater than 0 and maxPool is greater than or equal
     * to minPool, this constructor creates a connection pool containing
     * minPool connections.
     * <P>If msWait is set to 0, the connection manager does not try again to
     * create or return a database connection if the first try fails.
     * For any other value, the connection manager waits 1000 milliseconds (ms)
     * (1 second) before trying again.
     * If the msWait value is less than 1000 ms, the connection manager
     * waits 1000 ms before trying.
     * The connection manager continues trying until the value specified
     * by msWait is met or exceeded.
     * <P>If you want to set the interval length yourself, you can use
     * the <code>ConnectionManager</code> constructor that
     * specifies the msInterval parameter or the <code>setInterval</code>
     * method.
     * @param	driverName	the name of JDBC driver.
     * @param   url			the default database URL for the data source.
     * @param   userName	the default user name for database connections.
     * @param	password	the default password for database connections.
     * @param	minPool		the default minimum size of the connection pool.
     * @param	maxPool		the default maximum size of the connection pool.
     * @param      msWait   the total number of milliseconds to wait for a successful connection.
     * @exception  ClassNotFoundException if the driver cannot be found.
     * @exception  SQLException  if a SQL error is encountered or if the
     * specified  value of minPool is not less than or equal to the specified
     * value of maxPool.
     */
    public ConnectionManager
            (
            String driverName,
            String url,
            String userName,
            String password,
            int minPool,
            int maxPool,
            int msWait
            ) throws ClassNotFoundException, SQLException {
        this();
        try {
            this.setDriverName(driverName);
            this.setURL(url);
            this.setUserName(userName);
            this.setPassword(password);
            this.setMinPool(minPool);
            this.setMaxPool(maxPool);
            this.setMsWait(msWait);
            this.setMsInterval(DEFAULT_RETRY_INTERVAL);
            startUp();
        } catch (SQLException se) {
            throw se;
        }
    }

    /**
     * Creates a new connection manager, loads the named JDBC driver, and
     * defines the default values for the database URL, user name, password, minimum and maximum
     * connection pool sizes, the length of time to wait for a database connection,
     * and how frequently to try again to get a database connection.
     * <P>If minPool and maxPool are 0, connection pooling is disabled.
     * If minPool is greater than 0 and maxPool is greater than or equal
     * to minPool, this constructor creates a connection pool containing
     * minPool connections.
     * <P>If msWait or msInterval is set to 0, the connection
     * manager does not try again to create or return a database connection
     * if the first try fails.
     * <P>For any other values greater than 0, the The connection manager
     * continues trying after every specified value for msInterval
     * until the value specified by msWait is met or exceeded.
     * If the value for msInterval is greater than the value for msWait,
     * the connection manager tries again to return a connection once, then
     * fails if it could get a connection.
     * @param	driverName	the name of JDBC driver.
     * @param   url			the default database URL for the data source.
     * @param   userName	the default user name for database connections.
     * @param	password	the default password for database connections.
     * @param	minPool		the default minimum size of the connection pool.
     * @param	maxPool		the default maximum size of the connection pool.
     * @param      msWait   the total number of milliseconds to wait to get a connection.
     * @param msInterval the number of milliseconds to wait before trying again to get a connection.
     * @exception  ClassNotFoundException if the driver cannot be found.
     * @exception  SQLException  if a SQL error is encountered or if the
     * specified  value of minPool is not less than or equal to the specified
     * value of maxPool.
     */

    public ConnectionManager
            (
            String driverName,
            String url,
            String userName,
            String password,
            int minPool,
            int maxPool,
            int msWait,
            int msInterval
            ) throws ClassNotFoundException, SQLException {
        this();
        try {
            this.setDriverName(driverName);
            this.setURL(url);
            this.setUserName(userName);
            this.setPassword(password);
            this.setMinPool(minPool);
            this.setMaxPool(maxPool);
            this.setMsWait(msWait);
            this.setMsInterval(msInterval);
            startUp();
        } catch (SQLException se) {
            throw se;
        }
    }

    // ---------------------  Public Methods -----------------------


    /**
     * Establishes a connection to the default database URL
     * using the default user name and password.
     * <P>If the current connection manager maintains a
     * connection pool, this method returns a pooled connection
     * instead of establishing a new connection.
     * If all pooled connections are in use, and the total wait time (msWait)
     * for the connection manager and the retry interval (msInterval) are
     * not 0, then the connection manager tries to get a database connection
     * after the retry interval. The connection manager continues to
     * try until a pooled connection becomes available or
     * until the total time equals or exceeds the wait time.
     * If the wait time expires before the connection manager returns
     * a database connection, this method throws a <code>SQLException</code>
     * exception with SQLState = "08006".
     *
     * <P>If the current connection manager is not set to try again for connections
     * (the wait time is 0) and no pooled connections are available, this
     * method throws a <code>SQLException</code> exception
     * with SQLState = "08006".
     *
     * <P>If the current connection manager is being shut down,
     * this method throws a <code>SQLException</code> exception with
     * SQLState = "08003".
     *
     * @return      A new or pooled database connection.
     * @exception	SQLException	if no database connection is available.
     *
     */
    public synchronized Connection getConnection() throws SQLException {
        if (this.shutDownPending == true) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.isdown") // NOI18N
                    ),
                            SQL_NO_CONN
                    );
            throw se;
        }

        ConnectionImpl conn = this.checkXact();

        if (conn != null) {
            // We already know about this transaction.
        } else if (!this.pooling)					// Get a non-pooled connection.
        {
            conn = (ConnectionImpl) this.getConnection(this.userName,
                    this.password);
            conn.setPooled(false);
            conn.checkXact();
        } else	// This is a pooled connection.
        {
            if (this.freeList.size <= 0)			// Is pool empty?
            {
                if (this.poolSize < this.maxPool)	// Can we expand the pool?
                {
                    try {
                        this.expandPool(1); // Add new connection to the pool.
                    } catch (SQLException se) {
                        throw se;
                    }
                } else if (this.connectionBlocking != true)	// Can't expand the pool.
                {
                    // If not blocking, give up.
                    SQLException se = new SQLException
                            (
                                    StringScanner.createParamString
                            (
                                    I18NHelper.getMessage(messages,
                                            "connection.connectionmanager.maxpool") // NOI18N
                            ),
                                    SQL_INVAL_PARAM_VALUE // 22023
                            );
                    throw se;
                } else								// We are blocking, so...
                {
                    try {
                        this.waitForConnection();	// wait for a connection.
                    } catch (SQLException se) {
                        throw se;
                    }
                }
            }
            conn = (ConnectionImpl) (this.freeList.removeFromHead());
            if (conn == null) {
                // Shouldn't happen.
                SQLException se = new SQLException
                        (
                                StringScanner.createParamString
                        (
                                I18NHelper.getMessage(messages,
                                        "connection.connectionmanager.badvalue") // NOI18N
                        ),
                                SQL_INVAL_PARAM_VALUE	// 22023
                        );
                throw se;
            }
            conn.setPooled(true);
            conn.checkXact();
            this.busyList.insertAtTail((Linkable) conn);
        }
        conn.setFreePending(false);
        return ((Connection) conn);
    }

    /**
     * Establishes a connection to the database at the default database URL using
     * the specified user name and password.
     *
     * @param       userName the database user name.
     * @param       password the database password.
     * @return      A new database connection.
     * @exception  SQLException  if the connection fails.
     */
    public synchronized Connection getConnection
            (
            String userName,
            String password
            ) throws SQLException {
        boolean debug = logger.isLoggable(Logger.FINEST);


        if (this.shutDownPending == true) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.isdown") // NOI18N
                    ),
                            SQL_CONN_FAIL
                    );
            throw se;
        }

        ConnectionImpl conn = this.checkXact();

        if (conn == null) {
            if (freeConn != null) {
                // We have one available - use it
                if (debug) {
                    logger.finest("sqlstore.connection.conncectiomgr.found",freeConn); // NOI18N
                }
                conn = freeConn;
                freeConn = null;
            } else {
                // No connection is available - get new
                try {
                    // By default, a new ConnectionImpl is non-pooled.
                    conn = new ConnectionImpl
                            (
                                    DriverManager.getConnection
                            (
                                    this.expandedUrl,
                                    this.expandAttribute(userName),
                                    this.expandAttribute(password)
                            ),
                                    this.expandedUrl,
                                    this.expandAttribute(userName),
                                    this
                            );
                    if (debug) {
                        logger.finest("sqlstore.connection.conncectiomgr.getnewconn",conn); // NOI18N
                    }
                } catch (SQLException se) {
                    throw se;
                }
            }
            conn.checkXact();
        } else {
            if (!conn.getUserName().equals(this.expandAttribute(userName))) {
                SQLException se = new SQLException
                        (
                                StringScanner.createParamString
                        (
                                I18NHelper.getMessage(messages,
                                        "connection.connectionmanager.getconnection.mismatch") // NOI18N
                        ),
                                SQL_NO_CONN		// 08003
                        );
                throw se;
            }
        }
        conn.setFreePending(false);
        conn.setPooled(false);
        this.busyList.insertAtTail((Linkable) conn);
        return ((Connection) conn);
    }

    /**
     * Establishes a connection to the specified
     * database URL using the specified user name and password.
     *
     * @param       url      the database URL for the database.
     * @param       userName the database user name.
     * @param       password the database password.
     * @return      A new database connection.
     * @exception  SQLException  if the connection fails.
     */
    public synchronized Connection getConnection
            (
            String url,
            String userName,
            String password
            ) throws SQLException {
        boolean debug = logger.isLoggable(Logger.FINEST);

        if (this.shutDownPending == true) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.isdown") // NOI18N
                    ),
                            SQL_CONN_FAIL
                    );
            throw se;
        }

        ConnectionImpl conn = this.checkXact();

        if (conn == null) {
            if (freeConn != null) {
                // We have one available - use it
                if (debug) {
                    logger.finest("sqlstore.connection.conncectiomgr.found",freeConn); // NOI18N
                }
                conn = freeConn;
                freeConn = null;
            } else {
                // No connection is available - get new
                try {
                    // By default, a new ConnectionImpl is non-pooled.
                    conn = new ConnectionImpl
                            (
                                    DriverManager.getConnection
                            (
                                    this.expandAttribute(url),
                                    this.expandAttribute(userName),
                                    this.expandAttribute(password)
                            ),
                                    this.expandAttribute(url),
                                    this.expandAttribute(userName),
                                    this
                            );
                    if (debug) {
                        logger.finest("sqlstore.connection.conncectiomgr.getnewconn",conn); // NOI18N
                    }
                } catch (SQLException se) {
                    throw se;
                }
            }
            conn.checkXact();
        } else {
            if ((!conn.getURL().equals(this.expandAttribute(url))) ||
                    (!conn.getUserName().equals(this.expandAttribute(userName)))) {
                SQLException se = new SQLException
                        (
                                StringScanner.createParamString
                        (
                                I18NHelper.getMessage(messages,
                                        "connection.connectionmanager.getconnection.mismatch") // NOI18N
                        ),
                                SQL_NO_CONN	// 08003
                        );
                throw se;
            }
        }
        conn.setFreePending(false);
        conn.setPooled(false);
        this.busyList.insertAtTail((Linkable) conn);
        return ((Connection) conn);
    }

    /**
     * Check whether a ConnectionImpl is already associated with
     * the transaction on the current thread.
     * <p>
     * @return  ConnectionImpl associated with the transaction on
     *          the current thread; null otherwise.
     */
    private synchronized ConnectionImpl checkXact() {
        Transaction tran = null;

        /* RESOLVE: Need to reimplement this???
		try
		{
			// Is this ForteJDBCConnet participating in a transaction?
			tran = ThreadContext.transactionContext().getTransaction();
		}
		catch (SystemException ex)
		{
			// There is no transaction.
			return null;
		}
		*/

        // Return Connection associated with this transaction - maybe null?
        if (tran == null)
            return null;
        return (ConnectionImpl) this.xactConnections.get(tran);
    }

    /**
     * Starts up this ConnectionManager by loading the proper
     * JDBC driver class and initializing the pool if necessary.
     * <p>
     * You need to call this method if you are using the ConnectionManager
     * as a component, or if you use the default constructor and set the
     * attributes via the <code>set</code><I>XXX</I> methods.
     * @exception  ClassNotFoundException if the driver cannot be found.
     * @exception  SQLException  if a SQL error is encountered.
     */
    public void startUp() throws ClassNotFoundException, SQLException {
        if (this.initialized == true) return;

        this.busyList = new DoubleLinkedList();
        this.xactConnections = new Hashtable();
        this.expandedDriverName = this.expandAttribute(this.driverName);
        if (this.expandedDriverName == null) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.nulldriver") // NOI18N
                    ),
                            SQL_INVALID_VALUE // 21000
                    );
            throw se;
        }
        this.expandedUrl = this.expandAttribute(this.url);
        if (this.expandedUrl == null) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.nullurl") // NOI18N
                    ),
                            SQL_INVALID_VALUE // 21000
                    );
            throw se;
        }
        this.expandedUserName = this.expandAttribute(this.userName);
        if (this.expandedUserName == null) {
            this.expandedUserName = ""; // Allow null username. // NOI18N
        }
        this.expandedPassword = this.expandAttribute(this.password);
        if (this.expandedPassword == null) {
            this.expandedPassword = ""; // Allow null password. // NOI18N
        }
        try {
            Class.forName(this.expandedDriverName);

            // Check if connection pooling is requested.
            if ((this.minPool > 0) && (this.maxPool >= this.minPool)) {
                // Yes, create a connection of minPool size.
                this.pooling = true;
                this.freeList = new DoubleLinkedList();
                expandPool(this.minPool);
            } else if ((this.minPool == 0) && (this.maxPool == 0)) {
                // No, pooling is to be disabled.
                this.pooling = false;
            }

        } catch (SQLException se) {
            throw se;
        } catch (ClassNotFoundException e) {
            throw e;
        }
        this.initialized = true;
    }

    /**
     * Disconnects all free database connections managed by
     * the current connection manager and sets the shutDownPending
     * flag to true.
     * All busy connections that are not participating
     * in a transaction will be closed when a yieldConnection() is
     * performed.  If a connection is participating in a transaction,
     * the connection will be closed after the transaction is commited
     * or rolledback.
     *
     * @see #yieldConnection
     */
    public synchronized void shutDown() throws SQLException {
        this.shutDownPending = true;

        if (this.pooling == true) {
            ConnectionImpl conn;
            this.connectionBlocking = false;
            this.pooling = false;
            this.initialized = false;
            for
                    (
                    conn = (ConnectionImpl) this.freeList.getHead();
                    conn != null;
                    conn = (ConnectionImpl) conn.getNext()
                    ) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    throw e;
                }
            }
            this.freeList = null;
        }
    }


    /**
     * Disconnects all free database connections managed by
     * the current connection manager and sets the shutDownPending
     * flag to true.
     * All busy connections that are not participating
     * in a transaction will be closed when a yieldConnection() is
     * performed.  If a connection is participating in a transaction,
     * the connection will be closed after the transaction is commited
     * or rolledback.
     *
     * @see #yieldConnection
     */
    protected void finalize() {
        try {
            shutDown();
        } catch (SQLException se) {
            ; // Ignore it.
        }
    }

    // ----------- Public Methods to get and set properties --------------

    /**
     * Gets the name of the JDBC driver.
     * @return Name of the JDBC driver.
     * @see #setDriverName
     */
    public synchronized String getDriverName() {
        return (this.driverName);
    }


    /**
     * Sets the name of the JDBC driver.
     * @param driverName the name of the JDBC driver.
     * @exception SQLException if the driverName is NULL.
     * @see #getDriverName
     */
    public synchronized void setDriverName(String driverName) throws SQLException {
        if (driverName == null) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.nulldriver") // NOI18N
                    ),
                            SQL_INVALID_VALUE // 21000
                    );
            throw se;
        }
        this.driverName = driverName;
    }

    /**
     * Gets the default database URL for the data source. This default is only for
     * the current connection manager and was set by the
     * <code>ConnectionManager</code> constructor.
     * This default is used if you don't specify another database URL
     * with a <code>getConnection</code> method.
     * To change this default value, use the <code>setURL</code> method.
     * @return The name of the default database URL.
     * @see #getConnection
     * @see #setURL
     */
    public synchronized String getURL() {
        return (this.url);
    }

    /**
     * Sets the default database URL for the data source. This default is only
     * for the current connection manager.
     * To get a connection using a different data source than the default, use the
     * <code>getConnection</code> method that specifies a database URL as a parameter.
     * @param url  URL for this connection manager.
     * @exception SQLException if the URL is NULL.
     * @see #getConnection
     * @see #getURL
     */
    public synchronized void setURL(String url) throws SQLException {
        if (url == null) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.nullurl") // NOI18N
                    ),
                            SQL_INVALID_VALUE // 21000
                    );
            throw se;
        }
        this.url = url;
    }

    /**
     * Gets the default database user name for the current
     * connection manager.
     * This default was set by the <code>ConnectionManager</code>
     * constructor, and is used if you don't specify another user name
     * with a <code>getConnection</code> method.
     * To change this default value, use the <code>setUserName</code> method.
     * @return The default database user name.
     * @see #getConnection
     * @see #setUserName
     */
    public synchronized String getUserName() {
        return (this.userName);
    }

    /**
     * Sets the default database user name for the current
     * connection manager.
     *
     * @param userName  the default user name for the current connection manager.
     * @see #getUserName
     */
    public synchronized void setUserName(String userName) throws SQLException {
        this.userName = userName;
    }

    /**
     * Gets the default database password for the current
     * connection manager.
     * This default was set by the <code>ConnectionManager</code>
     * constructor, and is used if you don't specify another password
     * with a <code>getConnection</code> method.
     * To change this default value, use the <code>setPassword</code> method.
     * @return The default database password.
     * @see #getConnection
     * @see #setPassword
     */
    public synchronized String getPassword() {
        return (this.password);
    }

    /**
     * Sets the default database password for the current connection manager.
     * @param password  the default password for the current connection manager.
     * @see #getPassword
     */
    public synchronized void setPassword(String password) throws SQLException {
        this.password = password;
    }

    /**
     * Gets the minimum number of pooled connections for the current
     * connection manager.
     * If this value is 0, the connection manager does not maintain
     * a connection pool until a connection is requested using the
     * getConnection method with no parameters.
     * <P>To change the minimum number of pooled connections, use the
     * <CODE>setMinPool</CODE> method.
     * @return The minimum number of pooled connections.
     * @see #getConnection
     * @see #setMinPool
     *
     */
    public synchronized int getMinPool() {
        return (this.minPool);
    }

    /**
     * Sets the minimum number of pooled connections for the current
     * connection manager.
     * The default minimum number of pooled connections is 0, which means that
     * no connections are pooled until a pooled connection is requested.
     * <P>The specified value of the minPool parameter must be:
     * <UL>
     * <LI>greater than or equal to 0.
     * <LI>greater than or equal to the current minimum number of pooled
     * connections
     * <LI>less than or equal to the maximum number of pooled connections
     * </UL>
     * Otherwise, this method throws a SQLException.
     * @param minPool the minimum number of pooled connections.
     * @exception SQLException if the connection manager is being shut down or
     * if the minPool value is not valid.
     * @see #getMaxPool
     * @see #getMinPool
     * @see #setMaxPool
     */
    public synchronized void setMinPool(int minPool) throws SQLException {
        if (shutDownPending == true) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.isdown") // NOI18N
                    ),
                            SQL_CONN_FAIL // 08006
                    );

            throw se;
        }
        if (minPool < 0) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.zero") // NOI18N
                    ),
                            SQL_INVAL_PARAM_VALUE	// 22023
                    );
            throw se;
        }
        if (minPool < this.minPool) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.badnew"), // NOI18N
                            Integer.toString(minPool),
                            Integer.toString(minPool)
                    ),
                            SQL_INVAL_PARAM_VALUE	// 22023
                    );
            throw se;
        }
        if (pooling == true) {
            if (minPool > maxPool) {
                SQLException se = new SQLException
                        (
                                StringScanner.createParamString
                        (
                                I18NHelper.getMessage(messages,
                                        "connection.connectionmanager.poolsize") // NOI18N
                        ),
                                SQL_INVAL_PARAM_VALUE	// 22023
                        );
                throw se;
            }
        }
        this.minPool = minPool;
    }

    /**
     * Gets the maximum number of pooled connections for the current
     * connection manager.
     * If this value is 0, the connection manager does not maintain
     * a connection pool.
     * When you request a connection with the <CODE>getConnection</CODE>
     * method, the <CODE>getConnection</CODE>
     * method always returns a new connection.
     * <P>To change the maximum number of pooled connections, use the
     * <CODE>setMaxPool</CODE> method.
     * @return The maximum number of pooled connections the current connection
     * manager maintains.
     * @see #setMaxPool
     *
     */
    public synchronized int getMaxPool() {
        return (this.maxPool);
    }

    /**
     * Sets the maximum number of pooled connections for the current
     * connection manager.
     * The default maximum number of pooled connections is 0, which means
     * that no connections are pooled.
     * <P>The specified value of the maxPool parameter must be:
     * <UL>
     * <LI>greater than or equal to 0.
     * <LI>greater than or equal to the current maximum number of pooled
     * connections
     * <LI>greater than or equal to the minimum number of pooled connections
     * </UL>
     * Otherwise, this method throws a SQLException.
     *
     * @param maxPool the maximum number of pooled connections.
     * @exception SQLException if the connection manager is being shut down or
     * if the maxPool value is not valid.
     * @see #getMaxPool
     * @see #getMinPool
     * @see #setMinPool
     */
    public synchronized void setMaxPool(int maxPool) throws SQLException {
        if (shutDownPending == true) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.isdown") // NOI18N
                    ),
                            SQL_CONN_FAIL	// 08006
                    );
            throw se;
        }
        if (maxPool < 0) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.zero"), // NOI18N
                            Integer.toString(maxPool)
                    ),
                            SQL_INVAL_PARAM_VALUE	// 22023
                    );
            throw se;
        }
        if (pooling == true) {
            if (maxPool < this.maxPool) {
                SQLException se = new SQLException
                        (
                                StringScanner.createParamString
                        (
                                I18NHelper.getMessage(messages,
                                        "connection.connectionmanager.badnew"), // NOI18N
                                Integer.toString(maxPool),
                                Integer.toString(maxPool)
                        ),
                                SQL_INVAL_PARAM_VALUE	// 22023
                        );
                throw se;
            }
        }

        if (maxPool < this.minPool) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.poolsize") // NOI18N
                    ),
                            SQL_INVAL_PARAM_VALUE	// 22023
                    );
            throw se;
        }
        this.maxPool = maxPool;
    }

    /**
     * Gets the amount of time, in milliseconds, the connection manager should spend trying
     * to get a pooled connection, which is the amount of time a requester might wait.
     * <p> This value is only meaningful when you use the <code>getConnection</code>
     * to get a pooled connection, which means that no database URL, user name, or
     * password is specified.
     * @return The wait time in milliseconds.
     * @see #getConnection
     * @see #setMsInterval
     *
     */
    public synchronized int getMsWait() {
        return (this.msWait);
    }

    /**
     * Sets the amount of time, in milliseconds, the connection manager should spend trying
     * to get a pooled connection, which is the amount of time a requester might wait.
     * Setting this value to 0 means that the connection manager does not try again to
     * get a database connection if it fails on the first try.
     * <p> This value is only meaningful when you use the <code>getConnection</code>
     * to get a pooled connection, which means that no database URL, user name, or
     * password is specified.
     * <p> The connection manager retries after the set interval until the total wait
     * time is equal to or greater than the specified wait time.
     * You can determine the total number of tries for a connection based on wait time
     * and interval settings using the following formula,
     * where msWait is the wait time, msInterval is
     * the time between attempts to get a connection:
     * <pre>
     * tries = msWait/msInterval + 2
     * </pre>
     * For example, if msWait is set to 2000 ms and msInterval is set to 1500 ms, then the
     * connection manager will try to get a database connection 3 times before throwing
     * an exception if it has failed.
     * <p> If the wait time value is less than the set value for the interval between retries,
     * but not zero, the connection manager waits the amount of time specified by the
     * interval, tries once, then returns an exception if it still could not get a connection.
     * @param msWait the wait time in milliseconds.
     * @see #getConnection
     * @see #getMsInterval
     * @see #getMsWait
     * @see #setMsInterval
     *
     */
    public synchronized void setMsWait(int msWait) throws SQLException {
        if (msWait < 0) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.badvalue"), // NOI18N
                            Integer.toString(msWait)
                    ),
                            SQL_INVAL_PARAM_VALUE	// 22023
                    );
            throw se;
        } else if (msWait > 0) {
            this.msWait = msWait;
            this.connectionBlocking = true;
        } else {
            this.msWait = msWait;
            this.connectionBlocking = false;
        }
    }

    /**
     * Gets the amount of time, in milliseconds,
     * between the connection manager's attempts to get a pooled connection.
     * <p> This value is only meaningful when you use the <code>getConnection</code>
     * to get a pooled connection, which means that no database URL, user name, or
     * password is specified.
     * @return The length of the interval between tries in milliseconds.
     * @see #getConnection
     * @see #setMsInterval
     */
    public synchronized int getMsInterval() {
        return (this.msInterval);
    }

    /**
     * Sets the amount of time, in milliseconds, between the connection
     * manager's attempts to get a pooled connection.
     * <p> This value is only meaningful when you use the <code>getConnection</code>
     * to get a pooled connection, which means that no database URL, user name, or
     * password is specified.
     * <p> The connection manager retries after the specified interval until the total wait
     * time is equal to or greater than the set wait time.
     * You can determine the total number of tries for a connection based on wait time
     * and interval settings using the following formula,
     * where msWait is the wait time, msInterval is
     * the time between attempts to get a connection:
     * <pre>
     * tries = msWait/msInterval + 2
     * </pre>
     * For example, if msWait is set to 2000 ms and msInterval is set to 1500 ms, then the
     * connection manager will try to get a database connection 3 times before throwing
     * an exception if it has failed.
     * <p> If the wait time value is greater than 0 but less than the
     * set value for the interval between retries,
     * the connection manager waits the amount of time specified by the
     * interval, tries once, then returns an exception if it still could not get a connection.
     * @param msInterval the interval between attempts to get a database connection, in milliseconds.
     * @see #getConnection
     * @see #getMsInterval
     * @see #getMsWait
     * @see #setMsWait
     *
     */
    public synchronized void setMsInterval(int msInterval) throws SQLException {
        if ((msInterval < 0) || (this.msWait < msInterval)) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.badnew"), // NOI18N
                            "MsInterval", // NOI18N
                            "MsWait" // NOI18N
                    ),
                            SQL_INVAL_PARAM_VALUE	// 22023
                    );
            throw se;
        } else {
            this.msInterval = msInterval;
        }
    }

    /**
     * Returns a string representation of the current ConnectionManager object.
     * <p>
     * @return  A <code>String</code> decribing the contents of the current
     * ConnectionManager object.
     */
    public synchronized String toString() {
        /*
		TraceLogger lgr = ThreadContext.lgr();
		// Check for trace flag sp:1:1
		boolean dif = ThreadContext.lgr().test
		(
			TraceLogger.CONFIGURATION,
			TraceLogger.SVC_SP,
			SPLogFlags.CFG_DIFFABLE_EXCEPTS,
			1
		);
		String buf = "ConnectManager@\n"; // NOI18N
		if (dif == false)
		{
			buf = buf + "    busyList = " + this.busyList + "\n"; // NOI18N
		}
		if (this.busyList != null)
		{
			buf = buf + "    busyList Object = " + this.busyList.toString(); // NOI18N
		}
		buf = buf + "    connectionBlocking = " + this.connectionBlocking + "\n"; // NOI18N
		buf = buf + "    driverName = " + this.driverName + "\n"; // NOI18N
		if (dif == false)
		{
			buf = buf + "    expandedDriverName = " + this.expandedDriverName + "\n"; // NOI18N
			buf = buf + "    expandedPassword = " + this.expandedPassword + "\n"; // NOI18N
			buf = buf + "    expandedUrl = " + this.expandedUrl	+ "\n"; // NOI18N
			buf = buf + "    expandedUserName = " + this.expandedUserName + "\n"; // NOI18N
			buf = buf + "    freeList = " + this.freeList + "\n"; // NOI18N
		}
		if (this.freeList != null)
		{
			buf = buf + "    freeList Object = " + this.freeList.toString(); // NOI18N
		}
		if (dif == false)
		{
			buf = buf + "    hashCode = " + this.hashCode() + "\n"; // NOI18N
		}
		buf = buf + "    maxPool = " + this.maxPool + "\n"; // NOI18N
		buf = buf + "    minPool = " + this.minPool	+ "\n"; // NOI18N
		buf = buf + "    msInterval = " + this.msInterval + "\n"; // NOI18N
		buf = buf + "    msWait = " + this.msWait + "\n"; // NOI18N
		buf = buf + "    password = " + this.password + "\n"; // NOI18N
		buf = buf + "    pooling = " + this.pooling + "\n"; // NOI18N
		buf = buf + "    poolSize = " + this.poolSize + "\n"; // NOI18N
		buf = buf + "    shutDownPending = " + this.shutDownPending + "\n"; // NOI18N
		buf = buf + "    url = " + this.url + "\n"; // NOI18N
		buf = buf + "    userName = " + this.userName + "\n"; // NOI18N

        return buf;
		*/

        return null;
    }

    // ----------  Private and default (friendly) methods -----------

    /**
     * Associate a Connection with a transaction.
     * <p>
     * @param  tran    The Transaction's object.
     * @param  conn    The Connection.
     */
    synchronized void associateXact(Transaction tran, ConnectionImpl conn) {
        if (tran != null)
            this.xactConnections.put((Object) tran, (Object) conn);
    }

    /**
     * Disassociate a Connection with a transaction.
     * <p>
     * @param  tran    The Transaction's object.
     * @param  conn   The ForteJDBCConnect hosting the transaction.
     * @param  free    The ConnectionImpl should be returned to freePool.
     * @ForteInternal
     */
    synchronized void disassociateXact
            (
            Transaction tran,
            ConnectionImpl conn,
            boolean free
            ) throws SQLException {
        ConnectionImpl xactConn = null;

        if (tran != null)
            xactConn = (ConnectionImpl) this.xactConnections.remove((Object) tran);

        if (tran == null || xactConn.equals((Object) conn)) {
            if (free == true) {
                if (conn.connectionManager.shutDownPending == false) {
                    this.freeList.insertAtTail((Linkable) conn);
                } else {
                    conn.close();
                }
            }
        } else {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            //MsgCat.getStr(DbmsMsgCat.DB_ERR_XACT_MISMATCH)
                            "Internal Error: transaction mismatch" // NOI18N
                    ),
                            SQL_TRANS_UNK // 08007
                    );
            throw se;
        }
    }

    /**
     * Expand connection pool by connections size.
     * <p>
     * @param     connections   Number of connections to add to pool.
     * @exception SQLException	if connection fails.
     * @ForteInternal
     */
    private synchronized void expandPool(int connections) throws SQLException {
        ConnectionImpl conn = null;

        if (this.shutDownPending == true) {
            SQLException se = new SQLException
                    (
                            StringScanner.createParamString
                    (
                            I18NHelper.getMessage(messages,
                                    "connection.connectionmanager.isdown") // NOI18N
                    ),
                            SQL_CONN_FAIL
                    );
            throw se;
        }

        for (int i = 0; i < connections; i++) {
            if (this.poolSize >= this.maxPool) {
                // There is no room for a new connection.
                SQLException se = new SQLException
                        (
                                StringScanner.createParamString
                        (
                                I18NHelper.getMessage(messages,
                                        "connection.connectionmanager.maxpool") // NOI18N
                        ),
                                SQL_CONN_FAIL
                        );
                throw se;
            } else // There is room in the pool, so get a new connection.
            {
                try {
                    conn = new ConnectionImpl
                            (
                                    DriverManager.getConnection
                            (
                                    this.expandedUrl,
                                    this.expandedUserName,
                                    this.expandedPassword
                            ),
                                    this.expandedUrl,
                                    this.expandedUserName,
                                    this
                            );
                    conn.setPooled(true);
                    this.freeList.insertAtTail((Linkable) conn);
                    this.poolSize++;
                } catch (SQLException e) {
                    throw e;
                }
            }
        }
    }

    /**
     * Expand an environment variable specified in attributes into their
     * corresponding values; e.g, if url = ${MYURL}, expand ${MYURL} into
     * its corresponding value.
     * <P>
     * @param envname	environment variable name.
     * @exceptions  SQLException  We should come up with a better one.
     */
    private String expandAttribute(String envname) throws SQLException {
        String attribute = null;
        /*RESOLVE:
		try
		{
			attribute = ForteProperties.expandVars(envname);
		}
		catch (EnvVariableException e)
		{
			SQLException se = new SQLException
			(
				StringScanner.createParamString
				(
				  I18NHelper.getMessage(messages,
                                             "connection.connectionmanager.badvalue"), // NOI18N
					envname
				),
				SQL_INVAL_PARAM_VALUE
			);
			throw se;
		}
		*/
        if (attribute != null) {
            return attribute;
        } else {
            return envname;
        }
    }

    /**
     * Wait for a pool connection.  The thread will wait msInterval
     * milliseconds between tries to get a connection from the pool.
     * If no connection is available after msWait milliseconds, an
     * exception is thrown.
     * <p>
     * @exception SQLException	if connection fails.
     * @ForteInternal
     */
    private synchronized void waitForConnection() throws SQLException {
        int interval = this.msInterval;
        int wait = this.msWait;
        int totalTime = 0;
        boolean done = false;
        Thread t = Thread.currentThread();
        do {
            // If there are idle connections in the pool
            if (this.freeList.size > 0) {
                done = true;
            } else // There are no idle connection in the pool
            {
                // Can the pool be expanded?
                if (this.poolSize < this.maxPool) {
                    // Yes, try to expand the pool.
                    try {
                        expandPool(1);
                        done = true;
                    } catch (SQLException se) {
                        throw se;
                    }
                } else // the pool is at maximum size...
                {
                    // If we have waited long enough, throw an exception.
                    if (totalTime >= wait) {
                        SQLException se = new SQLException
                                (
                                        StringScanner.createParamString
                                (
                                        I18NHelper.getMessage(messages,
                                                "connection.connectionmanager.conntimeout") // NOI18N
                                ),
                                        SQL_CONN_FAIL
                                );
                        throw se;
                    } else // Timeout has not expired, sleep for awhile.
                    {
                        try {
                            this.wait(interval);
                        } catch (InterruptedException ie) {
                            SQLException se = new SQLException
                                    (
                                            StringScanner.createParamString
                                    (
                                            I18NHelper.getMessage(messages,
                                                    "connection.connectionmanager.threaditerupted") // NOI18N
                                    ),
                                            SQL_CONN_FAIL
                                    );
                            throw se;
                        }
                    }
                    totalTime += interval;
                }
            }
        } while (!done);
    }


    public void setLoginTimeout(int seconds)
            throws SQLException {
        loginTimeout = seconds;
    }

    public int getLoginTimeout()
            throws SQLException {
        return loginTimeout;
    }

    /**
     * Called by ConnectionImpl to save a connection when it is no longer used.
     * Previous free connection can be released (closed) when a new one becomes available.
     */
    protected synchronized void replaceFreeConnection(ConnectionImpl c) {
        boolean debug = logger.isLoggable(Logger.FINEST);

        if (debug) {
            logger.finest("sqlstore.connection.conncectiomgr.replacefreeconn",freeConn); // NOI18N
        }
        if (freeConn != null) {
            // Release (close) the old connection.
            freeConn.release();
        }
        freeConn = c;
    }
}