/* * Bitronix Transaction Manager * * Copyright (c) 2010, Bitronix Software. * * 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, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY 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 * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package bitronix.tm.recovery; import bitronix.tm.journal.JournalRecord; import bitronix.tm.resource.common.XAResourceProducer; import bitronix.tm.internal.XAResourceHolderState; import bitronix.tm.BitronixXid; import bitronix.tm.TransactionManagerServices; import bitronix.tm.utils.Uid; import bitronix.tm.utils.Decoder; import java.util.Set; import java.util.Map; import java.util.HashSet; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.transaction.Status; import javax.transaction.xa.XAException; /** * Incremental resource recoverer. * * @author lorban */ public class IncrementalRecoverer { private final static Logger log = LoggerFactory.getLogger(IncrementalRecoverer.class); /** * Run incremental recovery on the specified resource. * @param xaResourceProducer the resource to recover. * @throws RecoveryException when an error preventing recovery happens. */ public static void recover(XAResourceProducer xaResourceProducer) throws RecoveryException { String uniqueName = xaResourceProducer.getUniqueName(); if (log.isDebugEnabled()) log.debug("start of incremental recovery on resource " + uniqueName); try { XAResourceHolderState xaResourceHolderState = xaResourceProducer.startRecovery(); boolean success = true; Set<BitronixXid> xids = RecoveryHelper.recover(xaResourceHolderState); if (log.isDebugEnabled()) log.debug(xids.size() + " dangling transaction(s) found on resource"); Map<?, ?> danglingRecords = TransactionManagerServices.getJournal().collectDanglingRecords(); if (log.isDebugEnabled()) log.debug(danglingRecords.size() + " dangling transaction(s) found in journal"); int commitCount = 0; int rollbackCount = 0; for (BitronixXid xid : xids) { Uid gtrid = xid.getGlobalTransactionIdUid(); JournalRecord tlog = (JournalRecord) danglingRecords.get(gtrid); if (tlog != null) { if (log.isDebugEnabled()) log.debug("committing " + xid); success &= RecoveryHelper.commit(xaResourceHolderState, xid); updateJournal(xid.getGlobalTransactionIdUid(), uniqueName, Status.STATUS_COMMITTED); commitCount++; } else { if (log.isDebugEnabled()) log.debug("rolling back " + xid); success &= RecoveryHelper.rollback(xaResourceHolderState, xid); updateJournal(xid.getGlobalTransactionIdUid(), uniqueName, Status.STATUS_ROLLEDBACK); rollbackCount++; } } // if recovery isn't successful we don't mark the resource as failed: heuristics might have happened // but communication with the resouce is working. if (!success) throw new RecoveryException("error recovering resource '" + uniqueName + "' due to an incompatible heuristic decision"); xaResourceProducer.setFailed(false); log.info("incremental recovery committed " + commitCount + " dangling transaction(s) and rolled back " + rollbackCount + " aborted transaction(s) on resource [" + uniqueName + "]" + ((TransactionManagerServices.getConfiguration().isCurrentNodeOnlyRecovery()) ? " (restricted to serverId '" + TransactionManagerServices.getConfiguration().getServerId() + "')" : "")); } catch (XAException ex) { xaResourceProducer.setFailed(true); throw new RecoveryException("failed recovering resource " + uniqueName, ex); } catch (IOException ex) { xaResourceProducer.setFailed(true); throw new RecoveryException("failed recovering resource " + uniqueName, ex); } catch (RuntimeException ex) { xaResourceProducer.setFailed(true); throw new RecoveryException("failed recovering resource " + uniqueName, ex); } catch (RecoveryException ex) { xaResourceProducer.setFailed(true); throw ex; } finally { xaResourceProducer.endRecovery(); if (log.isDebugEnabled()) log.debug("end of incremental recovery on resource " + uniqueName); } } private static void updateJournal(Uid gtrid, String uniqueName, int status) throws IOException { if (log.isDebugEnabled()) log.debug("updating journal, adding " + Decoder.decodeStatus(status) + " entry for [" + uniqueName + "] on GTRID [" + gtrid + "]"); Set<String> participatingUniqueNames = new HashSet<String>(); participatingUniqueNames.add(uniqueName); TransactionManagerServices.getJournal().log(status, gtrid, participatingUniqueNames); } }