package org.tgdb.frame.advanced;
import org.tgdb.frame.Action;
import org.tgdb.frame.ActionException;
import org.tgdb.frame.ArxFrameException;
import org.tgdb.frame.Caller;
import org.tgdb.frame.IWorkFlowManager;
import org.tgdb.frame.WorkflowException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class AdvancedWorkflowManager implements IWorkFlowManager {
private static Logger logger = Logger.getLogger(AdvancedWorkflowManager.class);
private static final String WORKFLOWS_TAG = "workflows";
private static final String WORKFLOW_TAG = "workflow";
private static final String STATE_TAG = "state";
private static final String NAME_ATTR = "name";
private static final String ACTION_ATTR = "action";
private static final String PRE_ACTION_ATTR = "preaction";
private static final String VIEW_ATTR = "viewURI";
private static final String NEW_WORKFLOW_ATTR = "workflow";
private String ACTION_PREFIX = "action.";
private String DEFAULT_WORKFLOW;
private HashMap workflows;
private Workflow currentWorkflow;
private WorkflowHistory history;
private static final int MAX_HISTORY = 5;
private Caller caller;
private ArrayList workflowStack;
public AdvancedWorkflowManager() {}
public void setContext(ServletContext context) throws IOException {
InputStream is = context.getResourceAsStream("xml/workflow.xml");
try
{
if (context == null)
throw new Exception("Context is null!");
// Init the HashMap
workflows = new HashMap();
history = new WorkflowHistory(MAX_HISTORY);
workflowStack = new ArrayList();
// Parse XML and populate HashMap.
parseXML(is);
} catch (Exception e) {
e.printStackTrace();
throw new IOException("WorkflowManager#setContext: "+e.getMessage());
}
}
public void setCaller(Caller caller) {
if (caller==null)
logger.warn("----------------------------------->AdvancedWorkflowManager#setCaller: Caller is null");
this.caller = caller;
}
private Action getAction(String name) throws ArxFrameException {
try {
if (name != null && name.length() > 0)
{
Class c = Class.forName(ACTION_PREFIX + name);
return (Action)c.newInstance();
}
return null;
} catch (Exception e) {
throw new ArxFrameException("Failed to get action "+ACTION_PREFIX+name, e);
}
}
private boolean historyWorkflow(String workflow) {
boolean worthHistory = false;
String [] validWorkflows = {"ViewModels", "ViewModel", "ViewStrains", "ViewStrain", "ViewUser", "ViewGenes", "ViewGene", "ViewGeneticBackgroundValues", "ViewAvailableGeneticBackgrounds", "ViewRepositories", "DisseminationUpdate", "SearchKeywordFast", "ViewSimpleLogs"};
for(int i=0; i < validWorkflows.length; i++){
if(workflow.compareTo(validWorkflows[i])==0){
worthHistory = true;
return worthHistory;
}
}
return worthHistory;
}
private boolean repeatedWorkflow(String workflow, int depth) {
boolean repeatWorkflow = false;
if(history.elements()<depth){
return repeatWorkflow;
} else {
Workflow tmp = history.get(depth);
if(workflow.compareTo(tmp.getName())==0){
repeatWorkflow = true;
return repeatWorkflow;
}
}
return repeatWorkflow;
}
private boolean circleWorkflow(String workflow) {
boolean circleHistory = false;
String [] validWorkflows = {"ViewModels", "PhenoAssign", "ViewStrains", "DisseminationUpdate", "ViewSimpleLogs"};
for(int i=0; i < validWorkflows.length; i++){
if(workflow.compareTo(validWorkflows[i])==0){
circleHistory = true;
return circleHistory;
}
}
return circleHistory;
}
private Workflow getWorkflow(HttpServletRequest req) throws WorkflowException {
if (req.getParameter("workflow") != null || workflowStack.size()==0) {
String newWorkflow;
if (req.getParameter("workflow")==null && workflowStack.size()==0) {
//logger.debug("----------------------------------->AdvancedWorkflowManager#getWorkflow: Setting default workflow '"+DEFAULT_WORKFLOW+"'");
newWorkflow = DEFAULT_WORKFLOW;
} else {
newWorkflow = req.getParameter("workflow");
}
logger.debug("----------------------------------->AdvancedWorkflowManager#getWorkflow: Workflow is '"+newWorkflow+"'");
workflowStack = new ArrayList();
Workflow tmp = (Workflow)workflows.get(newWorkflow);
if (tmp==null) {
throw new WorkflowException("Workflow \""+newWorkflow+"\" not found");
}
// Do not add the same workflow twice in history.
// This helps the back buttons then using hide-windows.
if (currentWorkflow!=null && !currentWorkflow.getName().equals(newWorkflow) && historyWorkflow(currentWorkflow.getName()) && !repeatedWorkflow(currentWorkflow.getName(), 1)) {
history.add(currentWorkflow);
logger.debug("----------------------------------->AdvancedWorkflowManager#getWorkflow: Workflow '"+currentWorkflow.getName()+"' was added to history");
}
// Set the new workflow.
currentWorkflow = new Workflow(tmp);
workflowStack.add(0, currentWorkflow);
if (currentWorkflow==null)
throw new WorkflowException("Workflow "+newWorkflow+" not found");
currentWorkflow.setStart();
} else {
logger.debug("----------------------------------->AdvancedWorkflowManager#getWorkflow: Workflow is undefined");
}
for(int i=1; i < history.elements()+1;i++){
logger.debug("----------------------------------->AdvancedWorkflowManager#getWorkflow: Workflow history element "+i+" is '"+history.get(i).getName()+"'");
}
//logger.debug("AdvancedWorkflowManager#getWorkflow#FirstInHistory="+history.get(1).getName());
/** Get the right workflow object by the currentWorkflowName */
return (Workflow)workflowStack.get(0);
}
public void removeMaliciousWorkflows(String maliciousWorkflow) {
history.massRemove(maliciousWorkflow);
}
public String getNextPage(HttpServletRequest req, ServletContext context) throws ArxFrameException {
String page = null;
try {
currentWorkflow = getWorkflow(req);
//logger.debug("----------------------------------->AdvancedWorkflowManager#getNextPage: Current workflow '"+currentWorkflow.getName()+"'");
if (req.getParameter("back")!=null && req.getParameter("back").equals("state")) {
currentWorkflow.setPrevState();
} else if (req.getParameter("back")!=null || req.getParameter("back.x")!=null) {
Workflow tmp = history.get(1);
if(tmp.getName().compareTo("begin")==0){
// currentWorkflow = tmp;
// currentWorkflow.setStart();
// currentWorkflow.setCaller(caller);
// req.setAttribute("workflow", currentWorkflow);
// page = "welcome.html";
// return page;
} else if(!repeatedWorkflow(currentWorkflow.getName(), 1)){
if(circleWorkflow(currentWorkflow.getName())){
history.remove(0);
}
currentWorkflow = tmp;
currentWorkflow.setStart();
} else {
history.remove(0);
currentWorkflow = history.get(1);
currentWorkflow.setStart();
if(currentWorkflow.getName().compareTo("begin")==0){
// currentWorkflow.setCaller(caller);
// req.setAttribute("workflow", currentWorkflow);
// page = "welcome.html";
// return page;
}
if(currentWorkflow.getName().compareTo("ViewUser")==0){
currentWorkflow.setCaller(caller);
req.setAttribute("workflow", currentWorkflow);
page = "ViewUser";
return page;
}
}
}
if(req.getParameter("workflow")==null){
workflowStack.remove(0);
workflowStack.add(0,currentWorkflow);
}
currentWorkflow.setCaller(caller);
/* Save the workflow in the request object */
req.setAttribute("workflow", currentWorkflow);
page = currentWorkflow.getNextPage(req, context);
logger.debug("----------------------------------->AdvancedWorkflowManager#getNextPage: Page is '"+page+"'");
} catch (ForwardWorkflowException fwe) {
if (currentWorkflow!=null && historyWorkflow(currentWorkflow.getName()) && !repeatedWorkflow(currentWorkflow.getName(), 1)) {
history.add(currentWorkflow);
logger.debug("----------------------------------->AdvancedWorkflowManager#getNextPage: Workflow '"+currentWorkflow.getName()+"' was added to history via forward");
}
currentWorkflow = new Workflow((Workflow)workflows.get(currentWorkflow.getCurrentState().getNewWorkflow()));
workflowStack.remove(0);
workflowStack.add(0,currentWorkflow);
logger.debug("----------------------------------->AdvancedWorkflowManager#getNextPage: Workflow is '"+((Workflow)workflowStack.get(0)).getName()+"'");
/* Save the workflow in the request object */
req.setAttribute("workflow", currentWorkflow);
currentWorkflow.setCaller(caller);
page = currentWorkflow.getNextPage(req, context);
} catch (BackWorkflowException back) {
if(repeatedWorkflow(currentWorkflow.getName(), 1)){
history.remove(0);
}
currentWorkflow = history.get(1);
workflowStack.remove(0);
workflowStack.add(0,currentWorkflow);
logger.debug("----------------------------------->AdvancedWorkflowManager#getNextPage: Workflow is '"+((Workflow)workflowStack.get(0)).getName()+"'");
currentWorkflow.setStart();
currentWorkflow.setCaller(caller);
req.setAttribute("workflow", currentWorkflow);
page = currentWorkflow.getNextPage(req, context);
} catch (AlterWorkflowException alt) {
AltState a = currentWorkflow.getCurrentState().getAltState(alt.getName());
String wfName = a.getNewWorkflow();
Workflow tmp = new Workflow((Workflow)workflows.get(wfName));
workflowStack.add(0, tmp);
currentWorkflow = tmp;
/* Save the workflow in the request object */
req.setAttribute("workflow", currentWorkflow);
page = currentWorkflow.getNextPage(req, context);
} catch (ActionException ae) {
req.setAttribute("exception", ae);
page="error/GeneralError.jsp";
logger.error("----------------------------------->AdvancedWorkflowManager#getNextPage: Action exception. Page is '"+page+"' \n", ae);
throw ae;
} catch (Exception e) {
logger.error("General error in workflow logic.",e);
throw new ArxFrameException("Workflow failure",e);
}
for(int i=1; i<history.elements()+1;i++){
logger.debug("----------------------------------->AdvancedWorkflowManager#getNextPage: Workflow history element "+i+" is '"+history.get(i).getName()+"'");
}
return page;
}
private void debugWorkflowStack() {
if (workflowStack==null)
return;
String out = "----------------------------------->AdvancedWorkflowManager#debugWorkflowStack: \n";
out += "<workflow-stack>\n";
for (int i=0;i<workflowStack.size();i++) {
out += "\t<workflow id=\""+i+"\" name=\""+((Workflow)workflowStack.get(i)).getName()+"\"/>\n";
}
out += "</workflow-stack>\n";
logger.debug(out);
}
private void parseXML(InputStream is) throws ArxFrameException {
try
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(is);
// Find the workflows element
NodeList wrkflws = doc.getElementsByTagName(WORKFLOWS_TAG);
Element e_wrkflws = (Element)wrkflws.item(0);
if (e_wrkflws.getAttribute("action-prefix")!=null &&
!e_wrkflws.getAttribute("action-prefix").equals(""))
ACTION_PREFIX = e_wrkflws.getAttribute("action-prefix");
if (e_wrkflws.getAttribute("default-workflow")!=null &&
!e_wrkflws.getAttribute("default-workflow").equals(""))
DEFAULT_WORKFLOW = e_wrkflws.getAttribute("default-workflow");
// Find the workflow element
NodeList workflows = doc.getElementsByTagName(WORKFLOW_TAG);
//Element workflow = (Element)workflows.item(0);
for (int j=0;j<workflows.getLength();j++)
{
Element workflow = (Element)workflows.item(j);
Workflow w = new Workflow((String)workflow.getAttribute(NAME_ATTR));
// Find all states
NodeList states = workflow.getChildNodes();
//NodeList states = doc.getElementsByTagNameNS(workflow.getPrefix(), STATE_TAG);
// Create an array for states
ArrayList arr = new ArrayList();
// Read state info
for (int i=0;i<states.getLength();i++)
{
// Get element
Node node = states.item(i);
Element curState = null;
if (node.getNodeType()==node.ELEMENT_NODE)
{
curState = (Element)node;
State state = new State();
state.setName(curState.getAttribute(NAME_ATTR));
state.setView(curState.getAttribute(VIEW_ATTR));
state.setNewWorkflow(curState.getAttribute(NEW_WORKFLOW_ATTR));
// Convert Action names into class instances
state.setPostAction(getAction(curState.getAttribute(ACTION_ATTR)));
state.setPreAction(getAction(curState.getAttribute(PRE_ACTION_ATTR)));
AltState[] alts = getAltStates(curState);
state.altStates = alts;
arr.add(state);
}
}
State[] stateList = new State[arr.size()];
for (int k=0;k<arr.size();k++)
{
stateList[k] = (State)arr.get(k);
}
w.states = stateList;
// Add the workflow object to the hashmap.
this.workflows.put(w.getName(), w);
}
}
catch (Exception e)
{
logger.error("----------------------------------->AdvancedWorkflowManager#parseXML: Error parsing workflow.xml", e);
throw new ArxFrameException("Application error, problem reading workflow data", e);
}
}
private AltState[] getAltStates(Element curState) throws Exception {
NodeList alts = curState.getChildNodes();
ArrayList arr = new ArrayList();
for (int i=0;i<alts.getLength();i++)
{
// Get element
Node node = alts.item(i);
Element alt = null;
if (node.getNodeType()==node.ELEMENT_NODE)
{
alt = (Element)node;
AltState state = new AltState();
state.setName(alt.getAttribute(NAME_ATTR));
state.setView(alt.getAttribute(VIEW_ATTR));
state.setNewWorkflow(alt.getAttribute(NEW_WORKFLOW_ATTR));
// Convert Action names into class instances
state.setPostAction(getAction(alt.getAttribute(ACTION_ATTR)));
state.setPreAction(getAction(alt.getAttribute(PRE_ACTION_ATTR)));
arr.add(state);
}
}
AltState[] out = new AltState[arr.size()];
for (int i=0;i<arr.size();i++)
{
out[i] = (AltState)arr.get(i);
}
return out;
}
}