package org.mobicents.slee.runtime.eventrouter; import java.util.concurrent.ConcurrentHashMap; import javax.transaction.SystemException; import javax.transaction.Transaction; import org.mobicents.slee.container.SleeContainer; import org.mobicents.slee.runtime.activity.ActivityContext; import org.mobicents.slee.runtime.transaction.SleeTransactionManager; import org.mobicents.slee.runtime.transaction.TransactionalAction; /** * Manager of temporary attachements related with a {@link ActivityContextHandle}, by {@link Transaction}s being executed. * @author eduardomartins * */ public class PendingAttachementsMonitor { /** * possible states for a tx regarding attachements related with a {@link ActivityContext} * @author eduardomartins * */ private enum TxModifyingAttachsState { nullStateWithTxAction, attachingState, detachingState } private final ConcurrentHashMap<String,TxModifyingAttachsState> txsModifyingAttachs = new ConcurrentHashMap<String, TxModifyingAttachsState>(); private final Object monitor = new Object(); private void txModifyingAttachs(boolean attach) throws SystemException { SleeTransactionManager txManager = SleeContainer.lookupFromJndi().getTransactionManager(); String txId = txManager.getTransaction().toString(); // get the current value of the attachment TxModifyingAttachsState state = txsModifyingAttachs.get(txId); if (state == null) { // state doesn't exist, set it state = (attach ? TxModifyingAttachsState.attachingState : TxModifyingAttachsState.detachingState); txsModifyingAttachs.put(txId, state); // set tx action to remove tx after it ends WhenTransactionEndsAction txAction = new WhenTransactionEndsAction(txId); txManager.addAfterCommitAction(txAction); txManager.addAfterRollbackAction(txAction); } else { // state already exists if (state == TxModifyingAttachsState.nullStateWithTxAction) { // change state but don't set tx action state = (attach ? TxModifyingAttachsState.attachingState : TxModifyingAttachsState.detachingState); } else if ((attach && state == TxModifyingAttachsState.detachingState) || (!attach && state == TxModifyingAttachsState.attachingState)) { // state changed state = TxModifyingAttachsState.nullStateWithTxAction; } } } /** * Adds current tx to the set of txs attaching to the {@link ActivityContext} related with this object. * @throws SystemException */ public void txAttaching() throws SystemException { txModifyingAttachs(true); } /** * Adds current tx to the set of txs dettaching to the {@link ActivityContext} related with this object. * @throws SystemException */ public void txDetaching() throws SystemException { txModifyingAttachs(false); } /** * Holds the thread till all attach modifications are committed/rollback */ public void waitTillNoTxModifyingAttachs() { if (!txsModifyingAttachs.isEmpty()) { synchronized (monitor) { try { monitor.wait(5000); } catch (InterruptedException e) { // ignore } } } } /** * {@link TransactionalAction} implementation that removes one tx from the map of txs changing attachements for an {@link ActivityContext}. * @author eduardomartins * */ private class WhenTransactionEndsAction implements TransactionalAction { private final String transactionId; public WhenTransactionEndsAction(String transactionId) { this.transactionId = transactionId; } public void execute() { if (txsModifyingAttachs != null) { // if map exists then remove the tx txsModifyingAttachs.remove(transactionId); // there may be a event router executor thread waiting for this tx to end so we need to signal its monitor synchronized (monitor) { monitor.notify(); } } } } }