/*
 * 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.
 */

//----------------------------------------------------------------------------
//
// Module:      TransactionState.java
//
// Description: Transaction state manager.
//
// Product:     com.sun.jts.CosTransactions
//
// Author:      Simon Holdsworth
//
// Date:        March, 1997
//
// Copyright (c):   1995-1997 IBM Corp.
//
//   The source code for this program is not published or otherwise divested
//   of its trade secrets, irrespective of what has been deposited with the
//   U.S. Copyright Office.
//
//   This software contains confidential and proprietary information of
//   IBM Corp.
//----------------------------------------------------------------------------

package com.sun.jts.CosTransactions;

import java.io.*;
import java.util.*;
import com.sun.jts.trace.*;
import org.omg.CosTransactions.*;

import com.sun.jts.utils.RecoveryHooks.FailureInducer;

import java.util.logging.Logger;
import java.util.logging.Level;
import com.sun.logging.LogDomains;
import com.sun.jts.utils.LogFormatter;
/**
 * The TransactionState interface provides operations that maintain the
 * relative commitment state of a Coordinator object, and is responsible for
 * allocating new global and local transaction identifiers. This class is
 * contained in the TopCoordinator and SubCoordinator classes.
 *
 * @version 0.7
 *
 * @author Simon Holdsworth, IBM Corporation
 *
 * @see
 */

//----------------------------------------------------------------------------
// CHANGE HISTORY
//
// Version By     Change Description
//   0.1   SAJH   Initial implementation.
//   0.2   SAJH   GlobalTID interface changes.
//   0.3   SAJH   Repository synchronization.
//   0.4   SAJH   Conversion to new bindings.
//   0.5   SAJH   Modified log record writing.
//   0.6   SAJH   Allow preparing->rolled back for prepare heuristics.
//   0.7   SAJH   Renamed repository.
//   0.8   GDH    Added  commit_one_phase related states
//----------------------------------------------------------------------------

class TransactionState {

    /**
     * A state value indicating that the transaction has not yet been started.
     */
    final static int STATE_NONE = 0;

    /**
     * A state value indicating that the transaction has been started,
     * and not yet completed.
     */
    final static int STATE_ACTIVE = 1;

    /**
     * A state value indicating that the transaction is in the process of being
     * prepared.
     */
    final static int STATE_PREPARING = 2;

    /**
     * A state value indicating that the transaction has been
     * successfully prepared, but commit or rollback has not yet started.
     */
    final static int STATE_PREPARED_SUCCESS = 3;

    /**
     * A state value indicating that the transaction has failed to be prepared,
     * but rollback has not yet started.
     */
    final static int STATE_PREPARED_FAIL = 4;

    /**
     * A state value indicating that the transaction has been prepared
     * and is read-only, but rollback has not yet started.
     */
    final static int STATE_PREPARED_READONLY = 5;

    /**
     * A state value indicating that the transaction is in the process of being
     * committed.
     */
    final static int STATE_COMMITTING = 6;

    /**
     * A state value indicating that the transaction has been committed.
     */
    final static int STATE_COMMITTED = 7;

    /**
     * A state value indicating that the transaction is in the process of being
     * rolled back.
     */
    final static int STATE_ROLLING_BACK = 8;

    /**
     * A state value indicating that the transaction has been rolled back.
     */
    final static int STATE_ROLLED_BACK = 9;

    // GDH: New COP States
    /**
     * A state value indicating that the transaction is being commited
     * to a downstream resource using one phase commit
     */
    final static int STATE_COMMITTING_ONE_PHASE = 10;


    /**
     * A state value indicating that the transaction has been successfully
     * commited using commit one phase
     */
    final static int STATE_COMMITTED_ONE_PHASE_OK = 11;


    /**
     * A state value indicating that the transaction has been rolled back
     * after a commit one phase flow.
     */
    final static int STATE_COMMIT_ONE_PHASE_ROLLED_BACK = 12;

    /**
     * A state value indicating that the transaction has heuristic
     * hazard after a commit one phase flow.
     */
    final static int STATE_COMMIT_ONE_PHASE_HEURISTIC_HAZARD = 13;

    /**
     * A state value indicating that the resources are heuristic mixed
     * after a commit one phase flow.
     */
    final static int STATE_COMMIT_ONE_PHASE_HEURISTIC_MIXED = 14;

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

    static RWLock freezeLock = new RWLock();
    GlobalTID globalTID = null;
    Long      localTID = null;
    int       state =  STATE_NONE;

    boolean        subordinate = false;
    CoordinatorLog logRecord = null;
    Object         logSection = null;

    //static long epochNumber    = new Date().getTime();
    static long sequenceNumber = 1;

    static boolean inDoubt = false;

    //get server name only once
    //static String serverName = Configuration.getServerName();

    //half built cached TID - used as template for any globalTIDs generations
    static byte [] TIDTemplate=null;

    // GDH State table added to for one phase commit of single resource
    // could have extended further to split out:
    //    heuristic rollback
    //    rolling back huristic hazard
    //    rolling back heuristic mixed
    // but these are factored into rolled back and rolling back respectively


    // GDH: Added this column onwards
    //
    final static boolean[][] validStateChange = {
    /*  from      to         none   actve  ping    pds    pdf    pdr   cing    cd    ring    rd    c1p    1p_ok  1p_rb  1p_hh  1p_hm  */
    /*  none            */ { false, true,  false, false, false, false, false, false, true,  false, false, false, false, false, false  },
    /*  active          */ { false, false, true,  false, false, false, false, false, true,  false, true,  false, false, false, false  },
    /*  preparing       */ { false, false, false, true,  true,  true,  false, false, true,  true,  false, false, false, false, false  },
    /*  prepared_success*/ { false, false, false, false, false, false, true,  false, true,  false, false, false, false, false, false  },
    /*  prepared_fail   */ { false, false, false, false, false, false, false, false, true,  false, false, false, false, false, false  },
    /*  prepared_ro     */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false  },
    /*  committing      */ { false, false, false, false, false, false, true,  true,  false, false, false, false, false, false, false  },
    /*  committed       */ { true,  false, false, false, false, false, false, false, false, false, false, false, false, false, false  },
    /*  rolling back    */ { false, false, false, false, false, false, false, false, true,  true,  false, false, false, false, false  },
    /*  rolled back     */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false  },
    // GDH: Added this row onwards
    /* commit_one_phas  */ { false, false, false, false , false, false, false, false, true,  false, true,  true,  true,  true,  true   },
    /* cmt_one_phse_ok  */ { false, false, false, false, false, false, false, true,  false, false, false, false, false, false, false  },
    /* cmt_one_phse_rb  */ { false, false, false, false, false, false, false, false, false, true,  false, false, false, false, false  },
    /* cmt_one_phse_hh  */ { false, false, false, false, false, false, false, true,  false, false, false, false, false, false, false  },
    /* cmt_one_phse_hm  */ { false, false, false, false, false, false, false, true,  false, false, false, false, false, false, false  }};

    // XID format identifier.

    final static int XID_FORMAT_ID = ('J'<<16) + ('T'<<8) + 'S';

    private final static String LOG_SECTION_NAME = "TS"/*#Frozen*/;

    /**
     * Default TransactionState constructor.
     *
     * @param
     *
     * @return
     *
     * @see
     */
    TransactionState() {}

    /**
     * This constructor is used for a root Coordinator.
     * It allocates a global identifier for the transaction, and a local
     * identifier for the local Transaction Service. The global ID is used to
     * propagate to other processes to identify the transaction globally. The
     * local ID is used for local comparisons. The CoordinatorLog object is
     * used to recover the TransactionState at restart. Use of this operation
     * indicates that the transaction is a top-level transaction. A section is
     * created in the given CoordinatorLog object to maintain the transaction
     * state persistently.
     *
     * @param log  The CoordinatorLog object for the transaction.
     *
     * @return
     *
     * @see
     */
    TransactionState(CoordinatorLog log) {

        // Get the sequence number for the transaction identifier.

        localTID = new Long(getSequenceNumber());

        // Get the epoch number for this execution of the server.

        //int epoch = getEpochNumber();

        // Create a global identifier.

        globalTID = new GlobalTID(XID_FORMAT_ID, 0,
                                  generateTID(localTID.longValue()));

        // Store the values in instance variables.
        // This is a top-level transaction.

        state = STATE_NONE;
        subordinate = false;

        // Set the CoordinatorLog id to the InternalTid.

        if (log != null) {
            logRecord = log;
            logRecord.setLocalTID(localTID);

            // Create a section in the CoordinatorLog for TransactionState

            logSection = logRecord.createSection(LOG_SECTION_NAME);
        }
    }

    /**
     * This constructor is used for a subordinate Coordinator.
     * It allocates a local identifier for a subordinate transaction that is
     * represented by the given global identifier, and returns it.
     * The presence of a CoordinatorLog object indicates
     * whether the transaction is a top-level
     * transaction. A section is created in the given CoordinatorLog object to
     * maintain the transaction state persistently.
     *
     * @param globalTID  The global identifier for the transaction.
     * @param log        The CoordinatorLog for a top-level transaction.
     *
     * @return
     *
     * @see
     */
    TransactionState(GlobalTID globalTID, CoordinatorLog log) {

        // Get the sequence number for the transaction identifier.

        this.globalTID = globalTID;
        localTID = new Long(getSequenceNumber());

        // Store the values in instance variables.

        state = STATE_NONE;
        subordinate = true;

        // Create a section in the CoordinatorLog.

        if (log != null) {
            logRecord = log;

            // Set the CoordinatorLog id to the InternalTid.

            logRecord.setLocalTID(localTID);

            // Create a section in the CoordinatorLog for TransactionState

            logSection = logRecord.createSection(LOG_SECTION_NAME);
        }
    }

    /**
     * This constructor is used for a root nested transaction.
     * It allocates a global identifier and local identifier
     * for a child transaction based on the local
     * and global identifiers given for the parent, and returns
     * them.  The use of this operation indicates that the transaction is a
     * subtransaction.
     *
     * @param parentLocalTID   The parent's local identifier.
     * @param parentGlobalTID  The parent's global identifier.
     *
     * @return
     *
     * @see
     */
    TransactionState(Long parentLocalTID, GlobalTID parentGlobalTID) {

        // Get the sequence number for the transaction identifier.

        localTID = new Long(getSequenceNumber());

        // Get the epoch number for this execution of the server.

        //int epoch = getEpochNumber();

        // Create a global identifier.

        globalTID = new GlobalTID(XID_FORMAT_ID, 0,
                                  generateTID(localTID.longValue()));

        // Store the values in instance variables. This is a subtransaction.

        state = STATE_NONE;
        subordinate = false;
    }

    /**
     * Directs the TransactionState to recover its state
     * after a failure, based on the given CoordinatorLog object.
     * If the TransactionState has already been defined or
     * recovered, the operation returns the current state of the transaction.
     * If the state cannot be recovered, the operation returns none.
     * If the CoordinatorLog records information prior to a log record being
     * forced, this may result in recovery of an in-flight transaction. The
     * TransactionState returns active in this case.
     *
     * @param log  The CoordinatorLog for the transaction.
     *
     * @return  The current state of the transaction.
     *
     * @see
     */
    int reconstruct(CoordinatorLog log) {

        int result = STATE_NONE;

        // Get a section id in the CoordinatorLog for TransactionState

        logSection = log.createSection(LOG_SECTION_NAME);
        byte[][] logData = log.getData(logSection);

        // Go through the sequence to get the overall status

        int logState = 0;
        for (int i = 0; i < logData.length; i++) {
            if (logData[i].length > 1) {
                logState |= (((logData[i][0] & 255) << 8) +
                             (logData[i][1] & 255));
            } else {
                // If the log record data is invalid, then exit immediately.
				_logger.log(Level.SEVERE,"jts.invalid_log_record_data",
                        LOG_SECTION_NAME);
				String msg = LogFormatter.getLocalizedMessage(_logger,
							"jts.invalid_log_record_data",
							new java.lang.Object[] { LOG_SECTION_NAME });
 				throw  new org.omg.CORBA.INTERNAL(msg);
            }
        }

        // Set the state value returned from the reconstruct method

        if ((logState & (1 << STATE_ROLLED_BACK)) != 0)
            result = STATE_ROLLED_BACK;
        else if ((logState & (1 << STATE_COMMITTED)) != 0)
            result = STATE_COMMITTED;
        else if ((logState & (1 << STATE_COMMITTING)) != 0)
            result = STATE_COMMITTING;
        else if ((logState & (1 << STATE_ROLLING_BACK)) != 0)
            result = STATE_ROLLING_BACK;
        else if ((logState & (1 << STATE_PREPARED_READONLY)) != 0)
            result = STATE_PREPARED_READONLY;
        else if ((logState & (1 << STATE_PREPARED_FAIL)) != 0)
            result = STATE_PREPARED_FAIL;
        else if ((logState & (1 << STATE_PREPARED_SUCCESS)) != 0)
            result = STATE_PREPARED_SUCCESS;
        // GDH new states-->
        else if ((logState & (1 << STATE_COMMITTING_ONE_PHASE)) != 0)
            result = STATE_COMMITTING_ONE_PHASE;
        else if ((logState & (1 << STATE_COMMITTED_ONE_PHASE_OK)) != 0)
            result = STATE_COMMITTED_ONE_PHASE_OK;
        else if ((logState & (1 << STATE_COMMIT_ONE_PHASE_ROLLED_BACK)) != 0)
            result = STATE_COMMIT_ONE_PHASE_ROLLED_BACK;
        else if ((logState &
                 (1 << STATE_COMMIT_ONE_PHASE_HEURISTIC_HAZARD)) != 0)
            result = STATE_COMMIT_ONE_PHASE_HEURISTIC_HAZARD;
        else if ((logState &
                 (1 << STATE_COMMIT_ONE_PHASE_HEURISTIC_MIXED)) != 0)
            result = STATE_COMMIT_ONE_PHASE_HEURISTIC_MIXED;

        state = result;
        subordinate = false;
        logRecord = log;

        return result;
    }

    /**
     * Cleans up the state of the object.
     *
     * @param
     *
     * @return
     *
     * @see
     */
    /*
    public void finalize() {

        state = STATE_NONE;
        globalTID = null;
        localTID  = null;
        logRecord = null;
        logSection = null;
    }
    */

    /**
     * Sets the state to the given value and returns true.
     * If the state change is invalid, the state is not
     * changed and the operation returns false.  When a top-level
     * transaction has its state changed to prepared, the information
     * is stored in the CoordinatorLog object. When prepared_success, the log
     * record for the transaction is explicitly forced. Otherwise the
     * transaction will be treated as in-flight upon
     * recovery (i.e. will be rolled back). When
     * a subordinate transaction has its state changed to committing or
     * rolling_back, the state is added to the CoordinatorLog object, which is
     * then forced.
     *
     * @param newState  The new state of the transaction.
     *
     * @return  Indicates if the state change is possible.
     *
     * @see
     */
    boolean setState(int newState) {

        boolean result = false;

        // Check that the state change is valid

        if (validStateChange[state][newState]) {

            //Added code for counting and blocking.
            if(AdminUtil.bSampling)
            {
                switch ( newState )
                {
                    case STATE_PREPARED_SUCCESS :
                        AdminUtil.incrementPendingTransactionCount();
                        break ;
                    case STATE_PREPARED_READONLY :
                        AdminUtil.incrementPendingTransactionCount();
                        break ;
                    case STATE_COMMITTED : 
                        AdminUtil.incrementCommitedTransactionCount(); 
                        break ;
                    case STATE_ROLLED_BACK :
                        AdminUtil.incrementAbortedTransactionCount();
                        break ;
                    /*
		    case STATE_COMMITTED_ONE_PHASE_OK :
                        AdminUtil.incrementCommitedTransactionCount();
                        break ;
                    case STATE_COMMIT_ONE_PHASE_ROLLED_BACK :
                        AdminUtil.incrementCommitedTransactionCount();
                        break ;
		    */
		    case STATE_ROLLING_BACK :
                        AdminUtil.incrementUnpreparedAbortedTransactionCount();
                        break;
                }
            }


            //release the readlocks.
            switch ( state )
            {
                case STATE_PREPARING :
                case STATE_COMMITTING :

                case STATE_COMMITTING_ONE_PHASE : 
                    if(_logger.isLoggable(Level.FINEST)){
						String statestr=null;
						switch(newState ) {
						case STATE_PREPARING :
							statestr="PREPARING";
							break;
						case STATE_COMMITTING :
							statestr="COMMITTING";
							break;
						case STATE_COMMITTING_ONE_PHASE :
							statestr="COMMITTING_ONE_PHASE";
							break;
						default :
							statestr="Illegal state ";
							break;
						}
                        _logger.logp(Level.FINEST,"TransactionState","setState()",
								"Releasing read lock on freeze : state "+statestr);
                    } 
                    freezeLock.releaseReadLock();
                    if(_logger.isLoggable(Level.FINEST)){
						String statestr=null;
						_logger.logp(Level.FINEST,"TransactionState","setState()",
                         		"Released read lock on freeze");
					switch(newState ) {
						case STATE_PREPARING :
							statestr="PREPARING";
							break;
						case STATE_COMMITTING :
							statestr="COMMITTING";
							break;
						case STATE_COMMITTING_ONE_PHASE :
							statestr="COMMITTING_ONE_PHASE";
							break;
						default :
							statestr="Illegal state ";
							break;
						}
                        _logger.logp(Level.FINEST,"TransactionState","setState()",
								"Released read lock on freeze : state "+statestr);
                    } 
                    break;
            }

            //acquire read locks
            switch ( newState )
            {
                case STATE_PREPARING :
                case STATE_COMMITTING :
                //case STATE_ROLLING_BACK :
                case STATE_COMMITTING_ONE_PHASE :
                    if(_logger.isLoggable(Level.FINEST)){
						String statestr=null;
						switch(newState ) {
						case STATE_PREPARING :
							statestr="PREPARING";
							break;
						case STATE_COMMITTING :
							statestr="COMMITTING";
							break;
						case STATE_COMMITTING_ONE_PHASE :
							statestr="COMMITTING_ONE_PHASE";
							break;
						default :
							statestr="Illegal state ";
							break;
						}
                        _logger.logp(Level.FINEST,"TransactionState","setState()",
								"Acquiring read lock on freeze : state "+statestr);
                    }
                    freezeLock.acquireReadLock(); 
                    if(_logger.isLoggable(Level.FINEST)){
						String statestr=null;
						switch(newState ) {
						case STATE_PREPARING :
							statestr="PREPARING";
							break;
						case STATE_COMMITTING :
							statestr="COMMITTING";
							break;
						case STATE_COMMITTING_ONE_PHASE :
							statestr="COMMITTING_ONE_PHASE";
							break;
						default :
							statestr="Illegal state ";
							break;
						}
                        _logger.logp(Level.FINEST,"TransactionState","setState()",
								"Acquired read lock on freeze : state "+statestr);
                    }
                    break;
            }

            // RecoveryHook (for induced crashes and waits)  (Ram Jeyaraman)
            if (FailureInducer.isFailureInducerActive() &&
                    (!(Thread.currentThread().getName().
                        equals("JTS Resync Thread"/*#Frozen*/)))) {
                Integer failurePoint = null;
                switch (newState) {
                    case STATE_PREPARING :
                        failurePoint = FailureInducer.ACTIVE; break;
                    case STATE_PREPARED_SUCCESS :
                        failurePoint = FailureInducer.PREPARING; break;
                    case STATE_PREPARED_FAIL :
                        failurePoint = FailureInducer.PREPARING; break;
                    case STATE_PREPARED_READONLY :
                        failurePoint = FailureInducer.PREPARING; break;                        
                    case STATE_COMMITTING_ONE_PHASE :
                        failurePoint = FailureInducer.ACTIVE; break;
                    case STATE_COMMITTED_ONE_PHASE_OK :
                        failurePoint = FailureInducer.COMPLETING; break;
                    case STATE_COMMIT_ONE_PHASE_ROLLED_BACK :
                        failurePoint = FailureInducer.COMPLETING; break;
                    case STATE_COMMITTING :
                        failurePoint = FailureInducer.PREPARED; break;
                    case STATE_COMMITTED :
                        failurePoint = FailureInducer.COMPLETING; break;
                    case STATE_ROLLING_BACK :
                        if (state == STATE_PREPARED_SUCCESS) {
                            failurePoint = FailureInducer.PREPARED;
                        } else if (state == STATE_PREPARED_FAIL) {
                            failurePoint = FailureInducer.PREPARED;
                        } else if (state == STATE_PREPARED_READONLY) {
                            failurePoint = FailureInducer.PREPARED;
                        } else if (state == STATE_ACTIVE) {
                            failurePoint = FailureInducer.ACTIVE;
                        }
                        break;
                    case STATE_ROLLED_BACK :
                        failurePoint = FailureInducer.COMPLETING;
                }
                FailureInducer.waitForFailure(this.globalTID, failurePoint);
            }

            // Change state.  This is the point at which some
            // log records may be written

            state = newState;
            result = true;

            // Add state information to CoordinatorLog for various states.

            if (logRecord != null &&
                    (newState == STATE_PREPARED_SUCCESS ||
                     newState == STATE_PREPARED_FAIL ||
                     (newState == STATE_COMMITTING && subordinate) ||
                     (newState == STATE_ROLLING_BACK && subordinate) ||
                     newState == STATE_COMMITTED ||
                     newState == STATE_ROLLED_BACK ||
                     // GDH
                     // newState == STATE_COMMITTING_ONE_PHASE   ||
                     // newState == STATE_COMMITTED_ONE_PHASE_OK   ||
                     // newState == STATE_COMMIT_ONE_PHASE_ROLLED_BACK ||
                     newState == STATE_COMMIT_ONE_PHASE_HEURISTIC_HAZARD ||
                     newState == STATE_COMMIT_ONE_PHASE_HEURISTIC_MIXED
                     // GDH
                    )) {
                byte[] byteData = new byte[2];
                byteData[0] = (byte)(((1 << state) & 0xff00) >> 8);
                byteData[1] = (byte)( (1 << state) & 0x00ff);
                result = logRecord.addData(logSection, byteData);
            }

            // If the new state represents successful preparation,
            // and the transaction is a top-level transaction,
            //  or the new state indicates the beginning of commit or
            // rollback and the Coordinator is a subordinate, we want to make
            // sure that the log information is permanent.

            if (logRecord != null &&
                    (newState == STATE_PREPARED_SUCCESS ||
                     // GDH:  All the new states should be flushed to the log
                     //        cop as it represents begining of commit and
                     //        the others as they represent end of phase one
                     // (at least)
                      
                     newState == STATE_COMMIT_ONE_PHASE_HEURISTIC_HAZARD ||
                     newState == STATE_COMMIT_ONE_PHASE_HEURISTIC_MIXED ||
                     // \GDH
                     (newState == STATE_COMMITTING && subordinate) ||
                     (newState == STATE_ROLLING_BACK && subordinate))) {

                // If this is the first time a transaction has
                // gone in-doubt in this process, then ensure
                // that the restart required flag is set in the repository.

                setInDoubt(true);

                // Force the log record for the transaction.
                // How much 'force' can we use in Java?

                if (!(result = logRecord.write(true))) {
                    // empty
                }
            } else {
                if (newState == STATE_PREPARED_SUCCESS ||
                     newState == STATE_COMMIT_ONE_PHASE_HEURISTIC_HAZARD ||
                     newState == STATE_COMMIT_ONE_PHASE_HEURISTIC_MIXED ||
                     (newState == STATE_COMMITTING && subordinate) ||
                     (newState == STATE_ROLLING_BACK && subordinate)) {
                     setInDoubt(true);
                     // LogDBHelper.getInstance().addRecord(localTID.longValue(), globalTID.toTidBytes());
                }
	    }
               

            // If the new state represents completion of a
            // top-level transaction, a top-level transaction,
            // then write an unforced record to the log.

            if (logRecord != null && (newState == STATE_COMMITTED ||
                                      // newState == STATE_COMMITTING_ONE_PHASE   ||
                                      // newState == STATE_COMMITTED_ONE_PHASE_OK   ||
                                      // newState == STATE_COMMIT_ONE_PHASE_ROLLED_BACK ||
                                      newState == STATE_ROLLED_BACK)) {

                // Write the log record for the transaction (unforced).

                if (!(result = logRecord.write(false))) {
                    // empty
                }
            }

            // RecoveryHook (for induced crashes and waits)  (Ram Jeyaraman)
            if (FailureInducer.isFailureInducerActive() &&
                    (!(Thread.currentThread().getName().
                        equals("JTS Resync Thread"/*#Frozen*/)))) {
                Integer failurePoint = null;
                switch (newState) {
                    case STATE_COMMITTED_ONE_PHASE_OK :
                        failurePoint = FailureInducer.COMPLETED; break;
                    case STATE_COMMITTED :
                        failurePoint = FailureInducer.COMPLETED; break;
                    case STATE_ROLLED_BACK :
                        failurePoint = FailureInducer.COMPLETED;
                }
                FailureInducer.waitForFailure(this.globalTID, failurePoint);
            }
        }

        return result;
    }

    /**
     * Returns the current transaction sequence number and increments it.
     *
     * @param
     *
     * @return  The current transaction sequence number.
     *
     * @see
     */
    private static synchronized long getSequenceNumber() {

        return ++sequenceNumber;
    }

    /**
     * Returns the current epoch number.
     *
     * @param
     *
     * @return  The current epoch number.
     *
     * @see
     */
    /*private static int getEpochNumber() {

	
        int result = (int)epochNumber;
        return result;
    }*/

    /**
     * Returns a flag indicating whether any transactions may be in doubt.
     *
     * @param
     *
     * @return  The in doubt indicator.
     *
     * @see
     */
    static boolean inDoubt() {
        return inDoubt;
    }

    /**
     * Sets the in doubt indicator.
     *
     * @param value  The new value of the indicator.
     *
     * @return
     *
     * @see
     */
    static void setInDoubt(boolean value) {
        inDoubt = value;
    }

    /**
     * Generates the body of a transaction identifier.
     *
     * @param localTID    The local transaction identifier.
     * @param epoch       The epoch number.
     * @param serverName  The server name.
     *
     * @return
     *
     * @see
     */
    private static final byte[] generateTID(long localTID) {
        if(TIDTemplate==null){
	    synchronized(TransactionState.class){
                if(TIDTemplate==null){
                    String serverName = Configuration.getServerName();
                    int nameLength = (serverName == null ? 0 : serverName.length());
	            TIDTemplate = new byte[nameLength+8];
        
                    long epochNumber    = new Date().getTime();
                    TIDTemplate[4] = (byte) epochNumber;
                    TIDTemplate[5] = (byte)(epochNumber >> 8);
                    TIDTemplate[6] = (byte)(epochNumber >> 16);
                    TIDTemplate[7] = (byte)(epochNumber >> 24);

                    for( int i = 0; i < nameLength; i++ )
                        TIDTemplate[i+8] = (byte) serverName.charAt(i);
		}
	    }
	}
        byte[] result = new byte[TIDTemplate.length];
	System.arraycopy(TIDTemplate, 4, result, 4, TIDTemplate.length-4);

        result[0] = (byte) localTID;
        result[1] = (byte)(localTID >> 8);
        result[2] = (byte)(localTID >> 16);
        result[3] = (byte)(localTID >> 24);

        return result;
    }
}
