/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. 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_1_1.html * or packager/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 packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [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 org.glassfish.persistence.common.I18NHelper; import com.sun.jdo.api.persistence.support.Transaction; 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. * */ 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. * */ 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; } }