/*
 * 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_tm;

import java.rmi.RemoteException;
import java.rmi.NoSuchObjectException;
import java.rmi.ServerException;
import java.util.List;
import java.util.ArrayList;
import javax.transaction.Status;
import javax.transaction.TransactionRolledbackException;
import javax.transaction.xa.Xid;
import org.objectweb.common.RemoteObject;
import org.objectweb.jonas_timer.TimerEvent;
import org.objectweb.jonas_timer.TimerEventListener;
import org.objectweb.jonas_timer.TimerManager;

/**
 * Implementation of the object that represents a transaction.
 * This remote object has been created by a TransactionFactory.
 *
 * @see	   org.objectweb.jonas_tm.TransactionFactory
 * @author Philippe Durieux
 * Contributor(s):
 * 02/01/15 Dean Jennings - ArrayList for resourceList and synchronizationList
 */
public class ControlImpl extends RemoteObject 
    implements Control, Coordinator, RecoveryCoordinator, Terminator, Resource, TimerEventListener {
	
    // ===============================================================
    // Private data
    // ===============================================================
    /**
     * @serial
     */
    private List resourceList = new ArrayList();
    /**
     * @serial
     */
    private List synchronizationList = new ArrayList();
    /**
     * @serial
     */
    private int mystatus = Status.STATUS_UNKNOWN;
    /**
     * @serial
     */
    private boolean hasSupCoord = false;
    /**
     * @serial
     */
    private int mytimeout;
    /**
     * @serial
     */
    private TimerEvent mytimer = null;
    /**
     * @serial
     */
    private Xid xid;
    /**
     * @serial
     */
    private Log mylog;

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

    /**
     * Constructor for create or recreate
     *
     * @param timeout		Timeout in seconds for this transaction
     * @param x	     	Xid allocated to this transaction
     * @param coord		Superior coordinator (null if create); must be a
     * org.objectweb.jonas_tm.Coordinator (in case of a JOnAS supcoordinator) or a
     * org.omg.CosTransactions.Coordinator (in case of a CORBA supcoordinator, with the
     * DAVID orb only).
     */
    ControlImpl(int timeout, Xid x, Object supco) throws RemoteException {
		
	TraceTm.debugTm("JTM: ControlImpl constructor");

	// init object state
	mystatus = Status.STATUS_ACTIVE;
	xid = x;
	hasSupCoord = (supco != null);

	// XXX In case of sub-coordinator, should we arm a timer for rollback ???
	mytimeout = timeout;
	mytimer = TimerManager.getInstance().addTimer(this, timeout, (Object) new Integer(1), false);

	// If sub-coord, register resource
	if (supco != null) {
	    try {
		if (supco instanceof Coordinator)
		    ((Coordinator) supco).register_resource(this);
		else
		    // register the control as a CORBA resource in the remote CORBA Coordinator (we use a wraper)
		    //((org.omg.CosTransactions.Coordinator) supco).register_resource(new ControlResourceImpl(this));
		    throw new RemoteException("Not Implemented");
	    } catch (Exception e) {
		TraceTm.error("JTM: Cannot register sub-coordinator:\n", e);
	    }
	}
    }

    // ---------------------------------------------------------------
    // Control Interface 
    // ---------------------------------------------------------------

    /**
     * Gets the Terminator object for this transaction
     *
     * @return		Terminator for this transaction
     */
    public Terminator get_terminator() throws RemoteException {
	return this;
    }
	
    /**
     * Gets the Coordinator object for this transaction
     *
     * @return		Coordinator for this transaction
     */
    public Coordinator get_coordinator() throws RemoteException {
	return this;
    }
	

    // ---------------------------------------------------------------
    // Coordinator Interface 
    // ---------------------------------------------------------------

    /**
     * Gets the current status of this transaction
     *
     * @return		current transaction status
     */
    public int get_status() throws RemoteException {
	TraceTm.debugTm("JTM: get_status");
	return mystatus;
    }

    /**
     * Tests if the given coordinator represents this transaction
     *
     * @param Coordinator	Coordinator to be compared to this transaction
     * @return			true if it is the same transaction
     */
    public boolean is_same_transaction(Coordinator tc) throws RemoteException {

	TraceTm.debugTm("JTM: is_same_transaction");

	String other = null;

	try {
	    other = tc.get_transaction_name();
	} catch (Exception e) {
	    TraceTm.error("JTM: is_same_transaction raised exception:\n", e);
	    return false;
	}
	return other.equals(get_transaction_name());
    }

    /**
     * Registers a Resource object for this transaction
     *
     * @param res		Resource to be registered
     * @return			RecoveryCoordinator used for replay_completion
     */
    public synchronized RecoveryCoordinator register_resource(Resource res)  
	throws RemoteException {

	switch (mystatus) {
			
	case Status.STATUS_ACTIVE:
	    // Normal case
	    resourceList.add(res);
	    TraceTm.debugTm("JTM: Resource registered");
	    break;
			
	case Status.STATUS_MARKED_ROLLBACK:
	case Status.STATUS_ROLLEDBACK:
	case Status.STATUS_ROLLING_BACK:
	    TraceTm.error("JTM: register_resource: Transaction Rolled back");
	    throw new TransactionRolledbackException();
			
	default:
	    TraceTm.error("JTM: register_resource: Transaction Inactive");
	    throw new InactiveException();
	}
	return this;
    }
	
    /**
     * Registers a Synchronization object for this transaction
     *
     * @param sync		RemoteSynchro to be registered
     */
    public synchronized void register_synchronization(RemoteSynchro sync) 
	throws RemoteException {
		
	switch (mystatus) {
			
	case Status.STATUS_ACTIVE:
	    // Normal case
	    synchronizationList.add(sync);
	    TraceTm.debugTm("JTM: RemoteSynchro registered");
	    break;
			
	case Status.STATUS_MARKED_ROLLBACK:
	case Status.STATUS_ROLLEDBACK:
	case Status.STATUS_ROLLING_BACK:
	    TraceTm.error("JTM: register_synchronization: Transaction Rolled back");
	    throw new TransactionRolledbackException();
			
	default:
	    TraceTm.error("JTM: register_synchronization: Transaction Inactive");
	    throw new InactiveException();
	}
    }

    /**
     * Asks to rollback the transaction
     *
     */
    public synchronized void rollback_only() throws RemoteException {
		
	switch (mystatus) {
			
	case Status.STATUS_MARKED_ROLLBACK:
	    // nothing to do: Already marked rolledback.
	    TraceTm.error("JTM: rollback_only: Already marked rolledback");
	    break;
			
	case Status.STATUS_ACTIVE:
	    mystatus = Status.STATUS_MARKED_ROLLBACK;
	    TraceTm.debugTm("JTM: rollback_only: Marked rollback");
	    break;
			
	default:
	    // already prepared
	    TraceTm.error("JTM: rollback_only: Inactive");
	    throw new InactiveException();
	}
    }
	
    /**
     * Gets a String that represents the transaction name.
     * Only the Format Id and the Global Id are used to build it :
     * The Branch Qualifier is not used.
     *
     * @return			Transaction Name
     */
    public String get_transaction_name() throws RemoteException {
		
	TraceTm.debugTm("JTM: get_transaction_name");

	return xid.toString();
    }


    // ---------------------------------------------------------------
    // Terminator Interface 
    // ---------------------------------------------------------------

    /**
     * Commits this transaction
     *
     * @param report_heuristics want to report heuristics if any
     * 
     * @exception TransactionRolledbackException the transaction has been rolled back
     * @exception HeuristicMixed Resources have rolled back
     * @exception HeuristicHazard Resources may have rolled back
     */
    public synchronized void commit(boolean report_heuristics) throws RemoteException {
			
	TraceTm.debugTm("JTM: Terminator commit");

	// Stops the Timer.
	if (mytimer != null) {
	    mytimer.stop();
	}
	
	// Checks status
	switch (mystatus) {
			
	case Status.STATUS_ACTIVE:
	    // normal case
	    break;
			
	case Status.STATUS_COMMITTED:
	    TraceTm.error("JTM: commit: already done");
	    return;
			
	case Status.STATUS_ROLLEDBACK:
	    TraceTm.error("JTM: commit: already rolled back");
	    // We should be here in case of timeout expired.
	    // In this case, completed() was not called.
	    completed(true);
	    throw new TransactionRolledbackException();
			
	case Status.STATUS_MARKED_ROLLBACK:
	    // Synchronization objects
	    int errors = do_before_completion();
	    if (errors > 0) {
		TraceTm.verbose("JTM: before completion error at rollback");
	    }
	    do_rollback(report_heuristics);			
	    completed(true);
	    throw new TransactionRolledbackException();
			
	default:
	    TraceTm.error("JTM: commit: bad status");
	    throw new HeuristicMixed();
	}
	
	// Only one resource -> one phase commit.
	// If Resource raises an heuristic, forward it to the client.
	if (resourceList.size() == 1) {
	    TraceTm.debugTm("JTM: commit: 1 resource");
			
	    // Synchronization objects
	    int errors = do_before_completion();
	    if (errors > 0) {
		TraceTm.verbose("JTM: before completion error -> rollback");
		do_rollback(report_heuristics);			
		completed(true);
		throw new TransactionRolledbackException();
	    }
			
	    mystatus = Status.STATUS_COMMITTING;
	    try {
		Resource res = (Resource) resourceList.get(0);
		res.commit_one_phase();
		mystatus = Status.STATUS_COMMITTED;
	    } catch (TransactionRolledbackException e) {
		TraceTm.verbose("JTM: commit_one_phase = TransactionRolledbackException");
		mystatus = Status.STATUS_ROLLEDBACK;
	    } catch (HeuristicHazard e) {
		TraceTm.verbose("JTM: commit_one_phase = HeuristicException");
		mystatus = Status.STATUS_UNKNOWN;
	    } catch (NoSuchObjectException e) {
				// nothing done: can rollback transaction
		TraceTm.verbose("JTM: commit_one_phase = NoSuchObjectException");
		mystatus = Status.STATUS_ROLLEDBACK;
	    } catch (ServerException e) { // with RMI, may encapsulate TransactionRolledbackException
		TraceTm.verbose("JTM: commit_one_phase = ServerException: "+ e);
		mystatus = Status.STATUS_ROLLEDBACK;
	    } catch (Exception e) {
		TraceTm.error("JTM: commit_one_phase = Unexpected exception: ", e);
		mystatus = Status.STATUS_UNKNOWN;
	    }
			
	    // Synchronization objects
	    do_after_completion();
			
	    // Possibly report heuristics to the caller
	    switch (mystatus) {
	    case Status.STATUS_COMMITTED:			
		completed(true);
		break;
	    case Status.STATUS_ROLLEDBACK:			
		completed(true);
		throw new TransactionRolledbackException();
	    case Status.STATUS_UNKNOWN:			
		completed(false);
		if (report_heuristics) {
		    throw new HeuristicHazard();
		}
	    }
	    return;
	}
		
	// 2PC - phase 1
	int v = do_prepare(report_heuristics);
		
	// Depending on vote, commits or rollbacks transaction
	switch (v) {
	case Resource.VOTE_COMMIT:
	    TraceTm.debugTm("JTM: commit: committing Tx");
	    break;
	case Resource.VOTE_ROLLBACK:
	    TraceTm.debugTm("JTM: commit: rolling back Tx");
	    do_rollback(report_heuristics);
	    completed(true);
	    throw new TransactionRolledbackException();
	case Resource.VOTE_READONLY:
	    // No commit phase.
	    TraceTm.debugTm("JTM: commit: readonly resources");
	    mystatus = Status.STATUS_COMMITTED;
	    completed(true);
	    return;
	}
		
	// Prepare was OK: Decision to commit.
	mylog.flushLog(Log.DECISION_TO_COMMIT);

	// 2PC - phase 2
	if (do_commit(report_heuristics) == 0) {
	    completed(true);
	} else {
	    completed(false);
	}
    }
	
    // ---------------------------------------------------------------
    // Resource or Terminator Interface 
    // rollback() has the same signature in Terminator and Resource!
    // ---------------------------------------------------------------

    /**
     * Rolls back this transaction branch. Can be a sub-coordinator or
     * a normal coordinator.
     *
     */
    public synchronized void rollback()  throws RemoteException {

	TraceTm.debugTm("JTM: Terminator rollback");

	// Stops the Timer. 
	if (mytimer != null) {
	    mytimer.stop();
	}

	// Checks status
	switch (mystatus) {
			
	case Status.STATUS_ACTIVE:
	case Status.STATUS_MARKED_ROLLBACK:
	    // normal case
	    break;
			
	case Status.STATUS_ROLLEDBACK:
	    TraceTm.error("JTM: rollback: already rolled back");
	    return;
			
	default:
	    TraceTm.error("JTM: rollback: bad status");
	    completed(false);
	    throw new HeuristicMixed("rollback: bad status");
	}
	
	// Superior coordinator.
	if (!hasSupCoord) {
	    // Synchronization objects
	    int errors = do_before_completion();
	    if (errors > 0) {
		TraceTm.verbose("JTM: before completion error at rollback");
	    }
	}
	
	// rollback Tx
	try {
	    do_rollback(false);
	} catch (Exception e) {
	    TraceTm.error("JTM: rollback raised exception ", e);
	}
	completed(true);
    }


    // ---------------------------------------------------------------
    // Resource Interface (for sub-coordinators)
    // ---------------------------------------------------------------

    /**
     * Sub-coordinator has received prepare from its superior.
     * It must more or less do the same things that the phase 1 of the 2PC.
     *
     * @return		Vote : commit, roollback or read-only.
     */
    public synchronized int prepare() throws RemoteException {

	TraceTm.debugTm("JTM: sub-coordinator prepare");

	// Stops the Timer. 
	if (mytimer != null) {
	    mytimer.stop();
	}

	// Checks status
	switch (mystatus) {
			
	case Status.STATUS_ACTIVE:
	    // normal case
	    break;	

	case Status.STATUS_COMMITTED:
	    // Should not occur ?
	    TraceTm.error("JTm: transaction already commited");
	    return Resource.VOTE_COMMIT;
	
	case Status.STATUS_ROLLEDBACK:
	    // Should not occur ?
	    TraceTm.error("JTM: transaction already rolled back");
	    return Resource.VOTE_ROLLBACK;

	case Status.STATUS_MARKED_ROLLBACK:
	    do_rollback(false);
	    completed(true);
	    return Resource.VOTE_ROLLBACK;
				
	default:
	    // Don't know what to do here...
	    TraceTm.error("JTM: prepare: bad status");
	    completed(false);
	    return Resource.VOTE_ROLLBACK;
	}
			
	int ret = do_prepare(false);

	switch (ret) {
	case Resource.VOTE_COMMIT:
	    TraceTm.debugTm("JTM: prepare: vote commit");
	    break;
	case Resource.VOTE_ROLLBACK:
	    TraceTm.debugTm("JTM: prepare: vote rollback");
	    do_rollback(false);
	    completed(true);
	    return ret;
	case Resource.VOTE_READONLY:
	    TraceTm.debugTm("JTM: prepare: vote readonly");
	    mystatus = Status.STATUS_COMMITTED;
	    completed(true);
	    return ret;
	}

	// Flush log + recovery coordinator
	// TODO

	return ret;
    }

    /**
     * Sub-coordinator received commit from its superior.
     * It must more or less do the same things that the phase 2 of the 2PC.
     */
    public synchronized void commit() throws RemoteException {

	TraceTm.debugTm("JTM: sub-coordinator commit");


	// check status
	switch (mystatus) {
	case Status.STATUS_PREPARED:
	    // normal case
	    break;		
	default:
	    // Don't know what to do here...
	    TraceTm.error("JTM: commit: bad status");
	    completed(false);
	    return;
	}

	// send commit to resources
	if (do_commit(true) == 0) {
	    completed(true);
	} else {
	    completed(false);
	}
    }

    /**
     * Sub-coordinator received commit_one_phase from its superior.
     * It is more or less a Terminator.commit().
     */
    public void commit_one_phase() throws RemoteException {

	TraceTm.debugTm("JTM: sub-coordinator commit_one_phase");

	// Just call Terminator.commit
	commit(true);
    }

    /**
     * forget transaction
     *
     */
    public synchronized void forget() throws RemoteException {

	TraceTm.debugTm("JTM: sub-coordinator forget");

	completed(true);
    }

    // ---------------------------------------------------------------
    // RecoveryCoordinator Interface 
    // ---------------------------------------------------------------

    /**
     * Asks the status of this transaction, after recovery of a Resource
     *
     * @param res		Resource recovering
     */
    public int replay_completion(Resource res) 
	throws RemoteException {

	TraceTm.debugTm("JTM: replay_completion");
	return mystatus;
    }


    // ===============================================================
    // TimerEventListener implementation
    // ===============================================================

    /**
     * The transaction timeout has expired
     * Do not synchronize this method to avoid deadlocks!
     */
    public void timeoutExpired(Object arg) {
	int argvalue = ((Integer)arg).intValue();
	switch (argvalue) {
	case 1:
	    TraceTm.debugTm("JTM: timeout expired");
	    try {
		do_rollback(false);
	    } catch (Exception e) {
		TraceTm.error("JTM: rollback raised exception ", e);
	    }
	    break;
	case 2:
	    TraceTm.debugTm("JTM: removing ControlImpl");
	    explicit_destroy();
	    break;
	default:
	    TraceTm.error("JTM: timeoutExpired bad value="+argvalue);
	    break;
	}
    }

    // ===============================================================
    // Private methods
    // ===============================================================
	
    /**
     * timeout expired on this transaction.
     * This method is not private, because it's used by Timers
     */
    void ding() throws InactiveException {
    }

    /**
     * Phase 1 of the 2-Phase-Commit:
     * Sends prepare to each registered resource
     * This internal routine should be used either for prepare on sub-coordinator
     *  or for commit on Terminator.
     *
     * @return			global vote
     */
    private int do_prepare(boolean report_heuristics) {

	int errors = 0;
	int ret = Resource.VOTE_READONLY;

	// Synchronization objects
	errors = do_before_completion();
	if (errors > 0) {
	    TraceTm.debugTm("JTM: before_completion failed -> rollback");
	    return Resource.VOTE_ROLLBACK;
	}
			
	// No resource -> just forget transaction.
	if (resourceList.size() == 0) {
	    TraceTm.error("JTM: commit: no resource");
	    mystatus = Status.STATUS_COMMITTED;
	    do_after_completion();
	    completed(true);
	    return ret;
	}
	
	// Creates a log for that transaction, where we will add all the
	// resources that replied VOTE_COMMIT to prepare.
	// Do not flush the log on disk before decision to commit.
	mylog = new Log();
		  
	// Sends prepare to each resource.
	// In case of prepare on sub-coord. we may have only 1 resource.
	// In case of phase 1 of the 2PC, we have several resources, because
	// the case of 1 resource is treated with commit_one_phase (optimization)
	mystatus = Status.STATUS_PREPARING;
	for (int i = 0; i < resourceList.size(); i++) {
	    Resource res = (Resource) resourceList.get(i);
	    if (errors > 0) {
		// An error has occured: Send rollback to the Resource
		try {
		    TraceTm.debugTm("JTM: send rollback to resource");
		    res.rollback();
		} catch (Exception e) {
		    TraceTm.error("JTM: Cannot rollback resource: ", e);
		}
	    } else {
		// No error yet: Send prepare to the resource.
		try {
		    TraceTm.debugTm("JTM: send prepare to resource");
		    switch (res.prepare()) {
		    case Resource.VOTE_COMMIT:
			// Log resource
			mylog.addResource(res);
			ret = Resource.VOTE_COMMIT;
			break;
		    case Resource.VOTE_ROLLBACK:
			TraceTm.verbose("Resource replied rollback to prepare");
			ret = Resource.VOTE_ROLLBACK;
			errors++;
			break;
		    case Resource.VOTE_READONLY:
			TraceTm.debugTm("JTM: Resource replied rollback to prepare");
			break;
		    }
		} catch (HeuristicHazard e) { // Subcoordinator only
		    TraceTm.error("JTM: HeuristicHazard on prepare");
		    ret = Resource.VOTE_ROLLBACK;
		    errors++;
		} catch (HeuristicMixed e) { // Subcoordinator only
		    TraceTm.error("JTM: HeuristicMixed on prepare");
		    ret = Resource.VOTE_ROLLBACK;
		    errors++;
		} catch (Exception e) {
		    TraceTm.error("JTM: exception on prepare: ", e);
		    ret = Resource.VOTE_ROLLBACK;
		    errors++;
		}
	    }
	}
	if (ret == Resource.VOTE_READONLY) {
	    TraceTm.debugTm("JTM: All resources returned Readonly");
	    mystatus = Status.STATUS_COMMITTED;
	    // Synchronization objects
	    do_after_completion();
	}
	if (ret == Resource.VOTE_COMMIT) {
	    mystatus = Status.STATUS_PREPARED;
	}
	return ret;
    }

    /**
     * Phase 2 of the 2-Phase-Commit:
     * Sends commit to each registered resource
     * This internal routine should be used either for commit on sub-coordinator
     *  or for commit on Terminator.
     *
     * @return		0 if commit OK.
     */
    private int do_commit(boolean report_heuristics)
	throws TransactionRolledbackException, HeuristicMixed, HeuristicHazard {

	TraceTm.debugTm("JTM: Committing transaction");

	// First check that a log is initialized
	if (mylog == null) {
	    TraceTm.error("JTM: do_commit: no log");
	    return -1;
	}

	// Status Transaction = committing
	mystatus = Status.STATUS_COMMITTING;

	// Sends commit to each resource "prepared".
	int commitnb = 0;
	int heuristicnb = 0;
	int errors = 0;
	for (int i = 0; i < mylog.resourceLogged.size(); i++) {
	    ResourceInfo resinfo = (ResourceInfo) mylog.resourceLogged.elementAt(i);
	    if (resinfo.mystate != ResourceInfo.PREPARED) {
		TraceTm.verbose("do_commit: resource not prepared");
		continue;
	    }
	    // aborts transaction if error and no resource committed yet
	    if (commitnb == 0 && errors > 0) {
		try {
		    TraceTm.verbose("do_commit: Send rollback to resource");
		    resinfo.myres.rollback();
		    resinfo.mystate = ResourceInfo.ROLLEDBACK;
		} catch (HeuristicCommit e) {
		    TraceTm.error("JTM: do_commit: Heuristic commit");
		    resinfo.mystate = ResourceInfo.HEURISTIC_COMMIT;
		    commitnb++;
		    heuristicnb++;
		} catch (Exception e) {
		    TraceTm.error("JTM: do_commit: exception on rollback: ", e);
		}
		continue;
	    }
	    // commits resource
	    try {
		TraceTm.debugTm("JTM: do_commit: Send commit to resource");
		resinfo.myres.commit();
		resinfo.mystate = ResourceInfo.COMMITTED;
		commitnb++;
	    } catch (HeuristicRollback e) {
		TraceTm.error("JTM: do_commit: Heuristic Rollback");
		resinfo.mystate = ResourceInfo.HEURISTIC_ROLLBACK;
		errors++;
		if (commitnb > 0)
		    heuristicnb++;
	    } catch (HeuristicMixed e) {
		TraceTm.error("JTM: do_commit: Heuristic Mixed");
		resinfo.mystate = ResourceInfo.HEURISTIC_MIXED;
		errors++;
		commitnb++;
		heuristicnb++;
	    } catch (HeuristicHazard e) {
		TraceTm.error("JTM: do_commit: Heuristic Hazard");
		resinfo.mystate = ResourceInfo.HEURISTIC_HAZARD;
		errors++;
		commitnb++;
		heuristicnb++;
	    } catch (NotPreparedException e) {
		TraceTm.error("JTM: do_commit: Resource Not Prepared");
		resinfo.mystate = ResourceInfo.REGISTERED;
		errors++;
	    } catch (NoSuchObjectException e) {
		TraceTm.error("JTM: do_commit: invalid objref - assume committed");
		resinfo.mystate = ResourceInfo.COMMITTED;
		commitnb++;
	    } catch (Exception e) {
		TraceTm.error("JTM: do_commit: exception on commit: ", e);
		errors++;
	    }
	}
	if (errors == 0) {
	    // Everything's fine.
	    TraceTm.debugTm("JTM: do_commit: transaction committed");
	    mystatus = Status.STATUS_COMMITTED;
	    mylog.forgetLog();
	    // Synchronization Objects
	    do_after_completion();
	    return 0;
	}
	if (heuristicnb == 0) {
	    // Transaction has been eventually rolled back
	    TraceTm.verbose("do_commit: transaction rolled back");
	    mystatus = Status.STATUS_ROLLEDBACK;
	    mylog.forgetLog();
	    // Synchronization Objects
	    do_after_completion();
	    throw new TransactionRolledbackException();
	}
	// Heuristics must be logged
	TraceTm.verbose("do_commit: Heuristics must be logged");
	mystatus = Status.STATUS_UNKNOWN;
	mylog.updateLog();
	// Synchronization Objects
	do_after_completion();
	if (report_heuristics) {
	    throw new HeuristicHazard();
	}
	return -1;
    }

    /**
     * Rollbacks the transaction
     * This internal routine should be used either for rollback on sub-coordinator
     * or for commit on Terminator when prepare returned a vote RollBack.
     */
    private void do_rollback(boolean report_heuristics) throws HeuristicMixed {

	TraceTm.debugTm("JTM: Rolling back transaction");
	
	mystatus = Status.STATUS_ROLLEDBACK;

	// Rollback resources
	int commitnb = 0;
	for (int i = 0; i < resourceList.size(); i++) {
	    Resource res = (Resource) resourceList.get(i);
	    try {
		TraceTm.debugTm("JTM: Send rollback to Resource");
		res.rollback();
	    } catch (HeuristicCommit e) {
		TraceTm.error("JTM: Rollback raised HeuristicCommit");
		commitnb++;
	    } catch (Exception e) {
		TraceTm.error("JTM: Cannot rollback resource: ", e);
	    }
	}

	// Synchronization objects
	do_after_completion();

	if (commitnb > 0 && report_heuristics) {
	    // May be should throw HeuristicCommit instead
	    throw new HeuristicMixed();
	}
    }

    /**
     * Destroys transaction object.
     */
    private void explicit_destroy() {
	// unexportObject explicitly: useful for Jeremie.
	try {
	    unexportObject(this, true);
	} catch (Exception e) {
	}
    }

    /**
     * Delay destroy of the ControlImpl object.
     * We must keep alive this object a little while for recovery reasons, 
     * and also in case of Jeremie because we may propagate a reference to 
     * this object inside the propagation context.
     */
    private void completed(boolean removeit) {

	if (mytimer != null) {
	    if (removeit) {
		// Convert the timer to make a delayed removal of this object
		mytimer.change(60, (Object) new Integer(2));
	    } else {
		// In case of heuristic, must keep ControlImpl.
		// No more timer is needed.
		mytimer.unset();
		mytimer = null;
	    }
	}
    }

    /**
     * Sends before_completion to the registered synchronizations
     *
     * @return		0 if no error
     */
    private int do_before_completion() {
	int errors = 0;

	TraceTm.debugTm("JTM: do_before_completion");
	if (TraceTm.isDebugTm()) 
	    TraceTm.debugTm("JTM: synchronizationList.size()="+synchronizationList.size());
	for (int i = 0; i < synchronizationList.size(); i++) {

	    RemoteSynchro sync = (RemoteSynchro) synchronizationList.get(i);
	    try {
		sync.before_completion(this);
	    } catch (Exception e) {
		TraceTm.error("JTM: before_completion raised exception ", e);
		errors++;
	    }
	}
	return errors;
    }

    /**
     * Sends after_completion to the registered synchronizations
     */
    private void do_after_completion() {

	if (TraceTm.isDebugTm()) {
	    TraceTm.debugTm("JTM: do_after_completion "+mystatus);
	    TraceTm.debugTm("JTM: synchronizationList.size()="+synchronizationList.size());
	}	
	for (int i = 0; i < synchronizationList.size(); i++) {
	    RemoteSynchro sync = (RemoteSynchro) synchronizationList.get(i);
	    try {
		sync.after_completion(this, mystatus);
	    } catch (Exception e) {
		TraceTm.error("JTM: after_completion raised exception ", e);
	    }
	}
    }

}
