Package com.arjuna.ats.internal.jta.resources.arjunacore

Source Code of com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord

/*
* 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) 1998, 1999, 2000, 2001,
*
* Arjuna Solutions Limited,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: XAResourceRecord.java 2342 2006-03-30 13:06:17Z  $
*/

package com.arjuna.ats.internal.jta.resources.arjunacore;

import com.arjuna.ats.jta.recovery.*;

import com.arjuna.ats.jta.common.jtaPropertyManager;
import com.arjuna.ats.jta.common.Environment;

import com.arjuna.ats.jta.xa.RecoverableXAConnection;
import com.arjuna.ats.jta.xa.XidImple;
import com.arjuna.ats.jta.utils.XAHelper;
import com.arjuna.ats.jta.logging.*;
import com.arjuna.ats.jta.resources.StartXAResource;
import com.arjuna.ats.jta.resources.EndXAResource;

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

import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple;
import com.arjuna.ats.internal.jta.xa.TxInfo;
import com.arjuna.ats.internal.jta.resources.XAResourceErrorHandler;
import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;

import com.arjuna.ats.arjuna.ObjectType;
import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
import com.arjuna.ats.arjuna.coordinator.RecordType;
import com.arjuna.ats.arjuna.coordinator.TxControl;
import com.arjuna.ats.arjuna.common.*;
import com.arjuna.ats.arjuna.state.*;
import com.arjuna.ats.arjuna.gandiva.ClassName;
import com.arjuna.ats.arjuna.recovery.RecoveryModule;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;

import java.util.Vector;
import java.util.Enumeration;

import java.io.*;

import javax.transaction.xa.Xid;
import javax.transaction.xa.XAResource;

import javax.transaction.xa.XAException;

/**
* @author Mark Little (mark_little@hp.com)
* @version $Id: XAResourceRecord.java 2342 2006-03-30 13:06:17Z $
* @since JTS 1.2.4.
*
* @message com.arjuna.ats.internal.jta.resources.arjunacore.noresource
*          [com.arjuna.ats.internal.jta.resources.arjunacore.noresource]
*          No XAResource to recover {0}
* @message com.arjuna.ats.internal.jta.resources.arjunacore.assumecomplete
*          [com.arjuna.ats.internal.jta.resources.arjunacore.assumecomplete]
*          Being told to assume complete on Xid {0}
*/

public class XAResourceRecord extends AbstractRecord
{

  public static final int XACONNECTION = 0;

  private static final Uid START_XARESOURCE = Uid.minUid();

  private static final Uid END_XARESOURCE = Uid.maxUid();

  /**
   * The params represent specific parameters we need to recreate the
   * connection to the database in the event of a failure. If they're not set
   * then recovery is out of our control.
   *
   * Could also use it to pass other information, such as the readonly flag.
   */

  public XAResourceRecord(TransactionImple tx, XAResource res, Xid xid,
      Object[] params)
  {
    super(new Uid(), null, ObjectType.ANDPERSISTENT);

    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.CONSTRUCTORS,
          VisibilityLevel.VIS_PUBLIC,
          com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
          "XAResourceRecord.XAResourceRecord ( " + xid + " )");
    }
   
    _theXAResource = res;
    _recoveryObject = null;
    _tranID = xid;

    _valid = true;

    if (params != null)
    {
      if (params.length >= XACONNECTION)
      {
        if (params[XACONNECTION] instanceof RecoverableXAConnection)
          _recoveryObject = (RecoverableXAConnection) params[XACONNECTION];
      }
    }

    _prepared = false;
    _heuristic = TwoPhaseOutcome.FINISH_OK;

    _theTransaction = tx;
  }

  public final Xid getXid()
  {
    return _tranID;
  }

  public Uid order()
  {
    if (_theXAResource instanceof StartXAResource)
      return START_XARESOURCE;
    else
    {
      if (_theXAResource instanceof EndXAResource)
        return END_XARESOURCE;
      else
        return super.order();
    }
  }

  public boolean propagateOnCommit()
  {
    return false; // cannot ever be nested!
  }

  public int typeIs()
  {
    return RecordType.JTA_RECORD;
  }

  public ClassName className()
  {
    return new ClassName("RecordType.JTA_RECORD");
  }

  public Object value()
  {
    return _theXAResource;
  }

  /**
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.setvalue
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.setvalue] {0}
   *          called illegally.
   */

  public void setValue(Object o)
  {
    if (jtaLogger.loggerI18N.isWarnEnabled())
    {
      jtaLogger.loggerI18N
          .warn(
              "com.arjuna.ats.internal.jta.resources.arjunacore.setvalue",
              new Object[]
              { "XAResourceRecord::set_value()" });
    }
  }

  public int nestedAbort()
  {
    return TwoPhaseOutcome.FINISH_OK;
  }

  public int nestedCommit()
  {
    return TwoPhaseOutcome.FINISH_OK;
  }

  /*
   * XA is not subtransaction aware.
   */

  public int nestedPrepare()
  {
    return TwoPhaseOutcome.PREPARE_OK; // do nothing
  }

  /**
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.preparenulltx
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.preparenulltx]
   *          {0} - null transaction!
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.preparefailed
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.preparefailed]
   *          {0} - prepare failed with exception {1}
   */

  public int topLevelPrepare()
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
          VisibilityLevel.VIS_PUBLIC,
          com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
          "XAResourceRecord.topLevelPrepare for " + _tranID);
    }

    if (!_valid || (_theXAResource == null))
    {
      removeConnection();

      return TwoPhaseOutcome.PREPARE_READONLY;
    }

    if (_tranID == null)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.resources.arjunacore.preparenulltx",
                new Object[]
                { "XAResourceRecord.prepare" });
      }

      removeConnection();

      return TwoPhaseOutcome.PREPARE_NOTOK;
    }

    try
    {
      if (endAssociation())
      {
        _theXAResource.end(_tranID, XAResource.TMSUCCESS);
      }

      _prepared = true;

      if (_theXAResource.prepare(_tranID) == XAResource.XA_RDONLY)
      {
                if (TxControl.isReadonlyOptimisation())
                {
                    // we won't be called again, so we need to tidy up now
                    removeConnection();
                }
               
        return TwoPhaseOutcome.PREPARE_READONLY;
      }
      else
        return TwoPhaseOutcome.PREPARE_OK;
    }
    catch (XAException e1)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.resources.arjunacore.preparefailed",
                new Object[]
                { "XAResourceRecord.prepare",
                    XAHelper.printXAErrorCode(e1) });
      }

      /*
       * XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
       * XAER_PROTO.
       */

      if (_rollbackOptimization) // won't have rollback called on it
        removeConnection();

      switch (e1.errorCode)
      {
      case XAException.XAER_RMERR:
      case XAException.XAER_RMFAIL:
      case XAException.XA_RBROLLBACK:
      case XAException.XA_RBEND:
      case XAException.XA_RBCOMMFAIL:
      case XAException.XA_RBDEADLOCK:
      case XAException.XA_RBINTEGRITY:
      case XAException.XA_RBOTHER:
      case XAException.XA_RBPROTO:
      case XAException.XA_RBTIMEOUT:
      case XAException.XAER_INVAL:
      case XAException.XAER_PROTO:
      case XAException.XAER_NOTA: // resource may have arbitrarily rolled back (shouldn't, but ...)
        return TwoPhaseOutcome.PREPARE_NOTOK;  // will not call rollback
      default:
        return TwoPhaseOutcome.HEURISTIC_HAZARD; // we're not really sure (shouldn't get here though).
      }
    }
    catch (Exception e2)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.resources.arjunacore.preparefailed",
                new Object[]
                { "XAResourceRecord.prepare", e2 });
      }

      if (_rollbackOptimization) // won't have rollback called on it
        removeConnection();

      return TwoPhaseOutcome.PREPARE_NOTOK;
    }
  }

  /**
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.rollbacknulltx
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.rollbacknulltx]
   *          {0} - null transaction!
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.rollbackxaerror
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.rollbackxaerror]
   *          {0} - xa error {1}
   */

  public int topLevelAbort()
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
          VisibilityLevel.VIS_PUBLIC,
          com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
          "XAResourceRecord.topLevelAbort for " + _tranID);
    }

    if (!_valid)
      return TwoPhaseOutcome.FINISH_ERROR;

    if (_theTransaction != null
        && _theTransaction.getXAResourceState(_theXAResource) == TxInfo.OPTIMIZED_ROLLBACK)
    {
      /*
       * Already rolledback during delist.
       */

      return TwoPhaseOutcome.FINISH_OK;
    }
   
    if (_tranID == null)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.resources.arjunacore.rollbacknulltx",
                new Object[]
                { "XAResourceRecord.rollback" });
      }

      return TwoPhaseOutcome.FINISH_ERROR;
    }
    else
    {
      if (_theXAResource == null)
        _theXAResource = getNewXAResource();

      if (_theXAResource != null)
      {
        if (_heuristic != TwoPhaseOutcome.FINISH_OK)
          return _heuristic;

        try
        {
          if (!_prepared)
          {
            if (endAssociation())
            {
              _theXAResource.end(_tranID, XAResource.TMSUCCESS);
            }
          }
        }
        catch (XAException e1)
        {
            if ((e1.errorCode >= XAException.XA_RBBASE)
            && (e1.errorCode < XAException.XA_RBEND))
            {
          /*
           * Has been marked as rollback-only. We still
           * need to call rollback.
           */
            }
            else
            {
          removeConnection();
         
          return TwoPhaseOutcome.FINISH_ERROR;
            }
        }
       
        try
        {
          _theXAResource.rollback(_tranID);
        }
        catch (XAException e1)
        {
          if (notAProblem(e1, false))
          {
            // some other thread got there first (probably)
          }
          else
          {
            if (jtaLogger.loggerI18N.isWarnEnabled())
            {
              jtaLogger.loggerI18N
                  .warn(
                      "com.arjuna.ats.internal.jta.resources.arjunacore.rollbackxaerror",
                      new Object[]
                      {
                          "XAResourceRecord.rollback",
                          XAHelper
                              .printXAErrorCode(e1) });
            }

            switch (e1.errorCode)
            {
            case XAException.XAER_RMERR:
              if (!_prepared)
                break; // just do the finally block
            case XAException.XA_HEURHAZ:
              return TwoPhaseOutcome.HEURISTIC_HAZARD;
            case XAException.XA_HEURCOM:
              return TwoPhaseOutcome.HEURISTIC_COMMIT;
            case XAException.XA_HEURMIX:
              return TwoPhaseOutcome.HEURISTIC_MIXED;
            case XAException.XAER_NOTA:
                if (_recovered)
              break; // rolled back previously and recovery completed
            case XAException.XA_HEURRB: // forget?
            case XAException.XA_RBROLLBACK:
            case XAException.XA_RBEND:
            case XAException.XA_RBCOMMFAIL:
            case XAException.XA_RBDEADLOCK:
            case XAException.XA_RBINTEGRITY:
            case XAException.XA_RBOTHER:
            case XAException.XA_RBPROTO:
            case XAException.XA_RBTIMEOUT:
              break;
            default:
              return TwoPhaseOutcome.FINISH_ERROR;
            }
          }
        }
        catch (Exception e2)
        {
          e2.printStackTrace();

          return TwoPhaseOutcome.FINISH_ERROR;
        }
        finally
        {
          if (!_prepared)
            removeConnection();
        }
      }
      else
      {
          if (jtaLogger.loggerI18N.isWarnEnabled())
          {
        jtaLogger.loggerI18N
            .warn(
            "com.arjuna.ats.internal.jta.resources.arjunacore.noresource",
            new Object[] {_tranID});
          }

          if (XAResourceRecord._assumedComplete)
          {
        if (jtaLogger.loggerI18N.isInfoEnabled())
        {
            jtaLogger.loggerI18N
          .info(
                "com.arjuna.ats.internal.jta.resources.arjunacore.assumecomplete",
                new Object[] {_tranID});
        }

        return TwoPhaseOutcome.FINISH_OK;
          }
          else
        return TwoPhaseOutcome.FINISH_ERROR;
      }
    }

    return TwoPhaseOutcome.FINISH_OK;
  }

  /**
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.commitnulltx
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.commitnulltx]
   *          {0} - null transaction!
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.commitxaerror
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.commitxaerror]
   *          {0} - xa error {1}
   *
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.commitexception
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.commitexception] {0}
   *          caught: {1}
   */
  public int topLevelCommit()
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
          VisibilityLevel.VIS_PUBLIC,
          com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
          "XAResourceRecord.topLevelCommit for " + _tranID);
    }

    if (!_prepared)
      return TwoPhaseOutcome.NOT_PREPARED;

    if (_tranID == null)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.resources.arjunacore.commitnulltx",
                new Object[]
                { "XAResourceRecord.commit" });
      }

      return TwoPhaseOutcome.FINISH_ERROR;
    }
    else
    {
      if (_theXAResource == null)
        _theXAResource = getNewXAResource();

      if (_theXAResource != null)
      {
        if (_heuristic != TwoPhaseOutcome.FINISH_OK)
          return _heuristic;

        /*
         * No need for end call here since we can only get to this
         * point by going through prepare.
         */
       
        try
        {
          _theXAResource.commit(_tranID, false);
        }
        catch (XAException e1)
        {
          if (notAProblem(e1, true))
          {
            // some other thread got there first (probably)
          }
          else
          {
            if (jtaLogger.loggerI18N.isWarnEnabled())
            {
              jtaLogger.loggerI18N
                  .warn(
                      "com.arjuna.ats.internal.jta.resources.arjunacore.commitxaerror",
                      new Object[]
                      {
                          "XAResourceRecord.commit",
                          XAHelper
                              .printXAErrorCode(e1) });
            }

            /*
             * XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX,
             * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
             * XAER_PROTO.
             */

            switch (e1.errorCode)
            {
            case XAException.XA_HEURHAZ:
              return TwoPhaseOutcome.HEURISTIC_HAZARD;
            case XAException.XA_HEURCOM: // what about forget?
                            // OTS doesn't support
                            // this code here.
              break;
            case XAException.XA_HEURRB:
            case XAException.XA_RBROLLBACK:
            case XAException.XA_RBCOMMFAIL:
            case XAException.XA_RBDEADLOCK:
            case XAException.XA_RBINTEGRITY:
            case XAException.XA_RBOTHER:
            case XAException.XA_RBPROTO:
            case XAException.XA_RBTIMEOUT:
            case XAException.XA_RBTRANSIENT:
            case XAException.XAER_RMERR:
              return TwoPhaseOutcome.HEURISTIC_ROLLBACK;
            case XAException.XA_HEURMIX:
              return TwoPhaseOutcome.HEURISTIC_MIXED;
            case XAException.XAER_NOTA:
                if (_recovered)
              break; // committed previously and recovery completed
                else
                    return TwoPhaseOutcome.HEURISTIC_HAZARD;  // something terminated the transaction!
            case XAException.XAER_PROTO:
            case XAException.XA_RETRY:
              return TwoPhaseOutcome.FINISH_ERROR;
            case XAException.XAER_INVAL:
            case XAException.XAER_RMFAIL: // resource manager
              // failed, did it
              // rollback?
              return TwoPhaseOutcome.HEURISTIC_HAZARD;
            default:
              return TwoPhaseOutcome.HEURISTIC_HAZARD;
            }
          }
        }
        catch (Exception e2)
        {
          if (jtaLogger.loggerI18N.isWarnEnabled())
          {
            jtaLogger.loggerI18N
                .warn("com.arjuna.ats.internal.jta.resources.arjunacore.commitexception",
                    new Object[] { "XAResourceRecord.topLevelCommit", e2 }, e2);
          }
                   
                    return TwoPhaseOutcome.FINISH_ERROR;
        }
        finally
        {
          removeConnection();
        }
      }
      else
      {
          if (jtaLogger.loggerI18N.isWarnEnabled())
          {
        jtaLogger.loggerI18N
            .warn(
            "com.arjuna.ats.internal.jta.resources.arjunacore.noresource",
            new Object[] {_tranID});
          }

          if (XAResourceRecord._assumedComplete)
          {
        if (jtaLogger.loggerI18N.isInfoEnabled())
        {
            jtaLogger.loggerI18N
          .info(
                "com.arjuna.ats.internal.jta.resources.arjunacore.assumecomplete",
                new Object[] {_tranID});
        }

        return TwoPhaseOutcome.FINISH_OK;
          }
          else
        return TwoPhaseOutcome.FINISH_ERROR;
      }
    }

    return TwoPhaseOutcome.FINISH_OK;
  }

  /**
   * Is the XAException a non-error when received in reply to commit or
   * rollback ? It normally is, but may be overridden in recovery.
   */

  protected boolean notAProblem(XAException ex, boolean commit)
  {
    return XAResourceErrorHandler.notAProblem(_theXAResource, ex, commit);
  }

  public int nestedOnePhaseCommit()
  {
    return TwoPhaseOutcome.FINISH_ERROR;
  }

  /**
   * For commit_one_phase we can do whatever we want since the transaction
   * outcome is whatever we want. Therefore, we do not need to save any
   * additional recoverable state, such as a reference to the transaction
   * coordinator, since it will not have an intentions list anyway.
   *
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.opcnulltx
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.opcnulltx] {0} -
   *          null transaction!
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.opcerror
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.opcerror] {0}
   *          caught: {1}
   */

  public int topLevelOnePhaseCommit()
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
          VisibilityLevel.VIS_PUBLIC,
          com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
          "XAResourceRecord.topLevelOnePhaseCommit for " + _tranID);
    }

    if (_tranID == null)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.resources.arjunacore.opcnulltx",
                new Object[]
                { "XAResourceRecord.1pc" });
      }

      return TwoPhaseOutcome.FINISH_ERROR;
    }
    else
    {
      if (_theXAResource != null)
      {
        if (_heuristic != TwoPhaseOutcome.FINISH_OK)
          return _heuristic;

        boolean commit = true;
        XAException endHeuristic = null;
       
        try
        {
          /*
           * TODO in Oracle the end is not needed. Is this common
           * across other RMs?
           */

          if (endAssociation())
          {
            _theXAResource.end(_tranID, XAResource.TMSUCCESS);
          }
        }
        catch (XAException e1)
        {   
            /*
             * Now it's not legal to return a heuristic from end, but
             * apparently Oracle does (http://jira.jboss.com/jira/browse/JBTM-343)
             * Since this is 1PC we can call forget: the outcome of the
             * transaction is the outcome of the participant.
             */

            switch (e1.errorCode)
            {
            case XAException.XA_HEURHAZ:
            case XAException.XA_HEURMIX:
            case XAException.XA_HEURCOM:
            case XAException.XA_HEURRB:
          endHeuristic = e1;
          break;
            case XAException.XA_RBROLLBACK:
            case XAException.XA_RBCOMMFAIL:
            case XAException.XA_RBDEADLOCK:
            case XAException.XA_RBINTEGRITY:
            case XAException.XA_RBOTHER:
            case XAException.XA_RBPROTO:
            case XAException.XA_RBTIMEOUT:
            case XAException.XA_RBTRANSIENT:
          /*
           * Has been marked as rollback-only. We still
           * need to call rollback.
           */
         
          commit = false;
          break;
            case XAException.XAER_RMERR:
            case XAException.XAER_NOTA:
            case XAException.XAER_PROTO:
            case XAException.XAER_INVAL:
            case XAException.XAER_RMFAIL:
            default:
            {
          removeConnection();
          return TwoPhaseOutcome.FINISH_ERROR;
            }
            }
        }
       
        try
        {
            /*
             * Not strictly necessary since calling commit will
             * do the rollback if end failed as above.
             */
           
            if (endHeuristic != null) // catch those RMs that terminate in end rather than follow the spec
          throw endHeuristic;
           
            if (commit)
          _theXAResource.commit(_tranID, true);
            else
          _theXAResource.rollback(_tranID);
        }
        catch (XAException e1)
        {
          /*
           * XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX,
           * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
           * XAER_PROTO. XA_RB*
           */

          switch (e1.errorCode)
          {
          case XAException.XA_HEURHAZ:
          case XAException.XA_HEURMIX:
            return TwoPhaseOutcome.HEURISTIC_HAZARD;
          case XAException.XA_HEURCOM:
            forget();
            break;
          case XAException.XA_HEURRB:
          case XAException.XA_RBROLLBACK:
          case XAException.XA_RBCOMMFAIL:
          case XAException.XA_RBDEADLOCK:
          case XAException.XA_RBINTEGRITY:
          case XAException.XA_RBOTHER:
          case XAException.XA_RBPROTO:
          case XAException.XA_RBTIMEOUT:
          case XAException.XA_RBTRANSIENT:
          case XAException.XAER_RMERR:
            forget();
            return TwoPhaseOutcome.FINISH_ERROR;
          case XAException.XAER_NOTA:
            return TwoPhaseOutcome.HEURISTIC_HAZARD; // something committed or rolled back without asking us!
          case XAException.XAER_PROTO:
          case XAException.XAER_INVAL:
          case XAException.XAER_RMFAIL: // resource manager failed,
            // did it rollback?
            return TwoPhaseOutcome.FINISH_ERROR;
          default:
            return TwoPhaseOutcome.FINISH_ERROR;
          }
        }
        catch (Exception e2)
        {
          if (jtaLogger.loggerI18N.isWarnEnabled())
          {
            jtaLogger.loggerI18N
                .warn(
                    "com.arjuna.ats.internal.jta.resources.arjunacore.opcerror",
                    new Object[]
                    { "XAResourceRecord.commit_one_phase",
                        e2 });
          }

          return TwoPhaseOutcome.FINISH_ERROR;
        }
        finally
        {
          removeConnection();
        }
      }
      else
        return TwoPhaseOutcome.FINISH_ERROR;
    }

    return TwoPhaseOutcome.FINISH_OK;
  }

  public boolean forgetHeuristic()
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
          VisibilityLevel.VIS_PUBLIC,
          com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
          "XAResourceRecord.forget for " + _tranID);
    }

    forget();

    removeConnection();

    return true;
  }

  private void forget()
  {
    if ((_theXAResource != null) && (_tranID != null))
    {
      try
      {
        _theXAResource.forget(_tranID);
      }
      catch (Exception e)
      {
      }
    }
  }

  /*
   * Independant recovery cannot occur. Must be driven by the recovery of the
   * local transaction, i.e., top-down recovery.
   */

  protected int recover()
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
          VisibilityLevel.VIS_PROTECTED,
          com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
          "XAResourceRecord.recover");
    }

    return XARecoveryResource.FAILED_TO_RECOVER;
  }

  public static AbstractRecord create()
  {
    return new XAResourceRecord();
  }

  public static void remove(AbstractRecord toDelete)
  {
    toDelete = null;
  }

  /**
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.savestate
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.savestate]
   *          Could not serialize a Serializable XAResource!
   */

  public boolean save_state(OutputObjectState os, int t)
  {
    boolean res = false;

    try
    {
      os.packInt(_heuristic);

      /*
       * Since we don't know what type of Xid we are using, leave it up to
       * XID to pack.
       */

      XidImple.pack(os, _tranID);

      /*
       * If no recovery object set then rely upon object serialisation!
       */

      if (_recoveryObject == null)
      {
        os.packInt(RecoverableXAConnection.OBJECT_RECOVERY);

                                if (_theXAResource instanceof Serializable)
                                {
                try
                {
                  ByteArrayOutputStream s = new ByteArrayOutputStream();
                  ObjectOutputStream o = new ObjectOutputStream(s);
       
                  o.writeObject(_theXAResource);
                  o.close();
       
                  os.packBoolean(true);
       
                  os.packBytes(s.toByteArray());
                }
                catch (NotSerializableException ex)
                {
                    if (jtaLogger.loggerI18N.isWarnEnabled())
                    {
                        jtaLogger.loggerI18N
                            .warn("com.arjuna.ats.internal.jta.resources.arjunacore.savestate");
                    }
                                           
                                            return false;
                }
                                }
                                else
                                {
                                    // have to rely upon XAResource.recover!
                                   
                                    os.packBoolean(false);
                                }
      }
      else
      {
        os.packInt(RecoverableXAConnection.AUTO_RECOVERY);
        os.packString(_recoveryObject.getClass().getName());

        _recoveryObject.packInto(os);
      }

      res = true;
    }
    catch (Exception e)
    {
      e.printStackTrace();

      res = false;
    }

    if (res)
      res = super.save_state(os, t);

    return res;
  }

  /**
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.restorestate
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.restorestate]
   *          Exception on attempting to restore XAResource
   * @message com.arjuna.ats.internal.jta.resources.arjunacore.norecoveryxa
   *          [com.arjuna.ats.internal.jta.resources.arjunacore.norecoveryxa]
   *          Could not find new XAResource to use for recovering
   *          non-serializable XAResource {0}
   */

  public boolean restore_state(InputObjectState os, int t)
  {
    boolean res = false;

    try
    {
      _heuristic = os.unpackInt();
      _tranID = XidImple.unpack(os);

      _theXAResource = null;
      _recoveryObject = null;

      if (os.unpackInt() == RecoverableXAConnection.OBJECT_RECOVERY)
      {
        boolean haveXAResource = os.unpackBoolean();

        if (haveXAResource)
        {
          try
          {
            byte[] b = os.unpackBytes();

            ByteArrayInputStream s = new ByteArrayInputStream(b);
            ObjectInputStream o = new ObjectInputStream(s);

            _theXAResource = (XAResource) o.readObject();
            o.close();

            if (jtaLogger.logger.isDebugEnabled())
            {
              jtaLogger.logger
                  .debug(
                      DebugLevel.FUNCTIONS,
                      VisibilityLevel.VIS_PUBLIC,
                      com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
                      "XAResourceRecord.restore_state - XAResource de-serialized");
            }
          }
          catch (Exception ex)
          {
            // not serializable in the first place!

            if (jtaLogger.loggerI18N.isWarnEnabled())
            {
              jtaLogger.loggerI18N
                  .warn(
                      "com.arjuna.ats.internal.jta.resources.arjunacore.restorestate",
                      ex);
            }

            return false;
          }
        }
        else
        {
          /*
           * Lookup new XAResource via XARecoveryModule if possible.
           */

          _theXAResource = getNewXAResource();

          if (_theXAResource == null)
          {
            jtaLogger.loggerI18N
                .warn(
                    "com.arjuna.ats.internal.jta.resources.arjunacore.norecoveryxa",
                    new Object[]
                    { _tranID });

            /*
             * Don't prevent tx from activating because there may be
             * other participants that can still recover. Plus, we will
             * try to get a new XAResource later for this instance.
             */
           
            res = true;
          }
        }
      }
      else
      {
        String creatorName = os.unpackString();
        Class c = Thread.currentThread().getContextClassLoader()
            .loadClass(creatorName);

        _recoveryObject = (RecoverableXAConnection) c.newInstance();

        _recoveryObject.unpackFrom(os);
        _theXAResource = _recoveryObject.getResource();

        if (jtaLogger.logger.isDebugEnabled())
        {
          jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
              VisibilityLevel.VIS_PUBLIC,
              com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
              "XAResourceRecord.restore_state - XAResource got from "
                  + creatorName);
        }
      }

      res = true;
    }
    catch (Exception e)
    {
      e.printStackTrace();

      res = false;
    }

    if (res)
      res = super.restore_state(os, t);

    return res;
  }

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

  public static String typeName()
  {
    return "/StateManager/AbstractRecord/XAResourceRecord";
  }

  public boolean doSave()
  {
    return true;
  }

  public void merge(AbstractRecord a)
  {
  }

  public void alter(AbstractRecord a)
  {
  }

  public boolean shouldAdd(AbstractRecord a)
  {
    return false;
  }

  public boolean shouldAlter(AbstractRecord a)
  {
    return false;
  }

  public boolean shouldMerge(AbstractRecord a)
  {
    return false;
  }

  public boolean shouldReplace(AbstractRecord a)
  {
    return false;
  }

  protected XAResourceRecord()
  {
    super();

    _theXAResource = null;
    _recoveryObject = null;
    _tranID = null;
    _prepared = true;
    _heuristic = TwoPhaseOutcome.FINISH_OK;
    _valid = true;
    _theTransaction = null;
    _recovered = true;
  }

  protected XAResourceRecord(Uid u)
  {
    super(u, null, ObjectType.ANDPERSISTENT);

    _theXAResource = null;
    _recoveryObject = null;
    _tranID = null;
    _prepared = true;
    _heuristic = TwoPhaseOutcome.FINISH_OK;
    _valid = true;
    _theTransaction = null;
    _recovered = true;
  }

  /**
   * For those objects where the original XAResource could not be saved.
   */

  protected synchronized void setXAResource(XAResource res)
  {
    _theXAResource = res;
  }

  /**
   * This routine finds the new XAResource for the transaction that used the
   * old resource we couldn't serialize. It does this by looking up the
   * XARecoveryModule in the recovery manager and asking it for the
   * XAResource. The recovery manager will then look through its list of
   * registered XARecoveryResource implementations for the appropriate
   * instance. If the XARecoveryModule hasn't been initialised yet then this
   * routine will fail, but on the next scan it should work.
   */

  private final XAResource getNewXAResource()
  {
    RecoveryManager recMan = RecoveryManager.manager();
    Vector recoveryModules = recMan.getModules();

    if (recoveryModules != null)
    {
      Enumeration modules = recoveryModules.elements();

      while (modules.hasMoreElements())
      {
        RecoveryModule m = (RecoveryModule) modules.nextElement();

        if (m instanceof XARecoveryModule)
        {
            /*
             * Blaargh! There are better ways to do this!
             */

          return ((XARecoveryModule) m).getNewXAResource(_tranID);
        }
      }
    }

    return null;
  }

  private final void removeConnection()
  {
    /*
     * Should only be called once. Remove the connection so that user can
     * reuse the driver as though it were fresh (e.g., can do read only
     * optimisation).
     */

    if (_recoveryObject != null)
    {
      _recoveryObject.close();
      _recoveryObject = null;
    }

    if (_theTransaction != null)
      _theTransaction = null;
  }

  /*
   * Ask the transaction whether or not this XAResource is still associated
   * with the thread, i.e., has end already been called on it?
   */

  private final boolean endAssociation()
  {
    boolean doEnd = true;

    if (_theTransaction != null)
    {
      if (_theTransaction.getXAResourceState(_theXAResource) == TxInfo.NOT_ASSOCIATED)
      {
        // end has been called so we don't need to do it again!

        doEnd = false;
      }
    }
    else
      doEnd = false; // Recovery mode

    return doEnd;
  }

  protected XAResource _theXAResource;

  private RecoverableXAConnection _recoveryObject;
  private Xid _tranID;

  private boolean _prepared;

  private boolean _valid;

  private int _heuristic;

  private TransactionImple _theTransaction;
    private boolean _recovered = false;

  private static boolean _rollbackOptimization = false;
    private static boolean _assumedComplete = false;

  static
  {
    String optimization = jtaPropertyManager.propertyManager.getProperty(
        Environment.JTA_TM_IMPLEMENTATION, "OFF");

    if (optimization.equals("ON"))
      _rollbackOptimization = true;

    /*
     * WARNING: USE WITH EXTEREME CARE!!
     *
     * This assumes that if there is no XAResource that can deal with an Xid
     * after recovery, then we failed after successfully committing the transaction
     * but before updating the log. In which case we just need to ignore this
     * resource and remove the entry from the log.
     *
     * BUT if not all XAResourceRecovery instances are correctly implemented
     * (or present) we may end up removing participants that have not been dealt
     * with. Hence USE WITH EXTREME CARE!!
     */

    String assumedComplete = jtaPropertyManager.propertyManager.getProperty(
        Environment.XA_ASSUME_RECOVERY_COMPLETE, "FALSE");

    if (assumedComplete.equalsIgnoreCase("true"))
      _assumedComplete = true;
  }

}
TOP

Related Classes of com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord

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.