/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.agila.engine;
import org.apache.agila.impl.EngineMessageImpl;
import org.apache.agila.impl.NodeContextImpl;
import org.apache.agila.model.BusinessProcess;
import org.apache.agila.model.Connection;
import org.apache.agila.model.Node;
import org.apache.agila.services.BusinessProcessService;
import org.apache.agila.services.InstanceService;
import org.apache.agila.services.log.LogService;
import org.apache.agila.services.notification.NotificationService;
import org.apache.agila.services.QueueService;
import org.apache.agila.services.task.TaskService;
import org.apache.agila.services.TimerService;
import org.apache.agila.services.TokenService;
/**
* Core class to process messages. Messages are the mechanism through which
* state is advanced for each instance.
*
* Each engine message contains a command, currently either 'advance' or 'halt'
* and information required to perform the action, including token id.
*
*
* @author <a href="mailto:geir@gluecode.com">Geir Magnusson Jr.</a>
* @version $Id: MessageProcessor.java 38 2005-06-01 19:39:54Z chirino $
*/
public class MessageProcessor {
private TokenService tokenService = null;
private BusinessProcessService wm = null;
private InstanceService eiSvc = null;
private TimerService timerService;
private TaskService taskService = null;
private QueueService queue = null;
private NotificationService notificationService = null;
private LogService logger = null;
public void setLogService(LogService l) {
this.logger = l;
}
public void setQueueService(QueueService qs) {
this.queue = qs;
}
public void setTokenService(TokenService tokenService) {
this.tokenService = tokenService;
}
public void setBusinessProcessService(BusinessProcessService wm) {
this.wm = wm;
}
public void setExecutionInstanceService(InstanceService eiSvc) {
this.eiSvc = eiSvc;
}
public void setTimerService(TimerService timerService) {
this.timerService = timerService;
}
public void setTaskService(TaskService taskService) {
this.taskService = taskService;
}
public void setNotificationService(NotificationService notificationService) {
this.notificationService = notificationService;
}
/**
* Message processing routine. This method is called by the QueueService.
*
* @param msg
*/
public boolean processMessage(EngineMessage msg) {
if (msg == null) {
logger.error("Received null EngineMessage");
return false;
}
/*
* get the current token
*/
Token tk = tokenService.getTokenByID(msg.getCurrentTokenID());
if (tk == null) {
logger.error("Received null token for msg = " + msg);
return false;
}
Instance ei = eiSvc.getInstanceByID(tk.getInstanceID());
logger.debug("processMessage : " + msg + " for procid="
+ ei.getBusinessProcessID()
+ " instanceid = " + ei.getInstanceID());
/*
* safety check
*/
if (!tk.isActive()) {
logger.error("error! out of synch for instance id = "
+ tk.getInstanceID() + " instance no longer active");
return false;
}
/*
* // TODO - handle 'verb' in message
*/
BusinessProcess graph = wm.getGraphByID(
eiSvc.getInstanceByID(tk.getInstanceID()).getBusinessProcessID());
Node n = graph.getNode(tk.getCurrentNodeID());
NodeContextImpl nc = new NodeContextImpl(n, ei, timerService,
taskService, notificationService);
nc.setAppData(msg.getAppData());
/*
* now examine the state of this token and process accordingly.
*/
switch(tk.getCurrentState()) {
/*
* in 'pre start' state for this node. Setup, call the doStart()
* method, and then based on the return from doStart(), either
* create and fling another message back into the queue, or stop
* and wait for response from outside
*/
case Token.PRE : {
logger.debug("processMessage : Current state = PRE for " + tk);
/**
* must send new token b/c it can be used as the current token...
*/
Token newToken = tokenService.newToken(tk.getInstanceID(),
tk.getCurrentNodeID(),
Token.MID);
nc.setCurrentExecutionToken(tk);
nc.setNextExecutionToken(newToken);
boolean ret = n.doStart(nc);
if (ret == true) {
EngineMessage newMsg = new EngineMessageImpl();
newMsg.setCurrentTokenID(newToken.getTokenID());
queue.enqueue(newMsg);
}
break;
}
/*
* this state is 'between' doStart() and doEnd(). So prepare
* context and call doEnd()
*/
case Token.MID : {
logger.debug("processMessage : Current state = MID for " + tk);
nc.setCurrentExecutionToken(tk);
/*
* call doEnd() and get the set of conenctions that we need
* to traverse, or null if we are to stop
*/
Connection[] c = n.doEnd(nc);
if (c != null && c.length > 0) {
Token[] newTokens = tokenService.nextToken(c, tk);
for(int i = 0; i < c.length; i++) {
EngineMessage newMsg = new EngineMessageImpl();
newMsg.setCurrentTokenID(newTokens[i].getTokenID());
queue.enqueue(newMsg);
}
}
else {
// all done
ei.setStatus(Instance.STATUS_COMPLETE);
logger.debug("no connection - done");
}
break;
}
default :
break;
};
/*
* now set the old token status to inactive
*
* TODO - think about this... we want to make sure that we always have
* an active token for any given execution thread to be sure that joins
* don't get fooled
*/
tk.setActive(false);
return true;
}
}