/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. 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.html
 * or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [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.
 */

/*
 * Copyright 2004-2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

package com.sun.jts.jta;

import java.util.*;
import org.omg.CosTransactions.*;
import javax.transaction.*;
import javax.transaction.xa.*;

import com.sun.jts.CosTransactions.Configuration;
import com.sun.jts.CosTransactions.ControlImpl;
import com.sun.jts.CosTransactions.TopCoordinator;
import com.sun.jts.CosTransactions.GlobalTID;

import javax.transaction.Synchronization;
import javax.transaction.SystemException;

import org.omg.CosTransactions.Status;
import org.omg.CORBA.TRANSACTION_ROLLEDBACK;
import org.omg.CORBA.INVALID_TRANSACTION;
import org.omg.CORBA.NO_PERMISSION;
import java.util.logging.Logger;
import java.util.logging.Level;
import com.sun.logging.LogDomains;
/**
 * An implementation of javax.transaction.Transaction using JTS
 * XXX TODO should catch all org.omg.CORBA.SystemException
 * and throw javax.transaction.SystemException
 *
 * @author Tony Ng
 */
public class TransactionImpl implements Transaction {

    /**
     * OTS Control object for this transaction
     */
    private Control control;

    private GlobalTID gtid;

    private TransactionState tranState = null;

    private static TransactionManagerImpl tm = TransactionManagerImpl.getTransactionManagerImpl();
	/*
		Logger to log transaction messages
	*/  
    static Logger _logger = LogDomains.getLogger(LogDomains.TRANSACTION_LOGGER);

    // START 4662745
    private long startTime;
    // END 4662745

    public TransactionImpl(Control control, GlobalTID gtid)
        throws SystemException {

        this.control = control;
        this.gtid = gtid;
        startTime=System.currentTimeMillis();
    }

    /**
     * return the OTS Control object for this transaction.
     */
    Control getControl() {
        return control;
    }

    //-----------------------------------------------------------------
    // The following implements javax.transaction.Trasaction interface
    //-----------------------------------------------------------------

    /**
     * Complete the transaction represented by this Transaction object
     */
    public void commit() throws HeuristicMixedException,
        RollbackException, HeuristicRollbackException, IllegalStateException,
        SecurityException, SystemException
    {
        try {
            if (Configuration.isLocalFactory()) {
              ((ControlImpl) control).get_localTerminator().commit(true);
            } else {
                control.get_terminator().commit(true);
            }
        } catch (TRANSACTION_ROLLEDBACK ex) {
            throw new RollbackException();
        } catch (INVALID_TRANSACTION ex) {
            throw new IllegalStateException();
        } catch (HeuristicMixed ex) {
            throw new HeuristicMixedException();
        } catch (HeuristicHazard ex) {
            throw new HeuristicMixedException();
        } catch (NO_PERMISSION ex) {
            throw new SecurityException();
        } catch (Unavailable ex) {
            SystemException sException = new SystemException();
            sException.initCause(ex);
            throw sException;
        } catch (Exception ex) {
            SystemException sException = new SystemException();
            sException.initCause(ex);
            throw sException;
        }

    }


    /**
     * Rollback the transaction represented by this Transaction object.
     */
    public void rollback()
        throws IllegalStateException, SystemException {

        try {
            if (Configuration.isLocalFactory()) {
              ((ControlImpl) control).get_localTerminator().rollback();
            } else {
              control.get_terminator().rollback();
            }
        } catch (INVALID_TRANSACTION ex) {
            throw new IllegalStateException();
        } catch (TRANSACTION_ROLLEDBACK ex) {
            throw new IllegalStateException();
        } catch (Unavailable ex) {
            SystemException sException = new SystemException();
            sException.initCause(ex);
            throw sException;
        } catch (Exception ex) {
            SystemException sException = new SystemException();
            sException.initCause(ex);
            throw sException;
        }
    }


    /**
     * enlist a resource with the current transaction
     * If a transaction is marked as rollback, enlistment will
     * succeed if the resource has been enlisted before. Otherwise,
     * enlistment will fail. In both cases, a RollbackException will
     * be thrown.
     */
    public boolean enlistResource(XAResource res)
        throws RollbackException, IllegalStateException,
            SystemException {

        int status = getStatus();
        if (status != javax.transaction.Status.STATUS_ACTIVE &&
            status != javax.transaction.Status.STATUS_MARKED_ROLLBACK) {
            throw new IllegalStateException();
        }
	//START IASRI 4706150
	try{
	    if(tm.getXAResourceTimeOut() > 0)
	        res.setTransactionTimeout(tm.getXAResourceTimeOut());
	}catch(Exception ex){
	    _logger.log(Level.WARNING,"jts.error_while_setting_xares_txn_timeout",ex);
	}
	//END IASRI 4706150
        try {
            if (tranState == null) {
                tranState = new TransactionState(gtid, this);
                // Synchronization sync = new SynchronizationListener(tranState);
                // registerSynchronization(sync);
            }
            tranState.startAssociation(res, control, status);
            if (status == javax.transaction.Status.STATUS_MARKED_ROLLBACK) {
                throw new RollbackException();
            }
            return true;
        } catch (XAException ex) {
			_logger.log(Level.WARNING,"jts.resource_outside_transaction",ex);
            if (ex.errorCode == XAException.XAER_OUTSIDE) {
                throw new IllegalStateException();
            }
            // XXX FIXME should throw rollback exception on XARB_*
            // for now just throw SystemException
            throw new SystemException();
        }

    }

    public boolean delistResource(XAResource res, int flags)
        throws IllegalStateException, SystemException {

        /*
        int status = getStatus();
        if (status != javax.transaction.Status.STATUS_ACTIVE &&
            status != javax.transaction.Status.STATUS_MARKED_ROLLBACK) {
            throw new IllegalStateException();
        }
        */

        try {
            // TransactionState tranState = tm.getTransactionState(gtid, this);
            if (tranState == null) {
                // transaction has completed
                throw new IllegalStateException();
            }
            if (tranState.containsXAResource(res) == false) {
                throw new IllegalStateException();
            }
            tranState.endAssociation(res, flags);
            if ((flags & XAResource.TMFAIL) != 0) {
                // set transaction to rollback only if TMFAIL used
                setRollbackOnly();
            }
            return true;
        } catch (XAException ex) {
            setRollbackOnly();
            throw new SystemException();
        }
    }

    public int getStatus() throws SystemException {
        // XXX what should getStatus return on exception?
        Status  status;
        try {
            if (Configuration.isLocalFactory()) {
              status = ((ControlImpl) control).get_localCoordinator().get_status();
            } else {
              status = control.get_coordinator().get_status();
            }
            return TransactionManagerImpl.mapStatus(status);
        } catch (TRANSACTION_ROLLEDBACK ex) {
            return javax.transaction.Status.STATUS_NO_TRANSACTION;
        } catch (INVALID_TRANSACTION ex) {
            return javax.transaction.Status.STATUS_NO_TRANSACTION;
        } catch (Unavailable ex) {
            return javax.transaction.Status.STATUS_NO_TRANSACTION;
        } catch (Exception ex) {
			_logger.log(Level.WARNING,"jts.unexpected_error_in_getstatus",ex);
            throw new SystemException();
        }
    }

    public boolean equals(Object object) {
        if ((object instanceof TransactionImpl) == false) {
            return false;
        } else if (object == this) {
            return true;
        } else {
            return gtid.equals(((TransactionImpl) object).gtid);
        }
    }

    public int hashCode() {
        return gtid.hashCode();
    }

    public void registerSynchronization(Synchronization sync)
        throws RollbackException, IllegalStateException,
        SystemException {

        int status = getStatus();
        if (status == javax.transaction.Status.STATUS_MARKED_ROLLBACK) {
            throw new RollbackException();
        }
        if (status != javax.transaction.Status.STATUS_ACTIVE) {
            throw new IllegalStateException();
        }
        if (tranState == null) {
            tranState = new TransactionState(gtid, this);
        }
        tranState.registerSynchronization(sync, control, false);
    }

    public void registerInterposedSynchronization(Synchronization sync)
        throws RollbackException, IllegalStateException,
        SystemException {

        int status = getStatus();
        if (status == javax.transaction.Status.STATUS_MARKED_ROLLBACK) {
            throw new RollbackException();
        }
        if (status != javax.transaction.Status.STATUS_ACTIVE) {
            throw new IllegalStateException();
        }
        if (tranState == null) {
            tranState = new TransactionState(gtid, this);
        }
        tranState.registerSynchronization(sync, control, true);
    }

    public void setRollbackOnly()
        throws IllegalStateException, SystemException {

        int status = getStatus();
        if (status != javax.transaction.Status.STATUS_MARKED_ROLLBACK &&
            status != javax.transaction.Status.STATUS_ACTIVE) {
            throw new IllegalStateException();
        }
        try {
            if (Configuration.isLocalFactory()) {
              ((ControlImpl) control).get_localCoordinator().rollback_only();
            } else {
              control.get_coordinator().rollback_only();
            }
        } catch (Unavailable ex) {
            throw new SystemException();
        } catch (Inactive ex) {
            throw new IllegalStateException();
        } catch (Exception ex) {
            throw new SystemException();
        }
    }


    /**
     * a simple assertion mechanism that print stack trace
     * if assertion fails
     */
    static private void assert_prejdk14(boolean value) {
        if (!value) {
            Exception e = new Exception();
			_logger.log(Level.WARNING,"jts.assert",e);
        }
    }
    // START IASRI 4662745
    /*
     * This method is used for the Admin Framework displaying
     * of Transactions Ids
     */
    public String getTransactionId(){
        return gtid.toString();
    }

    /*
     * This method returns the time this transaction was started
     */
    public long getStartTime(){
        return startTime;
    }
    // END IASRI 4662745


/**
class SynchronizationListener implements Synchronization {

    private GlobalTID gtid;
    private TransactionState tranState;

    SynchronizationListener(TransactionState tranState) {
        this.tranState = tranState;
    }

    public void afterCompletion(int status) {
        // tranState.cleanupTransactionStateMapping();
    }

    public void beforeCompletion() {
        try {
	    tranState.beforeCompletion();
	}catch(XAException xaex){
	    _logger.log(Level.WARNING,"jts.unexpected_xa_error_in_beforecompletion", new java.lang.Object[] {new Integer(xaex.errorCode), xaex.getMessage()});
	    _logger.log(Level.WARNING,"",xaex);
        } catch (Exception ex) {
	    _logger.log(Level.WARNING,"jts.unexpected_error_in_beforecompletion",ex);
        }
    }
}
**/

}
