/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.mobicents.slee.runtime.activity;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import javax.slee.resource.FailureReason;
import javax.transaction.Transaction;
import org.apache.log4j.Logger;
import org.mobicents.slee.container.activity.ActivityEventQueueManager;
import org.mobicents.slee.container.event.EventContext;
/**
*
* Manages the queuing of events for a specific activity. Note that this impl of
* {@link ActivityEventQueueManager} is only thread safe if the local AC
* executive service is single thread.
*
* @author Eduardo Martins
*
*/
public class ActivityEventQueueManagerImpl implements ActivityEventQueueManager {
private static final Logger logger = Logger
.getLogger(ActivityEventQueueManagerImpl.class);
private boolean doTraceLogs = logger.isTraceEnabled();
/**
* stores the activity end event when set
*/
private EventContext activityEndEvent;
private boolean activityEndEventRouted;
/**
* the set of pending events, i.e., events not committed yet, for this
* activity
*/
private Set<EventContext> pendingEvents;
/**
* the events hold due to barriers set
*/
private Deque<EventContext> eventsBarriered;
/**
* the transactions that hold barriers to the activity event queue
*/
private Set<Transaction> eventBarriers;
/**
* the local view of the related activity context
*/
private final LocalActivityContextImpl localAC;
/**
*
* @param localAC
*/
public ActivityEventQueueManagerImpl(LocalActivityContextImpl localAC) {
this.localAC = localAC;
}
@Override
public void pending(final EventContext event) {
if (doTraceLogs) {
logger.trace("Pending event of type "
+ event.getEventTypeId() + " in AC with handle "
+ event.getActivityContextHandle());
}
// let the event know it was fired
event.fired();
// add event to pending set
Runnable r = new Runnable() {
@Override
public void run() {
if (pendingEvents == null) {
pendingEvents = new HashSet<EventContext>(4);
}
pendingEvents.add(event);
}
};
localAC.getExecutorService().execute(r);
}
@Override
public void commit(final EventContext event) {
Runnable r = new Runnable() {
@Override
public void run() {
if (activityEndEvent == null) {
commit(event, true);
} else {
// processing of the event failed
if (doTraceLogs) {
logger.trace("Unable to commit event of type "
+ event.getEventTypeId()
+ " in AC with handle "
+ event.getActivityContextHandle()
+ ", the activity end event is already committed");
}
event.eventProcessingFailed(FailureReason.OTHER_REASON);
}
}
};
localAC.getExecutorService().execute(r);
}
@Override
public void fireNotTransacted(final EventContext event) {
// let the event know it was fired
event.fired();
// commit event
Runnable r = new Runnable() {
@Override
public void run() {
if (activityEndEvent == null) {
// activity end event not set
// commit event
commit(event, false);
} else {
// processing of the event failed
if (doTraceLogs) {
logger.trace("Unable to commit event of type "
+ event.getEventTypeId()
+ " in AC with handle "
+ event.getActivityContextHandle()
+ ", the activity end event is already committed");
}
event.eventProcessingFailed(FailureReason.OTHER_REASON);
}
}
};
localAC.getExecutorService().execute(r);
}
private void commit(EventContext event, boolean isPendingEvent) {
if (isPendingEvent) {
if (pendingEvents == null || !pendingEvents.remove(event)) {
// processing of the event failed
if (doTraceLogs) {
logger.trace("Unable to commit event of type "
+ event.getEventTypeId()
+ " in AC with handle "
+ event.getActivityContextHandle()
+ ", the event was not found in the pending events set.");
}
event.eventProcessingFailed(FailureReason.OTHER_REASON);
return;
}
}
if (eventBarriers == null || eventBarriers.isEmpty()) {
// barriers are not set, proceed with commit of event
commitAndNotSuspended(event);
} else {
// barriers are set add the event to the frozen queue
if (eventsBarriered == null) {
eventsBarriered = new LinkedList<EventContext>();
}
eventsBarriered.add(event);
}
}
private void commitAndNotSuspended(EventContext event) {
if (doTraceLogs) {
logger.trace("Commiting event of type " + event.getEventTypeId()
+ " in AC with handle " + event.getActivityContextHandle());
}
if (event.isActivityEndEvent()) {
// store it
activityEndEvent = event;
// check we can route it
routeActivityEndEventIfNeeded();
} else {
// cancel any check for references possibly queued after commiting
// the event
localAC.setActivityReferencesCheck(null);
// route the event
localAC.getExecutorService().routeEvent(event);
// perhaps we need to route a frozen activity end event too
routeActivityEndEventIfNeeded();
}
}
private void routeActivityEndEventIfNeeded() {
if (pendingEvents == null || pendingEvents.isEmpty()) {
// no pending events
// now check if we have a frozen activity end event
if (activityEndEvent == null || activityEndEventRouted) {
return;
}
activityEndEventRouted = true;
// route the activity end event on hold
localAC.getExecutorService().routeEvent(activityEndEvent);
}
}
@Override
public void rollback(final EventContext event) {
// let the event know it was canceled
event.canceled();
Runnable r = new Runnable() {
@Override
public void run() {
if (doTraceLogs) {
logger.trace("Rolled back event of type "
+ event.getEventTypeId() + " in AC with handle "
+ event.getActivityContextHandle());
}
if (pendingEvents != null && pendingEvents.remove(event)) {
// confirmed the event was pending
routeActivityEndEventIfNeeded();
}
}
};
localAC.getExecutorService().execute(r);
}
@Override
public void createBarrier(final Transaction transaction) {
Runnable r = new Runnable() {
@Override
public void run() {
// raise barrier
if (eventBarriers == null) {
eventBarriers = new HashSet<Transaction>(2);
}
eventBarriers.add(transaction);
}
};
localAC.getExecutorService().execute(r);
}
@Override
public void removeBarrier(final Transaction transaction) {
Runnable r = new Runnable() {
@Override
public void run() {
if (eventBarriers != null && eventBarriers.remove(transaction)) {
if (eventBarriers.isEmpty()) {
// no barriers, proceed with commit of all events stored
if (eventsBarriered != null) {
EventContext e = null;
while (true) {
e = eventsBarriered.pollFirst();
if (e == null) {
break;
} else {
if (!e.isActivityEndEvent()) {
commitAndNotSuspended(e);
} else {
activityEndEvent = e;
}
}
}
}
routeActivityEndEventIfNeeded();
}
}
}
};
localAC.getExecutorService().execute(r);
}
@Override
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == this.getClass()) {
return ((ActivityEventQueueManagerImpl) obj).localAC
.equals(this.localAC);
} else {
return false;
}
}
@Override
public int hashCode() {
return localAC.hashCode();
}
}