/** * 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.bytetcc; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.InvalidTransactionException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import org.bytesoft.bytejta.supports.wire.RemoteCoordinator; import org.bytesoft.bytetcc.supports.CompensableSynchronization; import org.bytesoft.common.utils.ByteUtils; import org.bytesoft.compensable.CompensableBeanFactory; import org.bytesoft.compensable.CompensableManager; import org.bytesoft.compensable.CompensableTransaction; import org.bytesoft.compensable.TransactionContext; import org.bytesoft.compensable.aware.CompensableBeanFactoryAware; import org.bytesoft.compensable.aware.CompensableEndpointAware; import org.bytesoft.compensable.logging.CompensableLogger; import org.bytesoft.transaction.Transaction; import org.bytesoft.transaction.TransactionLock; import org.bytesoft.transaction.TransactionManager; import org.bytesoft.transaction.TransactionRepository; import org.bytesoft.transaction.xa.TransactionXid; import org.bytesoft.transaction.xa.XidFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CompensableManagerImpl implements CompensableManager, CompensableBeanFactoryAware, CompensableEndpointAware { static final Logger logger = LoggerFactory.getLogger(CompensableManagerImpl.class); private CompensableBeanFactory beanFactory; private String endpoint; private final Map<Thread, Transaction> compensableMap = new ConcurrentHashMap<Thread, Transaction>(); public void associateThread(Transaction transaction) { this.compensableMap.put(Thread.currentThread(), (CompensableTransaction) transaction); } public CompensableTransaction desociateThread() { return (CompensableTransaction) this.compensableMap.remove(Thread.currentThread()); } public int getStatus() throws SystemException { Transaction transaction = this.getTransactionQuietly(); return transaction == null ? Status.STATUS_NO_TRANSACTION : transaction.getStatus(); } public Transaction getTransactionQuietly() { CompensableTransaction transaction = this.getCompensableTransactionQuietly(); return transaction == null ? null : transaction.getTransaction(); } public Transaction getTransaction() throws SystemException { CompensableTransaction transaction = this.getCompensableTransactionQuietly(); return transaction == null ? null : transaction.getTransaction(); } public CompensableTransaction getCompensableTransactionQuietly() { return (CompensableTransaction) this.compensableMap.get(Thread.currentThread()); } public void resume(javax.transaction.Transaction tobj) throws InvalidTransactionException, IllegalStateException, SystemException { if (Transaction.class.isInstance(tobj)) { TransactionManager transactionManager = this.beanFactory.getTransactionManager(); Transaction transaction = (Transaction) tobj; CompensableTransaction compensable = (CompensableTransaction) transaction.getTransactionalExtra(); compensable.setTransactionalExtra(transaction); compensable.resume(); TransactionContext compensableContext = compensable.getTransactionContext(); compensableContext.setPropagationLevel(compensableContext.getPropagationLevel() - 1); transactionManager.resume(transaction); } else { throw new InvalidTransactionException(); } } public Transaction suspend() throws SystemException { CompensableTransaction compensable = (CompensableTransaction) this.compensableMap.get(Thread.currentThread()); if (compensable == null) { throw new SystemException(); } TransactionManager transactionManager = this.beanFactory.getTransactionManager(); Transaction transaction = transactionManager.suspend(); TransactionContext compensableContext = compensable.getTransactionContext(); compensableContext.setPropagationLevel(compensableContext.getPropagationLevel() + 1); compensable.suspend(); compensable.setTransactionalExtra(null); return transaction; } public void begin() throws NotSupportedException, SystemException { CompensableTransaction compensable = this.getCompensableTransactionQuietly(); if (compensable == null || compensable.getTransaction() != null) { throw new SystemException(); } TransactionContext compensableContext = compensable.getTransactionContext(); XidFactory transactionXidFactory = this.beanFactory.getTransactionXidFactory(); TransactionXid transactionXid = transactionXidFactory.createGlobalXid(); TransactionContext transactionContext = compensableContext.clone(); transactionContext.setXid(transactionXid); this.invokeBegin(transactionContext, false); } protected void invokeBegin(TransactionContext transactionContext, boolean createFlag) throws NotSupportedException, SystemException { RemoteCoordinator transactionCoordinator = this.beanFactory.getTransactionCoordinator(); CompensableTransaction compensable = this.getCompensableTransactionQuietly(); TransactionContext compensableContext = compensable.getTransactionContext(); TransactionXid compensableXid = compensableContext.getXid(); TransactionXid transactionXid = transactionContext.getXid(); try { Transaction transaction = transactionCoordinator.start(transactionContext, XAResource.TMNOFLAGS); transaction.setTransactionalExtra(compensable); compensable.setTransactionalExtra(transaction); transaction.registerTransactionResourceListener(compensable); transaction.registerTransactionListener(compensable); CompensableSynchronization synchronization = this.beanFactory.getCompensableSynchronization(); synchronization.afterBegin(compensable.getTransaction(), createFlag); } catch (XAException tex) { logger.info("[{}] begin-transaction: error occurred while starting jta-transaction: {}", ByteUtils.byteArrayToString(compensableXid.getGlobalTransactionId()), ByteUtils.byteArrayToString(transactionXid.getGlobalTransactionId())); try { transactionCoordinator.end(transactionContext, XAResource.TMFAIL); throw new SystemException("Error occurred while beginning a compensable-transaction!"); } catch (XAException ignore) { throw new SystemException("Error occurred while beginning a compensable-transaction!"); } } } public void compensableBegin() throws NotSupportedException, SystemException { if (this.getCompensableTransactionQuietly() != null) { throw new NotSupportedException(); } CompensableLogger compensableLogger = this.beanFactory.getCompensableLogger(); TransactionLock compensableLock = this.beanFactory.getCompensableLock(); TransactionRepository compensableRepository = this.beanFactory.getCompensableRepository(); RemoteCoordinator compensableCoordinator = this.beanFactory.getCompensableCoordinator(); XidFactory transactionXidFactory = this.beanFactory.getTransactionXidFactory(); XidFactory compensableXidFactory = this.beanFactory.getCompensableXidFactory(); TransactionXid compensableXid = compensableXidFactory.createGlobalXid(); TransactionXid transactionXid = transactionXidFactory.createGlobalXid(compensableXid.getGlobalTransactionId()); TransactionContext compensableContext = new TransactionContext(); compensableContext.setCoordinator(true); compensableContext.setCompensable(true); compensableContext.setXid(compensableXid); compensableContext.setPropagatedBy(compensableCoordinator.getIdentifier()); CompensableTransactionImpl compensable = new CompensableTransactionImpl(compensableContext); compensable.setBeanFactory(this.beanFactory); this.associateThread(compensable); boolean failure = true; try { TransactionContext transactionContext = new TransactionContext(); transactionContext.setXid(transactionXid); this.invokeBegin(transactionContext, true); failure = false; } finally { if (failure) { this.desociateThread(); } } compensableRepository.putTransaction(compensableXid, compensable); compensableLock.lockTransaction(compensableXid, this.endpoint); compensableLogger.createTransaction(compensable.getTransactionArchive()); logger.info("{}| compensable transaction begin!", ByteUtils.byteArrayToString(compensableXid.getGlobalTransactionId())); } public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { CompensableTransaction transaction = this.getCompensableTransactionQuietly(); if (transaction == null) { throw new IllegalStateException(); } TransactionContext transactionContext = transaction.getTransactionContext(); boolean coordinator = transactionContext.isCoordinator(); boolean propagated = transactionContext.isPropagated(); boolean compensable = transactionContext.isCompensable(); boolean compensating = transactionContext.isCompensating(); int propagatedLevel = transactionContext.getPropagationLevel(); if (compensable == false) { throw new IllegalStateException(); } else if (compensating) { this.invokeTransactionCommit(transaction); } else if (coordinator) { if (propagated) { this.invokeTransactionCommit(transaction); } else if (propagatedLevel > 0) { this.invokeTransactionCommit(transaction); } else { throw new IllegalStateException(); } } else { this.invokeTransactionCommit(transaction); } } protected void invokeTransactionCommit(CompensableTransaction compensable) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { Transaction transaction = compensable.getTransaction(); boolean isLocalTransaction = transaction.isLocalTransaction(); try { if (isLocalTransaction) { this.invokeTransactionCommitIfLocalTransaction(compensable); } else { this.invokeTransactionCommitIfNotLocalTransaction(compensable); } } finally { compensable.setTransactionalExtra(null); } } protected void invokeTransactionCommitIfLocalTransaction(CompensableTransaction compensable) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { Transaction transaction = compensable.getTransaction(); org.bytesoft.transaction.TransactionContext transactionContext = transaction.getTransactionContext(); TransactionXid xid = transactionContext.getXid(); RemoteCoordinator transactionCoordinator = this.beanFactory.getTransactionCoordinator(); try { transactionCoordinator.end(transactionContext, XAResource.TMSUCCESS); transactionCoordinator.commit(xid, true); } catch (XAException xae) { switch (xae.errorCode) { case XAException.XA_HEURCOM: break; case XAException.XA_HEURRB: throw new HeuristicRollbackException(); case XAException.XA_HEURMIX: throw new HeuristicMixedException(); case XAException.XAER_RMERR: default: throw new SystemException(); } } } protected void invokeTransactionCommitIfNotLocalTransaction(CompensableTransaction compensable) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { Transaction transaction = compensable.getTransaction(); org.bytesoft.transaction.TransactionContext transactionContext = transaction.getTransactionContext(); TransactionXid xid = transactionContext.getXid(); RemoteCoordinator transactionCoordinator = this.beanFactory.getTransactionCoordinator(); try { transactionCoordinator.end(transactionContext, XAResource.TMSUCCESS); TransactionContext compensableContext = compensable.getTransactionContext(); logger.error("[{}] jta-transaction in try-phase cannot be xa transaction.", ByteUtils.byteArrayToString(compensableContext.getXid().getGlobalTransactionId())); transactionCoordinator.rollback(xid); throw new HeuristicRollbackException(); } catch (XAException xae) { throw new SystemException(); } } public void fireCompensableCommit(CompensableTransaction transaction) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { try { this.associateThread(transaction); transaction.commit(); } finally { this.desociateThread(); } } public void rollback() throws IllegalStateException, SecurityException, SystemException { CompensableTransaction transaction = this.getCompensableTransactionQuietly(); if (transaction == null) { throw new IllegalStateException(); } TransactionContext transactionContext = transaction.getTransactionContext(); boolean coordinator = transactionContext.isCoordinator(); boolean propagated = transactionContext.isPropagated(); boolean compensable = transactionContext.isCompensable(); int propagatedLevel = transactionContext.getPropagationLevel(); if (compensable == false) { throw new IllegalStateException(); } else if (coordinator) { if (propagated) { this.invokeTransactionRollback(transaction); } else if (propagatedLevel > 0) { this.invokeTransactionRollback(transaction); } else { throw new IllegalStateException(); } } else { this.invokeTransactionRollback(transaction); } } protected void invokeTransactionRollback(CompensableTransaction compensable) throws IllegalStateException, SecurityException, SystemException { Transaction transaction = compensable.getTransaction(); org.bytesoft.transaction.TransactionContext transactionContext = transaction.getTransactionContext(); TransactionXid xid = transactionContext.getXid(); RemoteCoordinator transactionCoordinator = this.beanFactory.getTransactionCoordinator(); try { transactionCoordinator.end(transactionContext, XAResource.TMSUCCESS); transactionCoordinator.rollback(xid); } catch (XAException xae) { throw new SystemException(); } finally { compensable.setTransactionalExtra(null); } } public void fireCompensableRollback(CompensableTransaction transaction) throws IllegalStateException, SecurityException, SystemException { try { this.associateThread(transaction); transaction.rollback(); } finally { this.desociateThread(); } } public void compensableCommit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { CompensableTransaction transaction = this.getCompensableTransactionQuietly(); if (transaction == null) { throw new IllegalStateException(); } else if (transaction.getTransaction() == null) { throw new IllegalStateException(); } TransactionContext transactionContext = transaction.getTransactionContext(); boolean coordinator = transactionContext.isCoordinator(); boolean compensable = transactionContext.isCompensable(); boolean compensating = transactionContext.isCompensating(); if (compensable == false) { throw new IllegalStateException(); } else if (coordinator == false) { throw new IllegalStateException(); } else if (compensating) { throw new IllegalStateException(); } TransactionLock compensableLock = this.beanFactory.getCompensableLock(); TransactionXid xid = transactionContext.getXid(); try { this.desociateThread(); this.invokeCompensableCommit(transaction); } finally { compensableLock.unlockTransaction(xid, this.endpoint); } } protected void invokeCompensableCommit(CompensableTransaction compensable) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { TransactionRepository compensableRepository = this.beanFactory.getCompensableRepository(); Transaction transaction = compensable.getTransaction(); TransactionContext compensableContext = compensable.getTransactionContext(); boolean commitExists = false; boolean rollbackExists = false; boolean errorExists = false; boolean isLocalTransaction = transaction.isLocalTransaction(); try { if (isLocalTransaction) /* jta-transaction in try-phase cannot be xa transaction. */ { this.invokeCompensableCommitIfLocalTransaction(compensable); commitExists = true; } else { this.invokeCompensableCommitIfNotLocalTransaction(compensable); } } catch (HeuristicRollbackException ex) { rollbackExists = true; } catch (SystemException ex) { errorExists = true; } catch (RuntimeException ex) { errorExists = true; } finally { compensable.setTransactionalExtra(null); } boolean success = false; try { if (errorExists) { this.fireCompensableRollback(compensable); success = true; compensable.forgetQuietly(); // forget transaction } else if (commitExists) { this.fireCompensableCommit(compensable); success = true; compensable.forgetQuietly(); // forget transaction } else if (rollbackExists) { this.fireCompensableRollback(compensable); success = true; compensable.forgetQuietly(); // forget transaction throw new HeuristicRollbackException(); } else { success = true; } } finally { TransactionXid xid = compensableContext.getXid(); if (success) { compensableRepository.removeErrorTransaction(xid); compensableRepository.removeTransaction(xid); } else { compensableRepository.putErrorTransaction(xid, compensable); } } } protected void invokeCompensableCommitIfLocalTransaction(CompensableTransaction compensable) throws HeuristicRollbackException, SystemException { RemoteCoordinator transactionCoordinator = this.beanFactory.getTransactionCoordinator(); Transaction transaction = compensable.getTransaction(); org.bytesoft.transaction.TransactionContext transactionContext = transaction.getTransactionContext(); TransactionXid transactionXid = transactionContext.getXid(); try { transactionCoordinator.end(transactionContext, XAResource.TMSUCCESS); transactionCoordinator.commit(transactionXid, true); } catch (XAException xaex) { switch (xaex.errorCode) { case XAException.XA_HEURCOM: break; case XAException.XA_HEURRB: throw new HeuristicRollbackException(); default: throw new SystemException(); } } } protected void invokeCompensableCommitIfNotLocalTransaction(CompensableTransaction compensable) throws HeuristicRollbackException, SystemException { RemoteCoordinator transactionCoordinator = this.beanFactory.getTransactionCoordinator(); Transaction transaction = compensable.getTransaction(); org.bytesoft.transaction.TransactionContext transactionContext = transaction.getTransactionContext(); TransactionXid transactionXid = transactionContext.getXid(); try { transactionCoordinator.end(transactionContext, XAResource.TMSUCCESS); TransactionContext compensableContext = compensable.getTransactionContext(); logger.error("{}| jta-transaction in compensating-phase cannot be xa transaction.", ByteUtils.byteArrayToString(compensableContext.getXid().getGlobalTransactionId())); transactionCoordinator.rollback(transactionXid); throw new HeuristicRollbackException(); } catch (XAException xaex) { throw new SystemException(); } } public void compensableRollback() throws IllegalStateException, SecurityException, SystemException { CompensableTransaction transaction = this.getCompensableTransactionQuietly(); if (transaction == null) { throw new IllegalStateException(); } TransactionContext transactionContext = transaction.getTransactionContext(); boolean coordinator = transactionContext.isCoordinator(); boolean compensable = transactionContext.isCompensable(); boolean compensating = transactionContext.isCompensating(); if (compensable == false) { throw new IllegalStateException(); } else if (coordinator == false) { throw new IllegalStateException(); } else if (compensating) { throw new IllegalStateException(); } TransactionLock compensableLock = this.beanFactory.getCompensableLock(); TransactionXid xid = transactionContext.getXid(); try { this.desociateThread(); this.invokeCompensableRollback(transaction); } finally { compensableLock.unlockTransaction(xid, this.endpoint); } } protected void invokeCompensableRollback(CompensableTransaction compensable) throws IllegalStateException, SecurityException, SystemException { TransactionRepository compensableRepository = this.beanFactory.getCompensableRepository(); RemoteCoordinator transactionCoordinator = this.beanFactory.getTransactionCoordinator(); Transaction transaction = compensable.getTransaction(); org.bytesoft.compensable.TransactionContext compensableContext = compensable.getTransactionContext(); org.bytesoft.transaction.TransactionContext transactionContext = transaction.getTransactionContext(); try { TransactionXid transactionXid = transactionContext.getXid(); transactionCoordinator.end(transactionContext, XAResource.TMSUCCESS); transactionCoordinator.rollback(transactionXid); } catch (XAException ex) { logger.error("Error occurred while rolling back transaction in try phase!", ex); } finally { compensable.setTransactionalExtra(null); } boolean success = false; try { this.fireCompensableRollback(compensable); success = true; compensable.forgetQuietly(); // forget transaction } finally { TransactionXid xid = compensableContext.getXid(); if (success) { compensableRepository.removeErrorTransaction(xid); compensableRepository.removeTransaction(xid); } else { compensableRepository.putErrorTransaction(xid, compensable); } } } public void setRollbackOnly() throws IllegalStateException, SystemException { } public void setTransactionTimeout(int seconds) throws SystemException { } public int getTimeoutSeconds() { return 0; } public void setTimeoutSeconds(int timeoutSeconds) { } public void setBeanFactory(CompensableBeanFactory tbf) { this.beanFactory = tbf; } public void setEndpoint(String identifier) { this.endpoint = identifier; } }