/*************************************************** * * * Mobicents: The Open Source VoIP Platform * * * * Distributable under LGPL license. * * * ***************************************************/ package org.mobicents.slee.runtime.transaction; import java.util.concurrent.ConcurrentHashMap; import javax.slee.SLEEException; import javax.slee.TransactionRequiredLocalException; import javax.slee.transaction.CommitListener; import javax.slee.transaction.RollbackListener; import javax.slee.transaction.SleeTransaction; 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.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.apache.log4j.Logger; import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple; /** * Implementation of SLEE Tx manager. * * @author Tim Fox - Complete re-write. * @author M. Ranganathan * @author J. Deruelle * @author Ralf Siedow * @author Ivelin Ivanov * @author Eduardo Martins version 2 * */ public class SleeTransactionManagerImpl implements SleeTransactionManager { private static Logger logger = Logger .getLogger(SleeTransactionManagerImpl.class); /** * transaction context per transaction map */ private ConcurrentHashMap<Transaction, TransactionContext> transactionContexts = new ConcurrentHashMap<Transaction, TransactionContext>(); /** * the underlying JTA tx manager */ private final TransactionManager transactionManager; public SleeTransactionManagerImpl(TransactionManager transactionManager) { this.transactionManager = transactionManager; logger.info("SLEE Transaction Manager created."); } /* (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#getRealTransactionManager() */ public TransactionManager getRealTransactionManager() { return transactionManager; } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#displayOngoingSleeTransactions() */ public String displayOngoingSleeTransactions() { if (logger.isDebugEnabled()) { String msg = "---------+ Begin dump of SLEE TX map: +--------------------------"; for (Transaction tx : transactionContexts.keySet()) { msg += "\n----+ Transaction: " + tx; } msg += "\nEnd dump of SLEE TX map: -------------------------- \n"; return msg; } else { return ""; } } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#getRollbackOnly() */ public boolean getRollbackOnly() throws SystemException { Transaction tx = getTransaction(); if (tx == null) { throw new SystemException("no transaction"); } return tx.getStatus() == Status.STATUS_MARKED_ROLLBACK; // this code was hack to trap setRollbackOnly() due to jboss cache, don't remove it may be needed again //return tx.getStatus() == Status.STATUS_MARKED_ROLLBACK || (tx.getStatus() == Status.STATUS_ACTIVE && getTransactionContext().getRollbackOnly()); } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#mandateTransaction() */ public void mandateTransaction() throws TransactionRequiredLocalException { Transaction tx = null; try { tx = getTransaction(); } catch (SystemException e) { throw new SLEEException(e.getMessage(),e); } if (tx == null) throw new TransactionRequiredLocalException( "Transaction Mandatory"); int status = -1; try { status = tx.getStatus(); } catch (SystemException e) { throw new SLEEException(e.getMessage(),e); } if (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK) { throw new IllegalStateException( "There is no active tx, tx is in state: " + status); } } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#requireTransaction() */ public boolean requireTransaction() { try { Transaction tx = getTransaction(); int status = -1; if (tx != null) { status = tx.getStatus(); } if (tx == null || (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK)) { // start a new one this.begin(); return true; } } catch (NotSupportedException e) { logger.error("Exception creating transaction", e); } catch (SystemException e) { logger.error("Caught SystemException in checking transaction", e); } return false; } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#requireTransactionEnd(boolean, boolean) */ public void requireTransactionEnd(boolean terminateTx, boolean doRollback) throws IllegalStateException, SecurityException, SystemException, RollbackException, HeuristicMixedException, HeuristicRollbackException { if (terminateTx) { if (doRollback) { rollback(); } else { commit(); } } else { if (doRollback) { setRollbackOnly(); } } } /* * (non-Javadoc) * @see javax.slee.transaction.SleeTransactionManager#asSleeTransaction(javax.transaction.Transaction) */ public SleeTransaction asSleeTransaction(Transaction transaction) throws NullPointerException, IllegalArgumentException, SystemException { if (transaction == null) { throw new NullPointerException("null transaction"); } if (transaction instanceof SleeTransactionImpl) { return (SleeTransaction) transaction; } if (transaction instanceof TransactionImple) { return new SleeTransactionImpl((TransactionImple) transaction,this); } throw new IllegalArgumentException("unexpected transaction class type "+transaction.getClass()); } /* * (non-Javadoc) * @see javax.slee.transaction.SleeTransactionManager#asyncCommit(javax.slee.transaction.CommitListener) */ public void asyncCommit(CommitListener commitListener) throws IllegalStateException, SecurityException { try { SleeTransaction sleeTransaction = getSleeTransaction(); if (sleeTransaction == null) { throw new IllegalStateException("no transaction"); } else { sleeTransaction.asyncCommit(commitListener); } } catch (SystemException e) { if (commitListener != null) { commitListener.systemException(e); } } } /* * (non-Javadoc) * @see javax.slee.transaction.SleeTransactionManager#asyncRollback(javax.slee.transaction.RollbackListener) */ public void asyncRollback(RollbackListener rollbackListener) throws IllegalStateException, SecurityException { try { SleeTransaction sleeTransaction = getSleeTransaction(); if (sleeTransaction == null) { throw new IllegalStateException("no transaction"); } else { sleeTransaction.asyncRollback(rollbackListener); } } catch (SystemException e) { if (rollbackListener != null) { rollbackListener.systemException(e); } } } /* * (non-Javadoc) * @see javax.slee.transaction.SleeTransactionManager#beginSleeTransaction() */ public SleeTransaction beginSleeTransaction() throws NotSupportedException, SystemException { begin(); return getSleeTransaction(); } /* * (non-Javadoc) * @see javax.slee.transaction.SleeTransactionManager#getSleeTransaction() */ public SleeTransaction getSleeTransaction() throws SystemException { Transaction transaction = getTransaction(); if (transaction != null) { return new SleeTransactionImpl((TransactionImple)transaction,this); } else { return null; } } /* * (non-Javadoc) * @see javax.transaction.TransactionManager#begin() */ public void begin() throws NotSupportedException, SystemException { // begin transaction transactionManager.begin(); Transaction tx = getTransaction(); if (logger.isDebugEnabled()) { logger.debug("Transaction started: "+tx); } // register for call-backs try { tx.registerSynchronization(new SynchronizationHandler(tx)); } catch (RollbackException e) { throw new SystemException("Unable to register listener for created transaction. Error: "+e.getMessage()); } } /* * (non-Javadoc) * @see javax.transaction.TransactionManager#commit() */ public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { if (logger.isDebugEnabled()) { logger.debug("Starting commit of tx "+getTransaction()); } transactionManager.commit(); } /* * (non-Javadoc) * @see javax.transaction.TransactionManager#getStatus() */ public int getStatus() throws SystemException { int status = transactionManager.getStatus(); // this code was hack to trap setRollbackOnly() due to jboss cache, don't remove it may be needed again /* if (status == Status.STATUS_ACTIVE && getTransactionContext().getRollbackOnly()) { return Status.STATUS_MARKED_ROLLBACK; } else { return status; } */ return status; } /* * (non-Javadoc) * @see javax.transaction.TransactionManager#getTransaction() */ public Transaction getTransaction() throws SystemException { if (transactionManager != null) { return transactionManager.getTransaction(); } else { throw new SystemException("tx manager unavailable"); } } /* * (non-Javadoc) * @see javax.transaction.TransactionManager#resume(javax.transaction.Transaction) */ public void resume(Transaction transaction) throws InvalidTransactionException, IllegalStateException, SystemException { if (transactionManager != null) { if (logger.isDebugEnabled()) { logger.debug("Resuming tx "+transaction); } transactionManager.resume(transaction); } else { throw new SystemException("tx manager unavailable"); } } /* * (non-Javadoc) * @see javax.transaction.TransactionManager#rollback() */ public void rollback() throws IllegalStateException, SecurityException, SystemException { if (logger.isDebugEnabled()) { logger.debug("Starting rollback of tx "+getTransaction()); } transactionManager.rollback(); } /* * (non-Javadoc) * @see javax.transaction.TransactionManager#setRollbackOnly() */ public void setRollbackOnly() throws IllegalStateException, SystemException { if (transactionManager != null) { transactionManager.setRollbackOnly(); if (logger.isDebugEnabled()) { logger.debug("rollbackonly set on tx " + getTransaction()); } } else { throw new SystemException("tx manager unavailable"); } } /* * (non-Javadoc) * @see javax.transaction.TransactionManager#setTransactionTimeout(int) */ public void setTransactionTimeout(int seconds) throws SystemException { if (transactionManager != null) { transactionManager.setTransactionTimeout(seconds); } else { throw new SystemException("tx manager unavailable"); } } /* * (non-Javadoc) * @see javax.transaction.TransactionManager#suspend() */ public Transaction suspend() throws SystemException { if (transactionManager != null) { if (logger.isDebugEnabled()) { logger.debug("Suspending tx "+getTransaction()); } return transactionManager.suspend(); } else { throw new SystemException("tx manager unavailable"); } } @Override public String toString() { return "SLEE Transaction Manager: " + "\n+-- Number of transactions: " + transactionContexts.size(); } // --- TX CONTEXT AND ACTION METHODS /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#getTransactionContext() */ public TransactionContext getTransactionContext() throws SystemException { Transaction tx = getTransaction(); if (tx == null) { throw new SystemException("no transaction"); } TransactionContext tc = transactionContexts.get(tx); if (tc == null) { tc = new TransactionContext(); transactionContexts.put(tx, tc); } return tc; } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#addAfterCommitAction(org.mobicents.slee.runtime.transaction.TransactionalAction) */ public void addAfterCommitAction(TransactionalAction action) throws SystemException { getTransactionContext().getAfterCommitActions().add(action); } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#addAfterCommitPriorityAction(org.mobicents.slee.runtime.transaction.TransactionalAction) */ public void addAfterCommitPriorityAction(TransactionalAction action) throws SystemException { getTransactionContext().getAfterCommitPriorityActions().add(action); } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#addAfterRollbackAction(org.mobicents.slee.runtime.transaction.TransactionalAction) */ public void addAfterRollbackAction(TransactionalAction action) throws SystemException { getTransactionContext().getAfterRollbackActions().add(action); } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#addBeforeCommitAction(org.mobicents.slee.runtime.transaction.TransactionalAction) */ public void addBeforeCommitAction(TransactionalAction action) throws SystemException { getTransactionContext().getBeforeCommitActions().add(action); } /* * (non-Javadoc) * @see org.mobicents.slee.runtime.transaction.SleeTransactionManager#addBeforeCommitPriorityAction(org.mobicents.slee.runtime.transaction.TransactionalAction) */ public void addBeforeCommitPriorityAction(TransactionalAction action) throws SystemException { getTransactionContext().getBeforeCommitPriorityActions().add(action); } // --- TX MANAGER LISTENER class SynchronizationHandler implements Synchronization { private Transaction tx; public SynchronizationHandler(Transaction tx) { this.tx = tx; } public void afterCompletion(int status) { // get tx context TransactionContext txContext = transactionContexts.remove(tx); if (txContext != null) { switch (status) { case Status.STATUS_COMMITTED: if (logger.isDebugEnabled()) { logger.debug("Transaction committed: " + tx); } txContext.executeAfterCommitPriorityActions(); txContext.executeAfterCommitActions(); break; case Status.STATUS_ROLLEDBACK: if (logger.isDebugEnabled()) { logger.debug("Transaction rollback: " + tx); } txContext.executeAfterRollbackActions(); break; default: throw new IllegalStateException("Unexpected transaction state " + status); } txContext.cleanup(); } } public void beforeCompletion() { // get entry and execute before commit actions TransactionContext tc = transactionContexts.get(tx); if (tc != null) { tc.executeBeforeCommitPriorityActions(); tc.executeBeforeCommitActions(); } } } }