Package com.arjuna.ats.internal.jts.orbspecific.coordinator

Source Code of com.arjuna.ats.internal.jts.orbspecific.coordinator.ArjunaTransactionImple

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a full listing
* of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
/*
* Copyright (C) 2001, 2002,
*
* Hewlett-Packard Arjuna Labs,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: ArjunaTransactionImple.java 2342 2006-03-30 13:06:17Z  $
*/

package com.arjuna.ats.internal.jts.orbspecific.coordinator;

import com.arjuna.ats.jts.extensions.Arjuna;
import com.arjuna.ats.jts.exceptions.ExceptionCodes;
import com.arjuna.ats.jts.utils.Utility;
import com.arjuna.ats.jts.OTSManager;
import com.arjuna.ats.jts.common.jtsPropertyManager;
import com.arjuna.ats.jts.logging.*;

import com.arjuna.common.util.propertyservice.PropertyManager;
import com.arjuna.common.util.logging.*;

import com.arjuna.ats.internal.jts.recovery.RecoveryCreator;
import com.arjuna.ats.internal.jts.OTSImpleManager;
import com.arjuna.ats.internal.jts.utils.Helper;
import com.arjuna.ats.internal.jts.ORBManager;
import com.arjuna.ats.internal.jts.ControlWrapper;
import com.arjuna.ats.internal.jts.orbspecific.ControlImple;
import com.arjuna.ats.internal.jts.orbspecific.CurrentImple;
import com.arjuna.ats.internal.jts.orbspecific.TransactionFactoryImple;
import com.arjuna.ats.internal.jts.resources.SynchronizationRecord;
import com.arjuna.ats.internal.jts.resources.ResourceRecord;
import com.arjuna.ats.internal.jts.resources.ExtendedResourceRecord;
import com.arjuna.ats.internal.jts.coordinator.CheckedActions;

import com.arjuna.orbportability.*;

import com.arjuna.ats.arjuna.coordinator.*;
import com.arjuna.ats.arjuna.objectstore.ObjectStore;
import com.arjuna.ats.arjuna.state.*;
import com.arjuna.ats.arjuna.common.*;

import com.arjuna.ats.internal.arjuna.template.*;

import org.omg.CosTransactions.*;

import com.arjuna.ArjunaOTS.*;

import org.omg.CORBA.CompletionStatus;

import java.util.*;

import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.UNKNOWN;
import org.omg.CORBA.BAD_OPERATION;
import org.omg.CORBA.NO_PERMISSION;
import org.omg.CORBA.NO_IMPLEMENT;
import org.omg.CORBA.INVALID_TRANSACTION;
import org.omg.CORBA.TRANSACTION_ROLLEDBACK;

/**
* OTS implementation class.
*
* Implements both the Coordinator & Terminator interfaces of OTS as a single
* class.
*
* Note, because Java does not support multiple inheritance we must make use of
* the tie facility (uuuggghhhh!!!!)
*
* @author Mark Little (mark@arjuna.com)
* @version $Id: ArjunaTransactionImple.java 2342 2006-03-30 13:06:17Z  $
* @since JTS 1.0.
*
* @message com.arjuna.ats.internal.jts.orbspecific.coordinator.generror {0}
*          caught exception: {1}
* @message com.arjuna.ats.internal.jts.orbspecific.coordinator.rbofail {0}
*          attempt to mark transaction {1} as rollback only threw: {2}
*/

public class ArjunaTransactionImple extends
    com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator implements
    com.arjuna.ArjunaOTS.ArjunaTransactionOperations
{

  public ArjunaTransactionImple (Control myParent)
  {
    this(myParent, null);
  }

  public ArjunaTransactionImple (Control myParent, ArjunaTransactionImple parent)
  {
    super();

    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.CONSTRUCTORS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple Begin for < "
          + get_uid()
          + " , "
          + ((parent != null) ? parent.get_uid() : Uid.nullUid())
          + " >");
    }

    parentTransaction = parent;
    controlHandle = null;
    parentHandle = myParent;
    currentStatus = org.omg.CosTransactions.Status.StatusUnknown;

    rootAction = null;
    _synchs = null;

    super.Begin(parent);

    /*
     * Add uid of this action to parent.
     */

    if (parent != null)
      parent.addChildAction(this);

    currentStatus = determineStatus(this);
    rootAction = this;

    /*
     * Do this once to avoid overhead.
     */

    hashCode = get_uid().hashCode();

    if (parent != null)
    {
      while ((rootAction.parent()) != null)
        rootAction = rootAction.parent();

      topLevelHashCode = rootAction.get_uid().hashCode();
    }
    else
      topLevelHashCode = hashCode;

    if (ArjunaTransactionImple._checkedTransactions)
    {
      /*
       * Fully checked transactions only allow the thread which began the
       * transaction to terminate it. We get the id of the beginning
       * thread here.
       *
       * The spec. says nothing about crash recovery, so we better assume
       * it can always complete a transaction!
       *
       * If the creating thread dies before terminating the transaction
       * then we have a problem. Requires change to Thread class to abort
       * outstanding transactions in this case.
       *
       * Also, transaction timeouts cause abortion of the transaction by a
       * *different* thread! This must work even in the presence of
       * checked transactions!
       */

      transactionCreator = Thread.currentThread();
    }
    else
      transactionCreator = null;

    CheckedAction ca = CheckedActions.get();

    if (ca != null)
    {
      super.setCheckedAction(ca);
      ca = null;
    }
  }

  public ArjunaTransactionImple (Uid actUid, Control myParent)
  {
    this(actUid, myParent, null);
  }

  public ArjunaTransactionImple (Uid actUid, Control myParent, ArjunaTransactionImple parent)
  {
    super(actUid);

    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.CONSTRUCTORS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple Begin for < "
          + get_uid()
          + " , "
          + ((parent != null) ? parent.get_uid() : Uid.nullUid())
          + " >");
    }

    parentTransaction = parent;
    controlHandle = null;
    parentHandle = myParent;
    currentStatus = org.omg.CosTransactions.Status.StatusUnknown;

    rootAction = null;
    _synchs = null;

    super.Begin(parent);

    /*
     * Add uid of this action to parent.
     */

    if (parent != null)
      parent.addChildAction(this);

    currentStatus = determineStatus(this);
    rootAction = this;

    hashCode = get_uid().hashCode();

    if (parent != null)
    {
      while ((rootAction.parent()) != null)
        rootAction = rootAction.parent();

      topLevelHashCode = rootAction.get_uid().hashCode();
    }
    else
      topLevelHashCode = hashCode;

    if (ArjunaTransactionImple._checkedTransactions)
    {
      /*
       * Fully checked transactions only allow the thread which began the
       * transaction to terminate it. We get the id of the beginning
       * thread here.
       */

      transactionCreator = Thread.currentThread();
    }
    else
      transactionCreator = null;

    CheckedAction ca = CheckedActions.get();

    if (ca != null)
    {
      super.setCheckedAction(ca);
      ca = null;
    }
  }

  /*
   * Memory management is much better in Java, so we don't have the problem of
   * the Control referencing the transaction and vice versa.
   */

  /**
   * @message com.arjuna.ats.internal.jts.orbspecific.coordinator.zsync {0} -
   *          none zero Synchronization list!
   */

  public void finalize ()
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.DESTRUCTORS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple.finalize - called for < "
          + get_uid() + " >");
    }

    if (_synchs != null)
    {
      // should not happen if the transaction has terminated

      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.szync", new Object[]
        { "ArjunaTransactionImple.finalize()" });
      }

      // "delete" list anyway, but don't do anything with list elements

      _synchs = null;
    }

    controlHandle = null;

    super.finalize();
  }

  public final synchronized ControlImple getControlHandle ()
  {
    return controlHandle;
  }

  public final synchronized void setControlHandle (ControlImple handle)
  {
    controlHandle = handle;
  }

  /**
   * If the transaction has already been committed (by another thread, for
   * example) then we do nothing - could throw TransactionRequired or
   * INVALID_TRANSACTION. However, if it was rolledback then we throw
   * TRANSACTION_ROLLEDBACK. Seems like an inconsistency.
   *
   * report_heuristics is ignored if we are a subtransaction.
   */

  public void commit (boolean report_heuristics) throws HeuristicMixed,
      HeuristicHazard, SystemException
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::commit for "
          + get_uid());
    }

    if (ArjunaTransactionImple._checkedTransactions && !checkAccess())
    {
      throw new NO_PERMISSION(0, CompletionStatus.COMPLETED_NO);
    }

    int outcome = super.status();

    if ((outcome == ActionStatus.RUNNING)
        || (outcome == ActionStatus.ABORT_ONLY)) // have we already been
                             // committed?
    {
      try
      {
        if (_synchs != null)
                {
          if(outcome == ActionStatus.RUNNING ||
                            (outcome == ActionStatus.ABORT_ONLY && TxControl.isBeforeCompletionWhenRollbackOnly()))
                    {
                        doBeforeCompletion();
                    }
                }
      }
      catch (Exception e)
      {
        /*
         * Don't do anything, since we will have marked the transaction
         * as rollback only.
         */
      }

      /*
       * Remove the uid of this action from the parent.
       */

      if (parentTransaction != null)
      {
        parentTransaction.removeChildAction(this);
      }

      outcome = super.End(report_heuristics);

      try
      {
        if (_synchs != null)
        {
          currentStatus = determineStatus(this);

          doAfterCompletion(currentStatus);

          _synchs = null;
        }
      }
      catch (Exception e)
      {
      }

      destroyAction();
    }
    else
    {
      /*
       * Differentiate between us committing the transaction and some
       * other thread doing it.
       */

      throw new INVALID_TRANSACTION(0, CompletionStatus.COMPLETED_NO);
    }

    switch (outcome)
    {
    case ActionStatus.COMMITTED:
    case ActionStatus.H_COMMIT:
    case ActionStatus.COMMITTING: // in case asynchronous commit!
      return;
    case ActionStatus.ABORTED:
    case ActionStatus.ABORTING:  // in case of asynchronous abort!
    case ActionStatus.H_ROLLBACK:
      throw new TRANSACTION_ROLLEDBACK(ExceptionCodes.FAILED_TO_COMMIT,
          CompletionStatus.COMPLETED_NO);
    case ActionStatus.H_MIXED:
      throw new HeuristicMixed();
    case ActionStatus.H_HAZARD:
    default:
      throw new HeuristicHazard();
    }
  }

  public void rollback () throws SystemException
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::rollback for "
          + get_uid());
    }

    if (ArjunaTransactionImple._checkedTransactions && !checkAccess())
    {
      throw new NO_PERMISSION(0, CompletionStatus.COMPLETED_NO);
    }

    int status = super.status();

    if ((status == ActionStatus.RUNNING)
        || (status == ActionStatus.ABORT_ONLY)) // already aborted?
    {

            if (ArjunaTransactionImple._syncOn)
            {
                if(TxControl.isBeforeCompletionWhenRollbackOnly())
                {
                    try
                    {
                        if (_synchs != null)
                            doBeforeCompletion();
                    }
                    catch (Exception e)
                    {
                        /*
                           * Don't do anything - we're about to rollback anyway!
                           */
                    }
                }
            }
            else
            {
                /*
                     * If we have any synchronizations delete them now. Can only be
                     * a top-level action.
                     */

                _synchs = null;
            }

      /*
       * Remove uid of this action from parent even if remote.
       */

      if (parentTransaction != null)
      {
        parentTransaction.removeChildAction(this);
      }

      super.Abort();

      if (ArjunaTransactionImple._syncOn)
      {
        try
        {
          if (_synchs != null)
          {
            currentStatus = determineStatus(this);

            doAfterCompletion(currentStatus);
          }
        }
        catch (Exception e)
        {
        }
      }

      destroyAction();

      status = super.status();
    }
    else
    {
      /*
       * Differentiate between us ending the transaction and some other
       * thread doing it.
       */

      throw new INVALID_TRANSACTION(0, CompletionStatus.COMPLETED_NO); // means
                                       // transaction
                                       // already
                                       // terminated.
    }

    switch (status)
    {
    case ActionStatus.ABORTING:
    case ActionStatus.ABORTED:
    case ActionStatus.H_ROLLBACK:
      /*
       * If the transaction has already rolledback then silently ignore
       * the multiple rollback attempts.
       */
      return;
    case ActionStatus.PREPARING: // shouldn't be able to get heuristics or
                   // any of these!
    case ActionStatus.PREPARED:
    case ActionStatus.COMMITTING:
    case ActionStatus.COMMITTED:
    case ActionStatus.H_COMMIT:
    case ActionStatus.H_MIXED:
    case ActionStatus.H_HAZARD:
      throw new INVALID_TRANSACTION(0, CompletionStatus.COMPLETED_NO); // means
                                       // transaction
                                       // already
                                       // terminated.
    case ActionStatus.INVALID:
    case ActionStatus.CLEANUP:
      throw new UNKNOWN(ExceptionCodes.UNKNOWN_EXCEPTION,
          CompletionStatus.COMPLETED_MAYBE);
    }
  }

  public org.omg.CosTransactions.Status get_status () throws SystemException
  {
    Status s = determineStatus(this);

    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::get_status for "
          + get_uid() + " returning " + Utility.stringStatus(s));
    }

    return s;
  }

  public org.omg.CosTransactions.Status get_parent_status ()
      throws SystemException
  {
    if (parentTransaction != null)
      return parentTransaction.get_status();
    else
      return get_status();
  }

  public org.omg.CosTransactions.Status get_top_level_status ()
      throws SystemException
  {
    if (rootAction != null)
      return determineStatus(rootAction);
    else
      return get_status();
  }

  public boolean is_same_transaction (Coordinator tc) throws SystemException
  {
    if (tc == null)
      return false;

    /*
     * Cut down the amount of work we need to do. Hash values for the same
     * transaction must be the same!
     */

    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::is_same_transaction comparing hash codes: < "
          + tc.hash_transaction() + ", " + hash_transaction() + " >");
    }

    if (tc.hash_transaction() != hash_transaction())
      return false;

    boolean result = false;

    try
    {
      UidCoordinator ptr = com.arjuna.ArjunaOTS.UidCoordinatorHelper.narrow(tc);

      if (ptr != null)
      {
        /*
         * Must be an Arjuna coordinator.
         */

        String myUid = uid();
        String compareUid = ptr.uid();

        if (jtsLogger.logger.isDebugEnabled())
        {
          jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::is_same_transaction comparing uids < "
              + compareUid + ", " + myUid + " >");
        }

        if (myUid.compareTo(compareUid) == 0)
          result = true;

        myUid = null;
        compareUid = null;

        ptr = null;
      }
      else
        throw new BAD_PARAM();
    }
    catch (SystemException e)
    {
      /*
       * Narrow failed, so can't be an Arjuna Uid. Therefore, the answer
       * must be false.
       */
    }

    return result;
  }

  public boolean is_related_transaction (Coordinator tc)
      throws SystemException
  {
    if (tc == null)
      return false;

    boolean result = false;

    try
    {
      UidCoordinator ptr = com.arjuna.ArjunaOTS.UidCoordinatorHelper.narrow(tc);

      if (ptr != null)
      {
        /*
         * Must be an Arjuna coordinator.
         */

        /*
         * If they have the same parent, then they must be related.
         */

        String myTLUid = topLevelUid();
        String compareTLUid = ptr.topLevelUid();

        if (jtsLogger.logger.isDebugEnabled())
        {
          jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::is_related_transaction comparing uids < "
              + compareTLUid + ", " + myTLUid + " >");
        }

        if (myTLUid.compareTo(compareTLUid) == 0)
          result = true;

        myTLUid = null;
        compareTLUid = null;

        ptr = null;
      }
      else
        throw new BAD_PARAM();
    }
    catch (SystemException e)
    {
      /*
       * Narrow failed, so can't be an Arjuna Uid. Therefore, the answer
       * must be false.
       */
    }

    return result;
  }

  /**
   * Is this transaction an ancestor of tc?
   */

  public boolean is_ancestor_transaction (Coordinator tc)
      throws SystemException
  {
    if (tc == null)
      return false;

    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::is_ancestor_transaction ()");
    }

    if (is_same_transaction(tc))
      return true;
    else
    {
      /*
       * Are we related?
       */

      if (is_related_transaction(tc))
      {
        if (is_descendant_transaction(tc))
          return false;
        else
          return true;
      }
      else
        return false;
    }
  }

  /**
   * Is this transaction a descendant of tc?
   */

  public boolean is_descendant_transaction (Coordinator tc)
      throws SystemException
  {
    if (tc == null)
      return false;

    try
    {
      UidCoordinator ptr = com.arjuna.ArjunaOTS.UidCoordinatorHelper.narrow(tc);

      if (ptr != null)
      {
        /*
         * Must be an Arjuna coordinator.
         */

        Uid lookingFor = new Uid(ptr.uid());
        BasicAction lookingAt = this;

        if (jtsLogger.logger.isDebugEnabled())
        {
          jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::is_descendant_transaction - looking for "
              + lookingFor);
        }

        while (lookingAt != null)
        {
          if (jtsLogger.logger.isDebugEnabled())
          {
            jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::is_descendant_transaction - looking for "
                + lookingAt.get_uid());
          }

          if (lookingAt.get_uid().equals(lookingFor))
            return true;
          else
            lookingAt = lookingAt.parent();
        }

        ptr = null;
      }
      else
        throw new BAD_PARAM();
    }
    catch (SystemException e)
    {
      /*
       * Narrow failed, so can't be an Arjuna Uid. Therefore, the answer
       * must be false.
       */
    }

    return false;
  }

  public boolean is_top_level_transaction () throws SystemException
  {
    return (this == rootAction);
  }

  public int hash_transaction () throws SystemException
  {
    return hashCode;
  }

  public int hash_top_level_tran () throws SystemException
  {
    return topLevelHashCode;
  }

  /**
   * Resources are only registered with the current transaction, whereas
   * subtransaction aware resources are registered with their parents when the
   * current transaction ends.
   *
   * @message com.arjuna.ats.internal.jts.orbspecific.coordinator.rccreate
   *          Creation of RecoveryCoordinator for {0} threw: {1}
   * @message com.arjuna.ats.internal.jts.orbspecific.coordinator.rcnotcreated
   *          not created!
   */

  public RecoveryCoordinator register_resource (Resource r)
      throws SystemException, Inactive
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_resource ( "
          + r + " ) - called for " + get_uid());
    }

    if (r == null)
      throw new BAD_PARAM(0, CompletionStatus.COMPLETED_NO);

    currentStatus = determineStatus(this);

    if (currentStatus != Status.StatusActive)
    {
      if (jtsLogger.logger.isDebugEnabled())
      {
        jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_resource - transaction not active: "
            + Utility.stringStatus(currentStatus));
      }

      if (currentStatus == Status.StatusMarkedRollback)
      {
        throw new TRANSACTION_ROLLEDBACK(
            ExceptionCodes.MARKED_ROLLEDBACK,
            CompletionStatus.COMPLETED_NO);
      }
      else
        throw new Inactive();
    }

    AbstractRecord corbaRec = null;
    BasicAction registerIn = this;

    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple "
          + get_uid() + " ::register_resource: ");
    }

    //
    // Creation of recovery coordinator (DBI)
    //

    //
    // Pack the params:
    // [0] = Transaction* this
    //

    int index = 0;
    Object params[] = new Object[10];
    params[index++] = this;

    RecoveryCoordinator recoveryCoordinator = null;
    Uid recoveryCoordinatorUid = null;

    /*
     * A RecoveryCoordinator can be null but only if the implementation
     * throws NO_IMPLEMENT. If it tries to return null then that is
     * considered an error and we will roll back the transaction.
     */

    try
    {
      recoveryCoordinator = RecoveryCreator.createRecoveryCoordinator(r, params);

      if (recoveryCoordinator == null)
        throw new BAD_OPERATION(
            "RecoveryCoordinator "
                + jtsLogger.logMesg.getString("com.arjuna.ats.internal.jts.orbspecific.coordinator.rcnotcreated"));
    }
    catch (NO_IMPLEMENT ex)
    {
      /*
       * This is legal, and is meant to show that this ORB or
       * configuration simply doesn't support crash recovery.
       */

      recoveryCoordinator = null;
    }
    catch (SystemException e)
    {
      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.rccreate", new Object[]
        { get_uid(), e });
      }

      /*
       * Set transaction to rollback only and re-throw exception.
       */

      try
      {
        rollback_only();
      }
      catch (Inactive ex1)
      {
      }
      catch (SystemException ex2)
      {
        if (jtsLogger.loggerI18N.isWarnEnabled())
        {
          jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.rbofail", new Object[]
          { "ArjunaTransactionImple.register_resource", get_uid(), ex2 });
        }

        throw ex2;
      }

      throw e;
    }

    if (recoveryCoordinator != null)
    {
      //
      // We got a RecoveryCoordinator, so unpack the other return values:
      // [0] = RecoveryCoordinator Uid*
      //

      index = 0;
      recoveryCoordinatorUid = (Uid) params[index++];
    }
    else
    {
      //
      // We didn't get a RecoveryCoordinator, so we don't assume that
      // the other return values have been populated.
      //

      recoveryCoordinatorUid = Uid.nullUid();
    }

    try
    {
      SubtransactionAwareResource staResource = org.omg.CosTransactions.SubtransactionAwareResourceHelper.narrow(r);

      /*
       * Some Orbs (e.g., Orbix) throw BAD_PARAM is the object in X.narrow
       * is not of type X, whereas others (e.g., OrbPlus) simply return
       * NULL!
       */

      if (staResource != null)
      {
        if (jtsLogger.logger.isDebugEnabled())
        {
          jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_resource for "
              + get_uid()
              + " - subtransaction aware resource: YES");
        }

        /*
         * If here the narrow was ok so we have a subtran aware
         * resource.
         */

        Coordinator coord = null;

        if (parentHandle != null)
        {
          /*
           * If we are a SubTranResource then we get registered with
           * the current transaction and its parents upon completion.
           * The first parameter to the record indicates whether we
           * should be propagated (registered) with the parent
           * transaction.
           */

          coord = parentHandle.get_coordinator();
        }

        corbaRec = createOTSRecord(true, r, coord, recoveryCoordinatorUid);

        coord = null;
        staResource = null;
      }
      else
        throw new BAD_PARAM(0, CompletionStatus.COMPLETED_NO);
    }
    catch (BAD_PARAM ex)
    {
      if (jtsLogger.logger.isDebugEnabled())
      {
        jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_resource for "
            + get_uid() + " - subtransaction aware resource: NO");
      }

      /* narrow failed must be a plain resource */

      /*
       * Register with current transaction, but we only receive
       * invocations at top-level.
       */

      if (jtsLogger.logger.isDebugEnabled())
      {
        jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple "
            + get_uid()
            + " ::register_resource: Simple resource - " + ex);
      }

      corbaRec = createOTSRecord(true, r, null, recoveryCoordinatorUid);
    }
    catch (Unavailable e1)
    {
      throw new Inactive();
    }
    catch (SystemException e2)
    {
      if (jtsLogger.logger.isDebugEnabled())
      {
        jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_resource for "
            + get_uid() + " : catch (SystemException) - " + e2);
      }

      throw e2;
    }
    catch (Exception e3)
    {
      if (jtsLogger.logger.isDebugEnabled())
      {
        jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_resource for "
            + get_uid() + " : catch (...) - " + e3);
      }

      /*
       * Cannot just rethrow exception, so throw UNKNOWN.
       */

      throw new UNKNOWN(e3.toString(), ExceptionCodes.UNKNOWN_EXCEPTION,
          CompletionStatus.COMPLETED_NO);
    }

    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_resource for "
          + get_uid() + " : try end");
    }

    if (registerIn.add(corbaRec) != AddOutcome.AR_ADDED)
    {
      corbaRec = null;

      throw new INVALID_TRANSACTION(ExceptionCodes.ADD_FAILED,
          CompletionStatus.COMPLETED_NO);
    }
    else
    {
      if (jtsLogger.logger.isDebugEnabled())
      {
        jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_resource for "
            + get_uid() + " : resource registered");
      }
    }

    return recoveryCoordinator;
  }

  /**
   * Do not propagate the resource to the parent.
   */

  public void register_subtran_aware (SubtransactionAwareResource r)
      throws Inactive, NotSubtransaction, SystemException
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_subtran_aware called for "
          + get_uid());
    }

    if (r == null)
      throw new BAD_PARAM(0, CompletionStatus.COMPLETED_NO);

    currentStatus = determineStatus(this);

    if (currentStatus != Status.StatusActive)
    {
      if (currentStatus == Status.StatusMarkedRollback)
      {
        throw new TRANSACTION_ROLLEDBACK(
            ExceptionCodes.MARKED_ROLLEDBACK,
            CompletionStatus.COMPLETED_NO);
      }
      else
        throw new Inactive();
    }

    if (this == rootAction)
    {
      if (jtsLogger.logger.isDebugEnabled())
      {
        jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_subtran_aware called for "
            + get_uid() + " : not a subtransaction!");
      }

      throw new NotSubtransaction();
    }
    else
    {
      Coordinator coord = null;
      AbstractRecord corbaRec = null;

      try
      {
        coord = parentHandle.get_coordinator();
        corbaRec = createOTSRecord(false, r, coord);
      }
      catch (Unavailable ex)
      {
        throw new UNKNOWN(ExceptionCodes.INACTIVE_TRANSACTION,
            CompletionStatus.COMPLETED_NO); // what else to raise?
      }

      coord = null;

      /*
       * Throw some exception here?
       */

      if (add(corbaRec) != AddOutcome.AR_ADDED)
      {
        if (jtsLogger.logger.isDebugEnabled())
        {
          jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_subtran_aware called for "
              + get_uid() + " : could not add.");
        }

        corbaRec = null;
        throw new Inactive(); // what else to raise??
      }
    }

    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_subtran_aware called for "
          + get_uid() + " : subtran_aware_resource registered");
    }
  }

  public void rollback_only () throws SystemException, Inactive
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::rollback_only - called for "
          + get_uid());
    }

    if (determineStatus(this) != Status.StatusPrepared)
    {
      if (!preventCommit())
      {
        throw new INVALID_TRANSACTION(
            ExceptionCodes.INACTIVE_TRANSACTION,
            CompletionStatus.COMPLETED_NO);
      }
    }
    else
      throw new Inactive();
  }

  /**
   * To be used for debugging purposes only.
   */

  public String get_transaction_name () throws SystemException
  {
    return get_uid().stringForm();
  }

  public Control create_subtransaction () throws SystemException,
      SubtransactionsUnavailable, Inactive
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::create_subtransaction - called for "
          + get_uid());
    }

    if (determineStatus(this) != Status.StatusActive)
      throw new Inactive();
    else
    {
      if (!_subtran)
        throw new SubtransactionsUnavailable();
      else
      {
        if (controlHandle == null)
          throw new Inactive();
        else
          return TransactionFactoryImple.create_subtransaction(controlHandle.getControl(), this);
      }
    }
  }

  /**
   * The spec states that a synchronization is registered with a single
   * top-level action only. However, if this is a nested transaction there is
   * no appropriate exception to raise. So, we raise
   * SynchronizationUnavailable. We could simply get our parent and register
   * the synchronization with it, but this may not be what the user expects.
   * If it is, then the user can get the parent and do it directly!
   */

  // why not use SynchronizationRecords?
  public void register_synchronization (Synchronization sync)
      throws Inactive, SynchronizationUnavailable, SystemException
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_synchronization - called for "
          + get_uid());
    }

    if (sync == null)
      throw new BAD_PARAM(0, CompletionStatus.COMPLETED_NO);

    if (!is_top_level_transaction()) // are we a top-level transaction?
    {
      if (jtsLogger.logger.isDebugEnabled())
      {
        jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_synchronization - "
            + get_uid() + " is not a top-level transaction!");
      }

      throw new SynchronizationUnavailable();
    }
    else
    {
      currentStatus = determineStatus(this);

      if (currentStatus == Status.StatusActive) // is transaction still
                            // running?
      {
        synchronized (this)
        {
          if (_synchs == null)
                    {
                        // Synchronizations should be stored (or at least iterated) in their natural order
            _synchs = new TreeSet();
                    }
        }

                SynchronizationRecord otsSync;

                if(sync._is_a(JTAInterposedSynchronizationHelper.id()))
                {
                    otsSync = new SynchronizationRecord(sync, true);
                }
                else
                {
                    otsSync = new SynchronizationRecord(sync);
                }

                // disallow addition of Synchronizations that would appear
        // earlier in sequence than any that has already been called
        // during the pre-commmit phase. This is required for
        // JTA 1.1 Synchronization ordering behaviour
        if(_currentRecord != null) {
          Comparable c = (Comparable)otsSync;
          if(c.compareTo(_currentRecord) != 1) {
            throw new UNKNOWN(ExceptionCodes.ADD_FAILED, CompletionStatus.COMPLETED_NO);
          }
        }

                if (!_synchs.add(otsSync))
        {
          otsSync = null;
          throw new UNKNOWN(ExceptionCodes.ADD_FAILED,
              CompletionStatus.COMPLETED_NO); // what else to
                              // raise?
        }
      }
      else
      {
        if (jtsLogger.logger.isDebugEnabled())
        {
          jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::register_synchronization - "
              + get_uid()
              + " is not active: "
              + Utility.stringStatus(currentStatus));
        }

        if (currentStatus == Status.StatusMarkedRollback)
        {
          throw new TRANSACTION_ROLLEDBACK(
              ExceptionCodes.MARKED_ROLLEDBACK,
              CompletionStatus.COMPLETED_NO);
        }
        else
          throw new Inactive();
      }
    }
  }

  public PropagationContext get_txcontext () throws Unavailable,
      SystemException
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::get_txcontext - called for "
          + get_uid());
    }

    /*
     * Throw an exception if we are not active.
     */

    currentStatus = determineStatus(this);

    if ((currentStatus != Status.StatusActive)
        && (currentStatus != Status.StatusMarkedRollback))
    {
      /*
       * If XA compliant then return context even if we're inactive. Otherwise
       * throw Unavailable for consistency with other OTS implementations.
       */

      if (!XA_COMPLIANT)
        throw new Unavailable();
    }

    try
    {
      return propagationContext();
    }
    catch (Exception e)
    {
      throw new UNKNOWN(e.toString(), ExceptionCodes.UNKNOWN_EXCEPTION,
        CompletionStatus.COMPLETED_NO);
    }
  }

  /*
   * Some Arjuna specific methods.
   */

  /**
   * We use these to determine relationships between transactions. Using the
   * hash function is not sufficient, since a hash value is not guaranteed to
   * be unique.
   */

  public String uid () throws SystemException
  {
    return get_uid().stringForm();
  }

  public String topLevelUid () throws SystemException
  {
    if (rootAction != null)
      return rootAction.get_uid().stringForm();
    else
      return null;
  }

  public String type ()
  {
    return ArjunaTransactionImple.typeName();
  }

  public static final int interpositionType ()
  {
    return _ipType;
  }

  public static String typeName ()
  {
    return "/StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple";
  }

  public String toString ()
  {
    return "ArjunaTransactionImple < " + get_uid() + " >";
  }

  public boolean equals (Object o)
  {
    if (o instanceof ArjunaTransactionImple)
    {
      ArjunaTransactionImple tx = (ArjunaTransactionImple) o;

      if (tx == this)
        return true;
      else
        return tx.get_uid().equals(get_uid());
    }
    else
      return false;
  }

  public boolean forgetHeuristics ()
  {
    return super.forgetHeuristics();
  }

  /**
   * For crash recovery purposes only.
   */

  protected ArjunaTransactionImple (Uid actUid)
  {
    super(actUid);

    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.CONSTRUCTORS, VisibilityLevel.VIS_PROTECTED, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::ArjunaTransactionImple ( "
          + actUid + " )");
    }

    parentTransaction = null;
    controlHandle = null;
    parentHandle = null;
    currentStatus = org.omg.CosTransactions.Status.StatusUnknown;

    transactionCreator = null;

    rootAction = null;
    _synchs = null;

    /*
     * Leave activation of transaction up to caller. Transaction status will
     * remain as Unknown until then.
     */

    rootAction = this;

    /*
     * Do this once to avoid overhead.
     */

    hashCode = actUid.hashCode();
    topLevelHashCode = hashCode; // this must be a top-level transaction

    /*
     * Don't bother with checked transactions for recovery.
     */
  }

  protected void doBeforeCompletion () throws SystemException
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PROTECTED, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::doBeforeCompletion for "
          + get_uid());
    }

      boolean problem = false;
      SystemException exp = null;

      /*
       * If we have a synchronization list then we must be top-level.
       */
      if (_synchs != null)
      {
          boolean doSuspend = false;
          ControlWrapper cw = null;

          try
          {
              /*
               * Make sure that this transaction is active on the thread
               * before we invoke any Synchronizations. They are
               * TransactionalObjects and must have the context flowed to
               * them.
               */

              try
              {
                  //        cw = OTSImpleManager.systemCurrent().getControlWrapper();

                  cw = OTSImpleManager.current().getControlWrapper();

                  /*
                   * If there's no transaction incoming, then use the one that
                   * we got at creation time.
                   */

                  if ((cw == null) || (!controlHandle.equals(cw.getImple())))
                  {
                      //      OTSImpleManager.systemCurrent().resumeImple(controlHandle);

                      OTSImpleManager.current().resumeImple(controlHandle);

                      doSuspend = true;
                  }
              }
              catch (Exception ex)
              {
                  /*
                   * It should be OK to continue with the invocations even if
                   * we couldn't resume, because a Synchronization is only
                   * supposed to be associated with a single transaction. So,
                   * it should be able to infer the transaction.
                   */
              }

              /*
               * Since Synchronizations may add register other Synchronizations, we can't simply
               * iterate the collection. Instead we work from an ordered copy, which we periodically
               * check for freshness. The addSynchronization method uses _currentRecord to disallow
               * adding records in the part of the array we have already traversed, thus all
               * Synchronization will be called and the (jta only) rules on ordering of interposed
               * Synchronization will be respected.
               */
              int lastIndexProcessed = -1;
              SynchronizationRecord[] copiedSynchs = (SynchronizationRecord[])_synchs.toArray(new SynchronizationRecord[] {});

              while( (lastIndexProcessed < _synchs.size()-1) && !problem) {

                  // if new Synchronization have been registered, refresh our copy of the collection:
                  if(copiedSynchs.length != _synchs.size()) {
                      copiedSynchs = (SynchronizationRecord[])_synchs.toArray(new SynchronizationRecord[] {});
                  }

                  lastIndexProcessed = lastIndexProcessed+1;
                  _currentRecord = copiedSynchs[lastIndexProcessed];

          Synchronization c = _currentRecord.contents();
          c.before_completion();
        }
      }
      catch (SystemException e)
      {
        if (jtsLogger.loggerI18N.isWarnEnabled())
        {
          jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.generror", new Object[]
          { "ArjunaTransactionImple.doBeforeCompletion", e });
        }

              if (!problem)
                {
                  exp = e;

                  problem = true;

                  /*
                   * Mark as rollback_only, so when we try to commit it will
                   * fail.
                   */

                  try
                    {
                      rollback_only();
                  }
                  catch (Inactive ex)
                    {
                      /*
                       * This should not happen. If it does, continue with
                       * commit to tidy-up.
                       */

            if (jtsLogger.loggerI18N.isWarnEnabled())
            {
              jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.rbofail", new Object[]
              { "ArjunaTransactionImple.doBeforeCompletion", get_uid(), ex });
            }
          }
        }
      }
      finally
      {
        if (doSuspend)
        {
          try
          {
            //      OTSImpleManager.systemCurrent().resumeWrapper(cw);

                      if (cw != null)
                          OTSImpleManager.current().resumeWrapper(cw);
                      else
                          OTSImpleManager.current().suspend();
                  }
                  catch (Exception ex)
                  {
                  }

                  //        OTSImpleManager.systemCurrent().suspend();
              }
          }
      }
     
      /*
       * If there's no problem so far then call beforeCompletion on the underlying TwoPhaseCoordinator.
       */

        if (!problem)
            problem = !super.beforeCompletion();
     
      if (problem)
      {
          if (exp != null)
              throw exp;
          else
              throw new UNKNOWN(ExceptionCodes.SYNCHRONIZATION_EXCEPTION,
                      CompletionStatus.COMPLETED_NO);
      }
  }

  /**
   * @message com.arjuna.ats.internal.jts.orbspecific.coordinator.txrun {0}
   *          called on still running transaction!
   */

  protected void doAfterCompletion (org.omg.CosTransactions.Status myStatus)
      throws SystemException
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PROTECTED, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::doAfterCompletion for "
          + get_uid());
    }

    if (myStatus == Status.StatusActive)
    {
      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.txrun", new Object[]
        { "ArjunaTransactionImple.doAfterCompletion" });
      }

      return;
    }

      boolean problem = false;
      SystemException exp = null;

      if (_synchs != null)
      {
          ControlWrapper cw = null;
          boolean doSuspend = false;

          try
          {
              //    cw = OTSImpleManager.systemCurrent().getControlWrapper();

              cw = OTSImpleManager.current().getControlWrapper();

              /*
               * If there isn't a transaction context shipped, then use the
               * one we had during creation.
               */

              if ((cw == null) || (!controlHandle.equals(cw.getImple())))
              {
                  //        OTSImpleManager.systemCurrent().resumeImple(controlHandle);

                  OTSImpleManager.current().resumeImple(controlHandle);

                  doSuspend = true;
              }
          }
          catch (Exception ex)
          {
              /*
               * It should still be OK to make the call without a context
               * because a Synchronization can only be associated with a
               * single transaction.
               */

              problem = true;
          }

          /*
           * Regardless of failures, we must tell all synchronizations what
           * happened.
           */

          // afterCompletions should run in reverse order compared to beforeCompletions
          Stack stack = new Stack();
          Iterator iterator = _synchs.iterator();
          while(iterator.hasNext()) {
              stack.push(iterator.next());
          }

          iterator = stack.iterator();

          /*
           * Regardless of failures, we must tell all synchronizations what
           * happened.
           */
          while(!stack.isEmpty())
          {
              SynchronizationRecord value = (SynchronizationRecord)stack.pop();
              Synchronization c = value.contents();

        try
        {
          c.after_completion(myStatus);
        }
        catch (SystemException e)
        {
          if (jtsLogger.logger.isDebugEnabled())
          {
            jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple.doAfterCompletion - caught exception "
                + e);
          }

                  problem = true;

                  /*
                   * Remember the first exception we get, because it may well
                   * be the only one. In which case we can return it, rather
                   * than UNKNOWN.
                   */

                  if (exp == null)
                      exp = e;
              }
          }

          if (doSuspend)
          {
              try
              {
                  //        OTSImpleManager.systemCurrent().resumeWrapper(cw);

                  if (cw != null)
                      OTSImpleManager.current().resumeWrapper(cw);
                  else
                      OTSImpleManager.current().suspend();
              }
              catch (Exception ex)
              {
              }
          }

          _synchs = null;
      }

        /*
        * If there's no problem so far then call afterCompletion on the underlying TwoPhaseCoordinator.
        */

        problem = !super.afterCompletion(myStatus == Status.StatusCommitted ? ActionStatus.COMMITTED : ActionStatus.ABORTED);

        if (problem)
      {
          if (exp != null)
              throw exp;
          else
              throw new UNKNOWN(ExceptionCodes.SYNCHRONIZATION_EXCEPTION,
                      CompletionStatus.COMPLETED_NO);
      }
  }

  /**
   * Called by transaction reaper to force rollback. We do not check the id of
   * the calling thread here, as it will definitely not be the thread which
   * created this transaction. With checked transactions this is normally not
   * allowed, so we need some way to circumvent this.
   */

  final void forceRollback () throws SystemException
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PACKAGE, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::forceRollback for "
          + get_uid());
    }

    if (super.status() == ActionStatus.RUNNING) // already aborted?
    {
      /*
       * If we have any synchronizations delete them now. Can only be a
       * top-level action.
       */

      if (ArjunaTransactionImple._syncOn)
      {
                if(TxControl.isBeforeCompletionWhenRollbackOnly())
                {
                    try
                    {
                        if (_synchs != null)
                            doBeforeCompletion();
                    }
                    catch (Exception e)
                    {
                        /*
                           * Don't do anything - we're about to rollback anyway!
                           */
                    }
                }
      }
      else
      {
        /*
         * If we have any synchronizations delete them now. Can only be
         * a top-level action.
         */

        _synchs = null;
      }

      /*
       * Remove uid of this action from parent even if remote.
       */

      if (parentTransaction != null)
      {
        parentTransaction.removeChildAction(this);
      }

      Abort();

      if (ArjunaTransactionImple._syncOn)
      {
        try
        {
          if (_synchs != null)
          {
            currentStatus = determineStatus(this);

            doAfterCompletion(currentStatus);
          }
        }
        catch (Exception e)
        {
        }
      }

      destroyAction();
    }
  }

  private final org.omg.CosTransactions.Status determineStatus (BasicAction whichAction)
  {
    org.omg.CosTransactions.Status theStatus = org.omg.CosTransactions.Status.StatusUnknown;

    if (whichAction != null)
    {
      switch (whichAction.status())
      {
      case ActionStatus.INVALID: // probably locked, so try again later
        theStatus = Status.StatusUnknown;
        break;
      case ActionStatus.RUNNING:
        theStatus = Status.StatusActive;
        break;
      case ActionStatus.PREPARED:
        theStatus = Status.StatusPrepared;
        break;
      case ActionStatus.COMMITTED:
      case ActionStatus.H_COMMIT:
      case ActionStatus.H_MIXED:
      case ActionStatus.H_HAZARD:
        theStatus = Status.StatusCommitted;
        break;
      case ActionStatus.ABORTED:
      case ActionStatus.H_ROLLBACK:
        theStatus = Status.StatusRolledBack;
        break;
      case ActionStatus.ABORT_ONLY:
        theStatus = Status.StatusMarkedRollback;
        break;
      case ActionStatus.PREPARING:
        theStatus = Status.StatusPreparing;
        break;
      case ActionStatus.COMMITTING:
        theStatus = Status.StatusCommitting;
        break;
      case ActionStatus.ABORTING:
        theStatus = Status.StatusRollingBack;
        break;
      default:
        theStatus = Status.StatusUnknown;
      }
    }
    else
      theStatus = Status.StatusNoTransaction;

    return theStatus;
  }

  /**
   * @message com.arjuna.ats.internal.jts.orbspecific.coordinator.uidfail {0} -
   *          could not get unique identifier of object.
   */

  protected final AbstractRecord createOTSRecord (boolean propagate, Resource resource, Coordinator coord)
  {
    return createOTSRecord(propagate, resource, coord, null);
  }

  protected final AbstractRecord createOTSRecord (boolean propagate, Resource resource, Coordinator coord, Uid recCoordUid)
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PROTECTED, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::createOTSRecord for "
          + get_uid());
    }

    /*
     * If the resource is an ArjunaOTS.OTSAbstractRecord or an
     * ArjunaOTS.ArjunaSubtranAwareResource then we can do better record
     * manipulation, and proper nested actions.
     *
     * Based on the type of resource we create the right abstract record to
     * handle it, rather than a single abstract record which switches
     * protocols internally.
     */

    ArjunaSubtranAwareResource absRec = null;
    AbstractRecord corbaRec = null;

    if (resource != null)
    {
      try
      {
        absRec = com.arjuna.ArjunaOTS.ArjunaSubtranAwareResourceHelper.narrow(resource);

        if (absRec == null)
          throw new BAD_PARAM(0, CompletionStatus.COMPLETED_NO);
      }
      catch (Exception e)
      {
        // can't be an ArjunaOTS.ArjunaSubtranAwareResource

        absRec = null;
      }
    }

    if (absRec == null)
    {
      corbaRec = new ResourceRecord(propagate, resource, coord,
          recCoordUid, this);
    }
    else
    {
      Uid u = null;
      OTSAbstractRecord otsRec;

      try
      {
        otsRec = com.arjuna.ArjunaOTS.OTSAbstractRecordHelper.narrow(absRec);

        if (otsRec == null)
          throw new BAD_PARAM(0, CompletionStatus.COMPLETED_NO);
      }
      catch (Exception e)
      {
        otsRec = null;
      }

      if (otsRec != null)
      {
        try
        {
          u = new Uid(otsRec.uid());
        }
        catch (Exception e)
        {
          u = null;
        }

        if (u == null)
        {
          if (jtsLogger.loggerI18N.isWarnEnabled())
          {
            jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.uidfail", new Object[]
            { "ArjunaTransactionImple.createOTSRecord" });
          }
        }
      }

      if (u == null)
        u = new Uid();

      corbaRec = new ExtendedResourceRecord(propagate, u, absRec, coord,
          recCoordUid, this);

      otsRec = null;
      absRec = null;
      u = null;
    }

    return corbaRec;
  }

  /*
   * Is the calling thread the one which began this transaction?
   */

  private final boolean checkAccess ()
  {
    if (Thread.currentThread() == transactionCreator)
      return true;
    else
      return false;
  }

  /*
   * The caller should delete the context.
   *
   * The propagation context is specified on a per client thread basis.
   * Therefore, at the server side we must maintain a hierarchy for each
   * thread. However, the server cannot simply tear down this hierarchy
   * whenever it receives a completely new one from the same thread, since the
   * OTS lets a thread suspend/resume contexts at will. Potential for memory
   * leaks in C++ version, but not Java!!
   *
   * Currently we assume that the hierarchy will be JBoss transactions so we
   * can get the parents of transactions. If it is not then we could simply
   * just call get_txcontext on the control!
   */

  private final PropagationContext propagationContext () throws Unavailable,
      Inactive, SystemException
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::propagationContext for "
          + get_uid());
    }

    String theUid = null;
    Control currentControl = controlHandle.getControl();
    PropagationContext context = new PropagationContext();
    int sequenceThreshold = 1; // most transactions will be top-level
    int sequenceIncrement = 5;

    context.parents = null;

    context.current = new TransIdentity();
    context.implementation_specific_data = ORBManager.getORB().orb().create_any(); // uughh!!

    /*
     * Some ORBs (e.g., JBroker) don't like to pass round an unused Any,
     * i.e., one which has only been created and had nothing put in it! So
     * we have to put something in it!!
     */

    context.implementation_specific_data.insert_short((short) 0);

    /*
     * Set up current information. We must leave the timeout for now, since
     * it only applies to the top-level transaction.
     */

    try
    {
      context.current.coord = controlHandle.get_coordinator();
      context.timeout = 0; // will reset later!

      /*
       * Only send the terminator if explicitly asked to. This prevents
       * anyone other than the creator from terminating a transaction.
       */

      if (ArjunaTransactionImple._propagateTerminator)
      {
        context.current.term = controlHandle.get_terminator();
      }
      else
        context.current.term = null;
    }
    catch (Exception e)
    {
      return null;
    }

    /*
     * We send the Uid hierarchy as the otid_t part of the TransIdentity.
     */

    // the sequence should do the memory management for us.
    theUid = controlHandle.get_uid().stringForm();

    context.current.otid = Utility.uidToOtid(theUid);
    context.current.otid.formatID = ArjunaTransactionImple._ipType;

    int index = 0;

    while (currentControl != null)
    {
      try
      {
        ActionControl control = com.arjuna.ArjunaOTS.ActionControlHelper.narrow(currentControl);

        if (control != null)
        {
          /*
           * Must be an Arjuna control.
           */

          currentControl = control.getParentControl();

          if (currentControl != null)
          {
            if (index == 0) // first time
            {
              context.parents = new TransIdentity[sequenceThreshold]; // initial
                                          // length
                                          // to
                                          // avoid
                                          // realloc

              for (int ii = 0; ii < sequenceThreshold; ii++)
                context.parents[ii] = null;
            }

            context.parents[index] = new TransIdentity();
            context.parents[index].coord = currentControl.get_coordinator();

            if (ArjunaTransactionImple._propagateTerminator)
              context.parents[index].term = currentControl.get_terminator();
            else
              context.parents[index].term = null;

            /*
             * Don't bother checking whether narrow works because we
             * can't cope with mixed transaction types anyway! If we
             * got here then the root transaction must be an Arjuna
             * transaction, so the nested transactions *must* also
             * be JBoss transactions!
             */

            UidCoordinator uidCoord = Helper.getUidCoordinator(context.parents[index].coord);

            theUid = uidCoord.uid();

            context.parents[index].otid = Utility.uidToOtid(theUid);
            context.parents[index].otid.formatID = ArjunaTransactionImple._ipType;

            theUid = null;
            uidCoord = null;

            index++;

            if (index >= sequenceThreshold)
            {
              sequenceThreshold = index + sequenceIncrement;
              context.parents = resizeHierarchy(context.parents, index
                  + sequenceIncrement);
            }
          }
          else
          {
            /*
             * Found the top-level transaction, so we can now setup
             * the timeout value. All transactions will non-zero
             * timeouts will have been registered with the
             * transaction reaper.
             */

            /*
             * By default we send over the time remaining (in seconds), since that
             * is what OTS 1.2 requires. For backward compatibility with earlier
             * versions, there's a configurable option.
             */

              if (_propagateRemainingTimeout)
                        {
                            long timeInMills = TransactionReaper.transactionReaper().getRemainingTimeoutMills(control);
                            context.timeout = (int)(timeInMills/1000L);
                        }
            else
                        {
                            context.timeout = TransactionReaper.transactionReaper().getTimeout(control);
                        }
                    }

          control = null;
        }
        else
          throw new BAD_PARAM(0, CompletionStatus.COMPLETED_NO);
      }
      catch (SystemException e)
      {
        /*
         * Not an Arjuna control!! Should not happen!!
         */

        currentControl = null;
      }
      catch (Exception e)
      {
        e.printStackTrace();

        currentControl = null;
      }
    }

    try
    {
      context.parents = resizeHierarchy(context.parents, index);
    }
    catch (Exception e)
    {
      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.generror", new Object[]
        { "ArjunaTransactionImple.resizeHierarchy", e });
      }

      context = null;
    }

    return context;
  }

  /*
   * Watch out - we can resize down as well as up!
   */

  private final TransIdentity[] resizeHierarchy (TransIdentity[] current, int size)
  {
    if ((current == null) || (size == 0))
      return new TransIdentity[0];

    if (current.length == size)
      return current;

    TransIdentity[] toReturn = new TransIdentity[size];
    int copySize = ((size < current.length) ? size : current.length);

    System.arraycopy(current, 0, toReturn, 0, copySize);

    if (copySize < size)
    {
      for (int j = copySize; j < size; j++)
        toReturn[j] = null;
    }

    return toReturn;
  }

  /*
   * Could perhaps do garbage collection here after a certain number of
   * transactions have been destroyed. Environment variable enabled?
   */

  protected final void destroyAction ()
  {
    if (jtsLogger.logger.isDebugEnabled())
    {
      jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PROTECTED, com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "ArjunaTransactionImple::destroyAction for "
          + get_uid());
    }

    /*
     * Only do if we are a top-level transaction, since otherwise we cannot
     * have created recovery coordinators.
     */

    if (parentHandle == null)
    {
      Object params[] = new Object[1];

      params[0] = this;

      RecoveryCreator.destroyAllRecoveryCoordinators(params);
    }
    else
      parentHandle = null;

    /*
     * switch (super.getHeuristicDecision()) { case
     * TwoPhaseOutcome.HEURISTIC_ROLLBACK: case
     * TwoPhaseOutcome.HEURISTIC_COMMIT: case
     * TwoPhaseOutcome.HEURISTIC_MIXED: case
     * TwoPhaseOutcome.HEURISTIC_HAZARD: { if
     * (BasicAction.maintainHeuristics()) return; } }
     */

    try
    {
      /*
       * We do not need to do worry about deleting the transaction in Java
       * as we do in C++ because of the way garbage collection works - the
       * committing thread has a reference to the transaction which keeps
       * it alive.
       */

      if (controlHandle != null)
      {
        OTSManager.destroyControl(controlHandle);

        controlHandle = null;
      }
    }
    catch (ActiveThreads ex1)
    {
      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.generror", new Object[]
        { "ArjunaTransactionImple.destroyAction", ex1 });
      }
    }
    catch (BadControl ex2)
    {
      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.generror", new Object[]
        { "ArjunaTransactionImple.destroyAction", ex2 });
      }
    }
    catch (ActiveTransaction ex3)
    {
      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.generror", new Object[]
        { "ArjunaTransactionImple.destroyAction", ex3 });
      }
    }
    catch (Destroyed ex4)
    {
      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.generror", new Object[]
        { "ArjunaTransactionImple.destroyAction", ex4 });
      }
    }
    catch (OutOfMemoryError ex5)
    {
      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.generror", new Object[]
        { "ArjunaTransactionImple.destroyAction", ex5 });
      }

      /*
       * Rather than try again after running gc simply return and let the
       * user deal with it. May help with memory!
       */

      System.gc();
    }
    catch (Exception ex6)
    {
      if (jtsLogger.loggerI18N.isWarnEnabled())
      {
        jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.generror", new Object[]
        { "ArjunaTransactionImple.destroyAction", ex6 });
      }
    }
  }

  protected ArjunaTransactionImple parentTransaction; // rather than rely on

  // BasicAction.parent()
  protected ControlImple controlHandle;

  private int hashCode;

  private int topLevelHashCode;

  private Control parentHandle;

  private org.omg.CosTransactions.Status currentStatus;

  private Thread transactionCreator; // probably null most of the time!

  private BasicAction rootAction;

  private SortedSet _synchs;
    private SynchronizationRecord _currentRecord; // the most recently processed Synchronization.

    static int _ipType = Arjuna.XID();

  static boolean _subtran = true;

  static boolean _syncOn = true;

  static boolean _checkedTransactions = false;

  static boolean _propagateTerminator = false;

  static boolean _propagateRemainingTimeout = true// OTS 1.2 onwards supported this.

  private static final boolean XA_COMPLIANT = true; // if we ever want to disable this then add an mbean option.

  /**
   * @message com.arjuna.ats.internal.jts.orbspecific.coordinator.ipunknown
   *          {0} - unknown interposition type: {1}
   */

  static
  {
    String interpositionType = jtsPropertyManager.propertyManager.getProperty(com.arjuna.ats.jts.common.Environment.INTERPOSITION);

    if (interpositionType != null)
    {
      int ipType = Arjuna.nameToXID(interpositionType);

      if (ipType != -1)
        ArjunaTransactionImple._ipType = ipType;
      else
      {
        if (jtsLogger.loggerI18N.isWarnEnabled())
        {
          jtsLogger.loggerI18N.warn("com.arjuna.ats.internal.jts.orbspecific.coordinator.ipunknown", new Object[]
          { "ArjunaTransactionImple.init", interpositionType });
        }
      }
    }

    String supportSubtran = jtsPropertyManager.propertyManager.getProperty(com.arjuna.ats.jts.common.Environment.SUPPORT_SUBTRANSACTIONS);

    if (supportSubtran != null)
    {
      if (supportSubtran.compareTo("NO") == 0)
        _subtran = false;
    }

    String syncOn = jtsPropertyManager.propertyManager.getProperty(com.arjuna.ats.jts.common.Environment.SUPPORT_ROLLBACK_SYNC);

    if (syncOn != null)
    {
      if (syncOn.compareTo("NO") == 0)
        _syncOn = false;
    }

    String checked = jtsPropertyManager.propertyManager.getProperty(com.arjuna.ats.jts.common.Environment.CHECKED_TRANSACTIONS);

    if (checked != null)
    {
      if (checked.compareTo("YES") == 0)
        _checkedTransactions = true;
    }

    String propTerm = jtsPropertyManager.propertyManager.getProperty(com.arjuna.ats.jts.common.Environment.PROPAGATE_TERMINATOR);

    if (propTerm != null)
    {
      if (propTerm.compareTo("YES") == 0)
        _propagateTerminator = true;
    }

    String propRemainingTimeout = jtsPropertyManager.propertyManager.getProperty(com.arjuna.ats.jts.common.Environment.OTS_1_0_TIMEOUT_PROPAGATION);

    if (propRemainingTimeout != null)
    {
      if (propTerm.compareTo("NO") == 0)
        _propagateRemainingTimeout = false;
    }
  }

    public java.util.Map<Uid, String> getSynchronizations()
    {
        if (_synchs != null)
        {
            java.util.Map<Uid, String> synchMap = new java.util.HashMap<Uid, String> ();
            SynchronizationRecord[] synchs = (SynchronizationRecord[]) _synchs.toArray(new SynchronizationRecord[] {});

            for (SynchronizationRecord synch : synchs)
            {
                Synchronization c = synch.contents();
                String cn;

                if (c._is_a(ManagedSynchronizationHelper.id()))
                {
                    ManagedSynchronization mc = ManagedSynchronizationHelper.narrow(c);

                    try {
                        cn = mc.instanceName(); //implementationType() ;
                    } catch (Throwable t) {
                        cn = synch.getClass().getCanonicalName();
                    }
                }
                else {
                    cn = synch.getClass().getCanonicalName();
                }

                synchMap.put(synch.get_uid(), cn);
            }

            return synchMap;
        }

        return Collections.EMPTY_MAP;
    }
}
TOP

Related Classes of com.arjuna.ats.internal.jts.orbspecific.coordinator.ArjunaTransactionImple

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.