Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * 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 java.util.List;
 import java.util.Set;
 
 
Designed to be able to recover any XAResource.
 
 
 public class XARecoveryModule implements RecoveryModule
 {
 	public XARecoveryModule()
 	{
                 "Local XARecoveryModule");
 
 		com.arjuna.ats.internal.jta.Implementations.initialise();
 	}
 
 
     public void addXAResourceRecoveryHelper(XAResourceRecoveryHelper xaResourceRecoveryHelper) {
         synchronized () {
             if(!.contains(xaResourceRecoveryHelper)) {
                 .add(xaResourceRecoveryHelper);
             }
         }
     }
 
     public void removeXAResourceRecoveryHelper(XAResourceRecoveryHelper xaResourceRecoveryHelper) {
         synchronized () {
 			if () {
 			    XAResource[] xaResources = .get(xaResourceRecoveryHelper);
 			    if (xaResources != null) {
     			    for (int i = 0; i < xaResources.lengthi++) {
     			        RecoveryXids recoveryXids = .get(xaResources[i]);
     			        if (recoveryXids != null) {
     			            if (recoveryXids.size() > 0) {
    			                ..warn("RecoveryManager is using this service, may delay up to 10 seconds");
    			                try {
    			                    // do not allow a recovery helper to be removed while the
    			                    // scan is in progress
    			                    .wait();
    			                } catch (InterruptedException e) {
    			                    ..warn("problem waiting for scanLock"e);
    			                }
    			                break;
    			            }
    			        }
    			    }
			    }
	            .remove(xaResourceRecoveryHelper);
			}
        }
    }
    public void addXAResourceOrphanFilter(XAResourceOrphanFilter xaResourceOrphanFilter) {
        synchronized () {
            if(!.contains(xaResourceOrphanFilter)) {
                .add(xaResourceOrphanFilter);
            }
        }
    }
    public void removeXAResourceOrphanFilter(XAResourceOrphanFilter xaResourceOrphanFilter) {
        synchronized () {
            .remove(xaResourceOrphanFilter);
        }
    }
    
	public void addSerializableXAResourceDeserializer(SerializableXAResourceDeserializer serializableXAResourceDeserializer) {
		.add(serializableXAResourceDeserializer);		
	}
	}
	public synchronized void periodicWorkFirstPass()
	{
		// JBTM-1354 allow a second thread to execute the first pass but make sure it is only done once per scan (TMSTART/ENDSCAN)
		synchronized () {
			if () {
				return;
else {
				 = true;
			}
		}
        if(..isDebugEnabled()) {
            ..debugv("{0} - first pass");
        }
		/*
		 * Scan for resources in the object store.
		 */
		try
		{
			{
                ..warn_recovery_alluids();
			}
		}
		{
		}
		catch (Exception e)
		{
            ..warn_recovery_periodicfirstpass(+".periodicWorkFirstPass"e);
		}
		// JBTM-1354 JCA needs to be able to recover XAResources associated with a subordinate transaction so we have to do at least
		// the start scan to make sure that we have loaded all the XAResources we possibly can to assist subordinate transactions recovering
		// scan using statically configured plugins;
		// scan using dynamically configured plugins:
		for (XAResource xaResource : resources) {
			try {
catch (Exception ex) {
			}
		}
	}
	public void periodicWorkSecondPass()
	{
		if (..isDebugEnabled())
		{
            ..debugv("{0} - second pass");
		}
		try
		{
			// do the recovery on anything from the scan in first pass
			if (..isDebugEnabled()) {
                ..debug(
                        + ".transactionInitiatedRecovery completed");
            }
            bottomUpRecovery();
            if (..isDebugEnabled()) {
                ..debug(
                        + ".resourceInitiatedRecovery completed");
            }
		}
		catch (Exception e)
		{
            ..warn_recovery_periodicsecondpass(+".periodicWorkSecondPass"e);
		}
		synchronized () {
			 = false;
		}
	}
	public String id()
	{
		return "XARecoveryModule:" + ;
	}

Parameters:
xid The transaction to commit/rollback.
Returns:
the XAResource than can be used to commit/rollback the specified transaction.
	{
		// JBTM-1354 JCA needs to be able to recover XAResources associated with a subordinate transaction so we have to do at least
		// the start scan to make sure that we have loaded all the XAResources we possibly can to assist subordinate transactions recovering
    	// the reason we can't do bottom up recovery is if this server has an XAResource which tries to recover a remote server (e.g. distributed JTA)
    	// then we get deadlock on the secondpass
        if ( != null)
		{
			while (keys.hasMoreElements())
			{
				XAResource theKey = keys.nextElement();
				RecoveryXids xids = .get(theKey);
				// JBTM-1255 moved stale check back to bottomUpRecovery
				if (xids.contains(xid)) {
					// This Xid is going to be recovered by the AtomicAction
					xids.remove(xid);
					return theKey;
				}
			}
		}
		return null;
    }

       

Parameters:
xaResourceRecord The record to reassociate.
Returns:
the XAResource than can be used to commit/rollback the specified record.
    public XAResource getNewXAResource(XAResourceRecord xaResourceRecord)
    {
        return getNewXAResource(xaResourceRecord.getXid());
    }
	protected XARecoveryModule(XARecoveryResourceManager recoveryClassString logName)
    {
         = logName;
         = recoveryClass;
        if( == null) {
        }
         = jtaPropertyManager.getJTAEnvironmentBean().getXaResourceRecoveries();
    }
	private final boolean transactionInitiatedRecovery()
	{
		Uid theUid = null;
		while (Uid.nullUid().notEquals(theUid))
		{
			try
			{
				theUid = UidHelper.unpackFrom();
				if (theUid.notEquals(Uid.nullUid()))
				{
					/*
					 * Ignore it if it isn't in the store any more. Transaction
					 * probably recovered it.
					 */
					{
						boolean problem = false;
						XARecoveryResource record = null;
						try
						{
							problem = true;
							switch (record.recoverable())
							{
							{
								if (..isDebugEnabled()) {
                                    ..debug("XARecovery attempting recovery of "
                                            + theUid);
                                }
								int recoveryStatus = record.recover();
								if (recoveryStatus != .)
								{
									if (recoveryStatus == .)
									{
									    // resource initiated recovery not possible (no distribution).
										problem = false;
                                        ..info_recovery_recoverydelayed(theUid, Integer.toString(recoveryStatus));
									}
									else
									{
                                        ..warn_recovery_recoveryfailed(theUid, Integer.toString(recoveryStatus));
									}
								}
								else
									problem = false;
							}
								break;
							{
								/*
								 * Transaction was inflight and between us
								 * noticing it and trying to access the state,
								 * it finished and removed the state.
								 */
								problem = false;
							}
								break;
							default:
							{
								if (..isDebugEnabled()) {
                                    ..debug("XARecovery " + theUid
                                            + " is non-recoverable");
                                }
							}
								break;
							}
						}
						{
							problem = true;
						}
						catch (Throwable e)
						{
							problem = true;
                            ..warn_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)
							{
                                ..warn_recovery_cannotadd();
							}
							else
							{
								addFailure(record.getXid(), record.get_uid());
							}
						}
					}
				}
			}
			catch (IOException e)
			{
				theUid = Uid.nullUid();
			}
			catch (Throwable e)
			{
			}
		}
		return true;
	}

JBTM-895 garbage collection is now done when we return XAResources XARecoveryModule.getNewXAResource(XAResourceRecord)

    private void bottomUpRecovery() {
			for (XAResource xaResource : ) {
				try {
catch (Exception ex) {
				}
			}
        // JBTM-895 garbage collection is now done when we return XAResources {@see XARecoveryModule#getNewXAResource(XAResourceRecord)}
        // JBTM-924 requires this here garbage collection, see JBTM-1155:
        if ( != null) {
            Set<XAResourcekeys = new HashSet<XAResource>(.keySet());
            for(XAResource theKey : keys) {
                RecoveryXids recoveryXids = .get(theKey);
                if(recoveryXids.isStale()) {
                    .remove(theKey);
                }
            }
        }
    }

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.
	{
		/*
		 * Now any additional connections we may need to create. Relies upon
		 * information provided by the application.
		 */
		List<XAResourcexaresources = new ArrayList<XAResource>();
		if (.size() > 0)
		{
			for (int i = 0; i < .size(); i++)
			{
				try
				{
					while (ri.hasMoreResources())
					{
						xaresources.add(ri.getXAResource());
					}
				}
				catch (Exception ex)
				{
                    ..warn_recovery_getxaresource(ex);
				}
			}
		}
		return xaresources;
	}
    {
		List<XAResourcexaresources = new ArrayList<XAResource>();
        synchronized ()
        {
            .clear();
            
            for (XAResourceRecoveryHelper xaResourceRecoveryHelper : )
            {
                try
                {
                    XAResource[] xaResources = xaResourceRecoveryHelper.getXAResources();
                    if (xaResources != null)
                    {
                        for (XAResource xaResource : xaResources)
                        {
                        	xaresources.add(xaResource);
                        }
                        .put(xaResourceRecoveryHelperxaResources);
                    }
                }
                catch (Exception ex)
                {
                    ..warn_recovery_getxaresource(ex);
                }
            }
        }
        return xaresources;
    }
	private final void xaRecoveryFirstPass(XAResource xares)
	{
		if (..isDebugEnabled()) {
            ..debug("xarecovery of " + xares);
        }
			Xid[] trans = null;
			try
			{
				if (..isDebugEnabled()) {
                    ..debug("Found "
                            + ((trans != null) ? trans.length : 0)
                            + " xids in doubt");
                }
			}
			catch (XAException e)
			{
                ..warn_recovery_xarecovery1(+".xaRecovery", XAHelper.printXAErrorCode(e), e);
				try
				{
				}
				catch (Exception e1)
				{
				}
				return;
			}
			RecoveryXids xidsToRecover = null;
			if ( == null)
			else
			{
                refreshXidScansForEquivalentXAResourceImpl(xarestrans);
				xidsToRecover = .get(xares);
				if (xidsToRecover == null)
				{
                    // this is probably redundant now due to updateIfEquivalentRM,
                    // but in some implementations hashcode/equals does not behave itself.
					boolean found = false;
					while (elements.hasMoreElements())
					{
						xidsToRecover = elements.nextElement();
						if (xidsToRecover.isSameRM(xares))
						{
							found = true;
							break;
						}
					}
					if (!found)
						xidsToRecover = null;
				}
			}
			if (xidsToRecover == null)
			{
				xidsToRecover = new RecoveryXids(xares);
				.put(xaresxidsToRecover);
			}
			xidsToRecover.nextScan(trans);
	}
	private void xaRecoverySecondPass(XAResource xares) { 		
		RecoveryXids xidsToRecover = .get(xares);
		try {
			Xid[] xids = xidsToRecover.toRecover();
			if (xids != null)
			{
				if (..isDebugEnabled()) {
                    ..debug("Have "
                            + xids.length
                            + " Xids to recover on this pass.");
                }
				for (int j = 0; j < xids.lengthj++)
				{
					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(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.
                            */
                            doForget = handleOrphan(xaresxids[j]);
                        }
                        else
						{
							foundTransaction = true;
							/*
							 * In the failures list so it may be that we just
							 * need another XAResource to be able to recover
							 * this.
							 */
									.getResource(recordUidxares);
							int recoveryStatus = record.recover();
							if (recoveryStatus != .)
							{
                                ..warn_recovery_failedtorecover(+".xaRecovery", Integer.toString(recoveryStatus));
							}
							removeFailure(record.getXid(), record.get_uid());
						}
						if (doForget)
						{
							try
							{
								xares.forget(xids[j]);
							}
							catch (Exception e)
							{
                                ..warn_recovery_forgetfailed(+".xaRecovery"e);
							}
						}
while (recordUid != null);
				}
			}
		}
		catch (Exception e)
		{
            ..warn_recovery_generalrecoveryerror( + ".xaRecovery"e);
		}
		try
		{
			if (xares != null)
		}
		catch (XAException e)
		{
            ..warn_recovery_xarecovery1(+".xaRecovery", XAHelper.printXAErrorCode(e), e);
		}
		return;
	}

    
Apply use configurable filtering to determine how to handle the in-doubt resource.

Parameters:
xares
xid
Returns:
true if forget should be called, false otherwise.
    private boolean handleOrphan(XAResource xaresXid xid)
    {
        // be default we play it safe and leave resources alone unless a filter explicitly recognizes them.
        // getting presumed abort behaviour therefore requires appropriate filters to be registered.
        for(XAResourceOrphanFilter filter : ) {
            XAResourceOrphanFilter.Vote vote = filter.checkXid(xid);
            if(..isDebugEnabled()) {
                ..debug("XAResourceOrphanFilter " + filter.getClass().getName() + " voted " + vote);
            }
            if(vote == ..)
            {
                return false;
            }
            else if(vote == ..)
            {
                votingOutcome = vote;
            }
        }
        try
        {
            if(votingOutcome == ..)
            {
                ..info_recovery_rollingback(XAHelper.xidToString(xid));
                xares.rollback(xid);
            }
        }
        catch (XAException e1)
        {
        	..warn_recovery_xarecovery1(+".xaRecovery", XAHelper.printXAErrorCode(e1), e1);
            switch (e1.errorCode)
            {
                case .:
                    break;
                case .:
                case .:
                case .:
                case .:
                case .:
                {
                    return true;
                }
                default:
                    break;
            }
        }
        catch (Exception e2)
        {
            ..warn_recovery_xarecovery2(+".xaRecovery"e2);
        }
        return false;
    }

    
For some drivers, isSameRM is connection specific. If we have prev scan results for the same RM but using a different connection, we need to be able to identify them. Look at the data from previous scans, identify any for the same RM but different XAResource by checking for matching Xids, then replace the old XAResource with the supplied one.

Parameters:
xares
xids
    private void refreshXidScansForEquivalentXAResourceImpl(XAResource xaresXid[] xids)
    {
        Set<XAResourcekeys = new HashSet<XAResource>(.keySet());
        for(XAResource theKey : keys) {
            RecoveryXids recoveryXids = .get(theKey);
            if(recoveryXids.updateIfEquivalentRM(xaresxids)) {
                // recoveryXids is for this xares, but was originally obtained using
                // a different XAResource. rekey the hashtable to use the new one.
                .remove(theKey);
                .put(xaresrecoveryXids);
                .remove(theKey);
                // There could be two datasources pointed at the same resource manager
                if (!.contains(xares)) {
                	.add(xares);
                }
            }
        }
    }

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 ( == null)
		{
			return null;
		}
		while (e.hasMoreElements())
		{
			Xid theXid = (Xide.nextElement();
			if (XAHelper.sameXID(xidtheXid))
			{
				// remove uid from failure list
				Vector failureItem = (Vector.get(theXid);
				Uid u = (UidfailureItem.remove(0);
				if (failureItem.size() == 0)
				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 xidUid uid)
	{
		if ( == null)
			 = new Hashtable();
		Vector failureItem = (Vector.get(xid);
		if (failureItem == null)
		{
			failureItem = new Vector();
			.put(xidfailureItem);
		}
		failureItem.addElement(uid);
	}
	/* remove record uid from failure list */
	private void removeFailure(Xid xidUid uid)
	{
		// find the failure item for this xid
		Vector failureItem = (Vector.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)
		}
	}
	private void clearAllFailures()
	{
		if ( != null)
	}
	private RecoveryStore _recoveryStore = StoreManager.getRecoveryStore();
	private volatile boolean scanning;
    private Hashtable _failures = null;
	private String _logName = null;
New to GrepCode? Check out our FAQ X