/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.util.journal.queue; import org.mule.runtime.api.exception.MuleRuntimeException; import org.mule.runtime.core.util.CollectionUtils; import org.mule.runtime.core.util.queue.QueueProvider; import org.mule.runtime.core.util.queue.RecoverableQueueStore; import com.google.common.collect.Multimap; import java.io.Serializable; import java.util.Collection; import org.apache.commons.collections.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Process for recover pending transactions after a server crash. * * This process must be executed before accessing the transactional queues. */ public class LocalTxQueueTransactionRecoverer { private Logger logger = LoggerFactory.getLogger(getClass()); private final LocalTxQueueTransactionJournal localTxQueueTransactionJournal; private final QueueProvider queueProvider; public LocalTxQueueTransactionRecoverer(LocalTxQueueTransactionJournal localTxQueueTransactionJournal, QueueProvider queueProvider) { this.localTxQueueTransactionJournal = localTxQueueTransactionJournal; this.queueProvider = queueProvider; } /** * Recover all the pending transactions. * * Will undo all operations done over queues that were not commit or rolled back. * * Clears the transaction log after processing all the log entries since does entries are not longer required. */ public void recover() { if (logger.isDebugEnabled()) { logger.debug("Executing transaction recovery"); } Multimap<Integer, LocalQueueTxJournalEntry> allEntries = this.localTxQueueTransactionJournal.getAllLogEntries(); if (logger.isDebugEnabled()) { logger.debug("Found " + allEntries.size() + " txs to recover"); } int txRecovered = 0; for (Integer txId : allEntries.keySet()) { Collection<LocalQueueTxJournalEntry> entries = allEntries.get(txId); Object commitOrRollback = CollectionUtils.find(entries, new Predicate() { @Override public boolean evaluate(Object object) { LocalQueueTxJournalEntry logEntry = (LocalQueueTxJournalEntry) object; return logEntry.isCommit() || logEntry.isRollback(); } }); if (commitOrRollback != null) { continue; } txRecovered++; for (LocalQueueTxJournalEntry logEntry : entries) { if (logEntry.isRemove()) { String queueName = logEntry.getQueueName(); RecoverableQueueStore queue = queueProvider.getRecoveryQueue(queueName); Serializable polledValue = logEntry.getValue(); if (!queue.contains(polledValue)) { if (logger.isDebugEnabled()) { logger.debug("re-adding polled element that was not commited to queue " + queue.getName()); } try { queue.putNow(polledValue); } catch (InterruptedException e) { throw new MuleRuntimeException(e); } } } else if (logEntry.isAdd() || logEntry.isAddFirst()) { Serializable offeredValue = logEntry.getValue(); String queueName = logEntry.getQueueName(); RecoverableQueueStore queue = queueProvider.getRecoveryQueue(queueName); if (queue.contains(offeredValue)) { if (logger.isDebugEnabled()) { logger.debug("removing offer element that was not commited to queue " + queue.getName()); } queue.remove(offeredValue); } } } } if (logger.isDebugEnabled()) { logger.debug("Recovered " + txRecovered + " txs to recover"); } this.localTxQueueTransactionJournal.clear(); } }