`
ahuaxuan
  • 浏览: 633325 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

土制状态机在工作流引擎中的应用

阅读更多
/**
  * @author : ahuaxuan
  * @date 2009-10-27
  */

很早之前(应该是一年以前),ahuaxuan在用dfa实现文字过滤一文中使用确定有限自动机实现了词典的高速查询。其实在当时那段时间里,由于对状态机有了一定的研究,ahuaxuan也触类旁通的理解了工作流引擎的核心体制。于是当时就用python写了一个小巧的工作流引擎的示例,在这之前ahuaxuan没有看过任何工作流引擎的实现,该实现纯属思维的自我延伸。

现在我来说说我的实现。
状态机的本质是状态的迁移,即从A状态+某个动作===》B状态。到这里我们还要来看看这张图。

从这张图中我们可以看到,状态(大写字母)+动作(小写字母)可以到达新的状态。那么对于程序员来说,我们要做的就是将这种机制用程序表达出来。比如说我们最常想到的是什么?矩阵!

这很好理解,但是对于工作流引擎来说,由于状态的迁移涉及到:当前状态+动作+条件===》新状态。
所以用二维的矩阵无法表示出这种逻辑。那么我们可以将矩阵中的元素替换为条件数组。
这样,我们可以通过当前状态+动作得到一个条件数组,然后再遍历这个条件数组,条件数组中的元素即是条件和满足条件的下一个状态(虽然本质上是一个三维数组,但是在这里还是看成矩阵+数组元素比较符合逻辑)。



这里需要画一个图,一个矩阵,矩阵中的元素是一个条件数组

没错,这是一种方案,但是这里有一个问题,那就是每次做状态迁移的时候,我们必须知道某个状态在矩阵一维上的index,已经某个动作在矩阵二维上的index.有了这两个index我们才能得到条件数组。所以这里还有一个繁琐的转换操作操作。来看一段伪代码:
conditions = matrix[getStatusIndex[‘A’], getActionIndex[‘a’]]
For condiction in conditions:
       If condition match input:
              Return Condition.nextStatus


核心流程大概就是这样,当

那么除了矩阵这种数据结构,我们还有其他的数据结构可以用来表示: “当前状态+动作+条件===》新状态”吗。

当然有,那就是使用树结构。在了解了三维数组的实现之后,再来看树实现,应该和容易了,那就直接上图, 将上图的矩阵转换成树结构之后,我们可以得到如下的树结构。



那现在我们来审视一下现在的问题,打个比方,我们现在手里有两张牌,一张是状态A,一张是动作a,我们如何通过这两种牌来得到条件集合呢,最简单的方法是首先遍历第二层节点,找到A,然后再遍历A的子节点,找到a。通过两次for循环找到了条件集合,而条件集合中包含着下一个状态。

那么有没有更简单更快速的方式可以直接找到条件集合,而直接跳过两次遍历呢。有,ahuaxuan的想法是tree+hash.也就是通过A的hash值,我们可以直接找到tree上的A节点。然后再通过a的hash值,我们可以直接找到A的子中的a节点。得到a节点之后我们就可以条件集合。那么我们可以用什么样的数据结构来实现一个这样的模型呢。这里面有hash运算,那么我们首先选择HashMap来创建这么一颗树。

我们来看一下这个定义:
Map<String, Map<String, Map<String, List<Transition>>>> dfa。


那么我们如何根据A,和a来得到一个条件集合呢?
Dfa.get[“processName”].get[“A”].get[“a”]
通过这样的方式,我们就可以根据当前状态和当前的动作得到一个条件集合。然后遍历这个条件集合就是找到满足“输入“的条件,该条件会指向下一个状态。

我们来看一下代码实现:
首先我们来构造这么一个状态机:
private void constructDfa(List<WfProcess> processList) {
		for (WfProcess pro : processList) {
			Map<String, Map<String, List<Transition>>> pmap = new HashMap<String, Map<String,List<Transition>>>();
			dfa.put(pro.getName(), pmap);
			
			for (State sta : pro.getStates()) {
				Map<String, List<Transition>> smap = new HashMap<String, List<Transition>>();
				pmap.put(String.valueOf(sta.getName()), smap);
				
				for (Action action : sta.getActions()) {
					List<Transition> transitions = new ArrayList<Transition>();
					for (String transName : action.getTransNames()) {
						transitions.add(pro.getTransitions().get(transName));
					}
					
					smap.put(String.valueOf(action.getName()), transitions);
				}
				
			}
		}
	}


接着我们来看看如何根据这个状态机来做状态迁移:

public String getNextState(String processName, String stateId, String actionId, Map<String, String> conditions) {
		
		List<Transition> transitions = dfa.get(processName).get(String.valueOf(stateId)).get(String.valueOf(actionId));
		
		for (Transition trans : transitions) {
			if (match(trans.getConditions(), conditions)) {
				return trans.getToState();
			}
		}
		
		StringBuilder sb = new StringBuilder();
		sb.append("There is no state for process : ").append(processName);
		sb.append(", stateId : ").append(stateId);
		sb.append(", actionId : ").append(actionId);
		sb.append(", conditions : ").append(conditions);
		
		throw new WorkFlowStateException(sb.toString());
	}


通过这种tree + hash的方式,我们可以很容易的进行状态的迁移,不需要那么多for循环。但是for循环确实有这样的实现。

今天早上下载了osworkflow的代码,稍微看了一下AbstractWorkflow的doAction方法。
发现osworkflow就是通过循环来实现状态的迁移的,比如说上图中树结构的状态可以用以下伪代码:
For state in states:
	If state.name == inputStateName:
		For action in state.actions:
			If action.name == inputActionName:
					for transition in action.transitions:
	…………………………………………………………………



通过这种方式,用户传入inputStateName和inputActionName, osworkflow得到了一组transition,并根据条件选择某个transition, 这样也实现了状态的转移。

从这里面可以看出,osworkflow是利用广度优先的原则,先找到符合条件的state,然后再找到符合条件的action,以此类推。

说到这里,通过这种状态机实现工作流引擎的方式基本的完全的,较为清晰的呈现在我们眼前了。

未完待续

  • 大小: 16.2 KB
  • 大小: 21.8 KB
4
0
分享到:
评论
2 楼 ahuaxuan 2009-11-02  
把动作和条件合并在一起的做法不太流行


动作是一个操作,条件是对业务数据的一个抽象。
比如动作是一个类的方法,而条件是方法的参数决定的,这样更合理一些
1 楼 melin 2009-11-02  
打算写一个小的工作流引擎:
定义的流程定义为:
<processdefine name="TestFlow" version="1.1.1" description="">
	<Activitys>
		<Activity id="start" type="start" name="开始" description="">
			<SplitMode>XOR</SplitMode>
		</Activity>
		<Activity id="A01" type="manual" name="申请" description="">
			<SplitMode>XOR</SplitMode>
			<JoinMode>XOR</JoinMode>
			<participantType>organization</participantType>
			<participantList>
				<participant id="910150" name="俞文琦" type="person"/>
				<participant id="910115" name="李强" type="person"/>	
			</participantList>
		</Activity>
		<Activity id="A02" type="manual" name="审核" description="">
			<SplitMode>XOR</SplitMode>
			<JoinMode>XOR</JoinMode>
			<participantType>process-starter</participantType>
		</Activity>
		<Activity id="end" type="finish" name="结束" description="">
			<JoinMode>XOR</JoinMode>
		</Activity>
	</Activitys>
	
	<Transitions>
		<Transition id="" from="start" to="A01" name="">
			<IsDefault>true</IsDefault>
		</Transition>
		<Transition id="" from="A01" to="A02" name="">
			<IsDefault>true</IsDefault>	
		</Transition>
		<Transition id="" from="A02" to="end" name="">
			<IsDefault>false</IsDefault>
			<Expression>optId==1</Expression>
		</Transition>
	</Transitions>
</processdefine>


查找当前环节的后续环节为:
public Transition[] getNextTransitions(String aid) {
		Transition[] arr = null;
		List<Transition> list = new ArrayList<Transition>();
		Iterator<Transition> it = this.transitions.iterator();
		while (it.hasNext()) {
			Transition trans = it.next();
			if (trans.getFrom().equals(aid))
				list.add(trans);
		}
		arr = new Transition[list.size()];
		for (int i = 0; i < arr.length; ++i) {
			arr[i] = ((Transition) list.get(i));
		}
		return arr;
	}


查找满足条件的transition
public static List<String> getPossilbeListNormal(WFProcessInstance pi,
			Activity actDef, Transition[] transarr) {
		List<String> possibleList = new ArrayList<String>(4);

		if (actDef.getSplitMode().equals("AND")) {
			for (int i = 0; i < transarr.length; ++i) {
				possibleList.add(transarr[i].getTo());
			}
		} else {
			Transition[] oktrans;
			int i;
			if (actDef.getSplitMode().equals("XOR")) {
				oktrans = getXORModeNextTrans(transarr, pi.getRelDataMap());

				Transition tran = findMostOKByPrority(oktrans);

				possibleList.add(tran.getTo());
			} else if (actDef.getSplitMode().equals(DefineConst.SPLIT_MODE_OR)) {
				oktrans = getORModeNextTrans(transarr, pi.getRelDataMap());

				if (oktrans.length == 0) {
					return possibleList;
				}

				for (i = 0; i < oktrans.length; ++i) {
					possibleList.add(oktrans[i].getTo());
				}

			}

		}

		return possibleList;
	}

相关推荐

    论文研究 - 生土的NaOH活化:NaOH含量对干燥动力学的影响及其建模

    这项工作旨在确定氢氧化钠浓度对用生黏土制得的砖的干燥动力学的影响,并对该动力学进行建模。 结果表明,由于缺乏游离水,干燥动力学受水的扩散控制。 干燥时间随NaOH含量的增加而线性增加,而体积收缩率则下降,...

    硅藻土制备介孔SiO2气凝胶 (2013年)

    采用响应面法对由硅藻土制取水玻璃的工艺进行优化,进而选择最佳工艺参数在常压干燥下成功合成了SiO2气凝胶材料。试验结果表明:当碱硅比为3∶10,NaOH溶液浓度为10%,反应温度为90℃时,水玻璃模数测定值与SiO2溶出...

    不同材料的生态混凝土对污水净化效果的比较 (2011年)

    为比较不同材料所制成的多孔混凝土对有机污水净化的效果,采用了不同的材料来制作3种混凝土反应柱,一为普通硅酸盐水泥与粗砂制作的混凝土,二为复合硅酸盐水泥与粗砂制作的混凝土,三为复合硅酸盐水泥与红粘土制 作的...

    基于matlab实现V2G系统simulink仿真图以及电动汽车充电和放电图.rar

    基于matlab实现V2G系统simulink仿真图以及电动汽车充电和放电图.rar

    共创在线考试系统(JSP+SERVLET)130223.rar

    共创在线考试系统(JSP+SERVLET)130223.rar,这是一个针对计算机专业学生的JSP源码资料包,旨在帮助学生更好地理解和掌握Java Web开发技术。该资料包包含了一个基于JSP和Servlet技术的在线考试系统,具有以下特点:功能齐全:该系统包括了在线考试、成绩查询、试题管理、用户管理等多个模块,能够满足学生进行在线考试的需求。界面友好:系统采用了简洁明了的界面设计,使得用户能够快速上手,方便地进行操作。代码规范:源码遵循Java编程规范,结构清晰,注释详细,便于学生学习和理解。可扩展性强:系统采用了模块化的设计思路,可以根据需要进行功能的扩展和修改。数据库支持:系统使用了MySQL数据库进行数据存储,可以方便地进行数据的增删改查操作。通过学习这个JSP源码资料包,学生可以掌握JSP和Servlet的基本用法,了解Java Web开发的基本流程,提高自己的编程能力。同时,该系统还可以作为学生课程设计或者毕业设计的参考项目,帮助他们完成学业任务。总之,这个共创在线考试系统(JSP+SERVLET)130223.rar资料包对于计算机专业的学生来说,是一个非常有价值的学习资

    医药集团能源集团汽车集团大型集团战略规划顶层战略设计方案PPT(4份)

    医药集团能源集团汽车集团大型集团战略规划顶层战略设计方案PPT(4份)

    基于matlab实现非常齐全的wsn定位matlaB仿真程序.rar

    基于matlab实现非常齐全的wsn定位matlaB仿真程序.rar

    matlab GPS与捷联惯导的组合导航程序,可以运行.rar

    matlab GPS与捷联惯导的组合导航程序,可以运行.rar

    3D模型009,可用于建模、GIS、BIM、CIM学习

    3D模型009,可用于建模、GIS、BIM、CIM学习

    大一C++作业,功能完善的学生成绩管理系统 支持信息的增删改补,虚拟信息生成,排序,硬盘数据的写入与读取.zip

    大一C++作业,功能完善的学生成绩管理系统 支持信息的增删改补,虚拟信息生成,排序,硬盘数据的写入与读取.zip

    毕业设计:基于SSM的mysql-软件缺陷管理系统(源码 + 数据库 + 说明文档)

    毕业设计:基于SSM的mysql_软件缺陷管理系统(源码 + 数据库 + 说明文档) 第2章 可行性分析 3 2.1技术的可行性 3 2.2经济的可行性 3 2.3操作可行性 4 2.4法律的可行性 4 第3章 需求分析 5 3.1开发工具需求 5 3.1.1开发语言和工具 5 3.1.2基于B/S结构开发 5 3.1.3 JAVA语言简介 5 3.1.4 JavaScript技术 6 3.1.5 MySQL数据库 6 3.1.7软硬件需求 6 3.2 系统需求 6 第4章 总体设计 8 4.1 系统模块总体设计 8 4.2 数据库设计 10 4.2.1 数据分析 10 4.2.2 数据库的详细设计 10 4.3 本章小结 12 第5章 详细设计与实现 13 5.1 管理员管理 13 5.1.1 管理员登录管理 13 5.1.2 欢迎页 13 5.1.3 项目经理管理 14 5.1.4 员工管理 15 5.1.5 用户登录日志管理 15 5.1.6 个人信息管理 16 5.2 项目经理管理 17 5.2.1 项目经理登录 17 5.2.2 项目管理 18 5.3 调试员端 1

    大型集团企业财务共享业财一体化应用平台建设方案.pptx

    大型集团企业财务共享业财一体化应用平台建设方案.pptx

    银行智能化数据安全分类分级实践方案.pdf

    银行智能化数据安全分类分级实践方案.pdf

    node-v6.10.1.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    知乎小程序算法.zip

    知乎小程序算法.zip

    基于嵌入式AI的人脸识别课堂签到系统.zip

    优秀源码设计,详情请查看资源源码内容

    基于matlab实现文档+程序边缘计算任务卸载与资源调度的算法,是论文的源代码,具有价值.rar

    基于matlab实现文档+程序边缘计算任务卸载与资源调度的算法,是论文的源代码,具有价值.rar

    毕业设计:基于SSM的mysql-软件学院互助答疑平台(源码 + 数据库 + 说明文档)

    毕业设计:基于SSM的mysql_软件学院互助答疑平台(源码 + 数据库 + 说明文档) 2 开发技术简介 13 2.1 基于B/S结构开发 13 2.2 JSP简介 13 2.3 MySQL数据库 13 2.4 JDBC 13 2.5 SSM框架 14 3 需求分析 14 3.1 需求分析 14 3.2 可行性分析 15 3.2.1 经济可行性 15 3.2.2 技术可行性 15 3.2.3 操作可行性 16 3.3 非功能需求分析 16 4 系统设计 17 4.1 数据库表设计 17 4.2 功能设计 18 5 系统详细设计 18 5.1 用户登录 18 5.2 问题发布 19 5.3 回答提问 20 5.4 用户资料 20 5.5 热门回答 21 5.6 最新回答 21 6 系统测试 22 6.1 调试目的 22 6.2 调试的主要内容 23 6.3 调试案例 23 6.4 测试方法 23 6.5 测试的重要性 24 6.6 不登陆测试 25 6.7 性能测试 25

    基于JSP技术的猎头公司管理软件的设计和实现-内部事务部分(源代码+论文).rar

    这个资料包名为"基于JSP技术的猎头公司管理软件的设计和实现——内部事务部分(源代码+论文).rar",是一个针对计算机专业学习者或开发者提供的实用资源。它涵盖了一个以Java Server Pages (JSP)技术为基础开发的猎头公司管理软件项目,专注于公司的内部事务处理。该软件旨在简化猎头公司的工作流程,提高工作效率,并使得管理工作更加系统化和自动化。资料包中包含了完整的源代码,这意味着用户可以直接查看、修改和部署这些代码来适应自己的需求。源代码的开放性为用户提供了学习和自定义的巨大空间,可以深入理解JSP技术在实际项目中的应用,以及如何结合数据库、前端页面设计和后端逻辑控制来构建一个完整的Web应用程序。除了源代码之外,资料包还附带了一篇论文,这篇论文详细阐述了软件的设计理念、系统架构、功能模块划分、关键技术点以及实现过程等。对于学生或研究者来说,这篇论文不仅提供了技术上的指导,还展示了如何将理论知识转化为实践操作的过程,具有一定的学术价值和参考意义。整体而言,这个资料包是计算机专业学生、软件开发者或对JSP技术感兴趣的人士宝贵的学习资源。无论是作为教学案例、课程项目,还是实际

Global site tag (gtag.js) - Google Analytics