package org.mobicents.slee.runtime.eventrouter;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.slee.resource.EventFlags;
import javax.slee.resource.FailureReason;
import javax.transaction.Transaction;
import org.apache.log4j.Logger;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.runtime.activity.ActivityContextHandle;
import org.mobicents.slee.util.concurrent.ConcurrentHashSet;
/**
*
* Manages the queuing of events for a specific activity.
*
* @author Eduardo Martins
*
*/
public class ActivityEventQueueManager {
private static final Logger logger = Logger
.getLogger(ActivityEventQueueManager.class);
/**
* stores the activity end event when set
*/
private DeferredEvent activityEndEvent;
/**
* atomic flag to define if the activity end event was queued
*/
private AtomicBoolean activityEndEventQueued = new AtomicBoolean(false);
/**
* the set of pending events, i.e., events not committed yet, for this
* activity
*/
private ConcurrentHashSet<DeferredEvent> pendingEvents = new ConcurrentHashSet<DeferredEvent>();
/**
* the events hold due to barriers set
*/
private ConcurrentLinkedQueue<DeferredEvent> eventsBarriered = new ConcurrentLinkedQueue<DeferredEvent>();
/**
* the transactions that hold barriers to the activity event queue
*/
private ConcurrentHashSet<Transaction> eventBarriers = new ConcurrentHashSet<Transaction>();
/**
* the slee container
*/
private final SleeContainer sleeContainer;
/**
* the activity context id that identifies this object
*/
private final ActivityContextHandle ach;
public ActivityEventQueueManager(
ActivityContextHandle ach,
SleeContainer sleeContainer) {
this.sleeContainer = sleeContainer;
this.ach = ach;
}
/**
* Indicates if there are pending events to be routed for the related
* activity.
*
* @return
*/
public boolean noPendingEvents() {
return pendingEvents.isEmpty();
}
/**
* Defines that the specified event is now pending
*
* @param dE
*/
public void pending(DeferredEvent dE) {
if (logger.isDebugEnabled()) {
logger.debug("pending event of type " + dE.getEventTypeId()
+ " for "
+ dE.getActivityContextHandle());
}
if (activityEndEvent == null) {
// activity end event not set, we accept pending events
pendingEvents.add(dE);
// manage event references
DeferredEventReferencesManagement eventReferencesManagement = sleeContainer.getEventRouter().getDeferredEventReferencesManagement();
if (EventFlags.hasRequestEventReferenceReleasedCallback(dE.getEventFlags())) {
eventReferencesManagement.manageReferencesForEvent(dE);
}
else {
eventReferencesManagement.eventReferencedByActivity(dE.getEvent(), dE.getActivityContextHandle());
}
} else {
// processing of the event failed
dE.eventProcessingFailed(FailureReason.OTHER_REASON);
}
}
public void commitAndNotSuspended(DeferredEvent dE) {
if (logger.isDebugEnabled()) {
logger.debug("committing event of type " + dE.getEventTypeId()
+ " for "
+ dE.getActivityContextHandle());
}
if (pendingEvents.remove(dE)) {
// confirmed it was a pending event
if (dE.getEventTypeId().equals(
ActivityEndEventImpl.EVENT_TYPE_ID)) {
// store it
activityEndEvent = dE;
// check we can route it
if (pendingEvents.isEmpty()) {
// no pending events
if (activityEndEventQueued.compareAndSet(false, true)) {
// between element removal and check if it's empty there
// is no sync so we need to ensure only one thread
// routes the activity end event
sleeContainer.getEventRouter().routeEvent(dE);
}
}
} else {
// route the event
sleeContainer.getEventRouter().routeEvent(dE);
// perhaps we need to route a frozen activity end event too
routeActivityEndEventIfNeeded();
}
} else {
// processing of the event failed
dE.eventProcessingFailed(FailureReason.OTHER_REASON);
}
}
/**
* Signals the manager that the event was committed, and thus can be routed.
*
* @param dE
*/
public void commit(DeferredEvent dE) {
if (eventBarriers.isEmpty()) {
// barriers are not set, proceed with commit of event
commitAndNotSuspended(dE);
}
else {
// barriers are set
synchronized (eventBarriers) {
if (!eventBarriers.isEmpty()) {
// barriers are still set, add the event to the frozen queue
eventsBarriered.add(dE);
}
else {
// barriers were removed, proceed with commit of event
commitAndNotSuspended(dE);
}
}
}
}
private void routeActivityEndEventIfNeeded() {
if (pendingEvents.isEmpty()) {
// no pending events
// now check if we have a frozen activity end event
if (activityEndEvent != null) {
// route the frozen activity end event too
if (activityEndEventQueued.compareAndSet(false, true)) {
// between pending events map element removal and check if
// it's empty there is no sync so we need to ensure only one
// thread routes the activity end event
sleeContainer.getEventRouter().routeEvent(activityEndEvent);
}
}
}
}
/**
* Signals that the java transaction who fired the specified event did not
* commit, and thus the event should be not routed.
*
* @param dE
*/
public void rollback(DeferredEvent dE) {
if (logger.isDebugEnabled()) {
logger.debug("rolling back event of type " + dE.getEventTypeId()
+ " for "
+ dE.getActivityContextHandle());
}
if (pendingEvents.remove(dE)) {
// confirmed the event was pending
routeActivityEndEventIfNeeded();
// manage event references
DeferredEventReferencesManagement eventReferencesManagement = sleeContainer.getEventRouter().getDeferredEventReferencesManagement();
if (EventFlags.hasRequestEventReferenceReleasedCallback(dE.getEventFlags())) {
eventReferencesManagement.unmanageReferencesForEvent(dE);
}
else {
eventReferencesManagement.eventUnreferencedByActivity(dE.getEvent(), dE.getActivityContextHandle());
}
}
}
/**
* create a barrier for the specified transaction for this activity event
* queue, events committed are frozen till all barriers are removed
*/
public void createBarrier(Transaction transaction) {
// raise barrier
eventBarriers.add(transaction);
}
/**
* remove a barrier for the specified transaction for this activity event
* queue, if there are no more barriers then delivering of events frozen proceed
*/
public void removeBarrier(Transaction transaction) {
synchronized (eventBarriers) {
eventBarriers.remove(transaction);
if (eventBarriers.isEmpty()) {
// no barriers, proceed with commit of all events stored
for (DeferredEvent dE : eventsBarriered) {
commitAndNotSuspended(dE);
}
}
}
}
@Override
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == this.getClass()) {
return ((ActivityEventQueueManager) obj).ach
.equals(this.ach);
} else {
return false;
}
}
@Override
public int hashCode() {
return ach.hashCode();
}
}