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

Source Code of com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule

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

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

import com.arjuna.ats.arjuna.common.*;
import com.arjuna.ats.arjuna.state.*;
import com.arjuna.ats.arjuna.objectstore.ObjectStore;
import com.arjuna.ats.arjuna.coordinator.TxControl;
import com.arjuna.ats.arjuna.recovery.RecoveryModule;
import com.arjuna.ats.arjuna.logging.FacilityCode;

import com.arjuna.ats.internal.jta.utils.XAUtils;
import com.arjuna.ats.internal.jta.transaction.arjunacore.AtomicAction;

import com.arjuna.ats.jta.logging.jtaLogger;
import com.arjuna.ats.jta.common.jtaPropertyManager;
import com.arjuna.ats.jta.common.Environment;
import com.arjuna.ats.jta.recovery.*;
import com.arjuna.ats.jta.utils.XAHelper;
import com.arjuna.ats.jta.xa.XidImple;

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

import java.util.*;
import javax.transaction.xa.*;

import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;

import java.io.IOException;
import javax.transaction.xa.XAException;

/**
* Designed to be able to recover any XAResource.
*/

public class XARecoveryModule implements RecoveryModule
{

  public static final String XARecoveryPropertyNamePrefixOld = "XAConnectionRecovery";

    // why not in Environment?

  public static final String XARecoveryPropertyNamePrefix = "com.arjuna.ats.jta.recovery.XAResourceRecovery";

  private static final String RECOVER_ALL_NODES = "*";

  public XARecoveryModule ()
  {
    this(
        com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryResourceManagerImple.class
            .getName(), "Local XARecoveryModule");

    com.arjuna.ats.internal.jta.Implementations.initialise();
  }

  /**
   * @message com.arjuna.ats.internal.jta.recovery.xafirstpass {0} - first
   *          pass
   * @message com.arjuna.ats.internal.jta.recovery.alluids could not get all
   *          object Uids.
   * @message com.arjuna.ats.internal.jta.recovery.objstoreerror {0}
   * @message com.arjuna.ats.internal.jta.recovery.periodicfirstpass {0}
   *          exception
   * @message com.arjuna.ats.internal.jta.recovery.info.firstpass {0} - first pass
   */

  public void periodicWorkFirstPass ()
  {
    if (jtaLogger.loggerI18N.isInfoEnabled())
    {
      jtaLogger.loggerI18N.info("com.arjuna.ats.internal.jta.recovery.info.firstpass",
          new Object[] { _logName});
    }

    _uids = new InputObjectState();

    /*
     * Scan for resources in the object store.
     */

    try
    {
      if (!_objStore.allObjUids(_recoveryManagerClass.type(), _uids))
      {
        if (jtaLogger.loggerI18N.isWarnEnabled())
        {
          jtaLogger.loggerI18N
              .warn("com.arjuna.ats.internal.jta.recovery.alluids");
        }
      }
    }
    catch (ObjectStoreException e)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.recovery.objstoreerror",
                e);
      }
    }
    catch (Exception e)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.recovery.periodicfirstpass",
                new Object[] { _logName + ".periodicWorkFirstPass exception " },
                e);
      }
    }
  }

  /**
   * @message com.arjuna.ats.internal.jta.recovery.periodicsecondpass {0}
   *          exception
   * @message com.arjuna.ats.internal.jta.recovery.info.secondpass {0} - second pass
   */

  public void periodicWorkSecondPass ()
  {
    if (jtaLogger.logger.isInfoEnabled())
    {
      if (jtaLogger.loggerI18N.isInfoEnabled())
      {
        jtaLogger.loggerI18N.info("com.arjuna.ats.internal.jta.recovery.info.secondpass",
            new Object[] { _logName});
      }
    }

    try
    {
      // do the recovery on anything from the scan in first pass

      transactionInitiatedRecovery();

      if (jtaLogger.logger.isDebugEnabled())
      {
        jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
            VisibilityLevel.VIS_PUBLIC,
            FacilityCode.FAC_CRASH_RECOVERY,
            _logName + ".transactionInitiatedRecovery completed");
      }

      /*
       * See the comment about this routine!!
       */

      resourceInitiatedRecovery();

      if (jtaLogger.logger.isDebugEnabled())
      {
        jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
            VisibilityLevel.VIS_PUBLIC,
            FacilityCode.FAC_CRASH_RECOVERY,
            _logName + ".resourceInitiatedRecovery completed");
      }
    }
    catch (Exception e)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.recovery.periodicsecondpass",
                new Object[] { _logName + ".periodicWorkSecondPass exception " },
                e);
      }
    }

    clearAllFailures();
  }

  public String id ()
  {
    return "XARecoveryModule:" + _recoveryManagerClass;
  }

  /**
   * @param Xid
   *            xid The transaction to commit/rollback.
   *
   * @return the XAResource than can be used to commit/rollback the specified
   *         transaction.
   */

  public XAResource getNewXAResource (Xid xid)
  {
    if (_xidScans != null)
    {
      Enumeration keys = _xidScans.keys();

      while (keys.hasMoreElements())
      {
        XAResource theKey = (XAResource) keys.nextElement();
        RecoveryXids xids = (RecoveryXids) _xidScans.get(theKey);

        if (xids.contains(xid)) return theKey;
      }
    }

    return null;
  }

  /**
   * @message com.arjuna.ats.internal.jta.recovery.constfail {0}
   * @message com.arjuna.ats.internal.jta.recovery.classloadfail {0} - could
   *          not get class name for {1}
   * @message com.arjuna.ats.internal.jta.recovery.general Caught exception:
   *          {0} for {1}
   * @message com.arjuna.ats.internal.jta.recovery.info.loading {0} loading {1}
   */

  protected XARecoveryModule (String recoveryClass, String logName)
  {
    _xaRecoverers = new Vector();
    _logName = logName;

    try
    {
      Class c = Thread.currentThread().getContextClassLoader().loadClass(
          recoveryClass);

      _recoveryManagerClass = (XARecoveryResourceManager) c.newInstance();
    }
    catch (Exception ex)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N.warn(
            "com.arjuna.ats.internal.jta.recovery.constfail", ex);
      }

      _recoveryManagerClass = null;
    }

    Properties props = jtaPropertyManager.propertyManager.getProperties();

    if (props != null)
    {
      Enumeration names = props.propertyNames();

      while (names.hasMoreElements())
      {
        String propName = (String) names.nextElement();

        if (propName
            .startsWith(XARecoveryModule.XARecoveryPropertyNamePrefix) || propName
            .startsWith(XARecoveryModule.XARecoveryPropertyNamePrefixOld))
        {
          /*
           * Given the recovery string, create the class it refers to
           * and store it.
           */

          String theClassAndParameter = jtaPropertyManager.propertyManager
              .getProperty(propName);

          // see if there is a string parameter

          int breakPosition = theClassAndParameter
              .indexOf(BREAKCHARACTER);

          String theClass = null;
          String theParameter = null;

          if (breakPosition != -1)
          {
            theClass = theClassAndParameter.substring(0,
                breakPosition);
            theParameter = theClassAndParameter
                .substring(breakPosition + 1);
          }
          else
          {
            theClass = theClassAndParameter;
          }

          if (jtaLogger.loggerI18N.isInfoEnabled())
          {
            if (jtaLogger.loggerI18N.isInfoEnabled())
            {
              jtaLogger.loggerI18N.info("com.arjuna.ats.internal.jta.recovery.info.loading",
                  new Object[] { _logName, (theClass + ((theParameter != null) ? theParameter : ""))});
            }
          }

          if (theClass == null)
          {
            if (jtaLogger.loggerI18N.isWarnEnabled())
            {
              jtaLogger.loggerI18N
                  .warn(
                      "com.arjuna.ats.internal.jta.recovery.classloadfail",
                      new Object[] { _logName, propName });
            }
          }
          else
          {
            try
            {
              Class c = Thread.currentThread()
                  .getContextClassLoader()
                  .loadClass(theClass);
              boolean newInterface = false;

              try
              {
                XAConnectionRecovery ri = (XAConnectionRecovery) c
                    .newInstance();

                if (theParameter != null)
                  ri.initialise(theParameter);

                _xaRecoverers.addElement(ri);
              }
              catch (ClassCastException ex)
              {
                newInterface = true;
              }

              if (newInterface)
              {
                XAResourceRecovery ri = (XAResourceRecovery) c
                    .newInstance();

                if (theParameter != null)
                  ri.initialise(theParameter);

                _xaRecoverers.addElement(ri);
              }
            }
            catch (Exception e)
            {
              if (jtaLogger.loggerI18N.isWarnEnabled())
              {
                jtaLogger.loggerI18N
                    .warn(
                        "com.arjuna.ats.internal.jta.recovery.general",
                        new Object[] { e, theClass });
              }
            }
          }
        }
        else
        {
          if (propName.startsWith(Environment.XA_RECOVERY_NODE))
          {
            /*
             * Find the node(s) we can recover on behalf of.
             */

            String name = jtaPropertyManager.propertyManager
                .getProperty(propName);

            if (_xaRecoveryNodes == null)
              _xaRecoveryNodes = new Vector();

            _xaRecoveryNodes.addElement(name);
          }
        }
      }
    }

    if ((_xaRecoveryNodes == null) || (_xaRecoveryNodes.size() == 0))
    {
      if (jtaLogger.loggerI18N.isInfoEnabled())
      {
        jtaLogger.loggerI18N
            .info("com.arjuna.ats.internal.jta.recovery.noxanodes");
      }
    }
  }

  /**
   * @message com.arjuna.ats.internal.jta.recovery.recoveryfailed JTA failed
   *          to recovery {0}; got status {1}
   * @message com.arjuna.ats.internal.jta.recovery.recoverydelayed JTA
   *          recovery delayed for {0}; got status {1} so waiting for
   *          coordinator driven recovery
   * @message com.arjuna.ats.internal.jta.recovery.recoveryerror Recovery
   *          threw:
   * @message com.arjuna.ats.internal.jta.recovery.cannotadd Cannot add
   *          resource to table: no XID value available.
   * @message com.arjuna.ats.internal.jta.recovery.unexpectedrecoveryerror
   *          Unexpceted recovery error:
   * @message com.arjuna.ats.internal.jta.recovery.noxanodes No XA recovery
   *          nodes specified. Will only recover saved states.
   */

  private final boolean transactionInitiatedRecovery ()
  {
    Uid theUid = new Uid();

    while (theUid.notEquals(Uid.nullUid()))
    {
      try
      {
        theUid.unpack(_uids);

        if (theUid.notEquals(Uid.nullUid()))
        {
          /*
           * Ignore it if it isn't in the store any more. Transaction
           * probably recovered it.
           */

          if (_objStore.currentState(theUid, _recoveryManagerClass
              .type()) != ObjectStore.OS_UNKNOWN)
          {
            boolean problem = false;
            XARecoveryResource record = null;

            try
            {
              record = _recoveryManagerClass.getResource(theUid);

              problem = true;

              switch (record.recoverable())
              {
              case XARecoveryResource.RECOVERY_REQUIRED:
              {
                if (jtaLogger.logger.isDebugEnabled())
                {
                  jtaLogger.logger
                      .debug(
                          DebugLevel.FUNCTIONS,
                          VisibilityLevel.VIS_PUBLIC,
                          FacilityCode.FAC_CRASH_RECOVERY,
                          "XARecovery attempting recovery of " + theUid);
                }

                int recoveryStatus = record.recover();

                if (recoveryStatus != XARecoveryResource.RECOVERED_OK)
                {
                  if (recoveryStatus == XARecoveryResource.WAITING_FOR_RECOVERY)
                  {
                    problem = false;

                    if (jtaLogger.loggerI18N
                        .isInfoEnabled())
                    {
                      jtaLogger.loggerI18N
                          .info(
                              "com.arjuna.ats.internal.jta.recovery.recoverydelayed",
                              new Object[] { theUid, new Integer(
                                  recoveryStatus) });
                    }
                  }
                  else
                  {
                    if (jtaLogger.loggerI18N
                        .isWarnEnabled())
                    {
                      jtaLogger.loggerI18N
                          .warn(
                              "com.arjuna.ats.internal.jta.recovery.recoveryfailed",
                              new Object[] { theUid, new Integer(
                                  recoveryStatus) });
                    }
                  }
                }
                else
                  problem = false; // resource initiated
                            // recovery not possible
                            // (no distribution).
              }
                break;
              case XARecoveryResource.INFLIGHT_TRANSACTION:
              {
                /*
                 * Transaction was inflight and between us
                 * noticing it and trying to access the state,
                 * it finished and removed the state.
                 */

                problem = false;
              }
                break;
              case XARecoveryResource.INCOMPLETE_STATE:
              default:
              {
                if (jtaLogger.logger.isDebugEnabled())
                {
                  jtaLogger.logger
                      .debug(
                          DebugLevel.FUNCTIONS,
                          VisibilityLevel.VIS_PUBLIC,
                          FacilityCode.FAC_CRASH_RECOVERY,
                          "XARecovery " + theUid + " is non-recoverable");
                }
              }
                break;
              }
            }
            catch (NullPointerException ex)
            {
              problem = true;
            }
            catch (Throwable e)
            {
              problem = true;

              if (jtaLogger.loggerI18N.isWarnEnabled())
              {
                jtaLogger.loggerI18N
                    .warn(
                        "com.arjuna.ats.internal.jta.recovery.recoveryerror",
                        e);
              }
            }

            if (problem && (record != null))
            {
              /*
               * Some error occurred which prevented the state of
               * the resource from being read from the log. Hence
               * we don't have a valid key to use to insert it
               * into the list of records to be recovered. Print a
               * warning and move on. Force recovery via the
               * administration tool. Should be a rare occurrence.
               */

              if (record.getXid() == null)
              {
                if (jtaLogger.loggerI18N.isWarnEnabled())
                {
                  jtaLogger.loggerI18N
                      .warn("com.arjuna.ats.internal.jta.recovery.cannotadd");
                }
              }
              else
              {
                addFailure(record.getXid(), record.get_uid());
              }
            }
          }
        }
      }
      catch (IOException e)
      {
        theUid = Uid.nullUid();
      }
      catch (Throwable e)
      {
        if (jtaLogger.loggerI18N.isWarnEnabled())
        {
          jtaLogger.loggerI18N
              .warn(
                  "com.arjuna.ats.internal.jta.recovery.unexpectedrecoveryerror",
                  e);
        }
      }
    }

    return true;
  }

  /**
   * Now check for any outstanding transactions. If we didn't fail to recover
   * them, then roll them back - if they'd got through prepare we would have
   * an entry within the object store.
   *
   * Rely upon _xaRecoverers being set up properly (via properties).
   *
   * We cannot just remember the XAResourceRecords we used (if any) to cache
   * the JDBC connection information and use that since we may never have had
   * any such records!
   *
   * IMPORTANT: resourceInitiatedRecovery may rollback transactions which are
   * inflight: just because we have no entry for a transaction in the object
   * store does not mean it does not exist - it may be *about* to write its
   * intentions list. To try to reduce this probability we remember potential
   * rollback-ees at this scan, and wait for the next scan before actually
   * rolling them back.
   *
   * Note we cannot use the method that works with Transactions and
   * TransactionalObjects, of checking with original process that created the
   * transaction, because we don't know which process it was.
   *
   * @message com.arjuna.ats.internal.jta.recovery.getxaresource Caught:
   */

  private final boolean resourceInitiatedRecovery ()
  {
    /*
     * Now any additional connections we may need to create. Relies upon
     * information provided by the application.
     */

    if (_xaRecoverers.size() > 0)
    {
      for (int i = 0; i < _xaRecoverers.size(); i++)
      {
        XAResource resource = null;

        /*
         * All of this just because we deprecated XAConnectionRecovery.
         * Remove it once that class is removed.
         */

        if (_xaRecoverers.elementAt(i) instanceof XAConnectionRecovery)
        {
          try
          {
            XAConnectionRecovery ri = (XAConnectionRecovery) _xaRecoverers
                .elementAt(i);

            while (ri.hasMoreConnections())
            {
              try
              {
                resource = ri.getConnection().getXAResource();

                xaRecovery(resource);
              }
              catch (Exception exp)
              {
                if (jtaLogger.loggerI18N.isWarnEnabled())
                {
                  jtaLogger.loggerI18N
                      .warn(
                          "com.arjuna.ats.internal.jta.recovery.getxaresource",
                          exp);
                }
              }
            }
          }
          catch (Exception ex)
          {
            if (jtaLogger.loggerI18N.isWarnEnabled())
            {
              jtaLogger.loggerI18N
                  .warn(
                      "com.arjuna.ats.internal.jta.recovery.getxaresource",
                      ex);
            }
          }
        }
        else
        {
          try
          {
            XAResourceRecovery ri = (XAResourceRecovery) _xaRecoverers
                .elementAt(i);

            while (ri.hasMoreResources())
            {
              try
              {
                resource = ri.getXAResource();

                xaRecovery(resource);
              }
              catch (Exception exp)
              {
                if (jtaLogger.loggerI18N.isWarnEnabled())
                {
                  jtaLogger.loggerI18N
                      .warn(
                          "com.arjuna.ats.internal.jta.recovery.getxaresource",
                          exp);
                }
              }
            }
          }
          catch (Exception ex)
          {
            if (jtaLogger.loggerI18N.isWarnEnabled())
            {
              jtaLogger.loggerI18N
                  .warn(
                      "com.arjuna.ats.internal.jta.recovery.getxaresource",
                      ex);
            }
          }
        }
      }
    }

    return true;
  }

  /**
   * @message com.arjuna.ats.internal.jta.recovery.xarecovery1 {0} got XA
   *          exception {1}, {2}
   * @message com.arjuna.ats.internal.jta.recovery.xarecovery2 {0} got
   *          exception {1}
   * @message com.arjuna.ats.internal.jta.recovery.failedtorecover {0} -
   *          failed to recover XAResource.
   * @message com.arjuna.ats.internal.jta.recovery.forgetfailed {0} - forget
   *          threw: {1}
   * @message com.arjuna.ats.internal.jta.recovery.generalrecoveryerror {0} -
   *          caught {1}
   * @message com.arjuna.ats.internal.jta.recovery.info.rollingback Rolling back {0}
   * @message com.arjuna.ats.internal.jta.recovery.info.notrollback Told not to rollback {0}
   */

  private final boolean xaRecovery (XAResource xares)
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
          VisibilityLevel.VIS_PUBLIC,
          FacilityCode.FAC_CRASH_RECOVERY, "xarecovery of " + xares);
    }

    try
    {
      Xid[] trans = null;

      try
      {
        trans = xares.recover(XAResource.TMSTARTRSCAN);

        if (jtaLogger.loggerI18N.isDebugEnabled())
        {
          jtaLogger.logger
              .debug(
                  DebugLevel.FUNCTIONS,
                  VisibilityLevel.VIS_PUBLIC,
                  FacilityCode.FAC_CRASH_RECOVERY,
                  "Found " + ((trans != null) ? trans.length : 0) + " xids in doubt");
        }
      }
      catch (XAException e)
      {
        if (jtaLogger.loggerI18N.isWarnEnabled())
        {
          jtaLogger.loggerI18N
              .warn(
                  "com.arjuna.ats.internal.jta.recovery.xarecovery1",
                  new Object[] { _logName + ".xaRecovery ", e, XAHelper
                      .printXAErrorCode(e) });
        }

        try
        {
          xares.recover(XAResource.TMENDRSCAN);
        }
        catch (Exception e1)
        {
        }

        return false;
      }

      RecoveryXids xidsToRecover = null;

      if (_xidScans == null) _xidScans = new Hashtable();
      else
      {
        xidsToRecover = (RecoveryXids) _xidScans.get(xares);

        if (xidsToRecover == null)
        {
          java.util.Enumeration elements = _xidScans.elements();
          boolean found = false;

          while (elements.hasMoreElements())
          {
            xidsToRecover = (RecoveryXids) elements.nextElement();

            if (xidsToRecover.isSameRM(xares))
            {
              found = true;

              break;
            }
          }

          if (!found) xidsToRecover = null;
        }
      }

      if (xidsToRecover == null)
      {
        xidsToRecover = new RecoveryXids(xares);

        _xidScans.put(xares, xidsToRecover);
      }

      xidsToRecover.nextScan(trans);

      Object[] xids = xidsToRecover.toRecover();

      if (xids != null)
      {
        if (jtaLogger.loggerI18N.isDebugEnabled())
        {
          jtaLogger.logger
              .debug(
                  DebugLevel.FUNCTIONS,
                  VisibilityLevel.VIS_PUBLIC,
                  FacilityCode.FAC_CRASH_RECOVERY,
                  "Have " + xids.length + " Xids to recover on this pass.");
        }

        for (int j = 0; j < xids.length; j++)
        {
          boolean doForget = false;

          /*
           * Check if in failure list.
           */

          Uid recordUid = null;
          boolean foundTransaction = false;

          do
          {
            // is the xid known to be one that couldn't be recovered

            recordUid = previousFailure((Xid) xids[j]);

            if ((recordUid == null) && (foundTransaction)) break; // end
                                        // of
                                        // recovery
                                        // for
                                        // this
                                        // transaction

            if (recordUid == null)
            {
              /*
               * It wasn't an xid that we couldn't recover, so the
               * RM knows about it, but we don't. Therefore it may
               * have to be rolled back.
               */

              if (jtaLogger.loggerI18N.isDebugEnabled())
              {
                jtaLogger.logger
                    .debug(
                        DebugLevel.FUNCTIONS,
                        VisibilityLevel.VIS_PUBLIC,
                        FacilityCode.FAC_CRASH_RECOVERY,
                        "Checking node name of " + ((Xid) xids[j]));
              }

              String nodeName = XAUtils
                  .getXANodeName((Xid) xids[j]);
              boolean doRecovery = false;

              if (jtaLogger.loggerI18N.isDebugEnabled())
              {
                jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PUBLIC,
                    FacilityCode.FAC_CRASH_RECOVERY,
                    "Node name is " + nodeName);
              }

              /*
               * If there is no node name but we have been told to
               * recover everything, then we can roll it back.
               */

              if ((nodeName == null) && (_xaRecoveryNodes != null) && (_xaRecoveryNodes
                  .contains(RECOVER_ALL_NODES)))
              {
                if (jtaLogger.loggerI18N.isDebugEnabled())
                {
                  jtaLogger.logger.debug(
                      DebugLevel.FUNCTIONS,
                      VisibilityLevel.VIS_PUBLIC,
                      FacilityCode.FAC_CRASH_RECOVERY,
                      "Will recover this Xid (a)");
                }

                doRecovery = true;
              }
              else
              {
                if (nodeName != null)
                {
                  /*
                   * Check that the node name is in our
                   * recovery set or that we have been told to
                   * recover everything.
                   */

                  if (_xaRecoveryNodes != null)
                  {
                    if (_xaRecoveryNodes
                        .contains(RECOVER_ALL_NODES) || _xaRecoveryNodes
                        .contains(nodeName))
                    {
                      if (jtaLogger.loggerI18N
                          .isDebugEnabled())
                      {
                        jtaLogger.logger
                            .debug(
                                DebugLevel.FUNCTIONS,
                                VisibilityLevel.VIS_PUBLIC,
                                FacilityCode.FAC_CRASH_RECOVERY,
                                "Will recover this Xid (b)");
                      }

                      doRecovery = true;
                    }
                    else
                    {
                      if (jtaLogger.loggerI18N
                          .isDebugEnabled())
                      {
                        jtaLogger.logger
                            .debug(
                                DebugLevel.FUNCTIONS,
                                VisibilityLevel.VIS_PUBLIC,
                                FacilityCode.FAC_CRASH_RECOVERY,
                                "Will not recover this Xid (a)");
                      }
                    }
                  }
                  else
                  {
                    if (jtaLogger.loggerI18N
                        .isDebugEnabled())
                    {
                      jtaLogger.logger
                          .debug(
                              DebugLevel.FUNCTIONS,
                              VisibilityLevel.VIS_PUBLIC,
                              FacilityCode.FAC_CRASH_RECOVERY,
                              "Will not recover this Xid (b)");
                    }
                  }
                }
                else
                {
                  if (jtaLogger.loggerI18N.isDebugEnabled())
                  {
                    jtaLogger.logger
                        .debug(
                            DebugLevel.FUNCTIONS,
                            VisibilityLevel.VIS_PUBLIC,
                            FacilityCode.FAC_CRASH_RECOVERY,
                            "Will not recover this Xid");
                  }
                }
              }

              try
              {
                if (doRecovery)
                {
                  if (jtaLogger.loggerI18N.isInfoEnabled())
                  {
                    jtaLogger.loggerI18N.info("com.arjuna.ats.internal.jta.recovery.info.rollingback",
                        new Object[] { XAHelper.xidToString((Xid) xids[j]) });
                  }
                 
                  if (!transactionLog((Xid) xids[j])) xares
                      .rollback((Xid) xids[j]);
                  else
                  {
                    /*
                     * Ignore it as the transaction system
                     * will recovery it eventually.
                     */
                  }
                }
                else
                {
                  if (jtaLogger.loggerI18N.isInfoEnabled())
                  {
                    jtaLogger.loggerI18N.info("com.arjuna.ats.internal.jta.recovery.info.notrollback",
                        new Object[] { XAHelper.xidToString((Xid) xids[j]) });
                  }
                }
              }
              catch (XAException e1)
              {
                e1.printStackTrace();

                switch (e1.errorCode)
                {
                case XAException.XAER_RMERR:
                  break;
                case XAException.XA_HEURHAZ:
                case XAException.XA_HEURCOM:
                case XAException.XA_HEURMIX:
                case XAException.XA_HEURRB:
                case XAException.XA_RBROLLBACK:
                {
                  if (!doForget) // already done?
                    doForget = true;
                }
                  break;
                default:
                  break;
                }
              }
              catch (Exception e2)
              {
                if (jtaLogger.loggerI18N.isWarnEnabled())
                {
                  jtaLogger.loggerI18N
                      .warn(
                          "com.arjuna.ats.internal.jta.recovery.xarecovery2",
                          new Object[] { _logName + ".xaRecovery ", e2 });
                }
              }
            }
            else
            {
              foundTransaction = true;

              /*
               * In the failures list so it may be that we just
               * need another XAResource to be able to recover
               * this.
               */

              XARecoveryResource record = _recoveryManagerClass
                  .getResource(recordUid, xares);
              int recoveryStatus = record.recover();

              if (recoveryStatus != XARecoveryResource.RECOVERED_OK)
              {
                if (jtaLogger.loggerI18N.isWarnEnabled())
                {
                  jtaLogger.loggerI18N
                      .warn(
                          "com.arjuna.ats.internal.jta.recovery.failedtorecover",
                          new Object[] { _logName + ".xaRecovery ", new Integer(
                              recoveryStatus) });
                }
              }

              removeFailure(record.getXid(), record.get_uid());
            }

            if (doForget)
            {
              try
              {
                xares.forget((Xid) xids[j]);
              }
              catch (Exception e)
              {
                if (jtaLogger.loggerI18N.isWarnEnabled())
                {
                  jtaLogger.loggerI18N
                      .warn(
                          "com.arjuna.ats.internal.jta.recovery.forgetfailed",
                          new Object[] { _logName + ".xaRecovery", e });
                }
              }
            }

          } while (recordUid != null);
        }
      }
    }
    catch (Exception e)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N
            .warn(
                "com.arjuna.ats.internal.jta.recovery.generalrecoveryerror",
                new Object[] { _logName + ".xaRecovery", e });
      }

      e.printStackTrace();
    }

    try
    {
      if (xares != null) xares.recover(XAResource.TMENDRSCAN);
    }
    catch (XAException e)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N.warn(
            "com.arjuna.ats.internal.jta.recovery.xarecovery1",
            new Object[] { _logName + ".xaRecovery", e, XAHelper
                .printXAErrorCode(e) });
      }
    }

    return true;
  }

  /**
   * Is there a log file for this transaction?
   *
   * @param Xid
   *            xid the transaction to check.
   *
   * @return <code>boolean</code>true if there is a log file,
   *         <code>false</code> if there isn't.
   *
   * @message com.arjuna.ats.internal.jta.recovery.notaxid {0} not an Arjuna
   *          XID
   */

  private final boolean transactionLog (Xid xid)
  {
    if (_transactionStore == null)
    {
      _transactionStore = TxControl.getStore();
    }

    XidImple theXid = new XidImple(xid);
    Uid u = com.arjuna.ats.internal.arjuna.utils.XATxConverter
        .getUid(theXid.getXID());

    if (!u.equals(Uid.nullUid()))
    {
      try
      {
        if (_transactionStore.currentState(u, _transactionType) != ObjectStore.OS_UNKNOWN)
        {
          return true;
        }
      }
      catch (Exception ex)
      {
        ex.printStackTrace();
      }
    }
    else
    {
      if (jtaLogger.logger.isInfoEnabled())
      {
        jtaLogger.loggerI18N.info(
            "com.arjuna.ats.internal.jta.recovery.notaxid",
            new Object[] { xid });
      }
    }

    return false;
  }

  /**
   * Is the Xid is in the failure list, i.e., the list of those transactions
   * we couldn't recover, possibly because of transient failures. If so,
   * return the uid of (one of) the records and remove it from the list.
   */

  private final Uid previousFailure (Xid xid)
  {
    if (_failures == null)
    {
      return null;
    }

    Enumeration e = _failures.keys();

    while (e.hasMoreElements())
    {
      Xid theXid = (Xid) e.nextElement();

      if (XAHelper.sameXID(xid, theXid))
      {
        // remove uid from failure list
        Vector failureItem = (Vector) _failures.get(theXid);
        Uid u = (Uid) failureItem.remove(0);

        if (failureItem.size() == 0) _failures.remove(theXid);

        return u;
      }
    }

    // not present in the failures list.

    return null;
  }

  /* methods to manipulate the failure list */

  /**
   * Add record to failure list
   */

  private void addFailure (Xid xid, Uid uid)
  {
    if (_failures == null) _failures = new Hashtable();

    Vector failureItem = (Vector) _failures.get(xid);

    if (failureItem == null)
    {
      failureItem = new Vector();

      _failures.put(xid, failureItem);
    }

    failureItem.addElement(uid);
  }

  /**
   * @message com.arjuna.ats.internal.jta.recovery.removefailed {0} - could
   *          not remove record for {1}
   */

  /* remove record uid from failure list */
  private void removeFailure (Xid xid, Uid uid)
  {
    // find the failure item for this xid
    Vector failureItem = (Vector) _failures.get(xid);

    if (failureItem == null)
    {
      /*
       * if (jtaLogger.loggerI18N.isWarnEnabled()) {
       * jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.recovery.removefailed",
       * new Object[] { _logName, xid}); }
       */

      /*
       * Already removed via previousFailure.
       */
    }
    else
    {
      // remove this record from the item
      failureItem.remove(uid);

      // if that was the last one, remove the item altogether
      if (failureItem.size() == 0) _failures.remove(xid);
    }
  }

  private void clearAllFailures ()
  {
    if (_failures != null) _failures.clear();
  }

  private ObjectStore _objStore = new ObjectStore();

  private InputObjectState _uids = new InputObjectState();

  private Vector _xaRecoverers = null;

  private Hashtable _failures = null;

  private Vector _xaRecoveryNodes = null;

  private Hashtable _xidScans = null;

  private XARecoveryResourceManager _recoveryManagerClass = null;

  private String _logName = null;

  // 'type' within the Object Store for AtomicActions.
  private String _transactionType = new AtomicAction().type();

  // Reference to the Object Store.
  private static ObjectStore _transactionStore = null;

  private static int _backoffPeriod = 0;

  private static final int XA_BACKOFF_PERIOD = 20000; // backoff in
                            // milliseconds

  private static final char BREAKCHARACTER = ';'; // delimiter for xaconnrecov
                          // property

  static
  {
    String env = jtaPropertyManager.propertyManager
        .getProperty(com.arjuna.ats.jta.common.Environment.XA_BACKOFF_PERIOD);

    XARecoveryModule._backoffPeriod = XA_BACKOFF_PERIOD;

    if (env != null)
    {
      try
      {
        Integer i = new Integer(env);

        XARecoveryModule._backoffPeriod = i.intValue();
      }
      catch (Exception e)
      {
        throw new ExceptionInInitializerError(e);
      }
    }
  }

}
TOP

Related Classes of com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule

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.