/*-----------------------------------------------------------------------------
 *
 * Enhydra JdbcS
 * The Initial Developer of the Original Code is Lutris Technologies Inc.
 * Portions created by Lutris are Copyright 1997-2000 Lutris Technologies Inc.
 * All Rights Reserved.
 *
 * The contents of this file are subject to the Enhydra Public License
 * Version 1.1 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * www.enhydra.org/license/epl.html
 *
 * Software distributed under the License is distributed on an "ASIS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * -----------------------------------------------------------------------------
 * $Id$
 * -----------------------------------------------------------------------------
 */
package org.enhydra.jdbc.standard;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.Vector;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.PooledConnection;
import org.enhydra.jdbc.util.LRUCache;

/**
 * Provides an implementation of javax.sql.PooledConnection which
 * is completely generic (i.e. it relies only on JDBC 1 functionality).
 *
 * This class maintains a physical database connection which is
 * passed to each StandardXAConnectionHandle when it is created. It is the
 * StandardXAConnectionHandle object which the application receives and which
 * it perceives as the java.sql.Connection object.
 *
 * StandardXAConnectionHandle objects pass PreparedStatements back to the
 * StandardPooledConnection so that they can be retained across
 * StandardXAConnectionHandle instantiations.
 */
public class StandardPooledConnection implements PooledConnection {

    protected StandardConnectionPoolDataSource dataSource;
    public Connection		con;			// the physical database connection
    public StandardConnectionHandle	connectionHandle;	// the last StandardConnectionHandle created
    Vector			listeners;		// objects listening for events on this connection
    boolean			isClosed;		// true if this connection has been closed

    /**
     * Creates the physical database connection.
     */
    public StandardPooledConnection (StandardConnectionPoolDataSource dataSource, String user, String password) throws SQLException {
	this.dataSource = dataSource;
	con = dataSource.getConnection (user, password);
	listeners = new Vector (5,5);
    } // constructor
    
    /**
     * Creates a new StandardConnectionHandle for use by an application.
     * If there is already a StandardConnectionHandle in use then it is
     * closed (i.e. the application has the connection withdrawn).
     */
    synchronized public Connection getConnection() throws java.sql.SQLException {
	if (connectionHandle != null) {			// if there's already a connection handle
	    if (!connectionHandle.isClosed()) {		// and it hasn't been closed
		connectionHandle.close();		// close it now
	    }
	}
	newConnectionHandle();
	return connectionHandle;
    } // method getConnection
    
    protected void newConnectionHandle() {
	connectionHandle = new StandardConnectionHandle (this, dataSource.getMasterPrepStmtCache(), dataSource.getPreparedStmtCacheSize());
    } // method newConnectionHandle

    public void close() throws java.sql.SQLException {
	con.close();
	dataSource.getMasterPrepStmtCache().remove(con.toString());
    } // method close
    
    public void addConnectionEventListener(ConnectionEventListener listener) {
	listeners.addElement (listener);
    } // method addConnectionEventListener
    
    public void removeConnectionEventListener(ConnectionEventListener listener) {
	listeners.removeElement (listener);
    } // method removeConnectionEventListener
    
    /**
     * Notifies all listeners that the StandardConnectionHandle created by this
     * PooledConnection has been closed.
     */
    void closeEvent () {
	ConnectionEvent event = new ConnectionEvent (this);	// create the event that we'll send
	for (int i=0; i<listeners.size(); i++) {		// for each listener
	    Object obj = listeners.elementAt(i);		// get next listener
	    ConnectionEventListener cel = (ConnectionEventListener)obj;//cast to something more useful
	    cel.connectionClosed (event);				// notify this listener
	}

    } // method closeEvent

    /**
     * Invoked when a fatal connection error occurs, 
     * just before an SQLException is thrown to the application
     *
     * This method is automatically called when a fatal error 
     * is detected on the base connection. The base connection 
     * is the actual connection that backs the connection
     * handle provided by the getConnection() method
     */
    public void connectionErrorOccurred(ConnectionEvent event) {
	for (int i=0; i<listeners.size(); i++) {		// for each listener
	    Object obj = listeners.elementAt(i);		// get next listener
	    ConnectionEventListener cel = (ConnectionEventListener)obj;//cast to something more useful
	    cel.connectionErrorOccurred (event);				// notify this listener
	    //cel.connectionClosed (event);				// notify this listener
	}
    } // method connectionErrorOccurred
    
    /**
     * Access method allowing access to the underlying physical connection.
     */
    public Connection getPhysicalConnection () {
	return con;
    } // method getPhysicalConnection
    
} // class StandardPooledConnection
