package org.apache.struts.util;/*
 * $Header: /home/cvspublic/jakarta-struts/src/share/org/apache/struts/util/GenericConnection.java,v 1.7 2001/01/31 22:36:54 craigmcc Exp $
 * $Revision: 1.7 $
 * $Date: 2001/01/31 22:36:54 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

/*
 * $Header: /home/cvspublic/jakarta-struts/src/share/org/apache/struts/util/GenericConnection.java,v 1.7 2001/01/31 22:36:54 craigmcc Exp $
 * $Revision: 1.7 $
 * $Date: 2001/01/31 22:36:54 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.Map;
import javax.sql.DataSource;

/**
 * Generic wrapper implementation of a <strong>Connection</strong> that
 * works with <code>GenericDataSource</code> to wrap connections for any
 * JDBC driver.
 *
 * @author Craig R. McClanahan
 * @author Ted Husted
 * @version $Revision: 1.7 $ $Date: 2001/01/31 22:36:54 $

 */

public class GenericConnection implements Connection {

	// ----------------------------------------------------- Instance Constants

	private final static String SQLEXCEPTION_CLOSED = "Connection was closed.";

	// ----------------------------------------------------- Instance Variables

	/**
	 * The initial auto-commit state to which we should return after release.
	 */
	protected boolean autoCommit = false;

	/**
	 * The initial catalog to which we should return after release.
	 */
	protected String catalog = null;

	/**
	 * The closed flag for this wrapped connection.
	 */
	private boolean closed = false;

	/**
	 * The Connection that is being wrapped.
	 */
	protected Connection conn = null;

	/**
	 * The initial transaction isolation level to which we should return
	 * after release.
	 */
	protected int level = 0;

	/**
	 * The initial type map to which we should return after release.
	 */
	protected Map map = null;

	/**
	 * The initial read-only state to which we should return after release.
	 */
	protected boolean readOnly = false;

	/**
	 * The GenericDataSource that owns this connection.
	 */
	protected GenericDataSource source = null;

	/**
	 * The cache statements boolean if the PreparedStatements must be cached or not.
	 * default on false;
	 */
	protected boolean _bCacheStatements = false;
	 
	/**
	 * Hashmap with the pooled preparedstatements
	 */
	protected java.util.HashMap _hmPrepStatements = null;


	// ----------------------------------------------------------- Constructors
	
	/**
	 * Construct a new GenericConnection wrapping the specified connection.
	 *
	 * @param source The data source that owns this connection
	 * @param conn The connection to wrap
	 * @param autoCommit Desired auto-commit state for this connection
	 * @param readOnly Desired read-only state for this connection
	 *
	 * @exception SQLException if an SQL processing error occurs
	 */
	public GenericConnection(
		GenericDataSource source,
		Connection conn,
		boolean autoCommit,
		boolean readOnly)
		throws SQLException {
	
		super();
		this.source = source;
		this.conn = conn;
	
		this.autoCommit = autoCommit;
		this.catalog = conn.getCatalog();
		this.level = conn.getTransactionIsolation();
		this._hmPrepStatements = new java.util.HashMap();
		try
		{
			this.map = conn.getTypeMap();
		}
		catch (SQLException e)
		{
			; // PostgreSQL throws a "not yet implemented" exception
		}
		catch (UnsupportedOperationException e)
		{
			; // JDBC-ODBC bridge throws this
		}
		catch (AbstractMethodError e)
		{
			; // mm.mysql throws this
		}
		this.readOnly = readOnly;
		this.conn.setAutoCommit(this.autoCommit);
		try
		{
			this.conn.setReadOnly(this.readOnly);
		}
		catch (SQLException e)
		{
			; // Informix throws a "not supported" exception
		}
	
	}


























	// --------------------------------------------------------- Public Methods
	
	/**
	 * Clear all warnings reported for this Connection.
	 *
	 * @exception SQLException if a database access error occurs
	 */
	public void clearWarnings() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		conn.clearWarnings();
	
	}


	/**
		 * Return this wrapped Connection to our data source connection pool.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public void close() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		// Clean up any outstanding transaction as best we can
		try
		{
			conn.rollback();
		}
		catch (SQLException e)
		{
			;
		}
		try
		{
			conn.clearWarnings();
		}
		catch (SQLException e)
		{
			;
		}
		try
		{
			if(conn.getAutoCommit() != this.autoCommit)
			{
				conn.setAutoCommit(this.autoCommit);
			}
		}
		catch (SQLException e)
		{
			;
		}
		try
		{
			if(!conn.getCatalog().equals(this.catalog))
			{
				conn.setCatalog(this.catalog);
			}
		}
		catch (SQLException e)
		{
			;
		}
		try
		{
			if(conn.getTransactionIsolation() != this.level)
			{
				conn.setTransactionIsolation(this.level);
			}
		}
		catch (SQLException e)
		{
			;
		}
		try
		{
			if(!conn.getTypeMap().equals(this.map))
			{
				conn.setTypeMap(this.map);
			}
		}
		catch (UnsupportedOperationException e)
		{
			; // JDBC-ODBC driver throws this
		}
		catch (SQLException e)
		{
			;
		}
		try
		{
			if(conn.isReadOnly() != this.readOnly)
			{
				conn.setReadOnly(this.readOnly);
			}
		}
		catch (SQLException e)
		{
			;
		}
	
		// Flag that this connection is closed
		// All methods accessing conn will now throw SQLEXCEPTION_CLOSED
		closed = true;
	
		// Return this connection to the available connection pool
		source.returnConnection(this);
	
	}


	/**
		 * Make all changes made since the previous commit or rollback
		 * permanent, and releases any database locks currently held.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public void commit() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		conn.commit();
	
	}


	/**
		 * Create a <code>Statement</code> for sending SQL statements to the
		 * database.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public Statement createStatement() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.createStatement());
	
	}


	/**
		 * (JDBC 2.0) Create a Statement that will create a ResultSet of the
		 * specified type and concurrency.
		 *
		 * @param resultSetType A result set type
		 * @param resultSetConcurrency A result set concurrency
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.createStatement(resultSetType, resultSetConcurrency));
	
	}


	/**
		 * Return the current auto-commit state.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public boolean getAutoCommit() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.getAutoCommit());
	
	}


	/**
		 * Return the current catalog name for this Connection.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	
	public String getCatalog() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.getCatalog());
	
	}


	/**
		 * Get the metadata regarding this connection's database.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public DatabaseMetaData getMetaData() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.getMetaData());
	
	}


	/**
		 * Return this Connection's current transaction isolation level.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public int getTransactionIsolation() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.getTransactionIsolation());
	
	}


	/**
		 * (JDBC 2.0) Return the type map for this connection.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public Map getTypeMap() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.getTypeMap());
	
	}


	/**
		 * Return the first warning reported by calls to this Connection.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public SQLWarning getWarnings() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.getWarnings());
	
	}


	/**
		 * Return <code>true</code> if this Connection is closed.
		 *
		 * The GenericConnection.isClosed() method is only guaranteed to return true after
		 * GenericConnection.closed() has been called. This method cannot be called, in
		 * general, to determine if a database connection is valid or invalid.
		 *
		 * A typical JDBC client can determine that a connection is invalid by catching the
		 * exception that is thrown when a JDBC operation is attempted.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	
	public boolean isClosed() throws SQLException {
	
		return (closed);
	
	}


	/**
		 * Return <code>true</code> if this Connection is in read-only mode.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public boolean isReadOnly() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.isReadOnly());
	
	}


	/**
		 * Convert the given SQL statement into the system's native SQL grammer.
		 *
		 * @param sql Statement to be processed
		 */
	public String nativeSQL(String sql) throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.nativeSQL(sql));
	
	}


	/**
		 * Create a <code>CallableStatement</code> object for calling database
		 * stored procedures.
		 *
		 * @param sql Statement to be processed
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public CallableStatement prepareCall(String sql) throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.prepareCall(sql));
	
	}


	/**
		 * (JDBC 2.0) Create a CallableStatement object that will generate
		 * ResultSet objects with the given type and concurrency.
		 *
		 * @param sql Statement to be processed
		 * @param resultSetType A result set type
		 * @param resultSetConcurrency A result set concurrency
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
		throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.prepareCall(sql, resultSetType, resultSetConcurrency));
	
	}


	/**
		 * Create a <code>PreparedStatement</code> object for sending
		 * parameterized SQL statements to the database.
		 *
		 * @param sql Statement to be processed
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public PreparedStatement prepareStatement(String sql) throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		if(_bCacheStatements)
		{
			GenericPreparedStatement statement = null;
	
			synchronized (_hmPrepStatements) 
			{
				statement = (GenericPreparedStatement)_hmPrepStatements.remove(sql);
				if(statement == null)
				{
					statement = new GenericPreparedStatement(this,sql,conn.prepareStatement(sql));
				}
			}
			statement.setClosed(false);
			return statement;
		}
		else
		{
			return (conn.prepareStatement(sql));
		}
	
	}


	/**
		 * (JDBC 2.0) Create a PreparedStatement object that will generate
		 * ResultSet objects with the given type and concurrency.
		 *
		 * @param sql Statement to be processed
		 * @param resultSetType A result set type
		 * @param resultSetConcurrency A result set concurrency
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
		throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		return (conn.prepareStatement(sql, resultSetType, resultSetConcurrency));
	
	}


	/**
		 * Drop all changes made since the previous commit or rollback.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public void rollback() throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		conn.rollback();
	
	}


	/**
		 * Sets this connection's auto-commit mode.
		 *
		 * @param autoCommit The new auto-commit mode.
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public void setAutoCommit(boolean autoCommit) throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		conn.setAutoCommit(autoCommit);
	
	}


	/**
		 * Set the catalog name for this Connection.
		 *
		 * @param catalog The new catalog name
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public void setCatalog(String catalog) throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		conn.setCatalog(catalog);
	
	}


	/**
		 * Set the read-only mode of this connection.
		 *
		 * @param readOnly The new read-only mode
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public void setReadOnly(boolean readOnly) throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		conn.setReadOnly(readOnly);
	
	}


	/**
		 * Set the transaction isolation level for this Connection.
		 *
		 * @param level The new transaction isolation level
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public void setTransactionIsolation(int level) throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		conn.setTransactionIsolation(level);
	
	}


	/**
		 * (JDBC 2.0) Set the type map for this connection.
		 *
		 * @param map The new type map
		 *
		 * @exception SQLException if a database access error occurs
		 */
	public void setTypeMap(Map map) throws SQLException {
	
		if (closed)
			throw new SQLException(SQLEXCEPTION_CLOSED);
	
		conn.setTypeMap(map);
	
	}


	/**
		 * Return the data source that owns this connection.
		 */
	DataSource getDataSource()
	{
	
		// Do not check for closed exception, to allow for a fresh connection
	
		return (this.source);
	
	}


	/**
		 * Set the closed status of this connection wrapper.
		 *
		 * Would usually only be called by the owning DataSource (source), with
		 * setClosed(false), when a pooled connection is being recycled.
		 *
		 */
	void setClosed(boolean closed)
	{
	
		this.closed = closed;
	
	}




	// -------------------------------------------------------- Package Methods
	
	/**
	 * Return the actual connection that we are wrapping.
	 */
	void closeConnection() throws SQLException
	{
		java.util.Iterator iterator = _hmPrepStatements.values().iterator();
		while(iterator.hasNext())
		{
			GenericPreparedStatement statement = (GenericPreparedStatement)iterator.next();
			try
			{
				statement.getPreparedStatement().close();
			} catch(Exception e){}
		}
		_hmPrepStatements.clear();
		_hmPrepStatements = null;
		this.conn.close();
	}




	/**
	 * Insert the method's description here.
	 * Creation date: (3-2-2001 16:01:23)
	 * @return boolean
	 */
	public boolean isCacheStatements()
	{
		return _bCacheStatements;
	}




	void returnPreparedStatement(GenericPreparedStatement statement)
	{
		synchronized (_hmPrepStatements) 
		{
			statement = (GenericPreparedStatement)_hmPrepStatements.put(statement.getSqlString(), statement);
			if(statement != null)
			{
				try
				{
					statement.getPreparedStatement().close();
				} catch(Exception e){}
			}
		}
	}




	/**
	 * Insert the method's description here.
	 * Creation date: (3-2-2001 16:01:23)
	 * @param newCacheStatements boolean
	 */
	public void setCacheStatements(boolean newCacheStatements)
	{
		_bCacheStatements = newCacheStatements;
		if(!_bCacheStatements && _hmPrepStatements.size() > 0)
		{
			java.util.Iterator iterator = _hmPrepStatements.values().iterator();
			while(iterator.hasNext())
			{
				try
				{
					((GenericPreparedStatement)iterator.next()).getPreparedStatement().close();
				} catch(Exception e){}
			}
			_hmPrepStatements.clear();
		}
	}
}