/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.apache.geode.internal.jta;

/**
 * <p>
 * GlobalTransaction is the JTA concept of a Global Transaction.
 * </p>
 *
 *
 * @since GemFire 4.0
 *
 * @deprecated as of Geode 1.2.0 user should use a third party JTA transaction manager to manage JTA
 *             transactions.
 */
import java.util.*;

import javax.transaction.*;
import javax.transaction.xa.*;

import org.apache.geode.SystemFailure;
import org.apache.geode.distributed.DistributedSystemDisconnectedException;
import org.apache.geode.distributed.internal.DM;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.i18n.LogWriterI18n;
import org.apache.geode.internal.i18n.LocalizedStrings;

@Deprecated
public class GlobalTransaction {

  public static boolean DISABLE_TRANSACTION_TIMEOUT_SETTING = false;
  /**
   * GTid is a byte array identifying every instance of a global transaction uniquely
   */
  private final byte[] GTid;
  /**
   * An instance of the XidImpl class which implements Xid
   */
  private final Xid xid;
  /**
   * Status represents the state the Global Transaction is in
   */
  private int status = Status.STATUS_UNKNOWN;
  /**
   * A set of XAResources associated with the Global Transaction
   */
  private Map resourceMap = Collections.synchronizedMap(new HashMap());
  /**
   * List of local Transactions participating in a Global Transaction.
   */
  private List transactions = Collections.synchronizedList(new ArrayList());
  /**
   * A counter to uniquely generate the GTid
   */
  private static long mCounter = 1;
  /**
   * A timer Task for Transaction TimeOut
   */
  private boolean timedOut = false;
  /**
   * expirationTime for the Transaction
   */
  private volatile long expirationTime;

  /*
   * to enable VERBOSE = true pass System parameter jta.VERBOSE = true while running the test.
   */
  private static boolean VERBOSE = Boolean.getBoolean("jta.VERBOSE");

  /**
   * Construct a new Global Transaction. Generates the GTid and also the xid
   */
  public GlobalTransaction() throws SystemException {
    try {
      GTid = generateGTid();
      xid = XidImpl.createXid(GTid);
    } catch (Exception e) {
      LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
      if (writer.severeEnabled())
        writer.severe(
            LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_CONSTRUCTOR_ERROR_WHILE_TRYING_TO_CREATE_XID_DUE_TO_0,
            e, e);
      String exception =
          LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_CONSTRUCTOR_ERROR_WHILE_TRYING_TO_CREATE_XID_DUE_TO_0
              .toLocalizedString(new Object[] {e});
      throw new SystemException(exception);
    }
  }

  /**
   * Add a transaction to the list of transactions participating in this global Transaction The list
   * of transactions is being maintained so that we can remove the local transaction to global
   * transaction entries from the map being maintained by the Transaction Manager
   *
   * @param txn Transaction instance which is participating in this Global Transaction
   */
  public void addTransaction(Transaction txn) throws SystemException {
    if (txn == null) {
      String exception =
          LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_ADDTRANSACTION_CANNOT_ADD_A_NULL_TRANSACTION
              .toLocalizedString();
      LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
      if (VERBOSE)
        writer.fine(exception);
      throw new SystemException(exception);
    }
    transactions.add(txn);
  }

  /**
   * Delists the XAResources associated with the Global Transaction and Completes the Global
   * transaction associated with the current thread. If any exception is encountered, rollback is
   * called on the current transaction.
   *
   * Concurrency: Some paths invoke this method after taking a lock on "this" while other paths
   * invoke this method without taking a lock on "this". Since both types of path do act on the
   * resourceMap collection, it is being protected by a lock on resourceMap too.
   *
   * @throws RollbackException - Thrown to indicate that the transaction has been rolled back rather
   *         than committed.
   * @throws HeuristicMixedException - Thrown to indicate that a heuristic decision was made and
   *         that some relevant updates have been committed while others have been rolled back.
   * @throws HeuristicRollbackException - Thrown to indicate that a heuristic decision was made and
   *         that all relevant updates have been rolled back.
   * @throws java.lang.SecurityException - Thrown to indicate that the thread is not allowed to
   *         commit the transaction.
   * @throws java.lang.IllegalStateException - Thrown if the current thread is not associated with a
   *         transaction.
   * @throws SystemException - Thrown if the transaction manager encounters an unexpected error
   *         condition.
   *
   * @see javax.transaction.TransactionManager#commit()
   */
  // Asif : Changed the return type to int indicating the nature of Exception
  // encountered during commit
  public void commit() throws RollbackException, HeuristicMixedException,
      HeuristicRollbackException, SecurityException, SystemException {
    LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
    try {
      XAResource xar = null;
      XAResource xar1 = null;
      int loop = 0;
      Boolean isActive = Boolean.FALSE;
      synchronized (this.resourceMap) {
        Map.Entry entry;
        Iterator iterator = resourceMap.entrySet().iterator();
        while (iterator.hasNext()) {
          try {
            entry = (Map.Entry) iterator.next();
            xar = (XAResource) entry.getKey();
            isActive = (Boolean) entry.getValue();
            if (loop == 0)
              xar1 = xar;
            loop++;
            if (isActive.booleanValue()) {
              // delistResource(xar, XAResource.TMSUCCESS);
              xar.end(xid, XAResource.TMSUCCESS);
              entry.setValue(Boolean.FALSE);
            }
          } catch (Exception e) {
            if (VERBOSE)
              writer.info(LocalizedStrings.ONE_ARG,
                  "GlobalTransaction::commit:Exception in delisting XAResource", e);
          }
        }
      }
      if (xar1 != null)
        xar1.commit(xid, true);
      status = Status.STATUS_COMMITTED;
      if (VERBOSE)
        writer.fine("GlobalTransaction::commit:Transaction committed successfully");
    } catch (Exception e) {
      status = Status.STATUS_ROLLING_BACK;
      try {
        rollback();
      } catch (VirtualMachineError err) {
        SystemFailure.initiateFailure(err);
        // If this ever returns, rethrow the error. We're poisoned
        // now, so don't let this thread continue.
        throw err;
      } catch (Throwable t) {
        // Whenever you catch Error or Throwable, you must also
        // catch VirtualMachineError (see above). However, there is
        // _still_ a possibility that you are dealing with a cascading
        // error condition, so you also need to check to see if the JVM
        // is still usable:
        SystemFailure.checkFailure();
        // we will throw an error later, make sure that the synchronizations rollback
        status = Status.STATUS_ROLLEDBACK;
        String exception =
            LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_COMMIT_ERROR_IN_COMMITTING_BUT_TRANSACTION_COULD_NOT_BE_ROLLED_BACK_DUE_TO_EXCEPTION_0
                .toLocalizedString(t);
        if (VERBOSE)
          writer.fine(exception, t);
        SystemException sysEx = new SystemException(exception);
        sysEx.initCause(t);
        throw sysEx;
      }
      String exception =
          LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_COMMIT_ERROR_IN_COMMITTING_THE_TRANSACTION_TRANSACTION_ROLLED_BACK_EXCEPTION_0_1
              .toLocalizedString(new Object[] {e, " " + (e instanceof XAException
                  ? ("Error Code =" + ((XAException) e).errorCode) : "")});
      if (VERBOSE)
        writer.fine(exception, e);
      RollbackException rbEx = new RollbackException(exception);
      rbEx.initCause(e);
      throw rbEx;
    } finally {
      // Map globalTransactions = tm.getGlobalTransactionMap();
      TransactionManagerImpl.getTransactionManager().cleanGlobalTransactionMap(transactions);
      // Asif : Clear the list of transactions
      transactions.clear();
    }
  }

  /**
   * Delists the XAResources associated with the Global Transaction and Roll back the transaction
   * associated with the current thread.
   *
   * Concurrency: Some paths invoke this method after taking a lock on "this" while other paths
   * invoke this method without taking a lock on "this". Since both types of path do act on the
   * resourceMap collection, it is being protected by a lock on resourceMap too.
   *
   * @throws java.lang.SecurityException - Thrown to indicate that the thread is not allowed to roll
   *         back the transaction.
   * @throws java.lang.IllegalStateException - Thrown if the current thread is not associated with a
   *         transaction.
   * @throws SystemException - Thrown if the transaction manager encounters an unexpected error
   *         condition.
   *
   * @see javax.transaction.TransactionManager#rollback()
   */
  public void rollback() throws IllegalStateException, SystemException {
    LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
    try {
      XAResource xar = null;
      XAResource xar1 = null;
      int loop = 0;
      synchronized (this.resourceMap) {
        Iterator iterator = resourceMap.entrySet().iterator();
        Boolean isActive = Boolean.FALSE;
        Map.Entry entry;
        while (iterator.hasNext()) {
          try {
            entry = (Map.Entry) iterator.next();
            xar = (XAResource) entry.getKey();
            isActive = (Boolean) entry.getValue();
            if (loop == 0) {
              xar1 = xar;
            }
            loop++;
            if (isActive.booleanValue()) {
              // delistResource(xar, XAResource.TMSUCCESS);
              xar.end(xid, XAResource.TMSUCCESS);
              entry.setValue(Boolean.FALSE);
            }
          } catch (Exception e) {
            if (VERBOSE)
              writer.info(LocalizedStrings.ONE_ARG,
                  "GlobalTransaction::rollback:Exception in delisting XAResource", e);
          }
        }
      }
      if (xar1 != null)
        xar1.rollback(xid);
      status = Status.STATUS_ROLLEDBACK;
      if (VERBOSE)
        writer.fine("Transaction rolled back successfully");
    } catch (Exception e) {
      // we will throw an error later, make sure that the synchronizations rollback
      status = Status.STATUS_ROLLEDBACK;
      String exception =
          LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_ROLLBACK_ROLLBACK_NOT_SUCCESSFUL_DUE_TO_EXCEPTION_0_1
              .toLocalizedString(new Object[] {e, " " + (e instanceof XAException
                  ? ("Error Code =" + ((XAException) e).errorCode) : "")});
      if (VERBOSE)
        writer.fine(exception);
      SystemException sysEx = new SystemException(exception);
      sysEx.initCause(e);
      throw sysEx;
    } finally {
      // Map globalTransactions = tm.getGlobalTransactionMap();
      TransactionManagerImpl.getTransactionManager().cleanGlobalTransactionMap(transactions);
      // Asif : Clear the list of transactions
      transactions.clear();
    }
  }

  /**
   * Mark the state of the Global Transaction so that it can be rolled back
   */
  public void setRollbackOnly() throws IllegalStateException, SystemException {
    setStatus(Status.STATUS_MARKED_ROLLBACK);
  }

  /**
   * Get the transaction state of the Global Transaction
   */
  public int getStatus() throws SystemException {
    return status;
  }

  /**
   * Enlist the specified XAResource with this transaction. Currently only one Resource Manager is
   * being supported. enlistResource checks if there is no XAResource, then enlists the current
   * XAResource. For subsequent XAResources, it checks if is the same Resource Manager. If it is,
   * then the XAResources are addded, else an exception is thrown
   *
   * Concurrency: The order of acquiring lock will be lock on "this" followed by lock on
   * resourceMap. It is possible that in some functions of this class both the locks are not needed
   * , but if the two are acquired then the realitive order will always be"this" followed by
   * resourceMap.
   *
   * @param xaRes XAResource to be enlisted
   * @return true, if resource was enlisted successfully, otherwise false.
   * @throws SystemException - Thrown if the transaction manager encounters an unexpected error
   *         condition.
   * @throws IllegalStateException - Thrown if the transaction in the target object is in the
   *         prepared state or the transaction is inactive.
   * @throws RollbackException - Thrown to indicate that the transaction has been marked for
   *         rollback only.
   *
   * @see javax.transaction.Transaction#enlistResource(javax.transaction.xa.XAResource)
   */
  public boolean enlistResource(XAResource xaRes)
      throws RollbackException, IllegalStateException, SystemException {
    XAResource xar = null;
    try {
      synchronized (this) {
        if (status == Status.STATUS_MARKED_ROLLBACK) {
          String exception =
              "GlobalTransaction::enlistResource::Cannot enlist resource as the transaction has been marked for rollback";
          LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
          if (VERBOSE)
            writer.fine(exception);
          throw new RollbackException(exception);
        } else if (status != Status.STATUS_ACTIVE) {
          String exception =
              LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_ENLISTRESOURCE_CANNOT_ENLIST_A_RESOURCE_TO_A_TRANSACTION_WHICH_IS_NOT_ACTIVE
                  .toLocalizedString();
          LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
          if (VERBOSE)
            writer.fine(exception);
          throw new IllegalStateException(exception);
        }
        if (resourceMap.isEmpty()) {
          xaRes.start(xid, XAResource.TMNOFLAGS);
          int delay = (int) ((expirationTime - System.currentTimeMillis()) / 1000);
          try {
            if (!DISABLE_TRANSACTION_TIMEOUT_SETTING) {
              xaRes.setTransactionTimeout(delay);
            }
          } catch (XAException xe) {
            String exception =
                LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_ENLISTRESOURCE_EXCEPTION_OCCURRED_IN_TRYING_TO_SET_XARESOURCE_TIMEOUT_DUE_TO_0_ERROR_CODE_1
                    .toLocalizedString(new Object[] {xe, Integer.valueOf(xe.errorCode)});
            LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
            if (VERBOSE)
              writer.fine(exception);
            throw new SystemException(exception);
          }
          resourceMap.put(xaRes, Boolean.TRUE);
        } else {
          synchronized (this.resourceMap) {
            Iterator iterator = resourceMap.keySet().iterator();
            xar = (XAResource) iterator.next();
          }
          if (!xar.isSameRM(xaRes)) {
            LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
            if (writer.severeEnabled())
              writer.severe(
                  LocalizedStrings.GlobalTransaction_GLOBALTRANSACTIONENLISTRESOURCEONLY_ONE_RESOUCE_MANAGER_SUPPORTED);
            throw new SystemException(
                LocalizedStrings.GlobalTransaction_GLOBALTRANSACTIONENLISTRESOURCEONLY_ONE_RESOUCE_MANAGER_SUPPORTED
                    .toLocalizedString());
          } else {
            xaRes.start(xid, XAResource.TMJOIN);
            resourceMap.put(xaRes, Boolean.TRUE);
          }
        }
      }
    } catch (Exception e) {
      String addon =
          (e instanceof XAException ? ("Error Code =" + ((XAException) e).errorCode) : "");
      LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
      if (VERBOSE)
        writer.fine(
            LocalizedStrings.GLOBALTRANSACTION__ENLISTRESOURCE__ERROR_WHILE_ENLISTING_XARESOURCE_0_1
                .toLocalizedString(new Object[] {e, addon}),
            e);
      SystemException sysEx = new SystemException(
          LocalizedStrings.GLOBALTRANSACTION__ENLISTRESOURCE__ERROR_WHILE_ENLISTING_XARESOURCE_0_1
              .toLocalizedString(new Object[] {e, addon}));
      sysEx.initCause(e);
      throw sysEx;
    }
    return true;
  }

  /**
   * Disassociate the XAResource specified from this transaction.
   *
   * In the current implementation this call will never be made by the application server. The
   * delisting is happening at the time of commit/rollback
   *
   * @param xaRes XAResource to be delisted
   * @param flag One of the values of TMSUCCESS, TMSUSPEND, or TMFAIL.
   * @return true, if resource was delisted successfully, otherwise false.
   * @throws SystemException Thrown if the transaction manager encounters an unexpected error
   *         condition.
   * @throws IllegalStateException Thrown if the transaction in the target object is not active.
   *
   * @see javax.transaction.Transaction#delistResource(javax.transaction.xa.XAResource, int)
   */
  public boolean delistResource(XAResource xaRes, int flag)
      throws IllegalStateException, SystemException {
    try {
      if (resourceMap.containsKey(xaRes)) {
        Boolean isActive = (Boolean) resourceMap.get(xaRes);
        if (isActive.booleanValue()) {
          xaRes.end(xid, flag);
          resourceMap.put(xaRes, Boolean.FALSE);
        }
      }
    } catch (Exception e) {
      String exception = LocalizedStrings.GlobalTransaction_ERROR_WHILE_DELISTING_XARESOURCE_0_1
          .toLocalizedString(new Object[] {e, " "
              + (e instanceof XAException ? ("Error Code =" + ((XAException) e).errorCode) : "")});
      LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
      if (VERBOSE)
        writer.fine(exception, e);
      SystemException se = new SystemException(exception);
      se.initCause(e);
    }
    return true;
  }

  /**
   * Set the transaction state of the Global Transaction
   *
   * @param new_status Status (int)
   */
  public void setStatus(int new_status) {
    status = new_status;
  }

  /**
   * suspends the current transaction by deactivating the XAResource (delist)
   */
  public void suspend() throws SystemException {
    XAResource xar = null;
    synchronized (this.resourceMap) {
      Iterator iterator = resourceMap.entrySet().iterator();
      Map.Entry entry;
      Boolean isActive = Boolean.FALSE;
      while (iterator.hasNext()) {
        entry = (Map.Entry) iterator.next();
        xar = (XAResource) entry.getKey();
        isActive = (Boolean) entry.getValue();
        if (isActive.booleanValue())
          try {
            // delistResource(xar, XAResource.TMSUCCESS);
            xar.end(xid, XAResource.TMSUSPEND);
            entry.setValue(Boolean.FALSE);
          } catch (Exception e) {
            String exception =
                LocalizedStrings.GlobalTransaction_ERROR_WHILE_DELISTING_XARESOURCE_0_1
                    .toLocalizedString(new Object[] {e, " " + (e instanceof XAException
                        ? ("Error Code =" + ((XAException) e).errorCode) : "")});
            LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
            if (VERBOSE)
              writer.fine(exception);
            throw new SystemException(exception);
          }
        /*
         * catch (SystemException e) { String exception =
         * "GlobaTransaction::suspend not succesful due to " + e; LogWriterI18n writer =
         * TransactionUtils.getLogWriter(); if (VERBOSE) writer.fine(exception); throw new
         * SystemException(exception); }
         */
      }
    }
  }

  /**
   * resume the current transaction by activating all the XAResources associated with the current
   * transaction
   */
  public void resume() throws SystemException {
    XAResource xar = null;
    synchronized (this.resourceMap) {
      Iterator iterator = resourceMap.entrySet().iterator();
      Map.Entry entry;
      Boolean isActive = Boolean.FALSE;

      while (iterator.hasNext()) {
        entry = (Map.Entry) iterator.next();
        xar = (XAResource) entry.getKey();
        isActive = (Boolean) entry.getValue();
        if (!isActive.booleanValue())
          try {
            xar.start(xid, XAResource.TMRESUME);
            entry.setValue(Boolean.TRUE);
          } catch (Exception e) {
            String exception =
                LocalizedStrings.GlobalTransaction_GLOBATRANSACTION_RESUME_RESUME_NOT_SUCCESFUL_DUE_TO_0
                    .toLocalizedString(e);
            LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
            if (VERBOSE)
              writer.fine(exception, e);
            throw new SystemException(exception);
          }
      }
    }
  }

  /**
   * String for current distributed system
   *
   * @see #IdsForId
   * @guarded.By {@link #DmidMutex}
   */
  private static String DMid = null;

  /**
   * Distributed system for given string
   *
   * @see #DMid
   * @guarded.By {@link #DmidMutex}
   */
  private static InternalDistributedSystem IdsForId = null;

  /**
   * Mutex controls update of {@link #DMid}
   *
   * @see #DMid
   * @see #IdsForId
   */
  private static final Object DmidMutex = new Object();

  /**
   * Read current {@link #DMid} and return it
   *
   * @return current DMid
   */
  private static String getId() {
    synchronized (DmidMutex) {
      InternalDistributedSystem ids = InternalDistributedSystem.getAnyInstance();
      if (ids == null) {
        throw new DistributedSystemDisconnectedException("No distributed system");
      }
      if (ids == IdsForId) {
        return DMid;
      }
      IdsForId = ids;
      DM dm = ids.getDistributionManager();
      DMid = dm.getId().toString();
      return DMid;
    }
  }

  /**
   * Returns a byte array which uses a static synchronized counter to ensure uniqueness
   *
   */
  private static byte[] generateGTid() {
    // Asif: The counter should be attached to the string inside Synch block
    StringBuffer sbuff = new StringBuffer(getId());
    synchronized (GlobalTransaction.class) {
      if (mCounter == 99999)
        mCounter = 1;
      else
        ++mCounter;
      sbuff.append(String.valueOf(mCounter));
    }
    sbuff.append('_').append(System.currentTimeMillis());
    byte[] byte_array = sbuff.toString().getBytes();
    return byte_array;
  }

  /**
   * A timer task cleaup method. This is called when Transaction timeout occurs. On timeout the
   * transaction is rolled back and the thread removed from thread-Transaction Map.
   */
  void expireGTX() {
    if (timedOut)
      return; // this method is only called by a single thread so this is safe
    timedOut = true;
    LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
    try {
      if (writer.infoEnabled())
        writer.info(LocalizedStrings.GlobalTransaction_TRANSACTION_0_HAS_TIMED_OUT, this);
      TransactionManagerImpl.getTransactionManager().removeTranxnMappings(transactions);
      setStatus(Status.STATUS_NO_TRANSACTION);
    } catch (Exception e) {
      if (writer.severeEnabled())
        writer.severe(
            LocalizedStrings.GlobalTransaction_GLOBATRANSACTION_EXPIREGTX_ERROR_OCCURRED_WHILE_REMOVING_TRANSACTIONAL_MAPPINGS_0,
            e, e);
    }
  }

  /**
   * Set the transaction TimeOut of the Global Transaction Asif : It returns the new expiry time for
   * the GTX.
   *
   * @param seconds
   * @throws SystemException
   */
  long setTransactionTimeoutForXARes(int seconds) throws SystemException {

    XAResource xar = null;
    boolean resetXATimeOut = true;
    Map.Entry entry;
    synchronized (this.resourceMap) {
      Iterator iterator = resourceMap.entrySet().iterator();
      while (iterator.hasNext()) {
        entry = (Map.Entry) iterator.next();
        xar = (XAResource) entry.getKey();
        if (((Boolean) entry.getValue()).booleanValue()) {
          try {
            resetXATimeOut = xar.setTransactionTimeout(seconds);
          } catch (XAException e) {
            String exception =
                LocalizedStrings.GlobalTransaction_EXCEPTION_OCCURRED_WHILE_TRYING_TO_SET_THE_XARESOURCE_TIMEOUT_DUE_TO_0_ERROR_CODE_1
                    .toLocalizedString(new Object[] {e, Integer.valueOf(e.errorCode)});
            LogWriterI18n writer = TransactionUtils.getLogWriterI18n();
            if (VERBOSE)
              writer.fine(exception);
            throw new SystemException(exception);
          }
          break;
        }
      }
    }
    long newExp = System.currentTimeMillis() + (seconds * 1000);
    if (!resetXATimeOut)
      newExp = -1;
    return newExp;
  }

  /**
   * Testmethod to provide the size of the resource map
   *
   */
  int getResourceMapSize() {
    return this.resourceMap.size();
  }

  /**
   * Returns the List of Transactions associated with this GlobalTransaction Asif : Reduced the
   * visibility
   */
  List getTransactions() {
    return transactions;
  }

  long getExpirationTime() {
    return expirationTime;
  }

  void setTimeoutValue(long time) {
    expirationTime = time;
  }

  boolean isExpired() {
    return timedOut;
  }

  public int compare(GlobalTransaction other) {
    if (this == other) {
      return 0;
    }
    long compare = getExpirationTime() - other.getExpirationTime();
    if (compare < 0) {
      return -1;
    } else if (compare > 0) {
      return 1;
    }
    // need to compare something else to break the tie to fix bug 39579
    if (this.GTid.length < other.GTid.length) {
      return -1;
    } else if (this.GTid.length > other.GTid.length) {
      return 1;
    }
    // need to compare the bytes
    for (int i = 0; i < this.GTid.length; i++) {
      if (this.GTid[i] < other.GTid[i]) {
        return -1;
      } else if (this.GTid[i] > other.GTid[i]) {
        return 1;
      }
    }
    // If we get here the GTids are the same!
    int myId = System.identityHashCode(this);
    int otherId = System.identityHashCode(other);
    if (myId < otherId) {
      return -1;
    } else if (myId > otherId) {
      return 1;
    } else {
      // we could just add another field to this class which has a value
      // obtained from a static atomic
      throw new IllegalStateException(LocalizedStrings.GlobalTransaction_COULD_NOT_COMPARE_0_TO_1
          .toLocalizedString(new Object[] {this, other}));
    }
  }

  // public String toString() {
  // StringBuffer sb = new StringBuffer();
  // sb.append("<");
  // sb.append(super.toString());
  // sb.append(" expireTime=" + getExpirationTime());
  // sb.append(">");
  // return sb.toString();
  // }
}
