这是我写的一个DEOM,目的在于对
jquery 简易的流程图编辑控件 新版(20100907)
的一个流程支持而写的状态机DEMO
表达式使用IK EXPRESSION,以后会支持EL,也可能会支持JS,或者其他的脚本语言
没有测试,默认实现还有错误,以后会更新稳定版本的
下面是其中的部分代码:
package keng.core.workflow.state.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.TimeoutException;
import keng.core.util.StringUtil;
import keng.core.workflow.state.ISMActionProcessor;
import keng.core.workflow.state.ISMContext;
import keng.core.workflow.state.ISMListener;
import keng.core.workflow.state.ISMNode;
import keng.core.workflow.state.ISMTransition;
import keng.core.workflow.state.ISMTransitionMatcher;
import keng.core.workflow.state.IStateMachine;
import keng.core.workflow.state.StateMachineException;
/**
* 默认的状态机实现
*
* @author 5Di
*
*/
public class DefaultStateMachine implements IStateMachine {
/**
* 自动节点的默认动作
*/
public static final String ACTION_DEFAULT = "send";
/**
* 进入循环后的最大循环次数
*/
public static final int MAX_DEEPTH = 6;
// --------------------
Stack<ISMNode> register = new Stack<ISMNode>();
ISMTransitionMatcher matcher;
ISMContext context;
ISMActionProcessor processor;
List<ISMListener> listeners;
/**
* 根节点
*/
ISMNode nodeMap;
boolean strict = false;
public DefaultStateMachine(ISMNode nodeMap, ISMContext context) {
this.context = context;
this.nodeMap = nodeMap;
}
public DefaultStateMachine(ISMNode nodeMap, ISMContext context,
ISMTransitionMatcher matcher) {
this.nodeMap = nodeMap;
this.context = context;
this.matcher = matcher;
}
public DefaultStateMachine(ISMNode nodeMap, ISMContext context,
ISMTransitionMatcher matcher, boolean strict) {
this.context = context;
this.nodeMap = nodeMap;
this.matcher = matcher;
this.strict = strict;
}
@Override
public boolean transit(ISMNode node, String action, String state)
throws StateMachineException, TimeoutException {
if (action == null || action.isEmpty())
throw new IllegalArgumentException("action can not be empty!");
if (StringUtil.isEmpty(state))
throw new IllegalArgumentException("state can not be empty!");
if (node == null)
throw new IllegalArgumentException("node can not be null!");
List<ISMTransition> transitions = node.getTransitions();
if (transitions == null || transitions.isEmpty())
throw new StateMachineException("transitions can not be empty !");
// 检查节点图,限制自动节点的转化深度
if (node.isAuto())
if (!checkNodeMap(node))
throw new TimeoutException("die cycle");
boolean b = false;
for (ISMTransition transition : transitions) {
boolean bool = transit(transition, action, state, node);
if (bool) {
b = true;
if (register.indexOf(node) == -1)
register.push(node);
this.fireNodeTransit(node, state, transition.getToState());
if (node.isAuto())
this.fireAutoNodeTransit(node, state,
transition.getToState());
}
}
transitNextAutoNodes(node);
return b;
}
/**
* 转换 parent的直接下级自动节点<br />
* 当遇到非自动节点时,自动结束
*
* @param parent
* @throws StateMachineException
* @throws TimeoutException
*/
void transitNextAutoNodes(ISMNode parent) throws StateMachineException,
TimeoutException {
List<ISMNode> children = parent.getNextAutoNodes();
if (children != null && !children.isEmpty()) {
for (ISMNode child : children) {
String action = child.getAction();
if (action == null || action.isEmpty())
action = ACTION_DEFAULT;
transit(child, action, child.getState());
}
}
}
/**
*
* @param transition
* @param action
* @param state
* @param node
* @return
* @throws StateMachineException
* @throws TimeoutException
*
* @return 该transition 是否被执行
*/
boolean transit(ISMTransition transition, String action, String state,
ISMNode node) throws StateMachineException, TimeoutException {
boolean b = matcher.match(context, transition, action, state, strict);
this.fireTransitionMatch(action, state, transition, b);
if (!b)
return false;
else {
this.fireTransitionMatched(action, state, transition);
}
// process action
processor.process(transition);
node.setState(transition.getToState());
return true;
}
/**
* 检查节点图,防止进入死循环<br />
*
* @param node
* @return 是否成功
*/
public boolean checkNodeMap(ISMNode node) {
int i = 0;
Iterator<ISMNode> it = register.iterator();
while (it.hasNext()) {
i = node.equals(it.next()) ? i + 1 : i;
}
return i <= MAX_DEEPTH;
}
@Override
public boolean removeListener(ISMListener listener) {
if (listeners == null || listeners.isEmpty())
return false;
if (!listeners.contains(listener))
return false;
listeners.remove(listener);
return true;
}
@Override
public void addListener(ISMListener listener) {
if (listeners == null)
listeners = new ArrayList<ISMListener>();
listeners.add(listener);
}
@Override
public boolean existsListener(ISMListener listener) {
if (listeners == null || listeners.isEmpty())
return false;
return listeners.contains(listener);
}
/**
* 响应事件
*
* @param node
* @param fromState
* @param toState
*/
void fireNodeTransit(ISMNode node, String fromState, String toState) {
if (listeners == null || listeners.isEmpty())
return;
for (ISMListener listener : listeners)
listener.onNodeTransit(node, fromState, toState);
}
/**
*
* @param node
* @param fromState
* @param toState
*/
void fireAutoNodeTransit(ISMNode node, String fromState, String toState) {
if (listeners == null || listeners.isEmpty())
return;
for (ISMListener listener : listeners)
listener.onAutoNodeTransit(node, fromState, toState);
}
/**
* 响应事件
*
* @param action
* @param state
* @param transition
* @param matched
*/
void fireTransitionMatch(String action, String state,
ISMTransition transition, boolean matched) {
if (listeners == null || listeners.isEmpty())
return;
for (ISMListener listener : listeners)
listener.onTransitionMatch(action, state, transition, matched);
}
/**
* 响应事件
*
* @param action
* @param state
* @param transition
*/
void fireTransitionMatched(String action, String state,
ISMTransition transition) {
if (listeners == null || listeners.isEmpty())
return;
for (ISMListener listener : listeners)
listener.onTransitionMatched(action, state, transition);
}
}
package keng.core.workflow.state;
import java.util.concurrent.TimeoutException;
/**
* 状态机模型
*
* @author 5Di
*
*/
public interface IStateMachine {
/**
* 执行<br />
* 挨个try transition ,遇到第一个成功,理解返回<br />
* 执行逻辑:先匹配action 和 fromState ,然后calculate condition <br />
* 匹配策略,先匹配action,再匹配fromState,最后是condition<br />
*
* @param action
* 当前执行的行为
*
* @param state
* 当前的状态
* @param node
* 当前操作的节点
*
* @return 是否被执行(如果自动执行的子节点返回了false,也会返回false),false并不说明没有成功,因为如果步骤进入了死循环(
* 系统自动执行的的步骤之间会进入死循环) ,在这种情况下不会抛出异常,而是直接返回false
*/
public boolean transit(String action, String state, ISMNode node)
throws StateMachineException, TimeoutException;
}
package keng.core.workflow.state;
/**
* 状态机条件
*
* @author 5Di
*
*/
public interface ISMTransition {
/**
* 条件
*
* @return
*/
public ISMCondition getCondition();
/**
* 动作
*
* @return
*/
public String getAction();
/**
* 处理器
*
* @return
*/
public ISMActionProcessor getActionProcessor();
/**
* 原始状态
*
* @return
*/
public String getFromState();
/**
* 目标状态
*
* @return
*/
public String getToState();
}
package keng.core.workflow.state; import java.util.Map; import keng.core.workflow.model.WfStep; import keng.core.workflow.model.WorkFlow; /** * 上下文环境 * @author 5Di * */ public interface ISMContext extends Map<String,Object>{ public WorkFlow getWorkFlow(); public String getAction(); public WfStep getStep(); }
package keng.core.workflow.state;
/**
* Action处理器<br />
* 当Transit Transition 的时候,会执行相应的Action处理器,一般实现类会执行特定的业务代码
*
* @author 5Di
*
*/
public interface ISMActionProcessor {
/**
* 处理
*
* @return
*/
public Object process() throws StateMachineException;
public void beforeProcess() throws StateMachineException;
public void afterProcess() throws StateMachineException;
}
其他的还有:ISMNode.java,ISMCondition.java.. 就不贴出来了
下面是DefaultStateMachine.java 是IStateMachine.java的默认实现
package keng.core.workflow.state.impl;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.TimeoutException;
import keng.core.util.StringUtil;
import keng.core.workflow.state.ISMActionProcessor;
import keng.core.workflow.state.ISMCondition;
import keng.core.workflow.state.ISMContext;
import keng.core.workflow.state.ISMNode;
import keng.core.workflow.state.ISMTransition;
import keng.core.workflow.state.ISMTransitionMatcher;
import keng.core.workflow.state.IStateMachine;
import keng.core.workflow.state.StateMachineException;
/**
* 默认的状态机实现
*
* @author 5Di
*
*/
public class DefaultStateMachine implements IStateMachine {
Stack<ISMNode> register = new Stack<ISMNode>();
ISMTransitionMatcher matcher;
ISMContext context;
/**
* 根节点
*/
ISMNode root;
boolean strict = false;
public DefaultStateMachine(ISMNode root, ISMContext context) {
this.context = context;
this.root = root;
}
public DefaultStateMachine(ISMNode root, ISMContext context,
ISMTransitionMatcher matcher) {
this.root = root;
this.context = context;
this.matcher = matcher;
}
public DefaultStateMachine(ISMNode root, ISMContext context,
ISMTransitionMatcher matcher, boolean strict) {
this.context = context;
this.root = root;
this.matcher = matcher;
this.strict = strict;
}
@Override
public boolean transit(String action, String state, ISMNode node)
throws StateMachineException, TimeoutException {
if (StringUtil.isEmpty(action))
throw new IllegalArgumentException("action can not be empty!");
if (StringUtil.isEmpty(state))
throw new IllegalArgumentException("state can not be empty!");
if (node == null)
throw new IllegalArgumentException("node can not be null!");
List<ISMTransition> transitions = node.getTransitions();
if (transitions == null || transitions.isEmpty())
throw new StateMachineException("transitions can not be empty !");
// check che node
if (checkNodeMap(node))
return false;
// Match the transition
ISMTransition transition = matchTransition(action, state, transitions);
boolean b = false;
ISMCondition condition = transition.getCondition();
if (condition == null)
if (!strict)
b = true;
else
throw new StateMachineException(
"transition's condition can not be null!");
b = condition.calculate(context, strict);
if (!b)
throw new TimeoutException("no transition was matched!");
// process action
ISMActionProcessor processor = transition.getActionProcessor();
processor.process();
register.push(node);
// auto process next auto steps
List<ISMNode> children = node.getChildren();
if (children != null && !children.isEmpty()) {
for (ISMNode child : children) {
transit(action, state, child);
}
}
node.setState(transition.getToState());
return true;
}
/**
* 匹配transition <br />
* 使用<code>keng.core.workflow.state.ISMTransitionMatcher.match</code>匹配
*
* @param action
* @param state
* @param transitions
* @return
*/
ISMTransition matchTransition(String action, String state,
List<ISMTransition> transitions) {
if (matcher == null)
matcher = new DefaultSMTransitionMatcher();
transitions = matcher.sort(transitions);
int i, len = transitions.size();
for (i = 0; i < len; i++) {
ISMTransition transition = transitions.get(i);
if (matcher.match(action, state, transition))
return transition;
}
return null;
}
/**
* 检查节点图,是否进入了死循环<br />
* 目前还不完备,只是检查了已经执行过的node
*
* @param node
* @return 如果已经执行,返回true
*/
public boolean checkNodeMap(ISMNode node) {
return register.indexOf(node) != -1;
}
}
目前还处于DEMO 状态,如果大家有想要说的,尽管拍砖,共同进步
最后,给大家提供两个工作流方面的资料
《工作流模型分析》和《工作流管理联盟规范》