/**
* Copyright 2014-2016 yangming.liu.
*
* 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 .
*/
package org.bytesoft.bytetcc;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.bytejta.supports.jdbc.RecoveredResource;
import org.bytesoft.bytejta.supports.resource.RemoteResourceDescriptor;
import org.bytesoft.bytejta.supports.wire.RemoteCoordinator;
import org.bytesoft.bytetcc.supports.resource.LocalResourceCleaner;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.common.utils.CommonUtils;
import org.bytesoft.compensable.CompensableBeanFactory;
import org.bytesoft.compensable.CompensableInvocation;
import org.bytesoft.compensable.CompensableTransaction;
import org.bytesoft.compensable.ContainerContext;
import org.bytesoft.compensable.TransactionContext;
import org.bytesoft.compensable.archive.CompensableArchive;
import org.bytesoft.compensable.archive.TransactionArchive;
import org.bytesoft.compensable.logging.CompensableLogger;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
import org.bytesoft.transaction.Transaction;
import org.bytesoft.transaction.TransactionRepository;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.supports.TransactionListener;
import org.bytesoft.transaction.supports.TransactionListenerAdapter;
import org.bytesoft.transaction.supports.TransactionResourceListener;
import org.bytesoft.transaction.supports.resource.XAResourceDescriptor;
import org.bytesoft.transaction.supports.serialize.XAResourceDeserializer;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CompensableTransactionImpl extends TransactionListenerAdapter implements CompensableTransaction {
static final Logger logger = LoggerFactory.getLogger(CompensableTransactionImpl.class);
private final TransactionContext transactionContext;
private final List archiveList = new ArrayList();
private final Map resourceMap = new HashMap();
private final List resourceList = new ArrayList();
private final Map applicationMap = new HashMap();
private final Map transactionMap = new ConcurrentHashMap(4);
private CompensableBeanFactory beanFactory;
private int transactionVote;
private int transactionStatus = Status.STATUS_ACTIVE;
/* current comensable-decision in confirm/cancel phase. */
private transient Boolean positive;
/* current compense-archive in confirm/cancel phase. */
private transient CompensableArchive archive;
/* current compensable-archive list in try phase. */
private transient final List currentArchiveList = new ArrayList();
private transient final Map> archiveMap = new HashMap>();
private boolean participantStickyRequired;
private Map variables = new HashMap();
private Thread currentThread;
private final Lock lock = new ReentrantLock();
public CompensableTransactionImpl(TransactionContext txContext) {
this.transactionContext = txContext;
}
public TransactionArchive getTransactionArchive() {
TransactionArchive transactionArchive = new TransactionArchive();
transactionArchive.setVariables(this.variables);
transactionArchive.setCoordinator(this.transactionContext.isCoordinator());
transactionArchive.setPropagated(this.transactionContext.isPropagated());
transactionArchive.setCompensable(this.transactionContext.isCompensable());
transactionArchive.setCompensableStatus(this.transactionStatus);
transactionArchive.setVote(this.transactionVote);
transactionArchive.setXid(this.transactionContext.getXid());
transactionArchive.getRemoteResources().addAll(this.resourceList);
transactionArchive.getCompensableResourceList().addAll(this.archiveList);
transactionArchive.setPropagatedBy(this.transactionContext.getPropagatedBy());
return transactionArchive;
}
public synchronized void participantCommit(boolean opc) throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, SecurityException, IllegalStateException, CommitRequiredException, SystemException {
// Recover if transaction is recovered from tx-log.
this.recoverIfNecessary();
if (this.transactionStatus != Status.STATUS_COMMITTED) {
this.fireCommit(); // TODO
}
}
// 当前事务节点,进行commit操作(该方法由监听器回调调用)
public synchronized void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
SecurityException, IllegalStateException, SystemException {
if (this.transactionStatus == Status.STATUS_ACTIVE) { // 事务状态: 活跃有效
this.fireCommit();
} else if (this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) { // 事务状态: 标志回滚
this.fireRollback();
throw new HeuristicRollbackException();
} else if (this.transactionStatus == Status.STATUS_ROLLEDBACK) /* should never happen */ { // 事务状态:异常回滚,理论上不会出现这种情况
throw new RollbackException();
} else if (this.transactionStatus == Status.STATUS_COMMITTED) /* should never happen */ { // 事务状态: 已经提交,理论上不会出现这种情况
logger.debug("Current transaction has already been committed.");
} else {
throw new IllegalStateException();
}
}
// 执行发送提交
private void fireCommit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException {
CompensableLogger compensableLogger = this.beanFactory.getCompensableLogger();
this.transactionContext.setCompensating(true); // 正在补偿
this.transactionStatus = Status.STATUS_COMMITTING; // 事务状态:节点事务提交中
compensableLogger.updateTransaction(this.getTransactionArchive()); // 更新记录一下事务归档
SystemException systemEx = null;
try {
this.fireNativeParticipantConfirm(); // 执行本地事务参与者 提交节点事务
} catch (SystemException ex) {
systemEx = ex;
logger.info("{}| confirm native branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
} catch (RuntimeException ex) {
systemEx = new SystemException();
systemEx.initCause(ex);
logger.info("{}| confirm native branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
}
try {
this.fireRemoteParticipantConfirm(); // 执行远程事务参与者 提交节点事务
} catch (HeuristicMixedException ex) {
logger.info("{}| confirm remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
throw ex;
} catch (HeuristicRollbackException ex) {
logger.info("{}| confirm remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
throw ex;
} catch (SystemException ex) {
logger.info("{}| confirm remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
throw ex;
} catch (RuntimeException ex) {
logger.info("{}| confirm remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
throw ex;
}
if (systemEx != null) {
throw systemEx;
} else {
this.transactionStatus = Status.STATUS_COMMITTED; // 提交完成,事务状态更新为 已提交
compensableLogger.updateTransaction(this.getTransactionArchive()); // 更新记录一下节点事务归档
logger.info("{}| compensable transaction committed!",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()));
}
}
public synchronized void recoveryCommit() throws CommitRequiredException, SystemException {
this.recoverIfNecessary(); // Recover if transaction is recovered from tx-log.
try {
this.fireCommit();
} catch (SecurityException ex) {
logger.error("{}| confirm native/remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
} catch (RollbackException ex) {
logger.error("{}| confirm native/remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
} catch (HeuristicMixedException ex) {
logger.error("{}| confirm native/remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
} catch (HeuristicRollbackException ex) {
logger.error("{}| confirm native/remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
}
}
// 执行本地参与者的事务提交
private void fireNativeParticipantConfirm() throws SystemException {
boolean errorExists = false; // 标志提交逻辑是否出错
ContainerContext container = this.beanFactory.getContainerContext();
for (int i = this.archiveList.size() - 1; i >= 0; i--) { // 循环 处理本地的事务列表,本地可能有多个本地事务
CompensableArchive current = this.archiveList.get(i);
if (current.isConfirmed()) { // 已提交则不需要处理
continue;
}
try {
this.positive = true;
this.archive = current;
CompensableInvocation invocation = current.getCompensable(); // 封装了一些事务的执行方法,参数,事务提交key等
if (invocation == null) {
errorExists = true;
logger.error(
"{}| error occurred while confirming service: {}, please check whether the params of method(compensable-service) supports serialization.",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(current.getIdentifier().getGlobalTransactionId()));
} else if (StringUtils.isNotBlank(invocation.getConfirmableKey())) {
container.confirm(invocation); // 使用定义spring容器,代理反射执行confirm方法
} else {
current.setConfirmed(true); // 设置为 已提交
logger.info("{}| confirm: identifier= {}, resourceKey= {}, resourceXid= {}.",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(current.getIdentifier().getGlobalTransactionId()),
current.getCompensableResourceKey(), current.getCompensableXid());
}
} catch (RuntimeException rex) {
errorExists = true;
logger.error("{}| error occurred while confirming service: {}",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), current, rex);
} finally {
this.archive = null;
this.positive = null;
}
}
if (errorExists) {
throw new SystemException();
}
}
// 执行远程参与者的事务提交
private void fireRemoteParticipantConfirm()
throws HeuristicMixedException, HeuristicRollbackException, CommitRequiredException, SystemException {
boolean committedExists = false;
boolean rolledbackExists = false;
boolean unFinishExists = false;
boolean errorExists = false;
for (int i = 0; i < this.resourceList.size(); i++) { // 循环处理所有的XA事务资源列表
XAResourceArchive current = this.resourceList.get(i);
if (current.isCommitted()) {
committedExists = true; // 已经提交的不再处理
continue;
} else if (current.isRolledback()) {
rolledbackExists = true; // 回滚的不再处理
continue;
} else if (current.isReadonly()) {
continue; // 只读的不需要处理
}
CompensableLogger transactionLogger = this.beanFactory.getCompensableLogger();
XidFactory xidFactory = this.beanFactory.getCompensableXidFactory();
TransactionXid branchXid = (TransactionXid) current.getXid(); // 该XA事务的事务ID,以及对应的全局事务ID
TransactionXid globalXid = xidFactory.createGlobalXid(branchXid.getGlobalTransactionId());
try {
current.commit(globalXid, true); // 当前事务提交全局事务,核心方法
committedExists = true;
current.setCommitted(true);
current.setCompleted(true);
logger.info("{}| confirm remote branch: {}", ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()),
current.getDescriptor().getIdentifier());
} catch (XAException ex) {
switch (ex.errorCode) {
case XAException.XA_HEURCOM:
committedExists = true;
current.setHeuristic(true);
current.setCommitted(true);
current.setCompleted(true);
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
current.setHeuristic(true);
current.setCommitted(true);
current.setRolledback(true);
current.setCompleted(true);
logger.error("{}| error occurred while confirming remote branch: {}, transaction has been completed!",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()),
current.getDescriptor().getIdentifier(), ex);
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
current.setHeuristic(true);
current.setRolledback(true);
current.setCompleted(true);
break;
case XAException.XA_HEURHAZ:
unFinishExists = true;
current.setHeuristic(true);
logger.warn("{}| error occurred while confirming remote branch: {}, transaction may has been completd!",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()),
current.getDescriptor().getIdentifier(), ex);
break;
case XAException.XAER_RMFAIL:
unFinishExists = true;
logger.warn("{}| error occurred while confirming remote branch: {}, the remote branch is unreachable!",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()),
current.getDescriptor().getIdentifier(), ex);
break;
case XAException.XAER_NOTA:
committedExists = true; // TODO 1) tried & committed; 2) have not tried
current.setCommitted(true);
current.setCompleted(true);
break;
case XAException.XAER_RMERR:
case XAException.XAER_INVAL:
case XAException.XAER_PROTO:
errorExists = true;
logger.warn("{}| error occurred while confirming remote branch: {}!",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()),
current.getDescriptor().getIdentifier(), ex);
break;
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBROLLBACK:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
default:
rolledbackExists = true;
current.setRolledback(true);
current.setCompleted(true);
logger.error("{}| error occurred while confirming remote branch: {}, transaction has been rolled back!",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()),
current.getDescriptor().getIdentifier(), ex);
}
} catch (RuntimeException rex) {
errorExists = true;
logger.warn("{}| error occurred while confirming remote branch: {}!",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()),
current.getDescriptor().getIdentifier(), rex);
} finally {
if (current.isCompleted()) {
transactionLogger.updateCoordinator(current);
}
}
}
if (committedExists && rolledbackExists) {
throw new HeuristicMixedException();
} else if (unFinishExists) {
throw new CommitRequiredException();
} else if (errorExists) {
throw new SystemException();
} else if (rolledbackExists) {
throw new HeuristicRollbackException();
}
// else if (committedExists == false) { throw new XAException(XAException.XA_RDONLY); }
}
public int participantPrepare() throws RollbackRequiredException, CommitRequiredException {
throw new RuntimeException("Not supported!");
}
private void markCurrentBranchTransactionRollbackIfNecessary() {
List transactions = new ArrayList(this.transactionMap.values());
boolean recoveried = this.transactionContext.isRecoveried();
if (recoveried == false && transactions.isEmpty() == false) /* used by participant only. */ {
for (int i = 0; i < transactions.size(); i++) {
Transaction branch = transactions.get(i);
try {
branch.setRollbackOnly();
} catch (IllegalStateException ex) {
logger.info("The local transaction is not active.", ex); // tx in try-phase has been completed already.
} catch (SystemException ex) {
logger.warn("The local transaction is not active.", ex); // should never happen
} catch (RuntimeException ex) {
logger.warn("The local transaction is not active.", ex); // should never happen
}
} // end-for (int i = 0; i < transactions.size(); i++)
}
}
public synchronized void participantRollback() throws IllegalStateException, SystemException {
// Recover if transaction is recovered from tx-log.
this.recoverIfNecessary();
if (this.transactionStatus != Status.STATUS_ROLLEDBACK) {
this.fireRollback(); // TODO
}
}
// 回滚
public synchronized void rollback() throws IllegalStateException, SystemException {
if (this.transactionStatus == Status.STATUS_UNKNOWN) {
throw new IllegalStateException(); // 不明的事务状态,不回滚
} else if (this.transactionStatus == Status.STATUS_NO_TRANSACTION) {
throw new IllegalStateException(); // 非事务状态,不回滚
} else if (this.transactionStatus == Status.STATUS_COMMITTED) /* should never happen */ {
throw new IllegalStateException(); // 已提交的事务状态,不回滚
} else if (this.transactionStatus == Status.STATUS_ROLLEDBACK) /* should never happen */ {
logger.debug("Current transaction has already been rolled back."); // 已经回滚的事务状态,不再回滚
} else {
this.fireRollback(); // 执行回滚操作
}
}
private void fireRollback() throws IllegalStateException, SystemException {
CompensableLogger compensableLogger = this.beanFactory.getCompensableLogger();
this.transactionStatus = Status.STATUS_ROLLING_BACK; // 标志事务状态为回滚中
this.markCurrentBranchTransactionRollbackIfNecessary(); // 标志所有分支事务回滚
this.transactionContext.setCompensating(true); // 标志当前事务为正在补偿中
compensableLogger.updateTransaction(this.getTransactionArchive()); // 更新记录一下节点事务日志归档
SystemException systemEx = null;
try {
this.fireNativeParticipantCancel(); // 本地节点事务回滚后,执行本地其他分支事务节点的cancel
} catch (SystemException ex) {
systemEx = ex;
logger.info("{}| cancel native branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
} catch (RuntimeException ex) {
systemEx = new SystemException();
systemEx.initCause(ex);
logger.info("{}| cancel native branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
}
try {
this.fireRemoteParticipantCancel(); // 本地节点事务回滚后,执行远程其他分支事务节点的cancel
} catch (SystemException ex) {
logger.info("{}| cancel remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
throw ex;
} catch (RuntimeException ex) {
logger.info("{}| cancel remote branchs failed!",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), ex);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
}
if (systemEx != null) {
throw systemEx;
} else {
this.transactionStatus = Status.STATUS_ROLLEDBACK; // 标志事务状态为已回滚
compensableLogger.updateTransaction(this.getTransactionArchive());
logger.info("{}| compensable transaction rolled back!",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()));
}
}
public synchronized void recoveryRollback() throws RollbackRequiredException, SystemException {
// Recover if transaction is recovered from tx-log.
this.recoverIfNecessary();
this.fireRollback();
}
// 执行本地其他分支事务的cancel
private void fireNativeParticipantCancel() throws SystemException {
boolean errorExists = false;
ContainerContext container = this.beanFactory.getContainerContext(); // 使用定义spring容器
for (int i = this.archiveList.size() - 1; i >= 0; i--) { // 循环 处理本地的事务列表,本地可能有多个本地事务
CompensableArchive current = this.archiveList.get(i);
if (current.isTried() == false) {
logger.info(
"{}| The operation in try phase is rolled back, so the cancel operation is ignored, compensable service: {}.",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(current.getIdentifier().getGlobalTransactionId()));
continue;
} else if (current.isCancelled()) {
continue; // 已经cancel的不用处理
}
try {
this.positive = false;
this.archive = current;
CompensableInvocation invocation = current.getCompensable(); // 封装了一些事务的执行方法,参数,事务key等
if (invocation == null) {
errorExists = true;
logger.error(
"{}| error occurred while cancelling service: {}, please check whether the params of method(compensable-service) supports serialization.",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(current.getIdentifier().getGlobalTransactionId()));
} else if (StringUtils.isNotBlank(invocation.getCancellableKey())) {
container.cancel(invocation); // 代理反射执行该节点的cancel方法
} else {
current.setCancelled(true); // 设置为已取消
logger.info("{}| cancel: identifier= {}, resourceKey= {}, resourceXid= {}.",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(current.getIdentifier().getGlobalTransactionId()),
current.getCompensableResourceKey(), current.getCompensableXid());
}
} catch (RuntimeException rex) {
errorExists = true;
logger.error("{}| error occurred while cancelling service: {}",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), current, rex);
} finally {
this.archive = null;
this.positive = null;
}
}
if (errorExists) {
throw new SystemException();
}
}
// 执行远程其他事务的cancel
private void fireRemoteParticipantCancel() throws RollbackRequiredException, SystemException {
boolean committedExists = false;
boolean rolledbackExists = false;
boolean unFinishExists = false;
boolean errorExists = false;
for (int i = 0; i < this.resourceList.size(); i++) { // 循环处理所有的XA事务资源列表
XAResourceArchive current = this.resourceList.get(i);
if (current.isCommitted()) { // 已提交 不处理
committedExists = true;
continue;
} else if (current.isRolledback()) { // 已回滚,不处理
rolledbackExists = true;
continue;
} else if (current.isReadonly()) { // 只读,不处理
continue;
}
CompensableLogger transactionLogger = this.beanFactory.getCompensableLogger();
XidFactory xidFactory = this.beanFactory.getCompensableXidFactory();
TransactionXid branchXid = (TransactionXid) current.getXid(); // XA分支事务ID
TransactionXid globalXid = xidFactory.createGlobalXid(branchXid.getGlobalTransactionId()); // 根据事务分支ID获取事务全局ID
try {
current.rollback(globalXid); // 执行远程节点的XA事务回滚
rolledbackExists = true;
current.setRolledback(true); // 回滚完成
current.setCompleted(true);
logger.info("{}| cancel remote branch: {}", ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()),
current.getDescriptor().getIdentifier());
} catch (XAException xaex) {
switch (xaex.errorCode) {
case XAException.XA_HEURHAZ:
unFinishExists = true;
current.setHeuristic(true);
logger.error("{}| error occurred while cancelling remote branch: {}",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()), current, xaex);
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
current.setCommitted(true);
current.setRolledback(true);
current.setHeuristic(true);
current.setCompleted(true);
break;
case XAException.XA_HEURCOM:
committedExists = true;
current.setCommitted(true);
current.setHeuristic(true);
current.setCompleted(true);
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
current.setRolledback(true);
current.setHeuristic(true);
current.setCompleted(true);
break;
case XAException.XA_RDONLY:
current.setReadonly(true);
current.setCompleted(true);
break;
case XAException.XAER_RMFAIL:
unFinishExists = true;
logger.error("{}| error occurred while cancelling remote branch: {}, the remote branch is unreachable!",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()), current, xaex);
break;
case XAException.XAER_NOTA:
rolledbackExists = true;
current.setRolledback(true);
current.setCompleted(true);
break;
case XAException.XAER_RMERR:
default:
errorExists = true;
logger.error("{}| error occurred while cancelling remote branch: {}",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()), current, xaex);
}
} catch (RuntimeException rex) {
errorExists = true;
logger.error("{}| error occurred while cancelling remote branch: {}",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()), current, rex);
} finally {
if (current.isCompleted()) {
transactionLogger.updateCoordinator(current);
}
}
}
if (committedExists && rolledbackExists) {
throw new SystemException(XAException.XA_HEURMIX);
} else if (unFinishExists) {
throw new RollbackRequiredException();
} else if (errorExists) {
throw new SystemException(XAException.XAER_RMERR);
} else if (committedExists) {
throw new SystemException(XAException.XA_HEURCOM);
}
// else if (rolledbackExists == false) { throw new SystemException(XAException.XA_RDONLY); }
}
public boolean enlistResource(XAResource xaRes) throws RollbackException, IllegalStateException, SystemException {
if (this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) { // 事务被标志回滚,抛出回滚异常
throw new RollbackException();
} else if (this.transactionStatus != Status.STATUS_ACTIVE) { // 事务不是活跃状态,抛出逻辑异常
throw new IllegalStateException();
}
CompensableLogger compensableLogger = this.beanFactory.getCompensableLogger();
if (RemoteResourceDescriptor.class.isInstance(xaRes) == false) { // 不是远程事务资源,异常
throw new SystemException("Invalid resource!");
}
RemoteResourceDescriptor descriptor = (RemoteResourceDescriptor) xaRes;
XidFactory xidFactory = this.beanFactory.getCompensableXidFactory();
TransactionXid globalXid = this.transactionContext.getXid(); // 从事务上下文的全局ID,生成分支事务ID
TransactionXid branchXid = xidFactory.createBranchXid(globalXid);
try {
descriptor.start(branchXid, XAResource.TMNOFLAGS);
} catch (XAException ex) {
throw new RollbackException(); // should never happen
}
String identifier = descriptor.getIdentifier();
RemoteCoordinator transactionCoordinator = this.beanFactory.getCompensableCoordinator(); // 远程事务协调者
String self = transactionCoordinator.getIdentifier(); // 协调者识别码
String parent = String.valueOf(this.transactionContext.getPropagatedBy()); // 事务传播者
boolean resourceValid = StringUtils.equalsIgnoreCase(identifier, self) == false
&& CommonUtils.instanceEquals(parent, identifier) == false;
if (resourceValid == false) { // 如果事务传播者是事务协调者
logger.warn("Endpoint {} can not be its own remote branch!", identifier);
return false;
} // end-if (resourceValid == false)
XAResourceArchive resourceArchive = this.resourceMap.get(identifier);
if (resourceArchive == null) {
resourceArchive = new XAResourceArchive();
resourceArchive.setXid(branchXid);
resourceArchive.setDescriptor(descriptor);
this.resourceList.add(resourceArchive); // 远程事务资源
this.resourceMap.put(identifier, resourceArchive);
compensableLogger.createCoordinator(resourceArchive);
logger.info("{}| enlist remote resource: {}.", ByteUtils.byteArrayToString(globalXid.getGlobalTransactionId()),
identifier);
return true;
} else {
return false;
}
}
public boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException, SystemException {
CompensableLogger compensableLogger = this.beanFactory.getCompensableLogger();
if (RemoteResourceDescriptor.class.isInstance(xaRes)) {
RemoteResourceDescriptor descriptor = (RemoteResourceDescriptor) xaRes;
RemoteCoordinator resource = descriptor.getDelegate();
String identifier = descriptor.getIdentifier();
RemoteCoordinator transactionCoordinator = this.beanFactory.getCompensableCoordinator();
String self = transactionCoordinator.getIdentifier();
String parent = String.valueOf(this.transactionContext.getPropagatedBy());
if (StringUtils.equalsIgnoreCase(identifier, self) || CommonUtils.instanceEquals(parent, identifier)) {
return true;
}
XAResourceArchive archive = this.resourceMap.get(identifier);
if (flag == XAResource.TMFAIL) {
this.resourceMap.remove(identifier);
if (archive != null) {
this.resourceList.remove(archive);
} // end-if (archive != null)
compensableLogger.updateTransaction(this.getTransactionArchive());
} else {
if (archive != null) {
this.applicationMap.put(resource.getApplication(), archive);
} // end-if (archive != null)
}
} // end-if (RemoteResourceDescriptor.class.isInstance(xaRes))
return true;
}
public void resume() throws SystemException {
Transaction transaction = this.transactionMap.get(Thread.currentThread());
org.bytesoft.transaction.TransactionContext transactionContext = transaction.getTransactionContext();
TransactionXid xid = transactionContext.getXid();
List compensableList = this.archiveMap.remove(xid);
this.currentArchiveList.clear();
this.currentArchiveList.addAll(compensableList);
}
public void suspend() throws SystemException {
Transaction transaction = this.transactionMap.get(Thread.currentThread());
org.bytesoft.transaction.TransactionContext transactionContext = transaction.getTransactionContext();
TransactionXid xid = transactionContext.getXid();
List compensableList = new ArrayList();
compensableList.addAll(this.currentArchiveList);
this.currentArchiveList.clear();
this.archiveMap.put(xid, compensableList);
}
public void registerCompensable(CompensableInvocation invocation) {
XidFactory xidFactory = this.beanFactory.getTransactionXidFactory();
CompensableArchive archive = new CompensableArchive();
TransactionXid globalXid = xidFactory.createGlobalXid(this.transactionContext.getXid().getGlobalTransactionId());
TransactionXid branchXid = xidFactory.createBranchXid(globalXid);
archive.setIdentifier(branchXid);
archive.setCompensable(invocation);
this.archiveList.add(archive);
this.currentArchiveList.add(archive);
logger.info("{}| register compensable service: {}.",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(archive.getIdentifier().getGlobalTransactionId()));
}
public void registerSynchronization(Synchronization sync) throws RollbackException, IllegalStateException, SystemException {
}
public void registerTransactionListener(TransactionListener listener) {
}
public void registerTransactionResourceListener(TransactionResourceListener listener) {
}
public void onEnlistResource(Xid xid, XAResource xares) {
String resourceKey = null;
if (XAResourceDescriptor.class.isInstance(xares)) {
XAResourceDescriptor descriptor = (XAResourceDescriptor) xares;
resourceKey = descriptor.getIdentifier();
} else if (XAResourceArchive.class.isInstance(xares)) {
XAResourceArchive resourceArchive = (XAResourceArchive) xares;
XAResourceDescriptor descriptor = resourceArchive.getDescriptor();
resourceKey = descriptor == null ? null : descriptor.getIdentifier();
}
CompensableLogger compensableLogger = this.beanFactory.getCompensableLogger();
if (this.transactionContext.isCompensating()) {
this.archive.setCompensableXid(xid);
this.archive.setCompensableResourceKey(resourceKey);
compensableLogger.updateCompensable(this.archive);
} else {
for (int i = 0; i < this.currentArchiveList.size(); i++) {
CompensableArchive compensableArchive = this.currentArchiveList.get(i);
compensableArchive.setTransactionXid(xid);
compensableArchive.setTransactionResourceKey(resourceKey);
compensableLogger.createCompensable(compensableArchive);
}
}
}
public void onDelistResource(Xid xid, XAResource xares) {
}
public void onCommitSuccess(TransactionXid xid) {
CompensableLogger compensableLogger = this.beanFactory.getCompensableLogger();
if (this.transactionContext.isCompensating()) {
if (this.positive == null) {
// ignore
} else if (this.positive) {
this.archive.setConfirmed(true);
logger.info("{}| confirm: identifier= {}, resourceKey= {}, resourceXid= {}.",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(this.archive.getIdentifier().getGlobalTransactionId()),
this.archive.getCompensableResourceKey(), this.archive.getCompensableXid());
} else {
this.archive.setCancelled(true);
logger.info("{}| cancel: identifier= {}, resourceKey= {}, resourceXid= {}.",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(this.archive.getIdentifier().getGlobalTransactionId()),
this.archive.getCompensableResourceKey(), this.archive.getCompensableXid());
}
compensableLogger.updateCompensable(this.archive);
} else if (this.transactionContext.isCoordinator() && this.transactionContext.isPropagated() == false
&& this.transactionContext.getPropagationLevel() == 0) {
for (Iterator itr = this.currentArchiveList.iterator(); itr.hasNext();) {
CompensableArchive compensableArchive = itr.next();
itr.remove(); // remove
compensableArchive.setTried(true);
// compensableLogger.updateCompensable(compensableArchive);
logger.info("{}| try: identifier= {}, resourceKey= {}, resourceXid= {}.",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(compensableArchive.getIdentifier().getGlobalTransactionId()),
compensableArchive.getTransactionResourceKey(), compensableArchive.getTransactionXid());
}
TransactionArchive transactionArchive = this.getTransactionArchive();
transactionArchive.setCompensableStatus(Status.STATUS_COMMITTING);
compensableLogger.updateTransaction(transactionArchive);
logger.info("{}| try completed.",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()));
} else {
for (Iterator itr = this.currentArchiveList.iterator(); itr.hasNext();) {
CompensableArchive compensableArchive = itr.next();
itr.remove(); // remove
compensableArchive.setTried(true);
compensableLogger.updateCompensable(compensableArchive);
logger.info("{}| try: identifier= {}, resourceKey= {}, resourceXid= {}.",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(compensableArchive.getIdentifier().getGlobalTransactionId()),
compensableArchive.getTransactionResourceKey(), compensableArchive.getTransactionXid());
}
}
}
public void recoverIfNecessary() throws SystemException {
if (this.transactionContext.isRecoveried()) {
this.recover(); // recover transaction status
}
}
public synchronized void recover() throws SystemException {
if (this.transactionStatus == Status.STATUS_PREPARED //
|| this.transactionStatus == Status.STATUS_COMMITTING) {
this.recoverNativeResource(true);
this.recoverRemoteResource(true);
} else if (this.transactionStatus == Status.STATUS_PREPARING //
|| this.transactionStatus == Status.STATUS_ROLLING_BACK) {
this.recoverNativeResource(false);
this.recoverRemoteResource(false);
}
}
private void recoverNativeResource(boolean positiveFlag) throws SystemException {
XAResourceDeserializer resourceDeserializer = this.beanFactory.getResourceDeserializer();
CompensableLogger compensableLogger = this.beanFactory.getCompensableLogger();
boolean errorExists = false;
for (int i = this.archiveList.size() - 1; i >= 0; i--) {
CompensableArchive current = this.archiveList.get(i);
String identifier = current.getCompensableResourceKey();
if (StringUtils.isBlank(identifier)) {
continue;
} else if (current.isConfirmed()) {
continue;
} else if (current.isCancelled()) {
continue;
} else if (current.isTried() == false) {
logger.info("{}| the try operation is rolled back, so the cancel may be ignored, service: {}.",
ByteUtils.byteArrayToString(transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(current.getIdentifier().getGlobalTransactionId()));
continue;
}
try {
try {
XAResourceDescriptor descriptor = resourceDeserializer.deserialize(identifier);
RecoveredResource resource = (RecoveredResource) descriptor.getDelegate();
resource.recoverable(current.getCompensableXid());
if (positiveFlag) {
current.setConfirmed(true);
} else {
current.setCancelled(true);
}
compensableLogger.updateCompensable(current);
} catch (XAException xaex) {
switch (xaex.errorCode) {
case XAException.XAER_NOTA:
break;
case XAException.XAER_RMERR:
logger.warn(
"The database table 'bytejta' cannot found, the status of the current branch transaction is unknown!");
break;
case XAException.XAER_RMFAIL:
errorExists = true;
logger.error("{}| error occurred while recovering the branch transaction service: {}",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()),
ByteUtils.byteArrayToString(current.getIdentifier().getGlobalTransactionId()), xaex);
break;
default:
logger.error("Illegal state, the status of the current branch transaction is unknown!", xaex);
}
} catch (RuntimeException rex) {
logger.error("Illegal resources, the status of the current branch transaction is unknown!", rex);
}
} catch (RuntimeException rex) {
errorExists = true;
TransactionXid transactionXid = this.transactionContext.getXid();
logger.error("{}| error occurred while recovering the branch transaction service: {}",
ByteUtils.byteArrayToString(transactionXid.getGlobalTransactionId()), current, rex);
}
} // end-for
if (errorExists) {
throw new SystemException();
}
}
private void recoverRemoteResource(boolean positiveFlag) throws SystemException {
// for (int i = 0; i < this.resourceList.size(); i++) {
// XAResourceArchive archive = this.resourceList.get(i);
// boolean xidExists = this.recover(archive);
// }
}
// private boolean recover(XAResourceArchive archive) throws SystemException {
// boolean xidRecovered = false;
//
// Xid thisXid = archive.getXid();
// byte[] thisGlobalTransactionId = thisXid.getGlobalTransactionId();
// try {
// Xid[] array = archive.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);
// for (int j = 0; xidRecovered == false && array != null && j < array.length; j++) {
// Xid thatXid = array[j];
// byte[] thatGlobalTransactionId = thatXid.getGlobalTransactionId();
// boolean formatIdEquals = thisXid.getFormatId() == thatXid.getFormatId();
// boolean transactionIdEquals = Arrays.equals(thisGlobalTransactionId, thatGlobalTransactionId);
// xidRecovered = formatIdEquals && transactionIdEquals;
// }
// } catch (Exception ex) {
// TransactionXid globalXid = this.transactionContext.getXid();
// logger.error("[{}] recover-resource failed. branch= {}",
// ByteUtils.byteArrayToString(globalXid.getGlobalTransactionId()),
// ByteUtils.byteArrayToString(globalXid.getBranchQualifier()), ex);
// throw new SystemException();
// }
//
// archive.setRecovered(true);
//
// return xidRecovered;
// }
public synchronized void forgetQuietly() {
TransactionXid xid = this.transactionContext.getXid();
try {
this.forget();
} catch (SystemException ex) {
logger.error("Error occurred while forgetting transaction: {}",
ByteUtils.byteArrayToInt(xid.getGlobalTransactionId()), ex);
} catch (RuntimeException ex) {
logger.error("Error occurred while forgetting transaction: {}",
ByteUtils.byteArrayToInt(xid.getGlobalTransactionId()), ex);
}
}
public synchronized void forget() throws SystemException {
LocalResourceCleaner resourceCleaner = this.beanFactory.getLocalResourceCleaner();
boolean success = true;
Map xidMap = new HashMap();
for (int i = 0; i < this.archiveList.size(); i++) {
CompensableArchive current = this.archiveList.get(i);
Xid transactionXid = current.getTransactionXid();
Xid compensableXid = current.getCompensableXid();
if (transactionXid != null && current.isTried()) {
xidMap.put(transactionXid, current.getTransactionResourceKey());
}
if (compensableXid != null && (current.isConfirmed() || current.isCancelled())) {
xidMap.put(compensableXid, current.getCompensableResourceKey());
}
}
for (Iterator> itr = xidMap.entrySet().iterator(); itr.hasNext();) {
Map.Entry entry = itr.next();
Xid xid = entry.getKey();
String resource = entry.getValue();
try {
resourceCleaner.forget(xid, resource);
} catch (RuntimeException rex) {
success = false;
logger.error("forget-transaction: error occurred while forgetting xid: {}", xid, rex);
}
}
for (int i = 0; i < this.resourceList.size(); i++) {
XAResourceArchive current = this.resourceList.get(i);
if (current.isCompleted()) /* current.isHeuristic() */ {
continue; // ignore
}
XidFactory xidFactory = this.beanFactory.getCompensableXidFactory();
TransactionXid branchXid = (TransactionXid) current.getXid();
TransactionXid globalXid = xidFactory.createGlobalXid(branchXid.getGlobalTransactionId());
try {
current.forget(globalXid);
} catch (XAException ex) {
switch (ex.errorCode) {
case XAException.XAER_NOTA:
break;
default:
success = false;
logger.error("forget-transaction: error occurred while forgetting branch: {}", branchXid, ex);
}
} catch (RuntimeException rex) {
success = false;
logger.error("forget-transaction: error occurred while forgetting branch: {}", branchXid, rex);
}
}
if (success) {
CompensableLogger compensableLogger = this.beanFactory.getCompensableLogger();
TransactionRepository compensableRepository = this.beanFactory.getCompensableRepository();
compensableLogger.deleteTransaction(this.getTransactionArchive());
compensableRepository.removeErrorTransaction(this.transactionContext.getXid());
compensableRepository.removeTransaction(this.transactionContext.getXid());
logger.info("{}| forget transaction.",
ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()));
} else {
throw new SystemException();
}
}
public XAResourceDescriptor getResourceDescriptor(String identifier) {
Transaction transaction = this.transactionMap.get(Thread.currentThread());
XAResourceDescriptor descriptor = null;
if (transaction != null) {
descriptor = transaction.getResourceDescriptor(identifier);
}
if (descriptor == null) {
XAResourceArchive archive = this.resourceMap.get(identifier);
descriptor = archive == null ? descriptor : archive.getDescriptor();
}
return descriptor;
}
public XAResourceDescriptor getRemoteCoordinator(String application) {
XAResourceArchive archive = this.applicationMap.get(application);
return archive == null ? null : archive.getDescriptor();
}
public boolean lock(boolean tryFlag) {
if (tryFlag) {
boolean locked = this.lock.tryLock();
if (locked) {
this.currentThread = Thread.currentThread();
} // end-if (locked)
return locked;
} else {
this.lock.lock();
this.currentThread = Thread.currentThread();
return true;
}
}
public void release() {
Thread current = Thread.currentThread();
if (current == this.currentThread) {
this.currentThread = null;
this.lock.unlock();
} else {
logger.warn("Illegal thread: expect= {}, actual= {}.", this.currentThread, current);
}
}
public CompensableArchive getCompensableArchive() {
return this.archive;
}
/**
* only for recovery.
*/
public List getCompensableArchiveList() {
return this.archiveList;
}
/**
* only for recovery.
*/
public Map getParticipantArchiveMap() {
return this.resourceMap;
}
/**
* only for recovery.
*/
public List getParticipantArchiveList() {
return this.resourceList;
}
/**
* only for recovery.
*/
public Map getApplicationArchiveMap() {
return this.applicationMap;
}
public void setRollbackOnly() throws IllegalStateException, SystemException {
throw new IllegalStateException();
}
public void setRollbackOnlyQuietly() {
throw new IllegalStateException();
}
public boolean isLocalTransaction() {
throw new IllegalStateException();
}
public int getStatus() throws SystemException {
return this.transactionStatus;
}
public int getTransactionStatus() {
return this.transactionStatus;
}
public void setTransactionStatus(int status) {
this.transactionStatus = status;
}
public boolean isTiming() {
throw new IllegalStateException();
}
public void setTransactionTimeout(int seconds) {
throw new IllegalStateException();
}
public TransactionContext getTransactionContext() {
return this.transactionContext;
}
public void setBeanFactory(CompensableBeanFactory tbf) {
this.beanFactory = tbf;
}
public Serializable getVariable(String key) {
return this.variables.get(key);
}
public boolean isCurrentCompensableServiceTried() {
return this.archive.isTried();
}
public void setVariable(String key, Serializable variable) {
this.variables.put(key, variable);
}
public boolean isParticipantStickyRequired() {
return participantStickyRequired;
}
public void setParticipantStickyRequired(boolean participantStickyRequired) {
this.participantStickyRequired = participantStickyRequired;
}
public Object getTransactionalExtra() {
return this.transactionMap.get(Thread.currentThread());
}
public void setTransactionalExtra(Object transactionalExtra) {
if (transactionalExtra == null) {
this.transactionMap.remove(Thread.currentThread());
} else {
this.transactionMap.put(Thread.currentThread(), (Transaction) transactionalExtra);
}
}
public Transaction getTransaction() {
return (Transaction) this.getTransactionalExtra();
}
public int getTransactionVote() {
return transactionVote;
}
public void setTransactionVote(int transactionVote) {
this.transactionVote = transactionVote;
}
public Map getVariables() {
return variables;
}
public void setVariables(Map variables) {
this.variables = variables;
}
}