org.bytesoft.bytejta.TransactionRecoveryImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.bytesoft.bytejta.TransactionRecoveryImpl.java

Source

/**
 * Copyright 2014-2016 yangming.liu<bytefox@126.com>.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */
package org.bytesoft.bytejta;

import java.util.List;
import java.util.Map;

import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.xa.XAResource;

import org.apache.commons.lang3.StringUtils;
import org.bytesoft.bytejta.supports.resource.RemoteResourceDescriptor;
import org.bytesoft.bytejta.supports.wire.RemoteCoordinator;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
import org.bytesoft.transaction.Transaction;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.TransactionContext;
import org.bytesoft.transaction.TransactionRecovery;
import org.bytesoft.transaction.TransactionRepository;
import org.bytesoft.transaction.archive.TransactionArchive;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.aware.TransactionBeanFactoryAware;
import org.bytesoft.transaction.logging.TransactionLogger;
import org.bytesoft.transaction.recovery.TransactionRecoveryCallback;
import org.bytesoft.transaction.recovery.TransactionRecoveryListener;
import org.bytesoft.transaction.supports.resource.XAResourceDescriptor;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TransactionRecoveryImpl implements TransactionRecovery, TransactionBeanFactoryAware {
    static final Logger logger = LoggerFactory.getLogger(TransactionRecoveryImpl.class);

    private TransactionBeanFactory beanFactory;
    private TransactionRecoveryListener listener;

    public synchronized void timingRecover() {
        TransactionRepository transactionRepository = beanFactory.getTransactionRepository();
        List<Transaction> transactions = transactionRepository.getErrorTransactionList();
        int total = transactions == null ? 0 : transactions.size();
        int value = 0;
        for (int i = 0; transactions != null && i < transactions.size(); i++) {
            Transaction transaction = transactions.get(i);
            TransactionContext transactionContext = transaction.getTransactionContext();
            TransactionXid xid = transactionContext.getXid();
            try {
                this.recoverTransaction(transaction);
            } catch (CommitRequiredException ex) {
                logger.debug("[{}] recover: branch={}, message= commit-required",
                        ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
                        ByteUtils.byteArrayToString(xid.getBranchQualifier()));
                continue;
            } catch (RollbackRequiredException ex) {
                logger.debug("[{}] recover: branch={}, message= rollback-required",
                        ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
                        ByteUtils.byteArrayToString(xid.getBranchQualifier()));
                continue;
            } catch (SystemException ex) {
                logger.debug("[{}] recover: branch={}, message= {}",
                        ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
                        ByteUtils.byteArrayToString(xid.getBranchQualifier()), ex.getMessage());
                continue;
            } catch (RuntimeException ex) {
                logger.debug("[{}] recover: branch={}, message= {}",
                        ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
                        ByteUtils.byteArrayToString(xid.getBranchQualifier()), ex.getMessage());
                continue;
            }
        }
        logger.debug("[transaction-recovery] total= {}, success= {}", total, value);
    }

    public void recoverTransaction(Transaction transaction)
            throws CommitRequiredException, RollbackRequiredException, SystemException {

        TransactionContext transactionContext = transaction.getTransactionContext();
        boolean coordinator = transactionContext.isCoordinator();
        if (coordinator) {
            transaction.recover();
            this.recoverCoordinator(transaction);
        } else {
            transaction.recover();
            this.recoverParticipant(transaction);
        }

    }

    private void recoverCoordinator(Transaction transaction)
            throws CommitRequiredException, RollbackRequiredException, SystemException {

        switch (transaction.getTransactionStatus()) {
        case Status.STATUS_ACTIVE:
        case Status.STATUS_MARKED_ROLLBACK:
        case Status.STATUS_PREPARING:
        case Status.STATUS_ROLLING_BACK:
        case Status.STATUS_UNKNOWN:
            transaction.recoveryRollback();
            transaction.forgetQuietly();
            break;
        case Status.STATUS_PREPARED:
        case Status.STATUS_COMMITTING:
            transaction.recoveryCommit();
            transaction.forgetQuietly();
            break;
        case Status.STATUS_COMMITTED:
        case Status.STATUS_ROLLEDBACK:
            transaction.forgetQuietly();
            break;
        default:
            logger.debug("Current transaction has already been completed.");
        }
    }

    private void recoverParticipant(Transaction transaction)
            throws CommitRequiredException, RollbackRequiredException, SystemException {

        TransactionImpl transactionImpl = (TransactionImpl) transaction;
        switch (transaction.getTransactionStatus()) {
        case Status.STATUS_PREPARED:
        case Status.STATUS_COMMITTING:
            break;
        case Status.STATUS_COMMITTED:
        case Status.STATUS_ROLLEDBACK:
            break;
        case Status.STATUS_ACTIVE:
        case Status.STATUS_MARKED_ROLLBACK:
        case Status.STATUS_PREPARING:
        case Status.STATUS_UNKNOWN:
        case Status.STATUS_ROLLING_BACK:
        default:
            transactionImpl.recoveryRollback();
            transactionImpl.forgetQuietly();
        }
    }

    public synchronized void startRecovery() {
        final TransactionRepository transactionRepository = beanFactory.getTransactionRepository();
        final TransactionLogger transactionLogger = beanFactory.getTransactionLogger();
        transactionLogger.recover(new TransactionRecoveryCallback() {

            public void recover(TransactionArchive archive) {
                try {
                    TransactionImpl transaction = reconstructTransaction(archive);
                    if (listener != null) {
                        listener.onRecovery(transaction);
                    }
                    TransactionContext transactionContext = transaction.getTransactionContext();
                    TransactionXid globalXid = transactionContext.getXid();
                    transactionRepository.putTransaction(globalXid, transaction);
                    transactionRepository.putErrorTransaction(globalXid, transaction);
                } catch (IllegalStateException ex) {
                    transactionLogger.deleteTransaction(archive);
                }

            }
        });

        TransactionCoordinator transactionCoordinator = //
                (TransactionCoordinator) this.beanFactory.getTransactionCoordinator();
        transactionCoordinator.markParticipantReady();
    }

    private TransactionImpl reconstructTransaction(TransactionArchive archive) throws IllegalStateException {
        XidFactory xidFactory = this.beanFactory.getXidFactory();
        TransactionContext transactionContext = new TransactionContext();
        TransactionXid xid = (TransactionXid) archive.getXid();
        transactionContext.setXid(xidFactory.createGlobalXid(xid.getGlobalTransactionId()));
        transactionContext.setRecoveried(true);
        transactionContext.setCoordinator(archive.isCoordinator());
        transactionContext.setPropagatedBy(archive.getPropagatedBy());

        TransactionImpl transaction = new TransactionImpl(transactionContext);
        transaction.setBeanFactory(this.beanFactory);
        transaction.setTransactionStatus(archive.getStatus());

        List<XAResourceArchive> nativeResources = archive.getNativeResources();
        transaction.getNativeParticipantList().addAll(nativeResources);

        transaction.setParticipant(archive.getOptimizedResource());

        List<XAResourceArchive> remoteResources = archive.getRemoteResources();
        transaction.getRemoteParticipantList().addAll(remoteResources);

        List<XAResourceArchive> participants = transaction.getParticipantList();
        Map<String, XAResourceArchive> applicationMap = transaction.getApplicationMap();
        Map<String, XAResourceArchive> participantMap = transaction.getParticipantMap();
        if (archive.getOptimizedResource() != null) {
            participants.add(archive.getOptimizedResource());
        }
        participants.addAll(nativeResources);
        participants.addAll(remoteResources);

        for (int i = 0; i < participants.size(); i++) {
            XAResourceArchive element = participants.get(i);
            XAResourceDescriptor descriptor = element.getDescriptor();
            String identifier = StringUtils.trimToEmpty(descriptor.getIdentifier());

            if (RemoteResourceDescriptor.class.isInstance(descriptor)) {
                RemoteResourceDescriptor resourceDescriptor = (RemoteResourceDescriptor) descriptor;
                RemoteCoordinator remoteCoordinator = resourceDescriptor.getDelegate();
                applicationMap.put(remoteCoordinator.getApplication(), element);
            } // end-if (RemoteResourceDescriptor.class.isInstance(descriptor))

            participantMap.put(identifier, element);
        }

        transaction.recoverTransactionStrategy(archive.getTransactionStrategyType());

        if (archive.getVote() == XAResource.XA_RDONLY) {
            throw new IllegalStateException("Transaction has already been completed!");
        }

        return transaction;
    }

    public void setBeanFactory(TransactionBeanFactory tbf) {
        this.beanFactory = tbf;
    }

    public TransactionRecoveryListener getListener() {
        return listener;
    }

    public void setListener(TransactionRecoveryListener listener) {
        this.listener = listener;
    }
}