/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.org
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */


package org.objectweb.jonas.jdbc_xa;

import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.xa.XAResource;
import org.objectweb.jonas.common.Log;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;

/**
 * The XADataSource implementation in our pseudo JDBC-XA driver.
 * Acts as a factory for XAConnection objects.
 * Created by our DataSource implementation, in the EJBServer.
 * There is one XADataSource for each DataSource JDBC which do not have
 * this kind of driver in standard (all databases today!)
 *
 * @author Philippe Durieux
 * Contributor(s): 
 */
public class XADataSourceImpl implements XADataSource {

    static private Logger logger = null;

    // properties
    private String dataSourceName = null;
    private String url = null;
    private String className = null;
    private String userName = null;
    private String password = null;
    private int isolationLevel = -1;

    // private
    private int loginTimeout = 60;
    private PrintWriter log = null;
    private Class driverClass = null;

    // -----------------------------------------------------------------
    // Constructors
    // -----------------------------------------------------------------

    /**
     * Constructor for Factory
     */
    public XADataSourceImpl() {
    }

    // -----------------------------------------------------------------
    // Properties
    // -----------------------------------------------------------------
    public String getDataSourceName() {
	return dataSourceName;
    }
    public void setDataSourceName(String s) {
	logger = Log.getLogger(Log.JONAS_JDBCXA_PREFIX);
	dataSourceName = s;
    }

    public String getUrl() {
	return url;
    }
    public void setUrl(String s) {
	url = s;
    }

    public String getClassName() {
	return className;
    }
    public void setClassName(String s) {
	className = s;

	// Loads standard JDBC driver and keeps it loaded (via driverClass)
	logger.log(BasicLevel.DEBUG, "Load JDBC driver "+s);
	try {
	    driverClass = Class.forName(className);
	} catch (java.lang.ClassNotFoundException e) {
	    logger.log(BasicLevel.ERROR, "Cannot load JDBC driver : " + e);
	}
    }

    public String getUserName() {
	return userName;
    }
    public void setUserName(String s) {
	userName = s;
    }

    public String getPassword() {
	return password;
    }
    public void setPassword(String s) {
	password = s;
    }

    public void setTransactionIsolation(int level) {
	isolationLevel = level;
    }

    // -----------------------------------------------------------------
    // XADataSource implementation
    // -----------------------------------------------------------------

    /**
     * Attempt to establish a database connection.
     *
     * @return a Connection to the database
     *
     * @exception SQLException  if a database-access error occurs.
     */
    public XAConnection getXAConnection() throws SQLException {
	return getXAConnection(userName, password);
    }


    /**
     * Attempt to establish a database connection.
     *
     * @param user The database user on whose behalf the Connection is being made
     *
     * @param passwd the user's password
     *
     * @return a Connection to the database
     *
     * @exception SQLException  if a database-access error occurs.
     */
    public XAConnection getXAConnection(String user, String passwd) throws SQLException {

	logger.log(BasicLevel.DEBUG, "entering for "+url);

	// Create the actual connection in the std driver
	Connection conn = null;
	try {
	    if (user.length() == 0) {
		conn = DriverManager.getConnection(url);
		logger.log(BasicLevel.DEBUG, "    * New Connection on "+url);
	    } else {
		// Accept password of zero length.
		conn = DriverManager.getConnection(url, user, passwd);
		logger.log(BasicLevel.DEBUG, "    * New Connection on "+url+ " for "+user);
	    }
	} catch (SQLException e) {
	    logger.log(BasicLevel.ERROR, "Could not get Connection on "+url+":\n"+e);
	    throw e;
	}

	// Attempt to set the transaction isolation level
	// Depending on the underlaying database, this may not succeed.
	if (isolationLevel != -1) {
	    try {
		logger.log(BasicLevel.DEBUG, "set transaction isolation to "+isolationLevel);
		conn.setTransactionIsolation(isolationLevel);
	    } catch (SQLException e) {
		String ilstr = "?";
		switch (isolationLevel) {
		case Connection.TRANSACTION_SERIALIZABLE:
		    ilstr = "SERIALIZABLE";
		    break;
		case Connection.TRANSACTION_NONE:
		    ilstr = "NONE";
		    break;
		case Connection.TRANSACTION_READ_COMMITTED:
		    ilstr = "READ_COMMITTED";
		    break;
		case Connection.TRANSACTION_READ_UNCOMMITTED:
		    ilstr = "READ_UNCOMMITTED";
		    break;
		case Connection.TRANSACTION_REPEATABLE_READ:
		    ilstr = "REPEATABLE_READ";
		    break;
		}
		logger.log(BasicLevel.ERROR, "Cannot set transaction isolation to "+ilstr+" for this DataSource:");
		logger.log(BasicLevel.ERROR, url);
		isolationLevel = -1;
	    }
	}

	// Create the XAConnection object
	XAConnectionImpl xac = new XAConnectionImpl(conn, this);

	// return the XAConnection
	return (XAConnection) xac;
    }

    /**
     * Get the log writer for this data source. 
     *
     * @return PrintWritert he log writer for this data source, null if disabled
     *
     * @exception SQLException  if a database-access error occurs.
     */
    public PrintWriter getLogWriter() throws SQLException {
	return log;
    }

    /**
     * Set the log writer for this data source. 
     *
     * @param out the new log writer; to disable, set to null
     *
     * @exception SQLException  if a database-access error occurs.
     */
    public void setLogWriter(PrintWriter out) throws SQLException {
	log = out;
    }


    /**
     * Sets the maximum time in seconds that this data source will wait while attempting 
     * to connect to a database.
     *
     * @param seconds the data source login time limit
     *
     * @exception SQLException  if a database-access error occurs.
     */
    public void setLoginTimeout(int seconds) throws SQLException {
	loginTimeout = seconds;
    }

    /**
     * Gets the maximum time in seconds that this data source can wait 
     * while attempting to connect to a database.
     *
     * @return the data source login time limit
     *
     * @exception SQLException  if a database-access error occurs.
     */
    public int getLoginTimeout() throws SQLException {
	return loginTimeout;
    }

}
