/*
* Workflow.java
*
* Created on November 14, 2005, 12:15 PM
*
* To change this template, choose Tools | Options and locate the template under
* the Source Creation and Management node. Right-click the template and choose
* Open. You can then make changes to the template in the Source Editor.
*/
package org.tgdb.frame.advanced;
import org.tgdb.frame.ActionException;
import org.tgdb.frame.Caller;
import org.tgdb.frame.WorkflowException;
import java.util.Enumeration;
import java.util.HashMap;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
/**
* Handle a workflow in a web application.
*
* A workflow is a process that have multiple states, each state can have an
* alternative state if exceptions are thrown in a state. A new workflow can
* be started from within a state either from an alternative state, and then
* new workflow will be run imidiate and the rest of the first workflow will
* continue. Otherwise a new workflow can be started and the old discarded.
*
*
* Workflow:
* State1 -> State2 -> State3
* -> AltState
* -> New Workflow
*
* All workflows are handled by a WorkflowManager that is available to each
* user (an own instance of the manager) that is saved in the users session.
*
* @author tobias
*/
public class Workflow {
private static Logger logger = Logger.getLogger(Workflow.class);
private String name;
public State[] states;
private int currentState;
private String test;
private HashMap attributes;
private boolean preDone;
private Caller caller;
/**
* Creates a new instance of Workflow
* @param name is the name of the new workflow. Workflow name must be
* unique!
*/
public Workflow(String name) {
this.name = name;
attributes = new HashMap();
}
/**
* Create a new copy of a workflow. This create an independent copy, no
* references. Also, properties are NOT copied at all, both workflow properties
* and state properties!
* @param w is the workflow to copy.
*/
public Workflow(Workflow w)
{
name = w.getName();
attributes = new HashMap();
states = new State[w.getStateCount()];
for (int i=0;i<w.getStateCount();i++)
{
State s = new State();
s.setName(w.getState(i).getName());
s.setNewWorkflow(w.getState(i).getNewWorkflow());
s.setPostAction(w.getState(i).getPostAction());
s.setPreAction(w.getState(i).getPreAction());
s.setView(w.getState(i).getView());
s.altStates = new AltState[w.getState(i).altStates.length];
for (int j=0;j<w.getState(i).altStates.length;j++)
{
s.altStates[j] = w.getState(i).altStates[j];
}
states[i]=s;
}
}
/**
* Get the name of the workflow. The name is unique.
* @return the name of the workflow.
*/
public String getName()
{
return name;
}
/**
* Set the workflow to a starting position. Set the state to the first state.
*/
public void setStart()
{
currentState = 0;
preDone = false;
}
/**
* Get the current state in this workflow.
* @return the state object
*/
public State getCurrentState()
{
return states[currentState];
}
/**
* Set the previous state for this workflow. This checks if we already are
* at first state and silently accepts such calls.
*/
public void setPrevState()
{
if (currentState>0)
currentState--;
else
setStart();
}
/**
* Get the state with an index.
* @param index of the states
* @return the state object
*/
public State getState(int index)
{
return states[index];
}
/**
* Get the number of states available in this workflow.
* @return the number of states.
*/
public int getStateCount()
{
return states.length;
}
/**
* Get the workflow object in a text representation.
* @return the data in an xml format.
*/
public String toString()
{
String out = "<workflow name=\""+name+"\">\n";
for (int i=0;i<states.length;i++)
{
out += "\t"+states[i].toString();
}
out += "</workflow>\n";
return out;
}
/**
* Set a string attribute on the workflow.
* This could be used to save posted data to be able to go back in states
* and yet have posted data available.
* @param name is the name of the attribute.
* @param value is the value of the attribute.
*/
public void setAttribute(String name, String value)
{
attributes.put(name, value);
}
/**
* Get a string attribute on the workflow.
* This could be used to save posted data to be able to go back in states
* and yet have posted data available.
* @param name is the name of the attribute
* @return the value of the given name. Null is returned if not found.
*/
public String getAttribute(String name)
{
return (String)attributes.get(name);
}
/**
* Remove all attributes available in this workflow.
*/
public void removeAllAttributes()
{
attributes = new HashMap();
}
private HashMap params;
/**
* Collect all parameters inside the workflow
* in local variables. Only unique identifiers are valid!
*/
private void collectParameters(HttpServletRequest req)
{
if (params==null)
params = new HashMap();
Enumeration pNames = req.getParameterNames();
while (pNames.hasMoreElements())
{
String key = (String)pNames.nextElement();
String value = req.getParameter(key);
params.put(key, value);
//logger.debug("added param: "+key);
}
}
/**
* Get a collected parameter
*/
public String getParameter(String name)
{
//logger.debug("Parameters available: "+params.size());
return (String)params.get(name);
}
/**
* Get the next page in this workflow. This method keeps track of current state and increase states when necesary.
*
* @param req
* @param context
* @throws org.tgdb.frame.advanced.ForwardWorkflowException is thrown if a workflow contains a forwarder to another workflow. The new workflow will run instead of current one.
* @throws org.tgdb.frame.ActionException
* @throws org.tgdb.frame.WorkflowException
* @return a view url (page)
*/
public String getNextPage(HttpServletRequest req, ServletContext context) throws ForwardWorkflowException, ActionException, WorkflowException
{
String page = null;
State state = null;
// Get the state
state = states[currentState];
state.setCaller(caller);
state.setWorkflow(this);
// Collect parameters
collectParameters(req);
try
{
while (currentState<states.length && page == null)
{
// The first state has no prev action
if (currentState!=0 || preDone)
{
// Do post action
state.performPostAction(req, context);
// increase index
currentState++;
if (currentState==states.length)
throw new WorkflowException("Workflow '"+this.name+"' ended without view");
preDone = false;
}
// Get the state
state = states[currentState];
state.setCaller(caller);
state.setWorkflow(this);
// Do pre action
state.performPreAction(req, context);
preDone = true;
// Forward to previous workflow
if (state.getNewWorkflow()!=null && state.getNewWorkflow().equalsIgnoreCase("back"))
throw new BackWorkflowException("Back");
// Forward to new workflow if wanted.
if (state.getNewWorkflow()!=null)
throw new ForwardWorkflowException("Forward", state.getNewWorkflow());
// Get page
page = state.getView();
if (page!=null && page.equals(""))
page = null;
}
}
catch (ActionException ae)
{
throw ae;
}
catch (WorkflowException we)
{
//we.printStackTrace();
throw we;
}
catch (Exception e)
{
logger.error("Swallow exception", e);
//e.printStackTrace();
}
return page;
}
public void setCaller(Caller caller)
{
if (caller==null)
logger.error("Workflow#setCaller is null!!!");
this.caller = caller;
}
}