package org.mobicents.slee.runtime.eventrouter; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.slee.ActivityContextInterface; import javax.slee.Address; import javax.slee.EventContext; import javax.slee.SLEEException; import javax.slee.ServiceID; import javax.slee.TransactionRequiredLocalException; import javax.transaction.SystemException; import javax.transaction.Transaction; import org.apache.log4j.Logger; import org.mobicents.slee.container.SleeContainer; import org.mobicents.slee.container.component.ServiceComponent; import org.mobicents.slee.runtime.activity.ActivityContextInterfaceImpl; import org.mobicents.slee.runtime.transaction.TransactionalAction; /** * Implementation of SLEE 1.1 Event Context. The usage of this class is not * thread safe. * * @author martins * */ public class EventContextImpl implements EventContext { /** * default timeout for a context suspension, 1 min seems appropriate since * this will block other events in the activity this could be configurable * but since the app can specify its own timeout value ... */ private static final int DEFAULT_TIMEOUT = 10000; /** * the container */ private final SleeContainer sleeContainer; /** * the event related with this context */ private final DeferredEvent deferredEvent; /** * the id of this event context, set to the time when this instance was created, this id will be used */ private final EventContextID eventContextID; /** * the transaction being used to deliver this event */ private Transaction transaction; /** * indicates if the context is suspended or not */ private boolean suspended; /** * a queue of {@link DeferredEvent}s barried due to this context become * suspended */ private LinkedList<DeferredEvent> barriedEvents; /** * the set containing all sbb entities that handled the event so far */ private final Set<String> sbbEntitiesThatHandledEvent = new HashSet<String>(); /** * the ordered list containing all active services that will process this event as initial */ private final LinkedList<ServiceComponent> activeServicesToProcessEventAsInitial = new LinkedList<ServiceComponent>(); /** * the scheduled future for the task controlling the suspension timeout */ private ScheduledFuture<?> scheduledFuture; /** * transactional action action to change state */ private EventContextStateChange transactionalAction; public EventContextImpl(DeferredEvent deferredEvent, SleeContainer sleeContainer) { this.deferredEvent = deferredEvent; this.sleeContainer = sleeContainer; this.eventContextID = new EventContextID(deferredEvent.getActivityContextHandle(),deferredEvent.getEvent()); } public ActivityContextInterface getActivityContextInterface() { // perhaps we should cache this aci if it becomes frequently called return new ActivityContextInterfaceImpl(sleeContainer .getActivityContextFactory().getActivityContext( deferredEvent.getActivityContextHandle())); } public Address getAddress() { return deferredEvent.getAddress(); } public Object getEvent() { return deferredEvent.getEvent(); } public ServiceID getService() { return deferredEvent.getService(); } public boolean isSuspended() throws TransactionRequiredLocalException, SLEEException { sleeContainer.getTransactionManager().mandateTransaction(); if (isSuspendedNotTransacted()) { // current committed state is suspended if (transactionalAction != null && transactionalAction.op == EventContextStateChangeOp.resume) { // resume is the op queued for tx commit return false; } else { return true; } } else { // current committed state is not suspended if (transactionalAction != null && transactionalAction.op == EventContextStateChangeOp.suspend) { // suspend is the op queued for tx commit return true; } else { return false; } } } public boolean isSuspendedNotTransacted() { return suspended; } public void resumeDelivery() throws IllegalStateException, TransactionRequiredLocalException, SLEEException { if (!isSuspended()) { throw new IllegalStateException(); } else { if (transactionalAction == null) { transactionalAction = new EventContextStateChange(); transactionalAction.op = EventContextStateChangeOp.resume; try { sleeContainer.getTransactionManager().addAfterCommitAction(transactionalAction); TransactionalAction rollbackAction = new TransactionalAction() { public void execute() { transactionalAction = null; } }; sleeContainer.getTransactionManager().addAfterRollbackAction(rollbackAction); } catch (SystemException e) { transactionalAction = null; throw new SLEEException( "unable to add tx action to change event context state", e); } } else { throw new IllegalStateException(); } } } public void suspendDelivery() throws IllegalStateException, TransactionRequiredLocalException, SLEEException { suspendDelivery(DEFAULT_TIMEOUT); } public void suspendDelivery(final int timeout) throws IllegalArgumentException, IllegalStateException, TransactionRequiredLocalException, SLEEException { if (timeout < 1) { throw new IllegalArgumentException(); } if (isSuspended()) { throw new IllegalStateException(); } else { if (transactionalAction == null) { try { transactionalAction = new EventContextStateChange(); transactionalAction.op = EventContextStateChangeOp.suspend; transactionalAction.timeout = timeout; transactionalAction.tx = sleeContainer.getTransactionManager().getTransaction(); sleeContainer.getTransactionManager().addAfterCommitAction(transactionalAction); TransactionalAction rollbackAction = new TransactionalAction() { public void execute() { transactionalAction = null; } }; sleeContainer.getTransactionManager().addAfterRollbackAction(rollbackAction); } catch (SystemException e) { transactionalAction = null; throw new SLEEException( "unable to add tx action to change event context state", e); } } else { throw new IllegalStateException(); } } } /** * the real logic to resume the event context */ private void resume() { final EventRouter eventRouter = sleeContainer.getEventRouter(); final EventContextImpl eventContextImpl = this; // create runnable to resume the event context Runnable runnable = new Runnable() { public void run() { // cancel timer task scheduledFuture.cancel(false); scheduledFuture = null; // send events frozen to event router again, will be processed only after this one ends for (DeferredEvent deferredEvent : barriedEvents) { eventRouter.routeEvent(deferredEvent); } barriedEvents = null; // remove barrier on activity event queue deferredEvent.getEventRouterActivity().getEventQueueManager().removeBarrier(transaction); // remove suspension suspended = false; // continue routing the event related with this context eventRouter.resumeEventContext(eventContextImpl); } }; // run it using the activity executor service to avoid thread concurrency deferredEvent.getEventRouterActivity().getExecutorService().execute(runnable); } public void barrierEvent(DeferredEvent deferredEvent) { barriedEvents.add(deferredEvent); } public DeferredEvent getDeferredEvent() { return deferredEvent; } public Set<String> getSbbEntitiesThatHandledEvent() { return sbbEntitiesThatHandledEvent; } public LinkedList<ServiceComponent> getActiveServicesToProcessEventAsInitial() { return activeServicesToProcessEventAsInitial; } public EventContextID getEventContextID() { return eventContextID; } private class SuspensionTimerTask implements Runnable { public void run() { try { resume(); } catch(Throwable t) { logger.error("failed to resume event context "+getEventContextID(),t); } } } // -------- TX ACTION TO SUSPEND/RESUME private enum EventContextStateChangeOp { suspend , resume } private class EventContextStateChange implements TransactionalAction { private EventContextStateChangeOp op; private int timeout; private Transaction tx; public void execute() { transactionalAction = null; switch (op) { case suspend: suspended = true; transaction = tx; // put a barrier in the event queue manager for this activity, to // freeze the event routing on this activity at that level deferredEvent.getEventRouterActivity().getEventQueueManager().createBarrier(tx); // init queue to store events about to be routed (after this one), // which may have passed the barrier barriedEvents = new LinkedList<DeferredEvent>(); // set state as suspended suspended = true; // schedule task scheduledFuture = sleeContainer.getNonClusteredScheduler().schedule(new SuspensionTimerTask(),timeout,TimeUnit.MILLISECONDS); break; case resume: resume(); break; default: throw new SLEEException("unxpected op type when executing event context state change"); } } } private static final Logger logger = Logger.getLogger(EventContextImpl.class); }