/*-----------------------------------------------------------------------------
 *
 * 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.
 * ----------------------------------------------------------------------------
 * 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.enhydra.jdbc.pool;

import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Enumeration;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.TransactionManager;
import javax.transaction.Transaction;
import javax.transaction.SystemException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.XAException;

/**
 * StandardXAPoolDataSource class allows to make some operations on
 * XAConnection. It implements PoolHelper for the 3 methods :<p>
 * create   : create an XAConnection<p>
 * create(user,password)   : create a PooledConnection with an other user/password<p>
 * testThisObject : check if the object is still valid<p>
 * checkThisObject : check if the object is closed<p>
 * expire   : kill the object<p>
 */
public class StandardXAPoolDataSource extends StandardPoolDataSource {

    public XADataSource 	xads;		// object to build XAConnection
    public TransactionManager 	transactionManager; // the current Transaction Manager

    /**
     * Constructor
     */
    public StandardXAPoolDataSource() {
	super();
    } // constructor

    /**
     * Constructor
     */
    public StandardXAPoolDataSource(int initSize) {
	super(initSize);
    } // constructor

    public void setTransactionManager(TransactionManager tm) {
	transactionManager = tm;
    } // setTransactionManager

    public void initContext() throws SQLException {
	log("StandardXAPoolDataSource:initContext");
	try {
	    ictx =  new InitialContext();		// get the initial context
	    xads = (XADataSource)ictx.lookup(this.dataSourceName);// lookup the symbol to create object
	    log("StandardXAPoolDataSource:initContext lookup done");
	} catch (javax.naming.NameNotFoundException e) {
	    log("The XADataSource is not bound at " + this.dataSourceName);
	    throw new SQLException("The XADataSource is not bound at " + this.dataSourceName);
	} catch(javax.naming.NamingException e) {
	    log("Error javax.naming.NamingException in StandardXAPoolDataSource:create");
	}
	/*
	 * Commented out by thom -- no need to do this as this is managed
	 * separately
	 */
	/*
	try {
	    xads.setLogWriter(pool.getLogWriter());
	} catch(java.sql.SQLException e) {
	    log("setLogWriter error "+e);
	}
	*/
    } // method initContext

    /**
     * Invoked when the application calls close()
     * on its representation of the connection
     */
    public void connectionClosed(ConnectionEvent event) {
	Object obj = event.getSource();
	log("StandardXAPoolDataSource:connectionClosed ");
	XAConnection xac = (XAConnection)obj;	// cast it into an xaConnection

	Transaction tx = null;
	try {
	    tx = transactionManager.getTransaction();
	    log("StandardXAPoolDataSource:connectionClosed get a transaction");
	} catch (NullPointerException n) {
	    // current is null: we are not in EJBServer.
	    log("StandardXAPoolDataSource:connectionClosed should not be used outside an EJBServer");
	} catch (SystemException e) {
	    log("StandardXAPoolDataSource:connectionClosed getTransaction failed:"+e);
	}

	// delist Resource if in transaction
	// We must keep the connection till commit or rollback
	if (tx != null) {
	    try {
		tx.delistResource(xac.getXAResource(), XAResource.TMSUCCESS);	// delist the xaResource
	    } catch (Exception e) {
		log("StandardXAPoolDataSource:connectionClosed Exception in connectionClosed:"+e);
	    }
	}
	pool.checkIn((PooledConnection)obj);	// return the connection to the pool

    } // method connectionClosed

    public GenerationObject create(String _user, String _password) throws SQLException{
        GenerationObject genObject;
        XAConnection xaCon =
            xads.getXAConnection(_user,_password);       // get the xa connection
        xaCon.addConnectionEventListener(this);         // add it to the event listener
        log("StandardXAPoolDataSource:create create a object for the pool");
        genObject = new GenerationObject(xaCon, pool.getGeneration(), _user, _password);

        return genObject;
    } // method create

    /**
     * Retrieves the Reference of this object. Used at binding time by JNDI
     * to build a reference on this object.
     *
     * @return  The non-null Reference of this object.
     * @exception  NamingException  If a naming exception was encountered while
     * retrieving the reference.
     */
    public Reference getReference() throws NamingException {
	log("StandardXAPoolDataSource:getReference return a reference of the object");
	Reference ref = new Reference(getClass().getName(), getClass().getName(), null);
	ref.add(new StringRefAddr("checkLevelObject", Integer.toString(getCheckLevelObject())));
	ref.add(new StringRefAddr("lifeTime", Long.toString(getLifeTime())));
	ref.add(new StringRefAddr("jdbcTestStmt", getJdbcTestStmt()));
	ref.add(new StringRefAddr("maxSize", Integer.toString(getMaxSize())));
	ref.add(new StringRefAddr("minSize", Integer.toString(getMinSize())));
	ref.add(new StringRefAddr("dataSourceName", getDataSourceName()));

        return ref;
    }

    /** Getter for property dataSource.
     * @return Value of property dataSource.
     */
    public XADataSource getDataSource() {
        return xads;
    }

    /** Setter for property dataSource.
     * @param dataSource New value of property dataSource.
     */
    public void setDataSource(XADataSource dataSource) {
        this.xads = dataSource;
    }


} // class StandardXAPoolDataSource
