Fire Workflow工作流原理、设计与应用


FireFireFireFire Workflow Workflow Workflow Workflow 工作流 原理、设计与应用 V1.0 www.fireflow.org 更新历史 时间 更新内容 作者 2009-02-15 创建,v0.9(预览版) 非也, QQ:20674450 Email: nychen2000@163.com 2009-06-13-- 2009-07-23 更新,增加 1.0 特性,合编为一个 文档。 非也, QQ:20674450 Email: nychen2000@163.com i 目 录 I. 第一部分 概述.....................................................................................................................1 1. FAQ................................................................................................................................1 2. Fire Workflow 的构成...................................................................................................2 II. 第二部分 Fire Workflow 工作流模型..............................................................................3 1. 当前工作各种流模型的缺点.......................................................................................3 2. Fire Workflow 模型的设计思想...................................................................................3 3. Fire Workflow 模型的构成元素..................................................................................6 3.1. Workflow Process模型..................................................................................... 6 3.2. WorkflowProcess 与资源的关系......................................................................7 4. Fire Workflow 模型各个元素的约束..........................................................................7 5. Fire Workflow 模型的执行机理...................................................................................9 5.1. Fire workflow 相关数学定义........................................................................... 9 5.2. Fire workflow 工作流逻辑运行的规则......................................................... 11 5.3. 基于 Fire workflow 工作流网的业务逻辑的执行规则............................... 11 6. Fire Workflow 模型各种元素的详细属性.................................................................12 6.1. 所有流程元素通用属性.................................................................................12 6.2. WorkflowProcess 的属性................................................................................12 6.3. StartNode、Synchronizer、EndNode 属性....................................................13 6.4. Activity 属性....................................................................................................13 6.5. Transition 的属性............................................................................................ 13 6.6. Loop 的属性....................................................................................................14 6.7. Task 的公共属性.............................................................................................14 6.8. Subflow Task 的属性.......................................................................................15 6.9. Tool Task 的属性.............................................................................................15 6.10. Form Task 的属性......................................................................................... 15 7. 附,预览版文档中关于当前流程模型不足之处的描述——从流程建模说起....16 III. 第三部分 Engine 的设计及其扩展...............................................................................20 1. Engine 的设计理念..................................................................................................... 20 1.1. 合理的职责划分.............................................................................................20 1.2. 足够的扩展性.................................................................................................20 1.3. 方便集成到业务系统中.................................................................................21 2. Engine 的结构............................................................................................................. 21 2.1. Engine 总体结构.............................................................................................21 3. Engine API 对象......................................................................................................... 23 3.1. IProcessInstance...............................................................................................24 3.2. ITaskInstance....................................................................................................25 3.3. WorkItem..........................................................................................................26 4. WorkflowSession......................................................................................................... 27 4.1. WorkflowSession 的作用................................................................................27 4.2. WorkflowSession 是非线成安全的................................................................29 4.3. WorkflowSession 会保存在 API 对象中........................................................29 5. TaskInstanceManager...................................................................................................29 ii 6. BeanFactory Service....................................................................................................30 7. PersistenceService........................................................................................................31 8. CalendarService........................................................................................................... 32 9. DefinitionService......................................................................................................... 33 10. ConditionResolver..................................................................................................... 34 11. Engine 各种扩展....................................................................................................... 34 11.1. RuntimeContext Factory................................................................................34 11.2. 数据持久化服务...........................................................................................34 11.3. 日历服务.......................................................................................................34 11.4. 任务实例管理器...........................................................................................34 11.5. 流程定义服务...............................................................................................35 IV. 第四部分 FireWorkflow 应用..........................................................................................36 1. 通过流程设计器和模拟器快速了解 Fire Workflow................................................36 1.1. 设计器的安装.................................................................................................36 1.2. 设计器各部分介绍.........................................................................................38 1.3. 设计一个最简单的请假流程........................................................................ 41 1.4. 模拟流程的执行.............................................................................................47 1.5. 设计一个更复杂的请假流程........................................................................ 53 2. 业务代码如何调用 FireWorkflow API 实现流转.....................................................56 2.1. 往java project 中加入 Fire Workflow 支持..................................................56 2.2. 配置 Fire Workflow........................................................................................59 2.3. 编写并运行 LeaveApplicationTester.java..................................................... 61 2.4. AssignmentHandler 和ApplicationHandler....................................................61 3. 将Fire Workflow 嵌入你的 J2EE 系统中(1)............................................................63 3.1. J2EE 系统中常见的流程操作........................................................................63 3.2. 将Fire workflow 嵌入 J2EE 系统的详细步骤............................................64 3.3. 第J2EE 系统中常见的流程操作的实现......................................................64 4. 将Fire Workflow 嵌入你的 J2EE 系统中(2)............................................................67 4.1. 工作流数据 VS 业务数据............................................................................. 67 4.2. 如何填充 TaskInstance扩展表的数据......................................................... 69 4.3. 请假流程实例的扩展以及效果.................................................................... 70 5. 工作流应用中经典问题的解决方案........................................................................ 72 5.1. 流程定义文件的存储与版本控制................................................................ 72 5.2. 业务数据 vs 工作流数据...............................................................................73 5.3. 流程数据存取设计与事务一致性................................................................ 73 5.4. 与用户管理系统的接口.................................................................................74 5.5. 与业务表单的接口.........................................................................................76 5.6. 工作项签收与业务实际中的材料移交........................................................77 5.7. 流程“自定义”与“自调整”.................................................................... 77 5.8. 工作流系统的性能问题.................................................................................78 6. 各种工作流模式的实现.............................................................................................79 6.1. 概述.................................................................................................................79 6.2. 顺序、分支、汇聚.........................................................................................79 6.3. 子流程.............................................................................................................83 iii 6.4. “自由流”(Jump).........................................................................................86 6.5. 循环(Loop)......................................................................................................90 6.6. 略过(Skip).......................................................................................................92 6.7. 会签.................................................................................................................93 6.8. 取回与拒收.....................................................................................................95 6.9. 委派...............................................................................................................104 6.10. 任务完成期限.............................................................................................104 6.11. 监听工作流事件.........................................................................................105 V. 附录...................................................................................................................................108 1. Fire Workflow Examples 与各章节的对应关系......................................................108 1 I.第一部分 概述 1.FAQFAQFAQFAQ � 为什么要写 FireWorkflow 本人从事企业 MIS 系统开发很多年头了,感觉 MIS 系统很多领域都有比较好的解决方 案并已成为事实标准,例如 Spring,Hibernate 等等;然而工作流还没有令人满意的开源产 品。我了解过的工作流产品(主要是开源的,收费产品没有什么研究,仅仅看看其白皮书而 已)都存在如下毛病: a. 缺乏严密的理论做支撑,工作流模型大多千篇一律地照搬 WfMC 的xpdl, b. 因为缺乏理论支撑,所以工作流引擎的算法有点七拼八凑,扩展性也比较差。 c. 没有好的设计器,应用比较困难 最近研究并应用了一下 JBoss 的Jbpm,除了其面向图的引擎算法让我眼前一亮外,其 他的也不是令人满意。其引擎的扩展性不好,表结构太复杂,在大数据量系统中,性能令人 堪忧。 鉴于此,我动手写了一个 FireWorkflow,抛砖引玉。 � Fire Workflow 的定位 我从来不认为工作流可以 “自定义 ”,所以 Fire Workflow 是面向开发人员的。 Fire Workflow 和Spring、Hibernate 一样,是一个或几个普普通通的 jar 包,嵌入到系统中,用以 解决系统开发中工作流领域的问题。 因为 FireWorkflow 是面向开发人员的,所以在下面两个方面花了较大功夫。 a. 流程设计器。Fire Workflow 用于帮助开发人员解决系统中的流程问题,所以好的 流程设计器有助于开发人员提高开发效率。FireWorkflow 设计器和主流 IDE 紧密 集成(目前有 Eclipse 插件和 NetBeans 插件),使得开发人员开发调试流程就像开 发调试一个普通 java 类一样简单。 b. 工作流引擎。Fire Workflow 引擎设计充分考虑扩展性,因为业务系统的需求五花 八门,所以一个写的很死的引擎必然没有生命力。 Fire Workflow 引擎的各种服务 都可以扩展或者替换。 Fire Workflow 虽然不认可流程“自定义”需求,但是并不否认用户有调整业已存在的 流程的需求,而且这种需求还比较普遍。所以 Fire Workflow 计划提供 Web 界面,让最终用 户的系统管理员在一定范围内调整流程。 � Fireflow 的特点 a. 理论严密 Fire Workflow 以Petri Net 作为理论基础,流程的顺序流转、分支、汇聚、跳转等算法 都有定义/定理为依据。 b. 设计合理 Fire workflow 将工作流引擎的职责分解委派到各种服务中,每中服务都可以被扩展或者 替换。 c. 应用简单 2 Fire workflow 的API 以及数据库表结构非常简单。 d. 性能优良 Fire workflow 着重在流程实例的数据量,数据库 IO等方面进行性能优化。 2.FireFireFireFire Workflow Workflow Workflow Workflow 的构 成 的构 成 的构 成 的构 成 Fire Workflow 由模型、引擎、设计器(包含模拟器)、流程管理工具四部分组成,如下 图1-1。 模型部分规定了流程定义文件的各种元素及其相互关系,例如流程 (WorkflowProcess)、 活 动(Activity)、转 移( Transition)、循 环 (Loop)、开始 节 点(StartNode)、结束 节 点(EndNode)、 同步器(Synchronizer)等等。模型部分的实现在 org-fireflow-model.jar 中。 引擎读取流程定义文件并解释执行。引擎提供一组对象和相关的 API 供外部系统调用, 如流程实例(ProcessInstance)、任务实例(TaskInstance)、工作项(WorkItem)、事件等等 。 引擎部分的实现在 org-fireflow-engine.jar 中。 设计器编辑并输出流程定义文件。 Fire Workflow 的设计器附带了强大的模拟器,可以 在设计时模拟流程的执行,从而检查流程定义的正确性。 流程管理用具用于查询流程实例的执行状况,对流程实例执行一些特权操作(如终止、 挂起、召回等),流程实例数据管理,在线调整流程定义等等。 图i-2-1 某业务系统 Fire workflow 引擎 Fire workflow 模型 Fire Workflow 设 计器、模拟器 业务逻辑Fire Workflow 管 理工具 3 II.第二部分 FireFireFireFire Workflow Workflow Workflow Workflow 工作流模型 1.当前 工作 各种流 模型 的缺 点 当前 工作 各种流 模型 的缺 点 当前 工作 各种流 模型 的缺 点 当前 工作 各种流 模型 的缺 点 工作流模型的理想状况应该是:首先其语义精确,利于计算机执行;同时,其图形表示 简洁,便于人阅读和描述业务。然而,当前的各种工作流模型均未能达到上述目标。例如: XPDL:该规范历史较为悠久,模型中规定了诸多指导计算机执行的属性。但是设计一 个建立在 xpdl 上的流程引擎极端困难。xpdl 模型长期未能取得突破,以至于逐渐失去领导 地位。新出版的(2008 年10 月)xpdl2.1 规范改为追随 BPMN,作为 BPMN的序列化方案 之一,然而 BPMN亦不完整。 BPMN:该规范仅仅统一了流程中各种元素的图形符号,并没有规定这些图形的详细属 性,更没有建立一套流程图的序列化方案。 BPMN所设计的流程模型适合于阅读和描述业 务,但是非常不适合于计算机执行。 BPEL:BPEL纯粹是一个 Web 服务的编排语言,且是块状结构。虽然语法严密,适合 计算机执行;但是非常不适合图形化业务流程描述。块状结构的语言甚至无法表达某些流程 应用场景,如自由跳转等等。 Fire workflow 的流程模型正是在这种背景下提出的,它有精确语法和严密算法,所以适 合计算机执行;同时 Fire workflow 的流程定义语言 FPDL是面向图的,其图形表示非常简 洁,所以也适合于业务描述和业务分析。 2.FireFireFireFire Workflow Workflow Workflow Workflow 模型 的设 计思想 模型 的设 计思想 模型 的设 计思想 模型 的设 计思想 XPDL、BPMN、BPEL的流程模型都有一个共同的特点,就是没有区分模型中的工作 流逻辑和业务逻辑。 业务逻辑是指某个具体的业务操作,例如填写一张表单,调用一个 WebService 服 务 , 发送一个消息等等。 工作流逻辑是指对这些业务逻辑的某种编排方案,例如先执行哪个业务操作,然后执行 哪个业务操作,哪些可以并行等等。 FireFireFireFire Workflow Workflow Workflow Workflow 模型的核心思想是将两种逻辑解偶。 下面以一个请假流程来讲解一下这两种模型的区别,该业务规则如下: 首先由请假人提出申请;请假申请提交到部门经理处审批;审批通过后到人力资源部登 记备案。(暂且不考虑审批不通过的情况。) 这个业务用 XPDL 建模,其流程图如下。这个流程图和 UML 中的活动图差别不大,都 是从宏观的抽象的层次描述了业务。我认为这种模型便于分析但是不利于执行。其根源在于 流程元素的职责划分不清,在 XPDL 中,Activity 承担了太多的职责,可以代表一个人工活 动,也可以代表一个自动的程序调用,还可以代表一个子流程,还可以是一个复杂的路由等 4 等。 图ii-2-1 XPDL workflow process 如果用 FPDL建模,则稍有不同。FPDL认为一个系统是由工作流子系统和业务子系统 构成,流程的执行是实际就是控制权在这两个子系统之间转移。如果用圆圈表示工作流子系 统的操作,用方框业表示务子系统的操作。那么请假流程如下图。 图3.1-2 FPDL Workflow Process 图ii-2-2 FPDL workflow process 该流程图的执行过程描述如下: 首先,工作流子系统启动一个新的业务流程实例,然后创建一个新的任务实例——“申 请”,并将控制权交给业务子系统,业务子系统等待申请人填写表单。 申请人完成表单后,控制权再次被交给工作流子系统,由它决定下一步的路由。这个工 作是由称为 Synchronizer 的元素完成的(图中标有"S"的圆圈)。在这个业务示例中,它通过计 算得出下一步操作是“部门经理审批”。于是创建一个名字叫做“部门经理审批”的任务实 例,并将控制权交给业务子系统,业务子系统等待部门经理做审批操作。 决定下一步 业务操作 申请 部门经 理审批 人力资 源备案 S 申请 部门经 理审批 人力资 源备案 决定下一步 业务操作 S System 工作流子系统 业务子系统 5 部门经理完成后,控制权又被转移到工作流子系统,由工作流子系统决定下一步,如此 反复直到流程实例终结。 上图转换成一般的 FPDL流程图如下 图ii-2-3 FPDL workflow process-2 FPDL似乎把问题复杂化了,实际上不是的, FPDL把是把职责分解了。在 FPDL中 , Activity 代表业务活动,完全不关心工作流如何分支、汇聚等工作,所有的工作流相关的运 算全部委托给 Synchronizer 处理,由他来决定路由。 FPDL这种清晰的职责划分在复杂的流程中体现出强大的优势,例如,把上述案例再丰 富一下:如果请假天数不大于 3天,则部门经理审批即可;如果大于 3天,则必须提交给公 司经理审批。 这个流程用 XPDL 建模如下图。在这个图中“部门经理审批 Activity”需要执行分支运 算 ,“人力资源备案 Activity”必须执行汇聚运算。 图ii-2-4 如果用 FPDL建模,流程图如下。在这个流程图中,各个流程元素的职责更加清晰,代 表业务活动的 Activity 仅完成与业务相关的操作,分支汇聚等工作流运算由 Synchronizer 完 成,如图中的 S1和S2。 图ii-2-5 S2S1 days>3 days>3 days<=3 申请 部门经 理审批 人力资 源备案 公司经 理审批 days<=3 申请 部门经 理审批 人力资 源备案 公司经 理审批 (Skip) S申请 部门经 理审批 人力资 源备案S 6 3.FireFireFireFire WorkflowWorkflowWorkflowWorkflow 模型 的构 成元素 模型 的构 成元素 模型 的构 成元素 模型 的构 成元素 3.1.WorkflowWorkflowWorkflowWorkflow Process Process Process Process 模型 与当前很多工作流产品相比,Fire workflow 的工作流模型非常简洁。其主要构成如下图 图ii-3.1-1 FireWorkflow 模型组成 WorkflowProcess 主要有 3类元素组成: A) 代表业务逻辑的元素:FormTask ,ToolTask,SubflowTask B) 代表工作流逻辑的元素:StartNode ,Activity,Synchronizer,EndNode,Transition, Loop。 C) 流程数据项:DataField 在本人看来,可以将一个系统分成业务子系统和工作流子系统。 Task 代表业务子系统 的逻辑;Activity、Synchronizer、StartNode、EndNode、Transition、Loop 代表工作流子系统 的逻辑,其中 Transition、Loop 代表控制权在业务子系统和工作流子系统之间转移。 对于上述每一类元素,其内部继承关系如下图 图ii-3.1-2 FireWorkflow 模型继承关系 7 从图中可以看出,业务逻辑元素都继承自 Task。工作流逻辑是由节点(Node)和边(Edge) 构成;Activity 是一种节点类型,Synchornizer 是另一种节点类型,StartNode和EndNode 是 两类特殊的 Synchronizer;工作流逻辑中的边分为两种类型,一种是转移(Transition),另一 种是循环(Loop)。 在工作流逻辑中,Activity 节点代表业务活动,工作流引擎通过 Activity 实例启动其中 的Task 实例。Synchornizer 节点代表工作流引擎的计算活动,工作流引擎在 Synchronizer 实 例中需要处理顺序、分支、汇聚、循环等等逻辑。 一个 Activity 可以包含一个或者多个 Task,如下图。 图ii-3.1-3 一个 Activity 可以有多个 Task 3.2.WorkflowProcess WorkflowProcess WorkflowProcess WorkflowProcess 与资源的关系 在Fire Workflow 中, Workflow 与外部资源是分开存储的,外部资源是指被调用的 Application,流程的参与者,FormTask 中的 Form 信息等。 流程与资源以及各种资源的继承关系如下图 图ii-3.2-1 Workflow Process与资源的关系 4.FireFireFireFire WorkflowWorkflowWorkflowWorkflow 模型 各个 元素的 约束 模型 各个 元素的 约束 模型 各个 元素的 约束 模型 各个 元素的 约束 在画 Fire workflow 流程图时,需要遵守如下基本约束。 A)一个流程仅有 1个开始节点,可以有 1个或者多个结束节点 B)一个流程图中 Activity 和Synchronizer 可以有 Transition 相连,两个 Activity 之 间 , 或者两个 Synchronizer 之间不能直接相连。如下图: Activity1 Activity1 8 图ii-4-1 这个规定的业务含义是,业务子系统完成其工作后,必须将控制权交给工作流子系统, 由它决定下一步路由;工作流子系统决定了路由后必须将控制权交给业务子系统,由它完成 业务操作。 C) 任何 Activity 只能有一个输入 Transition 和一个输出 Transition。如下图: 图ii-4-2 这个规定的业务含义是,如果一个 activity 有多个输入,那么 Activity 必然需要处理汇 聚逻辑,显然,这个不是他的职责,这是 Synchronizer 的职责。 同理,如果一个 Activity 由多个输出,则,Activity 必然要处理分之逻辑,这不是他的 职责,这是 Synchronizer 的职责。 D)synchronizer 可以有多个输入,和多个输出,如图。多个输入的情况下,synchronizer 需要正确处理汇聚逻辑;多个输出的情况下,synchronizer 需要正确处理分支逻辑。 顺序 分支 汇聚 复杂路由 图ii-4-3 E)流程图中不允许出现由 Transition 构成的环。下图中有环,不允许。 图ii-4-14 F)Loop 只能连接同一条“执行线”上的两个 Synchronizer,并且从后继 Synchronizer 指 向前驱 Synchronizer。 同一条执行线的意思是 Line(Synchronizer1)=Line(Synchronizer2),即节点 1的前驱和后 继的集合(包含节点 1本身)等于节点 2的前驱和后继的集合(包含节点 2本 身 )。 Activity2Activity1 Activity3 Activity1 Activity2 Activity1 Activity1 9 5.FireFireFireFire Workflow Workflow Workflow Workflow 模型的执行机理模型的执行机理模型的执行机理模型的执行机理 5.1.FireFireFireFire workflow workflow workflow workflow 相关数学定义 定义1 工作流: 工作流是对业务进程的形式化描述,包括描述活动之间依赖关系(因果依赖与规章依赖 ) 的工作流逻辑和在此基础上增加显性内容的工作流语义。显性内容是指影响流程执行路径的 业务数据、操作员的决定等等。 定义2 工作流逻辑(网): 工作流逻辑(网)只关心活动之间的依赖关系,包括因果关系和规章依赖,不关心活动的 具体操作内容。工作流逻辑网符合如下规则。 规则1、工作流逻辑网是由业务活动集合T(即Activity)和流程同步器集合P 构成的加 权有向图。即Workflow Logic Net Σ=(P,T;F,K,W)。其中 F 是T 到P 或者T 到P 的所 有的有向边的集合。K 是P 的容量的集合。所谓容量,就是这个同步器能够容纳的Token的 数量。W表示边上的权。 流程同步器代表工作流子系统中决定下一步路由的逻辑,例如顺序、分发( split)、汇 聚(join)等等。 在图示中,我们用矩形表示业务活动 t(t∈T),用小圆圈表示 p(p∈P),用箭头表示f (f∈F),用箭头上的数字表示 w(w∈W),用圆圈中的数字表示 k(k∈K)。 图ii-5.1-1 规则2、任何p与p′之间都没有边相连;任何t与t′之间都没有边相连。这个规则可以 理解为,整个系统的控制权只能在工作流子系统与业务子系统之间交互,不可以将控制权从 一个业务活动直接转移到另一个业务活动,也不可以将控制权由一个同步器直接转移给另一 个同步器。 规则3、对于任何一个业务活动t∈T,都有·t≠Φ且 t·≠Φ。其中·t 表示任务t 的 输入集,t·表示任务t 的输出集,由规则1 知t 的输入集和输出集都是{p|p∈P}。该规则 通俗理解为所有业务操作的发生权都是由流程同步器授予,业务完成后交还给同步器。 10 规则4、任何一个业务任务t∈T,都有|·t|=1 且|t·|=1。这个规则表示,所有的任务 只有一个输入且只有一个输出。如下图4-2。如果|·t|>1 或者|t·|>1,则表现为图4-3 的 形式,则说明t除了完成业务操作之外,还要完成诸如“分发split”和“汇聚join”的操作 , 显然是越疽代庖,分发汇聚操作时流程同步器的职责。 图ii-5.1-2 规则5、对于任何 p∈P,如果 ·p = Φ,则规定|·p|=1。我们称输入集为空的同步 器为“起始节点 ”,整个网中,允许且只允许存在一个起始节点。如果 p· = Φ,则规定 |p·|=1,称p 为结束节点,整个网中可以出现多个结束节点。 规则6、工作流逻辑网是一个连通图;不允许出现环。 规则7、对于任何p∈P,p 的容量K(p) = |·p| * |p·|。特别地,对于起始节点,其 容 量等于 |p·|;对于结束节点,其容量等于 |·p| 规则8、对于任何f∈F,如果f∈{(t,p)|t∈T∧p∈P},则W(f) = K(p)/|p·|;如果f ∈{(p,t)|p∈P∧t∈T},则W(f)= K(p)/|·p|。 定义3 工作流网初始状态与终止状态: 工作流逻辑网的初始状 M0:对于起始节点 p有M0(p)=K(p),其他任何同步器及终 止节点有M0(p)=0; 工作流逻辑网的中止状态 Mend:对于中止节点 Mend(p)=K(p),其他任何同步器及起始 节点 M0(p)=0; 定义4 工作流语义: 工作流语义是指在工作流逻辑网的基础上增加的显性内容,如影响流程执行路径的业务 数据、操作员的决定等等 。 11 5.2.FireFireFireFire workflow workflow workflow workflow 工作流逻辑运行的规则 通过 5.1 的定义可以知道 Fire workflow 工作流逻辑网是一个特殊的 Petri Net,每个节点 的“发生(即执行)”也严格遵守 petri net 的定义。 定义5555 发生: 发生是指工作流逻辑网中的 p或者 t,在没有加载业务逻辑的情况下的一次执行。任何p 或者 t 是否具有发生权,是由 Petri Net 的相关规则来定义的。 从5.1 节相关的定义可知,一个正确的工作流逻辑网从初始状M0执行到终止状态Mend 后,对于任何 p或者 t,他都发生过一次,其仅发生了一次。 显然,在实际业务中,同一个业务流程每个业务活动 t所代表的业务逻辑并不是都要执 行。在给定的案例中,有的流程分支上的活动所代表的业务逻辑要被执行,有的分支上的活 动代表的业务逻辑不被执行,有的还可以被执行多次。那么“t发生过 1次且仅发生过 1次” 与“t代表的业务逻辑可以被执行 0到N次”这个“矛盾”是如何解决的呢?请看下一节。 5.3.基于FireFireFireFire workflow workflow workflow workflow 工作流网的业务逻辑的执行规则 我们考察一下对于任何一个活动 t所承载的业务逻辑被执行的充分必要条件。 定义6666 实例化、可被实例化: 如果 t对应的实际业务逻辑被执行了,我们称 t和·t被实例化了。称t·可被实例化。 在Fire workflow 中所有的业务逻辑都包含“执行条件”和“执行体”两部分。如请假 流程的公司经理审批环节,执行条件是“请假天数大于 3天”,执行体是“审批请假单”,只 有在执行条件计算为 true的情况下,执行体才有可能被执行。对于执行条件为空的情况,默 认为 true。我们用一个方框表示业务逻辑,方框的上部表示执行条件,下部表示执行体。如 下图 图ii-5.3-1 显然,业务逻辑被执行的另一个必要条件是 t取得发生权。 在实际业务中,业务逻辑的直接前驱是否被执行直接决定它本身是否可以被执行,只要 有一个前去逻辑被执行了,那么这个业务逻辑才有被执行的可能。 t 业务逻辑执行条件 业务逻辑执行体 12 因此在工作流网上的任何一个业务逻辑可以被执行的充分必要条件是: 1)对应的 t取得发生权 2)·t可以被实例化 3)执行条件计算结果为 true 由于在 Fire workflow 中 ,t只有一个输入边和一个输出边,把执行条件画在边上作为“转 移条件”不影响 t被实例化的条件,而且更符合通常的理解,如下图 图ii-5.3-2 由上面的讨论可知 t一定会在某个时候获得一次发生权,因此 t一定会发生一次。然而 t所承载的业务逻辑则不一定会执行,他还由另外两个条件决定。因此,在t发生的情况下, t承载的业务逻辑可能被执行 0次或 1次,那么执行 N次是怎么回事呢?在跳转和循环的情 况下 t承载的业务逻辑会被执行 N次。 6.FireFireFireFire Workflow Workflow Workflow Workflow 模型 各种 元素的 详细 属性 模型 各种 元素的 详细 属性 模型 各种 元素的 详细 属性 模型 各种 元素的 详细 属性 6.1.所有流程元素通用属性 6.2.WorkflowProcess WorkflowProcess WorkflowProcess WorkflowProcess 的属性 Workflow Process除了上述通用属性外,还有如下属性 属性名称 属性说明 ID 元素的 ID。不可编辑,由设计器自动生成 Name 元素的名称 Display Name 元素的显示名称,例如在中文应用系统中可以为元素命名 一个通俗易懂的中文名称。 Description 元素描述 ExtendAttribute 扩展属性,是一个 key-value 组成的 Map。您可以根据需 要给元素增加任意多的扩展属性。 属性名称 属性说明 EventListeners 事件监听器列表 TaskInstanceCreator 一个实现了 ITaskInstanceCreator 接口的类,负责创 建任务实例。通常每个 WorkflowProcess 都要定义一个自 业务逻辑执行条件 t 业务逻辑执行体 13 6.3.StartNodeStartNodeStartNodeStartNode、SynchronizerSynchronizerSynchronizerSynchronizer、EndNode EndNode EndNode EndNode 属性 目前只有通用属性 6.4.Activity Activity Activity Activity 属性 除了通用属性外,还有如下属性 6.5.Transition Transition Transition Transition 的属性 Transition 除了通用属性外,还有如下属性。 身特有的 TaskInstanceCreator。 FormTaskInstanceRunner 一个实现了 ITaskInstanceRunner 接口的类,负责所 有ForTask 实例的运行 。一般情况下无须定制这个类, 采用 BasicTaskInstanceManager 的缺省实现即可。 ToolTaskInstanceRunner 一个实现了 ITaskInstanceRunner 接口的类,负责所 有ToolTask 实例的运行 。一般情况下无须定制这个类, 采用 BasicTaskInstanceManager 的缺省实现即可。 SubflowTaskInstanceRunner 一个实现了 ITaskInstanceRunner 接口的类,负责所 有SubflowTask 实例的运行 。一般情况下无须定制这个 类,采用 BasicTaskInstanceManager 的缺省实现即可。 FormTaskInstanceCompletion Evaluator 一个实现了ITaskInstanceCompletionEvaluator 接口的 类,负责检查该流程的 ForTask 实例是否可以结束 。一 般情况下 WorkflowProcess 无须定制这个类。 ToolTaskInstanceCompletion Evaluator 一个实现了ITaskInstanceCompletionEvaluator 接口的 类,负责检查该流程的 ToolTask 实例是否可以结束 。一 般情况下 WorkflowProcess 无须定制这个类。 SubflowTaskInstanceComplet ionEvaluator 一个实现了ITaskInstanceCompletionEvaluator 接口的 类,负责检查该流程的 SubflowTask 实例是否可以结束 。 一般情况下 WorkflowProcess 无须定制这个类。 属性名称 属性说明 Complete Strategy Activity Instance的完成策略。可以取值 ANY 或ALL,缺 省值为 ALL 当取值为 ANY 时,他的任何一个 TaskInstance 完成时都 可以触发 Activity Instance执行 Complete()操作。 当取值为 ALL 时,只有当他所有的 Task Instance完成时, 才出发 Activity Instance的Complete()操作。 属性名称 属性说明 Condition 转移条件。是一个 EL表达式,如果此 EL表达式的计算 结果为 true,则此转移分支将被执行,否则不执行。 缺省值为空,为空时其值为 true。 14 6.6.Loop Loop Loop Loop 的属性 Loop 除了通用属性外,还有如下属性。 6.7.Task Task Task Task 的公共属性 转移条件除了 EL表达式外,还可以取值为一个字符串常 量“DEFAULT”,只有当其他所有条件的值都是 false 时,此 转移才被执行,否则此转移不被执行。 属性名称 属性说明 Condition 循环条件。是一个 EL表达式,如果此 EL表达式的计算 结果为 true,则此循环将被执行,否则不执行。 缺省值为空,为空时其值为 false(和 Transition 缺省情况 取值恰好相反)。 属性名称 属性说明 Duration 执行该 TaskInstance所需的时间。 TaskInstanceCreator 一个实现了 ITaskInstanceCreator 接口的类,负责创建任务 实例。 如果 Task 定义了该属性,则它具有最高优先级,覆盖 WorkflowProcess 的TaskInstanceCreator 属性 和 BasicTaskInstanceManager 中的缺省实现。一般情况下, Task 不需要定义该属性。 TaskInstanceRunner 一个实现了 ITaskInstanceRunner 接口的类,负责该 Task 实例的运行 。一般情况下无须定制这个类,采用 BasicTaskInstanceManager 的缺省实现即可。 如果 Task 定义了该属性,则它具有最高优先级,覆盖 WorkflowProcess 的相关的 TaskInstanceRunner 属性和 BasicTaskInstanceManager 中的缺省实现。 有些业务场景中需要动态创建子流程,此时需要在特定的 SubflowTask 上定义该属性。 TaskInstanceCompletio nEvaluator 一个实现了 ITaskInstanceCompletionEvaluator 接口的类, 负责检查 Task 实例是否可以结束 。 如果 Task 定义了该属性,则它具有最高优先级,覆盖 WorkflowProcess 的相关的 TaskInstanceCompletionEvaluator 属 性和 BasicTaskInstanceManager 中的缺省实现。 在某些业务场景中,例如 M:N 形式的会签,需要在 Task 上定义该属性。 LoopStrategy 控制 Task 在循环体中的行为特征。该属性有 3个取值, 含义如下: REDO 表示重做该 Task,对于 FormTask,其WorkItem 会 15 6.8.SubflowSubflowSubflowSubflow Task Task Task Task 的属性 子流程任务除了上述通用属性外,还有如下属性 6.9.ToolToolToolTool Task Task Task Task 的属性 6.10.FormFormFormForm Task Task Task Task 的属性 Form Task 除了通用属性外,还有如下属性 被自动分配给上一次完成该 Task 的操作者; SKIP表示该 Task仅执行一次,在循环的情况下不被执行 ; NONE 也表示重做 Task,对于 FormTask而言,其 WorkItem 的分配仍然通过解析 AssignmentHandler 完成,而不是分配给 上一次完成该 Task 的操作者。 属性名称 属性说明 Sub-Workflow 子流程的相关信息,如:子流程的 ID、Name、Display Name 等。 属性名称 属性说明 Execution 取值为 SYNCHR 或者 ASYNCHR,缺省为 SYNCHR SYNCHR 为同步执行对应 Application,ASYNCHR 为异 步执行对应的 Application Duration (目前的 engine 对此属性没有处理。) Application Task 执行的 application ,该 application handler 必须实现 org.fireflow.engine.taskinstance.IApplicationHandler 属性名称 属性说明 Performer 操作者 Assignment 任务分配策略,取值为 ANY 或ALL,默认值是 ANY ANY 表示任何一个操作员完成该其工作项则任务实例可 以结束。 ALL表示收到工作项的所有操作员都必须完成其工作项, 任务实例才能结束,即会签。 Duration 任务完成期限 Default View 缺省视图,取值为 EditForm、ViewForm、ListForm,缺省 值为 ViewForm Edit Form 可编辑的表单信息,在此填入相关的 url 可以用于链接业 务表单 View Form 只读表单信息 List Form 列表表单信息。 16 7.附, 预览版 文档中 关于 当前 流程模 型不 足之处 的描 述 附, 预览版 文档中 关于 当前 流程模 型不 足之处 的描 述 附, 预览版 文档中 关于 当前 流程模 型不 足之处 的描 述 附, 预览版 文档中 关于 当前 流程模 型不 足之处 的描 述 ———————— 从流 程建 模说起 从流 程建 模说起 从流 程建 模说起 从流 程建 模说起 我们以一个简单的案例讨论一下流程建模。 案例 7-1:某审批业务的业务流程如下,受理科接受用户的申请,并打印相关的回执; 受理后的业务资料移送给审批科进行审批;若审批通过,则继续移交给制证科制作相关的文 书和证件;最后业务材料由档案室归档。在这里暂不考虑审批不通过的情况,也不考虑文书 和证件发放的工作。 通常,我们有如下方法对这个业务进行建模。 第一种方法:我们可以用 UML 的活动图描述这个业务,如下。 UML 活动图从业务层次精确描述了这个案例,但是,这种建模一般只用在需求分析阶 段。因为现在还没有一个引擎可以去执行它,虽然他也可以转换为 xml 文件。因此,活动 图是概念层次的建模,离可执行的工作流建模有很大距离。 图ii-7-1 第二种方法:我们可以用工作流工具对这个业务进行建模。在这里,我用自己 2004 年 写的 Bestsolution Workflow Designer 建立的流程模型如下图。从图 ii-7-2 可以看出,用工作 流设计器建立的流程模型与 UML 活动图类似 。唯一的区别在于从图 ii-7-2 导出的流程定 义文件(xpdl1.0 格式)是从代码执行的角度描述业务的,从而比较方便开发出一个所谓的 工作流引擎来执行这个流程定义文件。 图ii-7-2 17 然而,WFMC 定义的工作流建模语言,以及其他和多工作流建模语言都没有回答一个 根本性的问题: 用这个建模语言定义的流程真的能执行吗?能正确无误地执行吗? 从我的经验看,xpdl 还缺乏逻辑严密的语义,在稍微复杂一点的情况下,它定义的流 程很难正确执行。 如果我们把“案例 7-1”所描述的审批系统分为两部分:工作流逻辑子系统和业务逻辑 子系统。那么“ii-7-2”建立的工作流模型完全没有描述出工作流逻辑子系统需要完成哪些 工作,业务逻辑子系统需要完成哪些工作;从这个意义上说,它和 “图ii-7-1”的UML 活 动图没有什么本质区别,都是很笼统的一个业务流程而已,只是 xml 文件的 DTD 有点不 同。 我个人认为,正是这种在两个子系统之间的职责的模糊与混乱导致工作流引擎无法进 行正确的计算。 下面,我想用第三种方法对“案例 7-1”进行建模。这种建模在本章节还停留在图示阶 段。目的是为了说明我所认为的工作流描述语言应该具备的特性。 我们把审批系统分成工作流逻辑子系统和业务逻辑子系统。工作流子系统的操作用圆圈 表示,业务逻辑子系统的操作用方框表示。如下图 ii-7-3。 系统的执行过程如下: 首先,工作流逻辑子系统启动一个新的业务流程实例,然后启动一个新的任务“受理”, 并将控制权交给业务逻辑子系统,由业务逻辑子系统完成受理工作。 第二步、受理完成后,控制权交给工作流逻辑子系统。由该子系统决定下一步的业务操 作。工作流逻辑子系统根据流程定义以及相关流程变量的计算和判断,得出下一步的工作是 “审批”,于是启动之,并将控制权交给业务逻辑子系统。 第三步、第四步以及后续步骤与之类似,直至最后工作流逻辑子系统计算得出流程实例 应该结束,于是结束该流程实例。 18 图ii-7-3 图ii-7-3 的建模似乎比图 ii-7-2 没有什么优越性,反而有点复杂,至少图形变得复杂了 。 但是我们如果考虑下面这种情况,那么图 ii-7-3 的建模的好处就非常明显了。在案例 7-1 中 , 我们没有考虑审批不通过的情况,现在把这种情况考虑进去,形成案例 7-2 案例7-27-27-27-2:在案例 3-1 的基础上考虑审批不通过的情况。如果审批不通过,则需要告知 申请人,然后结束业务流程。 如果我们用 xpdl 对案例 7-2 键模,则图形表示如下图 ii-7-4 图ii-7-4 19 图ii-7-4 的问题在于,业务逻辑和工作流逻辑叠加在一起,没有精确地指出到底在什么 时候,由谁来决定审批之后的操作。我们可以理解为由“审批”这个环节决定后续环节是“制 证”还是“告知申请人审核不通过”,这种情况下审批环节既代表了业务操作,又代表了工 作流逻辑操作。当然,还有其他的执行方式,例如:计算连接“审批”到“制证”之间的狐 (xpdl 称之为转移)以及联结“审批”到“告知申请人审核不通过”之间的狐得值来决定 下一步的操作。 如果我们用第三种方法,也就是图 ii-7-3 中的方法对案例 7-2 进行建模,那么这个模型 的局部图形如下。在图 ii-7-5 中,工作流逻辑和业务逻辑分得非常清晰,审批之后执行哪个 业务操作是由工作流逻辑子系统的一个“操作”决定的。业务逻辑子系统中的“审批”操作 仅仅负责完成业务特定的逻辑,其他的与之无关。 图ii-7-5 20 III.第三部分 Engine Engine Engine Engine 的设计及其扩展 1.Engine Engine Engine Engine 的设 计理 念 的设 计理 念 的设 计理 念 的设 计理 念 我们常常在投标书或者需求说明书里面吹嘘说:XYZ 系统具有良好的扩展性。这种话 说的多了也就变成了说说而已,很少人再往深层次追问一下: XYZ 系统如何做到“可扩展 性”?在我看来,任何系统要做到可扩展性其关键在于良好的设计。设计首先是发现系统面 对的各种问题,然后对每个问题作出合理的技术决策。不同的系统,设计的具体活动可能不 太一样,在 FireWorkflow Engine 的中,主要解决如下问题。 1.1.合理的职责划分 工作流子系统和业务子系统的功能界限在哪里?这是一个非常头疼的问题。很多朋友问 我FireWorkflow Engine 中有没有用户管理?有没有表单设计器?我的回答是:整个系统需 要用户管理,可以有表单设计器;但是,工作流引擎中这些功能都没有。 工作流引擎的“本职工作”就是按照流程定义的流转逻辑驱动流程实例一个环节一个 环节地往下执行。 就这么简单?是的,就是这么简单。正是由于工作流引擎的工作很“简单”,所以工作 流子系统和业务子系统才存在着很多的接口,做好了这些接口的话,工作流引擎才展现其扩 展性。如果工作流引擎把很多事情包办了,我觉得它反而没有什么价值了。 虽然说工作流引擎的职责用一句话就说清楚了,但是它的实现并不是很简单。引擎本身 需要完成如下工作。 1、正确的流转算法 2、流程实例各种中间状态的持久化 3、流转条件的解析 4、流程日历 5、流程定义文件的管理 6、BeanFactory 创建各种服务需要用到的 Bean 7、流程引擎对外 API 我暂时认为,工作引擎的这些职责分类是比较合理和完备的。 Fire Workflow 把这些工 作分别委派给不同的服务去实现。 1.2.足够的扩展性 作为一个通用的工作流引擎,它面对的应用环境是各种各样,引擎不可能将所有的情况 统统罗列进去。比较好的方案是:通过接口搭建起 Engine 运行的“骨架”,并且对每个接口 提供缺省实现,在实际应用中如果缺省实现不满足需求可以扩展或者替换之。 21 Fire Workflow 将引擎本身的职责划分成上节所述的 7个部分,正是这种思路。上述 7 个部分的具体实现都是可以替换的。 1.3.方便集成到业务系统中 我们常常有这样的感觉,某些开源产品很好、很强大,但是就是难用!难用的原因可能 很多,但是我觉得一个很重要的原因是:开发者没有把开源产品和其“宿主”当作一个整体 去考虑,往往以开源产品为中心,要求宿主迁就它。而我们宿主系统往往需要用到很多第三 方产品,如果这也迁就那也迁就,最终结果是系统极难维护。 Fire Workflow 在这方面考虑的比较多,尽量做到“简洁而不简单”。Fire Workflow 引擎 是什么?总结起来就是一句话:2个Jar 包7张表,Jar 包扔到/WEB-INF/lib中,表创建到数 据库中就可以了。 当然 Fire Workflow 还有一个配置文件,考虑到现在非常多的 J2EE 系统都用 Spring 做 容器,所以,这个配置文件也是 Spring 的,你只要把这个配置文件 import 到你的项目的主 配置中去即可。 如果你的系统没有用到 Spring,是否必须迁就 Fire Workflow 而引入 Spring 呢?不用! 在后面的第 4章会告诉你,不用Spring 也可以很好的把 Fire Workflow 集成到你的系统中去。 2.Engine Engine Engine Engine 的结构的结构的结构的结构 2.1.Engine Engine Engine Engine 总体结构 Fire workflow Engine 的结构如下图。 如图所示,Fire Workflow 把引擎的功能分解成很多的 Service,这些 Service 都“挂接” 在Engine 的“总线”org.fireflow.engine.RuntimeContext 上。下面逐一解释这些 Service。 � IWorkflowSession 这是业务代码调用 Engine API 的入口,通过 IWorkflowSession 你可以创建 IProcessInstance,获得相应的 ITaskInstance,IWorkItem 等等对象。 你可以 将 IWorkflowSession 类比 Jdbc 中的 connection。通过 Connection 可以获 得 Statement,ResultSet 等等对象。 那么在系统中如何取得 IWorkflowSession 对象呢? Fire workflow 缺省情况下是通过 Spring 将各种 Service组装(即注入)到RuntimeContext,所以首先要将 FireflowContext.xml 引入到你的系统中,这是一个 spring 配置文件;然后通过类似如下代码获得 IWorkflowSession。 RuntimeContext rtCtx = mySpringBeanFactory.getBean("runtimeContext"); IWorkflowSession workflowSession = rtCtx.getWorkflowSession(); 22 cd Fireflow Engine Fire Workflow Engine org.fireflow.engine.RuntimeContext «interface» IWorkflowSession «interface» IPersistenceService «interface» IDefinitionService «interface» ICalendarService «interface» IConditionResolver «interface» ITaskInstanceManager KenelManager Kenel + IActivityInstance + INetInstance + INodeInstance + ISynchronizerInstance + ITransitionInstance Engine API + IProcessInstance + ITaskInstance + IWorkItem Your Biz System Your Biz Database (include Fire Workflow tables) Get and use workflow session object 图iii-2.1-1 � KernelManager 即内核管理器。Fire Workflow 的内核实际上是“工作流逻辑网”的执行机。工作流逻 辑网的相关概念见“第二部分 Fire workflow 工作流模型”,它是Petri Net 改造后的新的网结 构。 内核管理器的职责是根据流程定义文件创建和维护工作流逻辑网实例 INetInstance。 INetInstance在Petri Net 定义的执行规则下一步一步驱动流程实例执行。 � IPersistenceService 存储服务。Fire Workflow 缺省情况下使用 hibernate 进行数据库存取。如果你的系统不 是使用 hibernate,则重新实现该类,然后通过修改 FireflowContext.xml 配置,将你的存储服 务实现类注入到 RuntimeContext 中。 � IDefinitionService 流程定义服务。该服务负责根据流程 ID和版本号获得流程定义对象 WorkflowDefinition。从该对象可以获得 WorkflowProcess,即真正的流程定义。 23 Fire Workflow 缺省提供两种实现,一种实现是 org.fireflow.engine.definition.DefinitionService4FileSystem。该实现类从文件系统中获得流程 定义对象,在开发阶段使用该类比较方便。该类从 class path 中读取流程定义文件,因此你 在项目中设计流程时,推荐将流程定义文件置于/src 或者其子目录中。 DefinitionService4FileSystem 忽略流程的版本,直接读取当前的流程定义文件。 另一个实现是 org.fireflow.engine.definition.DefinitionService4DBMS 。该实现类从数据库 表T_FF_DF_WORKFLOWDEF 中获得流程定义文件。因为表 T_FF_DF_WORKFLOWDEF 中保存了流程的版本号,因此该类在产品真正运行时使用。 在FireflowContext.xml 修改相关的配置即可实现这两个类的切换。 � ICalendarService 日历服务。日历服务负责获取系统时间和计算 TaskInstance的ExpiredDate。缺省实 现 中 , 系统时间是返回 new Date(),也只考虑了周六、周日作为节假日的情况。你可以扩展该类获 取数据库时间作为系统时间,增加节假日配置。 � IConditionResolver 转移条件解析器,用于计算转移条件中的 EL表达式的值。 � ITaskInstanceManager 任务管理器,负责创建任务实例,缺省实现是 BasicTaskInstanceManager。 � BeanFactory 在1.0 中,增加了一个新的服务: bean factory。该服务负责创建各种 javabean,例如: 进行工作项分派的时候,需要获得 AssignmentHandler 的实例;执行 ToolTask 的时候,需要 获得 ApplicationHandler 的实例,等等。这些实例都是由 bean factory 创建的。在 1.0 中,这 个bean factory 的缺省实现是 spring ioc 容器,即,将创建 bean 的工作委派给了 spring 。 注意:Bean factory 在上述的 Engine 结构图中没有画出来。 Fire workflow1.0 的引擎实 际结构如下,增加了 bean factory。 图iii-2.1-2 3.EngineEngineEngineEngine APIAPIAPIAPI 对象 对象 对象 对象 你的业务代码在调用 Fire Workflow Engine 时主要和 Engine 的API对象打交道,如下图 。 IProcessInstance是流程实例,包含多个任务实例 ITaskInstance,每个任务实例可以包含 0个或者多个 IWorkItem。 24 cd Engine API «interface» IProcessInstance «interface» ITaskInstance «interface» IWorkItem 0..11 0..11 图iii-3-1 3.1.IProcessInstanceIProcessInstanceIProcessInstanceIProcessInstance 流程被创建后生成一个流程实例对象,存储在 T_FF_RT_ProcessInstance表中。一般情 况下一个流程实例代表一单具体的业务。 流程实例的生命周期是“创建-->运行-->结束”,其状态转移图如下 图iii-3.1-1 流程实例的状态从总体上分为“活动状态(Alive States)”和“死亡状态(Dead States)”, Initialized 、Running 属于活动状态,Canceled、Completed 属于死亡状态。 流程实例对象创建后,进入 Initialized 状态,调用 IProcessInstance.run()方法后,流程实 例开始运行,并进入 Running 状态。在活动状态的下,调用 IProcessInstance.abort()进入 Canceled 状态。流程实例进入 Completed 状态并不是调用其某个方法实现的,而是流程实例 运行结束后自动进入的,判断流程实例结束的标准是当前已经没有活动的 token。 在Fire workflow 中 ,“挂起”作为一个特殊的标志,在活动状态的流程实例可以被挂起 。 AliveAliveAliveAlive StatesStatesStatesStates DeadDeadDeadDead StatesStatesStatesStates Initialized (0) Running (1) Completed (7) Canceled (9) new ProcessInstance() IProcessInstance.run() IProcessInstance.abort() 如果当前无活 动的 token 则结束 25 已经挂起的流程实例不可以进行任何流程操作(如签收工作项、结束工作项等)。被挂起的 流程实例可以被恢复,恢复后可以进行正常的流程操作。 流程实例的创建是通过 WorkflowSession 完成的,代码如下 IProcessInstance procInst = workflowSession .createProcessInstance(ProcessName, creatorName); 通过调用流程实例的 run()方法进入运行状态,流程实例根据流程定义文件的逻辑持续 向前推进,在执行过程中如果遇到 ToolTask、FormTask、SubflowTask,其行为特征如下。 当遇到 ToolTask 时,引擎同步调用 ToolTask 的ApplicationHandler,调用返回后流程实 例继续往下推进。 当遇到 FormTask 时,引擎创建相应的工作项,与该节点对应的流程分支进入等待状态, 但是流程的其他分支可以继续执行。当业务操作员完成业务,并且结束工作项后,触发该流 程分支继续往下推进。 当遇到 SubflowTask 时,引擎创建相应的子流程实例,与该节点对应的流程分支进入等 待状态,但是流程的其他分支可以继续执行。当子流程实例结束后,会触发父流程分支继续 往下推进。 当流程实例执行遇到 EndNode 时,检测自身是否有活动的 Token,如果没有活动的节点 , 则结束整个流程实例。 3.2.ITaskInstanceITaskInstanceITaskInstanceITaskInstance 当流程实例流转到某个 Activity 时,为Activity 中的每个 Task 创建相应的 TaskInstance, 然后运行这些新创建的 TaskInstance,最后结束 TaskInstance。TaskInstance 存储在 T_FF_RT_TaskInstance表中。TaskInstance的状态转移图如下。 图iii-3.2-1 和流程实例一样,任务实例(TaskInstance)的状态从总体上分为“活动状态(Alive States)” 和“死亡状态(Dead States)”,Initialized 、Running 属于活动状态,Canceled、Completed 属于 AliveAliveAliveAlive StatesStatesStatesStates DeadDeadDeadDead StatesStatesStatesStates Initialized (0) Running (1) Completed (7)Canceled (9) ITaskInstanceCreator.createTaskInstance() ITaskInstanceRunner.run() IProcessInstance.abort() TaskInstanceManager. completeTaskInstance() 26 死亡状态。 TaskInstance对象创建后,进入 Initialized 状态,调用 ITaskInstanceRunner.run()方法后, TaskInstance开始运行,并进入 Running 状态。当在 TaskInstance的工作项(WorkItem)上调用 complete()方法时,会触发 TaskInstanceManager 调用 completeTaskInstance()方法,使之进入 Completed 状态。在对应的流程实例上调用 IProcessInstance.abort(),会使该实例的所有 TaskInstance进入 Canceled 状态。 TaskInstance 的“挂起”也作为一个特殊的标志,在活动状态的任务实例可以被挂起。 已经挂起的任务实例,其所属的 WorkItem 不可以进行任何流程操作(如签收工作项、结束 工作项等)。被挂起的任务实例可以被恢复,恢复后可以进行正常的流程操作。 3.3.WorkItemWorkItemWorkItemWorkItem WorkItem 状态转移图 图iii-3.3-1 和流程实例一样,WorkItem 的状态从总体上分为“活动状态(Alive States)”和“死亡状 态(Dead States)”,Initialized 、Running 属于活动状态,Canceled、Completed 属于死亡状态。 WorkItem 对象创建后,进入 Initialized 状态,调用 IWorkItem.claim()方法后,IWorkItem 进入 Running 状态。当在 WorkItem 上调用 complete()方法时,会触发 TaskInstanceManager 调用 completeWorkItem()方法, 使之 进 入 Completed 状态。在对应的流程实例上调用 IProcessInstance.abort(),会使该实例的所有 IWorkItem 进入 Canceled 状态。在 WorkItem 调 用reject、reasignTo 等方法,可以使得 workItem 进入 canceled 状态。 当WorkItem 所属的 TaskInstance被挂起时,该WorkItem 不可以进行任何流程操作(如 签收工作项、结束工作项等)。被挂起的任务实例可以被恢复,恢复后可以进行正常的流程 DeadDeadDeadDead StatesStatesStatesStates AliveAliveAliveAlive StatesStatesStatesStatesItaskInstanceManager.createWorkItem Running (1) claimInitialized (0) reject reject, withdraw complete, jumpTo, loopTo Completed (7) Canceled (9) 27 操作。 4.WorkflowSessionWorkflowSessionWorkflowSessionWorkflowSession 4.1.WorkflowSession WorkflowSession WorkflowSession WorkflowSession 的作用 WorkflowSession 的作用有两点,首先它作为获取工作流 API 对象(ProcessInstance、 TaskInstance、WorkItem)的入口。这个设计类似 JDBC 中的 connection,通过connection 你可 以创建 Statement ,进而可以获得 ResultSet。 另外 WorkflowSession 可以作为一次完整的流程操作的共享信息的总线。我们以如下场 景说明 WorkflowSession 的作用。 如下图,当操作员 ActorA 完成 Task1 后,他想指定 Task2 的操作员,假设指定为 ActorB; 并且 Task1 完成后需要调用一个事件监听器 org.fireflow.exampel.test.Task1EventListener,该 事件监听器需要获取业务数据 x,并对 x做一些逻辑操作。 图iii-4.1-1 上述场景中,有两个问题需要解决。 1) ActorA 指定的 Task2 的操作者 ActorB 存储在哪里?通过什么机制使得工作流引擎在 创建 Task2 的WorkItem 时知晓这件事情。 2) Task1EventListner 如何获得 x的值? 上述信息都可以通过 WorkflowSession 传递,具体的,是放在 ActorA 结束 Task1 的相关 代码中,入下。 IWorkflowSession workflowSession = runtimeContext.getWorkflowSession(); //指定 Task2 的操作者为 ActorB DynamicAssignmentHandler dynamicAssignmentHandler = new DynamicAssignmentHandler (); 28 List actorIdsList = new ArrayList(); actorIdsList.add("ActorB"); dynamicAssignmentHandler.setActorIdsList(actorIdsList); workflowSessionworkflowSessionworkflowSessionworkflowSession .setDynamicAssigmentHandler(dynamicAssignmentHandler.setDynamicAssigmentHandler(dynamicAssignmentHandler.setDynamicAssigmentHandler(dynamicAssignmentHandler.setDynamicAssigmentHandler(dynamicAssignmentHandler );););); //设置 x的值 workflowSession.setAttribute("x",workflowSession.setAttribute("x",workflowSession.setAttribute("x",workflowSession.setAttribute("x", theValueOfX);theValueOfX);theValueOfX);theValueOfX); //结束 ActorA 在Task1 的工作项 workflowSession.completeWorkItem(theWorkItemIdOfTask1);workflowSession.completeWorkItem(theWorkItemIdOfTask1);workflowSession.completeWorkItem(theWorkItemIdOfTask1);workflowSession.completeWorkItem(theWorkItemIdOfTask1); 调用 workflowSession.completeWorkItem(theWorkItemIdOfTask1)会触发工作流引擎将流 程实例往下推进。在创建 Task2 的WorkItem 时,引擎发现当前的 WorkflowSession 中存在 DynamicAssignmentHandler 于是应用之,并忽略 Task2 上预先定义的 AssignmentHandler。 当然,你实际上也可以把 DynamicAssignmentHandler存储在 WorkflowSession 的attribute 中,然后为 Task2 定义一个特殊的 AssignmentHandler,该AssigmentHandler 从attribute 中取 出DynamicAssignmentHandler 对象,然后应用该 DynamicAssignmentHandler。如下 //将dynamicAssignmentHandler 存储在 attribute 中,而不是特殊地存放起来 workflowSessionworkflowSessionworkflowSessionworkflowSession .setDynamicAssigmentHandler(dynamicAssignmentHandler.setDynamicAssigmentHandler(dynamicAssignmentHandler.setDynamicAssigmentHandler(dynamicAssignmentHandler.setDynamicAssigmentHandler(dynamicAssignmentHandler );););); workflowSession.setAttribute("assgiment4Task2",workflowSession.setAttribute("assgiment4Task2",workflowSession.setAttribute("assgiment4Task2",workflowSession.setAttribute("assgiment4Task2", dynamicAssignmentHandlerdynamicAssignmentHandlerdynamicAssignmentHandlerdynamicAssignmentHandler );););); 然后,我们为 Task2 定义一个特殊的 AssigmentHandler ,假设其名称为 org.fireflow.example.test.AssignmentHander4Task2。其实现如下 public void assign(IAssignable asignable, String performerName){ TaskInstance taskInstance = (TaskInstance) asignable; IWorkflowSession workflowSession = taskInstance.getCurrentWorkflowSession(); //从workflowsession attribute 中取得 dynamicAssignementHandler DynamicAssignmentHandlerDynamicAssignmentHandlerDynamicAssignmentHandlerDynamicAssignmentHandler dynamicHandlerdynamicHandlerdynamicHandlerdynamicHandler ==== workflowSession.getAttribute("workflowSession.getAttribute("workflowSession.getAttribute("workflowSession.getAttribute("assgiment4Task2assgiment4Task2assgiment4Task2assgiment4Task2");");");"); ////////将任务分派工作委派给 dynamicHandler dynamicHandlerdynamicHandlerdynamicHandlerdynamicHandler .assign(workflowSession,.assign(workflowSession,.assign(workflowSession,.assign(workflowSession, processInstance,processInstance,processInstance,processInstance, asignable,performerName);asignable,performerName);asignable,performerName);asignable,performerName); } 那么在Task1EventListener中如何获取x的值呢?很简单,从WorkflowSession 的attribute 中取。代码如下 public void onTaskInstanceEventFired(TaskInstanceEvent e)throws EngineException{ IWorkflowSession workflowSession = e.getWorkflowSession(); ObjectObjectObjectObject xxxx ==== workflowSession.getAttribute("x");workflowSession.getAttribute("x");workflowSession.getAttribute("x");workflowSession.getAttribute("x"); } 特别注意:由于 WorkflowSession 是贯穿一个工作流调用的信息共享总线,所以,不能 29 够在事件监听器等扩展代码中重新通过 runtimeContext.getWorkflowSession()获得新 的 WorkflowSession 实例。 4.2.WorkflowSession WorkflowSession WorkflowSession WorkflowSession 是非线成安全的 WorkflowSession 对象是轻量级的、有状态的,而且是线程不安全的对象,所以不能在 多个线程建共享。 4.3.WorkflowSession WorkflowSession WorkflowSession WorkflowSession 会保存在 API API API API 对象中 (似乎是一个不太好的设计) 如果某个 WorkItem 通过如下方式获得 IWorkItem theWorkItem = workflowSession_1.findWorkItemById( workItemId ); 则theWorkItem 对象会自动将 workflowSession_1 保存在其一个成员变量中。这样就会 在某些情况下给“当前 WorkflwoSession”造成一些歧义。例如,如果你把 theWorkItem 保 存在 http session 中,并在下一个 http 请求中取出来,并执行如下代码。 IWorkflowSession workflowSession_2 = runtimeContext.getWorkflowSession(); workflowSession_2.setAttribute("x",theValueOfX); IWorkItem theWorkItem = (IWorkItem )httpSession.getAttribute("theWorkItem"); theWorkItem.complete(); 在这 种 情 况 下 , theWorkItem.complete() 后触发的流程运算中,所使用的“当前 WorkflwoSession”是workflowSession_1,而不是 workflowSession_2。所以在系统中尽量用 如下方法。 IWorkflowSession workflowSession_2 = runtimeContext.getWorkflowSession(); workflowSession_2.setAttribute("x",theValueOfX); IWorkItem theWorkItem = httpSession.getAttribute("theWorkItem"); theWorkItem.complete(); workflwoSession_2.completeWorkItem(theWorkItem.getId());workflwoSession_2.completeWorkItem(theWorkItem.getId());workflwoSession_2.completeWorkItem(theWorkItem.getId());workflwoSession_2.completeWorkItem(theWorkItem.getId()); 5.TaskInstanceManagerTaskInstanceManagerTaskInstanceManagerTaskInstanceManager TaskInstanceManager 负责 TaskInstance的创建、运行和结束。在Fire Workflow 中,有3 种类型的 Task,他们的生命周期行为(创建、运行、结束)也必有差异。 Fire Workflow 定 义了 3个接口,将具体的创建、运行、结束委派给这 3个接口的实现类。这 3个接口称为 TaskInstance的生命周期接口,如下: ITaskInstanceCreator:创建 task instance。 ITaskInstanceRunner:运行 task instance。 30 ITaskInstanceCompletionEvaluator:判断 task instance是否可以结束。 对于 FormTask、ToolTask、SubflowTask,Fire workflow 提供各接口的了缺省实现。每 个流程,甚至每个 Task 都可以对上述 3个接口进行定制。缺省生命周期实现类及接口的关 系如下图。TaskInstanceManager 通过管理 TaskInstance生命周期接口来达到管理 TaskInstance 的目的。 图iii-5-1 6.BeanFactoryBeanFactoryBeanFactoryBeanFactory ServiceServiceServiceService 工作流引擎在运行过程中,常常需要取得 AssignmentHandler、ApplicationHandler 的实 例。在预览版本中,创建实例的方法比较简单,就是用 class 得newInstance操作,这个方法 显然不够健壮,不灵活。在1.0 中,增加了一个服务叫做 BeanFactory,由这个服务根据 bean 的name 创建 bean 的实例,其接口定义如下。 图iii-6-1 1.0 中,bean factory 的缺省实现是 org.fireflow.engine.beanfactory.SpringBeanFactory,以 Spring ioc 容器作为 BeanFactory,如下图 31 图iii-6-2 使用外部容器作为 bean factory 使得 Engine 和业务代码之间的集成更加优雅。以与用 户管理系统的集成为例,假设某 Task 的AssignmentHandler 类名为 org.fireflow.example. Workflowextension.RoleBasedAssignmentHandler,则流程定义文件片断如下。 图iii-6-3 工作流 Engine 需要通过 bean factory 服务获得 org.fireflow.example.workflowextension. RoleBased AssignmentHandler 的实例。在使用 SpringBeanFactory 的情况下,该bean 的配置 如下图。从图中可以看出, RoleBased AssignmentHandler 可以很方便的引用到业务系统的 UserDAO 实例。业务代码和工作流引擎优雅地集成在一起了! 图iii-6-4 7.PersistenceServicePersistenceServicePersistenceServicePersistenceService PersistenceService 统一负责引擎运行过程中各种实例对象的存取工作,其地位相当于 DAO,如下图。 PersistenceService 的缺省实现采用的是 Hibernate ,实现类是 org.fireflow. engine. persistence. Hibernate. PersistenceServiceHibernateImpl。和所有业务代码 DAO 一 样 , 该类也扩展自 org.springframework.orm.hibernate3.support.HibernateDaoSupport。 32 图iii-7-1 因此,在工作流引擎数据存储与业务逻辑数据存储事务一致性方面,可以做到非常好的 统一。 如果你的系统采用的是申明式事务,将事务申明添加到 PersistenceServiceHibernateImpl 上即可以实现业务操作和流程操作在同一个事务中。 如果你的系统采用的是编码实现的事务,只要将调用流程引擎的代码和和业务逻辑代码 放在同一个事务中即可。在 Fire Workflow Example 中,采用的是编码实现的事务,可以参 考。 PersistenceService只提供了引擎必要的数据存取方法,如果你的应用需要对流程实例做 复杂查询,则需要自行实现,实现代码放在业务 DAO 中。FireWorkflow 的7张表并不是工 作流引擎“私有” 的,从查询的角度,你完全可以把它当作业务表对待,怎么查都可以。 在确省实现中,mapping 文件已经装载到系统中了,在任何一个 DAO 中都可以编写查询工 作流实例对象的方法。 8.CalendarServiceCalendarServiceCalendarServiceCalendarService 日历服务。日历服务负责获取系统时间和计算 TaskInstance的ExpiredDate。缺省实 现 中 , 系统时间是返回 new Date(),也只考虑了周六、周日作为节假日的情况。你可以扩展该类获 取数据库时间作为系统时间,增加节假日配置。 该接口的定义如下,一般情况下你只需要扩展 isBusinessDay(Date d)和getSysDate()两个 方法。dateAfter 方法在计算时,如果 duration 参数指定的是工作时间,则会自动将非工作日 除去。 业务逻辑 java bean 工作流逻辑 业务表 DAO PersistenceServiceHibe rnateImpl 数据库(包含业务表和工作流表) 调用 DAO 层 逻辑层 33 图iii-8-1 9.DefinitionServiceDefinitionServiceDefinitionServiceDefinitionService 流程定义服务为引擎提供流程定义文件。在 Fire Workflow 中,缺省提供了两种定义文 件的存储方案。 1) 存储在表 T_FF_DF_WORKFLOWDEF 中。存储在表中的流程定义可以进行版本控 制,适合于生产环境。这种方式的实现类是 org.fireflow.engine.definition. DefinitionService4DBMS 。 2) 直接存储在文件系统中。这种存储方式,流程定义文件没有版本控制,但是比较简 便,适合于开发调试环境。这种方案的实现类是 org.fireflow.engine.definition. DefinitionService4FileSystem。 两种流程定义服务通过配置 spring 文件 FireflowContext.xml 进行切换,如下图,只要 设置 RuntimeContext 对的 definitionService 引用正确的 DefinitionService Bean即可。 34 图iii-9-1 10.ConditionResolverConditionResolverConditionResolverConditionResolver ConditionResolver 用来计算转移条件表达式的值。 缺省实现采用 Appache commons 的Java Expression Language (JEXL)。其语法请参阅 http://commons.apache.org/jexl/reference/syntax.html。 用于转移条件的表达式一般都是比较简单的布尔表达式,Fire workflow 规定表达式中的 变量必须为流程变量,而且是大小写敏感的。如果流程变量没有找到,则表达式计算结果默 认为 false。 11.Engine Engine Engine Engine 各种 扩展 各种 扩展 各种 扩展 各种 扩展 11.1.RuntimeContextRuntimeContextRuntimeContextRuntimeContext FactoryFactoryFactoryFactory RuntimeContext 作为引擎的总入口,摆在我们面前的首要问题是:业务代码如何获得 RuntimeContext 实例。在缺省实现中,利用 Spring 容器“组装”并返回 RuntimeContext 的 实例。 如果你的系统不是采用 Spring 作为 IOC容器,而其他的 IOC容器(例如 Jboss 的Seam), 我想“组装”RuntimeContext 也是一件很简单的事情。唯一的要求是,RuntimeContext 必须 是signleton 的。 如果你什么容器都没有采用,那么你也可利用 org.fireflow.engine.RuntimeContextFactory 获得 RuntimeContext。用RuntimeContextFactory 的一个不足之处是,你必须将引擎各种服务 的实现类硬编码在这个 Factory 中,尤其对于要经常切换 IDefinitionService 的两个实现类很 不方便。 11.2.数据持久化服务 持久化服务是最可能被替换的一个服务之一,接口各方法的具体说明见 API 文档。 11.3.日历服务 日历服务是另外一个必须扩展的服务。接口各方法的具体说明见 API 文档。 11.4.任务实例管理器 在1.0 版本中,BasicTaskInstanceManager 本身不需要扩展,你可以订制 TaskInstance的 各种生命周期接口来控制 TaskInstance的行为。 35 11.5.流程定义服务 流程定义服务一般情况下不需要重新实现,只是在两个实现类 DefinitionService4FileSystem 和DefinitionService4DBMS 之间切换,前者一般用户开发环境, 后者用于生产环境。 36 IV.第四部分 FireWorkflow FireWorkflow FireWorkflow FireWorkflow 应用 1.通过 流程 设计器 和模 拟器 快速了 解 通过 流程 设计器 和模 拟器 快速了 解 通过 流程 设计器 和模 拟器 快速了 解 通过 流程 设计器 和模 拟器 快速了 解 FireFireFireFire WorkflowWorkflowWorkflowWorkflow 1.1.设计器的安装 1.1.1. EclipseEclipseEclipseEclipse设计器插件安 装 设计器插件安 装 设计器插件安 装 设计器插件安 装 1、Eclipse 设计器基于 GEF(Graphic Edite Framework),因此确保你的 eclipse IDE 已经 安装了 GEF 3.3.2 或者以上版本,如下 图iv-1.1.1-1。 图iv-1.1.1-1 2、将流程设计器插件包 FireflowDesigner4Eclipse_x.x.x.jar 拷贝到 Eclipse 的Plugins 目 录下。 3、安装成功后,在File->New->Others 对话框中会有 Fire workflow 的文件类型。如图iv- 1.1.1-2 图iv-1.1.1-2 37 1.1.2. NetBeansNetBeansNetBeansNetBeans设计器插件安 装 设计器插件安 装 设计器插件安 装 设计器插件安 装 注:截止 2009-01-26 日,NetBeans 尚有不少 bug 没有修正。:( 1、首先将 FireflowDesigner_Plugin_for_Netbeans.zip 解压缩,到一个临时目录。然后打 开netbeans 的插件管理器(Tools->Plugins),选择"Downloaded"Tab 页面,如下图 iv-1.1.2-1: 图iv-1.1.2-1 2、在该页面中,通过按钮“Add Plugins...”(添加插件按钮)将上一步解压的.nbm 文件 load 进来,如上图图 iv-1.1.2-1。 3、在上图图 iv-1.1.2-1 界面中点击“install”(安装按钮),将3个nbm 安装到 netbeans 中。 4、安装完毕后,重新启动 netbeans,可以在已安装的插件列表中看到 Fireflow 插 件 。 如图 iv-1.1.2-2 图iv-1.1.2-2 38 1.2.设计器各部分介绍 此处以 Eclipse 插件为例,介绍设计器的组成部分,NetBeans 插件的界面布局大同小异 , 不赘述。 Fire Workflow 设计器是一个多页面编辑器,包含了设计器页面、模拟器页面和源代码 页面。 1.2.1. 设计器界面 设计器界面 设计器界面 设计器界面 图iv-1.2.1-1 如上图 iv-1.2.1-1,设计器主要由如下几部分构成:1、图形化的设计界面,2、工具栏, 3、Outline 页面,4、属性编辑页。 工具栏上各 工具按钮的作用如下: 工具栏按钮 含义 使得设计界面的鼠标处于“select”状态 创建一个开始节点,每个流程有且仅有一个开始节点 创建一个新的环节,该环节自动包含一个表单类型的任务 (Form Task) 创建一个新的环节,该环节自动包含一个 Tool 类型任务。 Tool 类型的任务用于调用 java 程序。 创建一个新的环节,该环节自动包含一个 Subflow 类型的 任务。 创建一个空的环节,该环节不包含任何任务。当然,可以 在任何环节中追加任意数量的任务。 创建一个同步器。Fire workflow 是基于 Petri Net 的一个工 作流系统,同步器节点相当于 Petri Net 中的 Place,任何两个 环节直接都必须有一个同步器节点。 39 1.2.2. 模拟器界面 模拟器界面 模拟器界面 模拟器界面 点击编辑区的“Simulator”Tab 页,切换到模拟器界面。模拟器包含三部分: 1、位于 上方的工具栏,2、图形化的流程执行进程视图( Process instance state viewer),3、模拟数 据视图,如果该视图没有打开,则通过 “window-->show view-->other...-->Fire workflow-- >Simulation Data View”打开。如下图 iv-1.2.2-1 图iv-1.2.2-1 模拟数据视图由 6个Tab 页面组成,分别是: Work Items:展示当前的工作项信息,该表格展示的信息正是工作项表 T_FF_RT_WORKITEM 中的数据。 Task Instances:展示当前的任务实例信息,该表格展示的信息正是任务实例表 T_FF_RT_TASKINSTANCE 中的数据。 Process Instances:展示当前的流程实例信息,该表格展示的信息正是流程实例表 T_FF_RT_PROCESSINSTANCE 中的数据。 Process Instance Variables:展示当前的流程实例变量信息,该表格展示的信息正是实例 变量表 T_FF_RT_PROCINST_VAR 中的数据。 Tokens:观察流程实例执行的过程中 token 的变化情况。 Trace Info :记录了流程实例每一步执行的详细信息。 开始节点和结束节点是两类特殊的同步器。 创建一个结束节点,一个流程可以有任意多个结束节点。 创建节点间的“转移”,即连接线 在两个 Synchronizer 之间创建“循环”连接线 40 模拟器工具栏含义如下表 1.2.3. XmlXmlXmlXml源代码界面 源代码界面 源代码界面 源代码界面 点击编辑区的“Source ”Tab 按钮,打开源代码编辑页,如下图。源代码编辑页只能查 看xml 格式的流程定义文件,不能编辑。 图iv-1.2.3-1 按钮图标 含义 创建一个新的流程实例 签收当前工作项 完成当前工作项,并且按照预定义的流程逻辑启动下一个环 节及其任务 完成当前工作项,并且跳转到指定的环节及其任务,即 “自 由流程”。 撤销(取回)刚才的操作 拒收工作项 终止当前流程实例 设置当前流程实例的流程变量。在 Fire workflow 中,流程变 量并不需要预先定义 DataField,可以在任何时候设置任意数量的 流程变量。 清空所有的模拟数据。 流程执行进程视图中当前流程的名称,在有子流程的情况下, 该下拉列表用于在视图中切换不同的流程。 弹出对话框,显示模拟器当前采用的 mode 和engine 的版本。 41 1.3.设计一个最简单的请假流程 我们以一个简单的请假审批流程为例,说明如何设计和模拟 Fire workflow 流程。 业务描述: 1)首先,申请人填写请假条, 2)请假条提交给部门经理审批; 3)审批完成后,申请人会收到一封邮件,说明审批是否通过; 1.3.1. 创建一个流程 定义文件 创建一个流程 定义文件 创建一个流程 定义文件 创建一个流程 定义文件 首先需要创建一个新的流程定义文件。从 File->New->Other...打开对话框,在 “Fire Workflow Wizards”目录中选择“Fire workflow process file”,点击 Next,如下图 iv-1.3.1-1: 图iv-1.3.1-1 在下一 Wizard 页面中输入新的流程定义文件的名称“LeaveApplicationProcess.xml”,系 统默认将“LeaveApplicationProcess”作为新流程的流程名称。系统为新创建的流程定义文 件打开流程编辑界面,如下图 iv-1.3.1-2: 图iv-1.3.1-2 42 1.3.2. 定义流程变量 定义流程变量 定义流程变量 定义流程变量 流程变量一般是指影响流程流转路径的一些业务数据。例如,在本案例中,如请假天数 、 最终是否准假的决定等。 leaveDays :请假天数,Integer 类型,缺省值 1。 approvalFlag:审批标志,Boolean 类型,true表示审批同意,false 表示审批不同意。缺 省值 false。 在设计器的 Outline Pane 中,右击 "Data Fields"节点,在弹出菜单上选择" Add Data Field" 可以增加流程变量定义。本案例定义好的流程变量如下图 iv-1.3.2-1。 图iv-1.3.2-1 1.3.3. 定义申请环节 定义申请环节 定义申请环节 定义申请环节 1.3.3.1. 创建Activity Activity Activity Activity 以及TaskTaskTaskTask 根据的业务场景,我们首先增加“申请 ”环节。在流程设计器工具栏上选择 ,然后在设计界面点击鼠标左键。流程中新增了一个名称为 “Activity1”的环 节,同时里面自动增加了一个名称为 Task1 的Form 类型的任务。如下图 iv-1.3.3-1: 图iv-1.3.3-1 43 选中Activity1 ,在属性界面中将其 Name 属性的值改成 “Fulfill_The_ApplicationForm_Activity”,将其 DisplayName 属性的值改成“申请”。如下图 图iv-1.3.3-2 选中Task1,在属性界面中将其Name属性的值改成“Fulfill_The_ApplicationForm_Task”, 将其 DiplayName 的值也改成“填写请假单”。修改后如下图 图iv-1.3.3-3 1.3.3.2. 定义performerperformerperformerperformer 对于 Form 类型的 Task,一定要指定其操作者(Performer)属性。在属性页的 Performer 属性项中点击其按钮,打开Performer 编辑界面,如下图。我们将 Performer 的Name 设置为 “Self ”,DisplayName 设置为“当前用户”。Assignment Handler 是一个实现了 org.fireflow.engine.taskinstance.IAssignmentHandler 接口的任务分配处理类,任务分配的实际 工作由该类完成。录入一个名称为 org.fireflow.example.leaveapplication.workflowextension.CurrentUserAssignmentHandler 的类, 该实现类将新创建的工作项分配给当前用户,在我们这个实例中也就是分配个请假申请人。 操作者属性设置完成后,界面如下 44 图iv-1.3.3-4 1.3.3.3. 定义表单信息 对于 FormTask,一般需要和一个业务表单相关联。 Fire workflow 不提供表单设计器, engine 也不处理与表单相关的任何逻辑;但是,Fire workflow 流程定义文件提供一些属性方 便业务系统将表单“挂接”到流程中。FormTask 都有如下 4个属性来记录表单信息。 EditEditEditEdit FormFormFormForm:填写录入表单的 URL 等信息,Fire workflow Engine 不会处理这个信息,所 以URL 的格式只要你的业务系统自己能够理解就可以了。 ViewViewViewView FormFormFormForm:填写只读表单的 URL 等信息, Fire workflow Engine 不会处理这个信息, 所以 URL 的格式只要你的业务系统自己能够理解就可以了。 ListListListList FormFormFormForm:填写列表表单的 URL 等信息,Fire workflow Engine 不会处理这个信息,所 以URL 的格式只要你的业务系统自己能够理解就可以了。 DefaultDefaultDefaultDefault ViewViewViewView:缺省表单,可选值是 Edit Form,View Form,List Form。至于如何使用 Default View 这个属性,则由业务系统决定。 请假申请流程的表单定义信息如下图 iv-1.3.3-5 图iv-1.3.3-5 45 1.3.4. 部门经理审批环节部门经理审批环节部门经理审批环节部门经理审批环节 具体操作方法和申请环节类似,不同之处在于 Task 的Performer 和EditForm 属性不一 样。设置 Task 的Assignment Handler 是org.fireflow.example.leaveapplication. Workflowextension. RoleDepartmentBased AssignmentHandler,这个类要根据角色和部门信息 来分配工作项,使得请假申请流转到各自部门的部门经理那里。 此Task 的EidtForm 的URL 是/org/fireflow/example/leaveapplication/bizpages/ ApproveLeaveApplication.jsp。 定义好部门经理审批环节后,流程图如下 图iv-1.3.4-1 1.3.5. 将审批结果邮件通知申请人将审批结果邮件通知申请人将审批结果邮件通知申请人将审批结果邮件通知申请人 显然,将审批结果通知申请人是系统自动完成的,在 Fire Workflow 中ToolTask 负责调 用后台程序完成这种自动化的工作。 1.3.5.1. 创建ToolTaskToolTaskToolTaskToolTask 在工具栏上点击 创建一个附带 ToolTask 的环节,设置 Activity 的Name 和 DisplayName 分别为 Send_Email_Activity 、发送邮件,设置该 ToolTask 的Name 和 DisplayName 分别为 Send_Email_Task、邮件通知申请人审批结果。设置好后如下图: 46 图iv-1.3.5-1 1.3.5.2. 定义ToolTaskToolTaskToolTaskToolTask所调用的后台程序 在“邮件通知申请人审批结果”这个 ToolTask 的Concrete Attribute 页面设置 Application 信息,如下图。 图iv-1.3.5-2 1.3.6. 顺序连接各个 环节 顺序连接各个 环节 顺序连接各个 环节 顺序连接各个 环节 本示例是一个简单的顺序流程,用 transition 连接各个环节以及起始节点和结束节点即 完成整个流程的设计;在连接两个环节时,设计器自动在中间增加 Synchronizer。如下图 图iv-1.3.6-1 47 1.4.模拟流程的执行 1.4.1. 请假申请人的 流程操作 请假申请人的 流程操作 请假申请人的 流程操作 请假申请人的 流程操作 在实际的业务中,流程首个环节的操作者一般是感觉不到流程的存在的,他只管录入业 务数据存盘即可,启动流程实例、签收工作项、完成工作项等都是通过程序后台自动处理。 但是在模拟器中,把这些后台自动处理的操作暴露出来。流程首个岗位的流程操作主要是“创 建新的流程实例”、“签收工作项”、“完成工作项”等。 1.4.1.1. 创建新的流程实例 点击模拟器工具栏的 ,创建新的流程实例。新的流程实例创建完毕后,模拟器自动 启动第一个环节及其任务。“流程执行进程视图”用不同的颜色表示各节点的运行状态,蓝 色表示已经执行完毕,绿色表示正在运行中,粉红色表示已经初始化等待签收的任务。在下 图 中 ,“申请”环节正在运行中,“填写请假单”已经初始化,等待签收。 图iv-1.4.1-1 打开模拟数据视图,在“Work Items”页面中我们可以看到,产生了一个新的工作项, 其状态是 Initialized ,其 ActorId 是Fireflow_Simulator(模拟器默认的操作者)。如下图 图iv-1.4.1-2 切换到模拟数据视图的“Task Instances”页面,可以看到系统产生了一个新的任务实例 , 其状态也是 Initialized 。如下图 图iv-1.4.1-3 切换到模拟数据视图的“Process Instances”页面,可以看到系统产生了一个新的流程实 例,其状态是 Started。在模拟数据视图的“Process Instance Variables”页面,可以看到系统 创建了两个流程变量,并赋予了初始值,如下图。 48 图iv-1.4.1-4 模拟器创建流程实例的方法与你在你的项目中创建流程实例的方法完全相同,都是调用 Fire Workflow Engine 的相关 API 实现。唯一的不同在于,模拟器将后台数据库中数据的变 化情况及状态通过图形的方式展示出来。创建流程实例并执行该流程实例的代码如下: //获得 WorkflowSession 对象 IWorkflowSession fireflowSession = ctx.getWorkflowSession(); //根据流程名称创建流程实例 IProcessInstance processInstance = fireflowSession .createProcessInstance(workflowProcess.getName(), currentUserName); //调用流程实例的 run()方法启动流程实例, processInstance.run(); 1.4.1.2. 签收工作项 一般情况下,工作项(Work Item)都需要被签收后才允许执行相关的表单操作。签收 在实际业务中表现为上下工作岗位之间的工作交接,在 Fire Workflow 工作流系统中表现为 对工作项的“认领”。通常情况下 Fire Workflow 会将工作项分配给具有操作权限的所有用户 , 如果其中某个用户签收了该工作项,那么该工作项就会在其他用户的任务列表中删除(会签 情况除外)。 在“流程执行进程视图”中,选中粉红色的任务,然后点击 。则对应的工作项被 签收。相关的任务颜色由分红色变成绿色,WorkItem 的状态由 Initialized 变成 Running。如 下图 图iv-1.4.1-5 49 Fire workflow 的签收 API 如下: 如果你的业务代码当前已经获得了某个 WorkItem 实例,则可以直接调用 IWorkItem.claim()执行签收操作。 如果你的业务代码获得了某个 WorkItem 的Id,则可以调用如下代码执行签收操作。 IWorkflowSession workflowSession = ctx.getWorkflowSession(); IWorkItem claimedWorkItem = workflowSession.claimWorkItem(); 上述两种实现方法都要求在某一个数据库事务环境中,否则无法提交数据到数据库。 1.4.1.3. 设置流程变量的值 在实际的业务中,申请人填写请假天数、请假理由等信息后存盘。请假天数需要保存到 流程变量中去。 在模拟器中,点击 设置流程变量的值。假设请假 2天,则设置信息如下图。 图iv-1.4.1-6 设置后,可以看到 leaveDays 的值由初始值 1变成了 2,如下图。 图iv-1.4.1-7 在Fire workflow 中,流程变量可以不预先定义,在任何时候都可以调用如下 API 设置 流程变量。如果系统中存在同名变量,则新值覆盖旧值,否则创建一个新的流程变量。 IProcessInstance.setProcessInstanceVariable(String name, Object var) ; 1.4.1.4. 完成工作项 在实际业务中,工作项对应的业务表单录入完毕后需要点击界面的某个按钮完成当前的 50 工作项,并启动下一个环节。在模拟器中通过工具栏按钮 来结束选中的工作项。系统 弹出对话框,要求输入下一个环节的操作者,如果有多个则用半角逗号分割。如下图, 图iv-1.4.1-8 在上图中,设置下一个环节的工作项的操作者是 “zhangsan”,即送到下一环节送到 zhangsan 审 批 。“填写请假申请”Task 完成后,该task以及对应的 Activity 的颜色变成蓝色, 同时下一个环节被启动,新产生的 WorkItem 的Actor 字段的值是“zhangsan”。如下图。 图iv-1.4.1-9 在实际开发中,下一个环节的操作者是工作流引擎通过解析 Task 的performer属性,并 调用其 AssignmentHandler 获得的。当然也可以像模拟器一样指定下一个环节的工作项的操 作者,且如果指定了操作者,则系统不会再去调用 AssignmentHandler 了。 指定下一个环节的操作者的方法如下。 //创建动态 AssignmentHandler DynamicAssignmentHandler handler = new DynamicAssignmentHandler(); //将要指定的 Actor 的Id设置到动态 AssignmentHandler 中 handler.setActorIdsList(actorIdsList); handler.setNeedClaim(true);//指明下一个环节的操作者需要做签收操作, //将动态 AssignmentHandler 保存到当前 session 中 51 workflowSession.setDynamicAssignmentHandler(handler); //结束当前工作项 currentWorkItem.complete(); 需要特别注意的是,动态AssignmentHandler 只能被使用一次,如果当前 WorkItem 终结 后,启动了 2个或者 2个以上的后续 TaskInstance,则系统无法指定到底由哪个 TaskInstance 使用这个动态 AssignmentHandler。所以指定后续环节的操作人一般只能用于顺序流转的情 况下。 1.4.2. 部门经理岗的 操作 部门经理岗的 操作 部门经理岗的 操作 部门经理岗的 操作 1.4.2.1. 签收工作项 申请人操作结束后,流程实例流转到了“部门经理审批”环节。部门经理首先需要做签 收操作,此时签收操作不能点击 。因为这个按钮默认以“Fireflow_Simulator”这个用户 来登陆 engine 进行签收的,然而从上面的模拟过程可知,审批操作是分配给“zhangsan”这 个用户的。此时要点击其下拉菜单中的“claim as...”,如下图。 图iv-1.4.2-1 点击弹出对话框,要求输入 Actor Id,即当前操作流程实例的 Actor 的Id。此处我们输 入“zhangsan”,因为在申请环节结束的时,指定送给“zhangsan”审批。签收后,相应的 Task 的颜色变成绿色,即变成 running 状态。如下图。 图iv-1.4.2-2 1.4.2.2. 设置流程变量 领导的审批意见也是决定流程执行路径的一个重要数据,在部门经理审批完成后,我们 要把审批意见设置到流程变量中去。此时需要设置的流程变量是 approvalFlag。假设部门经 52 理同意请假,则流程变量设置界面如下图。 图iv-1.4.2-3 1.4.2.3. 结束工作项 和签收工作项一样,在此岗位,不能够点击 结束工作项,而要点击下来菜单中的 “Complete as...”,在弹出对话框中输入当前的 Actor Id,即“zhangsan”,系统紧接着又弹出 一个对话框,要求输入下一个环节的 Actor Id,此处我们不输入任何值,由系统调用后续 Task 的AssignmentHandler 来决定其操作者。在模拟器中,所有的 TaskInstance 的操作者都是 Fireflow_Simulator。 图iv-1.4.2-4 53 1.4.3. 发送邮件环节 的执行 发送邮件环节 的执行 发送邮件环节 的执行 发送邮件环节 的执行 “发送邮件”环节包含的“邮件通知申请人审批结果 ”Task 是一个 Tool 类型的任务, 模拟器会自动执行它,但是不会真正调用其 AppilcationHandler。 当部门经理审批的工作项结束后,整个流程实例结束了。模拟效果如下,并且在控制台 打印出 ToolTask 的ApplicationHander 的名称。 图iv-1.4.3-1 1.5.设计一个更复杂的请假流程 很多情况下,请假流程可能不会像 1.3 节那么简单,我们再完善一下这个案例,描述如 下。 业务描述: 1)首先,申请人填写请假条, 2)请假申请首先提交给部门经理审批; 3)对与请假天数大于 3天的申请还需要进一步提交给公司经理审批; 4)审批完成后,申请人会收到一封邮件,说明审批是否通过; 5)如果审批通过,需要提交到人力资源部备案,以便月末结算工资。 与1.3 节的案例比较,增加了公司经理审批和人力资源部备案两个环节。人力资源部备 案有可能是一个自动化的活动,即系统自动将员工的请假天数记录到工资管理系统中;但是 我们在这个示例中把它做成人工活动,让人力资源部的人员操作一下。新案例的流程环节如 下图。 图iv-1.5-1 54 1.5.1. 环节之间如何 流传? 环节之间如何 流传? 环节之间如何 流传? 环节之间如何 流传? 所有的 Activity 创建之后,我们来创建其中的转移关系。从开始节点到 “申请”,再到 “部门经理审批”,显然是顺序关系,增加 transition 后,如下图。 图iv-1.5.1-1 如果请假天数大于 3天,则“部门经理审批”完成后需要流转到“公司经理审批”;否 则略过“公司经理审批”环节。在 Fire worklow 中用空 Activity 表示略过,如下图。 图iv-1.5.1-2 在transition 中,我们要将转移条件表达式写进去。如下图。此时流程变量 leaveDays, approvalFlag 用上了。 图iv-1.5.1-3 “发送邮件”和“人力资源部备案”显然是在审批完成后可以并行的活动,而且“人力 资源部备案”需要有执行条件:同意请假。这个附带条件的并行分支的流程图如下,而且两 个并行环节汇聚于结束节点。请假审批标志在这个转移条件中用上了。 55 图iv-1.5.1-4 Fire workflow 允许有多个结束节点,所以“发送邮件”与“人力资源部备案”可以用如 下方案连接结束节点。 图iv-1.5.1-5 1.5.2. 不同场景的模 拟结果 不同场景的模 拟结果 不同场景的模 拟结果 不同场景的模 拟结果 假设请假天数为 5天,请假审批通过,通过模拟后可以看到流程实例的执行路径如下图 。 图iv-1.5.2-1 如果请假天数为 5天,部门经理审批同意,而公司经理审批不同意,通过模拟后可以看 到其流程实例的执行路径如下图。在此图中,“人力资源部备案”环节并没有执行。 图iv-1.5.2-2 56 2.业务 代码 如何调 用 业务 代码 如何调 用 业务 代码 如何调 用 业务 代码 如何调 用 FireWorkflowFireWorkflowFireWorkflowFireWorkflow API API API API 实现 流转 实现 流转 实现 流转 实现 流转 (注:本章对应的 Example 是LeaveApplicationProcessTest。可以从 google 的svn 下载到 该实例,http://fireflow.googlecode.com/svn/trunk/) 针对上一章节的请假流程,我们编写一个简单的 java 类LeaveApplicationTester.java, 在这个类的 main方法里面调用 Fire workflow 的API 实现创建流程实例、签收工作项、完成 工作项等等操作,直至整个流程实例结束。 为了使问题更简单,我们将请假申请人固定为 zhang ,部门经理固定为 manager_chen, 邮件通知审批结果的程序并不真正发送邮件,而是在控制台打印一行提示语句表明程序被执 行了。 为完成这个工作,我们创建名为 LeaveApplicationProcessTest 的java project。那么在这 个项目里面如何加入 fire workflow 呢?其实很简单,Fire workflow 就是两个 jar 包、7张表。 2.1.往javajavajavajava project project project project 中加入 FireFireFireFire Workflow Workflow Workflow Workflow 支持 2.1.1. FireFireFireFire WorkflowWorkflowWorkflowWorkflow Jar Jar Jar Jar 包包包包 如果不考虑流程设计器,那么Fire Workflow 和hibernate 或者 spring 一样,都是几个 jar 包而已。唯一的区别是,Fire Workflow 还附带了几张表,用于记录流程流转过程中的一些 状态。 Fire Workflow 的jar 包如下表 Fire Workflow 还需要如下第三方支持包(主要部分) 名称 用途 org-fireflow-model.jar 工作流模型部分,例如WorkflowProcess, Activity, Task, Synchronizer 等等静态对象都在该包中。 org-fireflow-engine.jar 工作流引擎部分,例如ProcessInstance,ActivityInstance, TaskInstance等等动态对象及其驱动算法都在该包中。 名称 用途 spring-core.jar,spring-context.jar, spring-beans.jar Spring 相关的 jar 包。 Fire Workflow 的Engine 的各种服务是通过 Spring IOC 容器组装在一起的。 这是缺省的组装方式,实际上你也可以写一个 Factory 将Engine 组装起来,或者用其他的 IOC 容器 进行组装。 dom4j.jar 用于流程定义文件的解析 commons-logging.jar,log4j.jar 用于日志 commons-jexl-1.1.jar 用于计算转移条件的 EL表达式 commons-beanutils-1.6.1.jar 用于 java Bean的拷贝 Hibernate 相关的 jar 包 缺省情况下,engine 通过 hibernate 进行持久化操作。 57 2.1.2. 表结构 表结构 表结构 表结构 Fire Workflow 的后台表结构如下 你或许有点困惑,为什么没有见到 ActivityInstance之类的表呢?在 Fire Workflow 中 , 将StartNodeInstance ,ActivityInstance ,SynchronizerInstance ,EndNodeInstance , TransitionInstance,LoopInstance等当作内核的对象,内核的对象是所有流程实例共享的无 状态的对象,不需要持久化。这样设计的目的之一是减少后台表的数量,相较于 JBPM 的30 多张后台表,Fire Workflow 是非常苗条的。Fire workflow 表结构的关系如下图。 图iv-2.1.2-1 名称 用途 T_FF_DF_WORKFLOWDEF 存储流程定义文件。在 Fire workflow 中,流程 运行时的表不需要和流程定义表做关联,因此没有将 流程定义文件分解,直接存为一个大字段。 T_FF_RT_PROCESSINSTANCE 流程实例表 T_FF_RT_PROCINST_VAR 流程变量表 T_FF_RT_TASKINSTANCE 任务实例表 T_FF_RT_WORKITEM 工作项表 T_FF_RT_TOKEN Token 表,一个流程实例同时可以有任意多个活 动的 Token;如果流程实例的活动 Token 数量为 0, 即表示流程已经结束了。 T_FF_HIST_TRACE 流程实例执行情况跟踪表。记录每次流转的详细 情况。 58 � T_FF_RT_PROCESSINSTANCE 表各字段含义如下图 图iv-2.1.2-2 � T_FF_RT_TASKINSTANCE 各字段的含义如下图 图iv-2.1.2-3 � T_FF_RT_WorkItem 各字段含义如下 图iv-2.1.2-4 � T_FF_RT_ProcInst_Var 的各字段含义如下 图iv-2.1.2-5 � T_FF_DF_WorkflowDef 各字段含义如下 图iv-2.1.2-6 59 2.1.3. 小结 小结 小结 小结 简而言之,将Fire Workflow 嵌入自己的系统非常简单,就是将上述 Fire Workflow 的两 个jar 包和第三方支持包扔到 classpath 中(或者 Web 项目的/WEB-INF/lib 目录下)。然后在 数据库端创建 Fire Workflow 的表结构。 如果你的项目是 J2SE 系统,仍然可以使用 Fire workflow,只要将 Fire workflow 的jar 包和第三方包加入 class path 即可。 在LeaveApplicationProcessTest 项目中,jar 包和数据库脚本已经 copy 到相关的目录中, 如下图。 图iv-2.1.3-1 2.2.配置FireFireFireFire WorkflowWorkflowWorkflowWorkflow Fire Workflow 默认采用 spring 来装配整个 Engine,主配置文件是 FireflowContext.xml。 另外,Fire Workflow 默认使用 spring ioc 容器来创建外部 bean,例如 AssignmentHandler 和 ApplicationHandler 等等,这个配置文件一般命名为 FireflowContext-beanfactory.xml。 LeaveApplicationProcessTest 项目的所有 spring 配置文件如下图。 图iv-2.2-1 60 其中 applicationContext.xml 是主配置文件,里面配置了数据源、hibernate session factory、 事务管理 template 等等,并且 import 了FireflowContext.xml 和FireflowContext- beanfactory.xml。applicationContext.xml 需要将 Fire workflow 的hibernate 映射文件注册到 hibernate session factory 中,如下图。 图iv-2.2-2 FireflowContext.xml 的默认配置需要有两处地方需要根据实际情况稍作修改。一处是 PersistenceService,需要将实际使用的 hibernate session factory 注入进去。另一处是指定适当 的DefinitionService,本项目采用 definitionService4FileSystem,所以要指定流程定义文件的 classpath 路径。如下图。 图iv-2.2-3 61 2.3.编写并运行 LeaveApplicationTester.javaLeaveApplicationTester.javaLeaveApplicationTester.javaLeaveApplicationTester.java LeaveApplicationTester.java 向你展示了 Fire workflow 的几个核心 api,他们是如何创建 并启动流程,如何驱动流程往下执行。 在Fire workflow 中,流程实例的创建是通过 IWorkflowSession 接口完成的,如下图, 相关代码请参阅 LeaveApplicationTester.testStartNewProcess()。 图iv-2.4-1 流程实例的运行调用 processInstance.run()方法,该方法会使得实例从开始节点往下执 行。当遇到 FormTask 时,会创建相应的 WorkItem 等待操作员来操作,因此该 FormTask 所 在的流程分支处于等待状态;当遇到 ToolTask 时, Fire Workflow 引擎 自 动 调 用 其 ApplicationHandler,调用返回后,流程实例继续往下执行;当遇到 SubflowTask 时, FireWorkflow 引擎启动子流程,只有子流程返回后父流程实例才会继续执行。 因此,在 Fire workflow 中,仅有一种方法可以主动驱动流程实例往下执行,那就是操 作员结束他的工作项,即业务代码调用 IWorkItem.complete()。相关的操作代码请参阅 testClaimAndCompleteSubmitApplication()和testClaimAndCompleteApproveApplication(),这 两个方法中分别展示了申请人和部门经理签收工作项、结束工作项的流程操作,以及这些操 作之后流程实例的状态变化。 LeaveApplicationTester 代码已经写好了,你可以直接运行,运行之前别忘了用 dbscript 下的数据库脚本建立数据库表,并修改 src\applicationContext.xml 中MyDataSource的连接属 性。 2.4.AssignmentHandler AssignmentHandler AssignmentHandler AssignmentHandler 和ApplicationHandlerApplicationHandlerApplicationHandlerApplicationHandler 在请假流程中,申请环节和部门经理审批环节的 Task 都通过 Performer 来指定相应的 WorkItem 的操作人。这些操作人并不是硬编码在流程定义文件中的,而是在运行时由 engine 调用 AssignmentHandler 解析得到的,这就可以比较方便地和各种用户管理系统进行集成。 在LeaveApplicationProcessTest 项目中,我们约定申请人固定为 zhang,部门经理固定为 manager_chen,因此相关的 AssignmentHandler 实现非常简单。 在LeaveApplicationProcessTest 中 ,“邮件通知申请人审批结果”这个 TookTask 的 ApplicationHandler 仅仅打印一行提示信息,并不真正发送邮件。 所有 handler 的代码在包 org.fireflow.example.leaveapplication.workflowextension 中,如 下图。 62 图iv-2.3-1 所有的 AssignmentHandler 和ApplicationHandler 必须注册到 spring 中,才能被引擎引用 (fire workflow 缺省使用 spring 作为 bean factory)。约定这个配置文件名称是 FireflowContext- beanfactory.xml。如下图 图iv-2.3-2 63 3.将将将将FireFireFireFire Workflow Workflow Workflow Workflow 嵌入 你 的 嵌入 你 的 嵌入 你 的 嵌入 你 的 J2EEJ2EEJ2EEJ2EE系统 中 系统 中 系统 中 系统 中 (1)(1)(1)(1) (注:本章对应的 Example 是LeaveApplicationProcess。可以从 google 的svn 下载到该实 例,http://fireflow.googlecode.com/svn/trunk/) 3.1.J2EEJ2EEJ2EEJ2EE系统中常见的流程操作 在上一章中,我们讲解了怎样调用 Fire workflow 的API 驱动流程 实例 流转 。在 LeaveApplicationProcessTester.main()方法中实现了流程实例的创建、启动、各个环节的工作 项的签收与结束等等流程操作,直至流程实例运行结束。 J2EE 系统中流程实例的操作和一个带 main 函数 java 类是不一样的。首先,J2EE 系统 中流程实例执行的时间跨度很长,从实例的创建到终止往往持续几天甚至几个月。另一方面 , J2EE 系统中的流程实例是多个参与者共同操作驱动其往下执行的。下面我们以请假流程为 例,分析一下各个参与者的流程操作。 3.1.1. 首个环节的参 与者的流 程操作特 点 首个环节的参 与者的流 程操作特 点 首个环节的参 与者的流 程操作特 点 首个环节的参 与者的流 程操作特 点 首先考察一下请假申请人。从系统用户的角度看,他只需要填写表单,保存数据即可, 无需其他动作。 但是,从程序系统的角度看,实际上后台代码“帮”他做了如下事情,才能够使得他的 申请流转到正确的部门经理那里等待审批。 1、创建并启动一个流程实例 2、设置流程变量,将业务数据的关键字段设置到流程实例中去。 3、自动签收并自动完成“填写请假单”这个任务的工作项。IWorkItem.complete()触发 流程实例往下流转。 首个环节的流程操作特点总结于下表 3.1.2. 后续环节的参 与者的流 程操作特 点 后续环节的参 与者的流 程操作特 点 后续环节的参 与者的流 程操作特 点 后续环节的参 与者的流 程操作特 点 再考察一下部门经理的操作。一般情况下,他需要签收待审核的工作项,然后填写审核 岗位名称 操作员的操作 业务系统自动完成的 流程操作业务操作 流程操作 申请 (流程的首个环节) 1、填写表单并保存 无 1、创建流程实例,并启动 流程实例 2、设置流程变量 3、自动签收并完成相关的 工作项,使得流程实例流转 到下一个环节 64 意见并保存审核意见,最后结束工作项,使得流程实例继续往下流转。在实际业务中,可以 在保存审核意见的同时,自动结束相关的工作项,使得流程流转到下一个环节;也可以,单 独提供一个按钮,让操作员点击一下才结束工作项。 如果我们的请假流程还需要公司经理审批的话,显然公司经理的操作步骤和部门经理是 一样的。后续环节的流程操作特点总结于下表 3.1.3. 总结 总结 总结 总结 由上面的分析可知,除了第一个环节的流程操作稍有不同之外,后续环节的流程操作是 雷同的,都是操作员“签收工作项-->录入业务数据并保存-->结束工作项”。流程实例的结束 操作是工作流引擎自动完成的,不需要任何外部操作。 3.2.将FireFireFireFire workflowworkflowworkflowworkflow 嵌入J2EEJ2EEJ2EEJ2EE系统的详细步骤 将Fire Workflow 嵌入 J2EE 系统的的步骤和第 2章描述的差不多。 首先,给项目增加 Fire Workflow 支持。即,将相关的 Jar 包扔到你的项目的 WEB_INF/lib 中去,并创建相关的表结构 然后,配置 FireWorkflow,主要配置内容如下。 1、1、将 Fire workflow Engine 的一些 hibernate 映射文件注册到 sessionFactory 中 2、打开 FireflowContext.xml,配置 PersistenceService的sessionFactory 属性 3、打开 FireflowContext.xml,给 runtimeContext bean 选择适当的 definitionService;如 果你使用 definitionService4FileSystem,则需要将流程定义文件的 classpath 路径注入进去。 最后,编码调用 Fire Workflow。 3.3.第J2EEJ2EEJ2EEJ2EE系统中常见的流程操作的实现 3.3.1. 申请环节的界 面及代码 申请环节的界 面及代码 申请环节的界 面及代码 申请环节的界 面及代码 请假申请作为流程的第一个环节,操作员感受不到流程的存在,后台帮他代劳了。在这 个Example 中,填写请假申请的界面如下图。 岗位名称 操作员的操作 业务系统自 动完成的流程操 作 业务操作 流程操作 审核岗 2、填写审核意见并保 存 1、签收工作项 3、点击相关按钮结束工 作项 无 65 图 iv-3.3.1-1 请假单提交后,由 SubmitApplicationServlet.java 进行处理,该 Servlet 在保存完业务数 据后,创建流程实例、设置流程变量并启动流程实例。相关代码如下图。 图 iv-3.3.1-2 在这个 servlet 里面并没有签收“申请”环节工作项和结束工作项的代码,流程怎实例 怎么往下流转呢?这个代码在 CurrentUserAssignmentHandler.java 中,在流程定义文件中, 这个 AssignmentHandler 正是分配给“填写请假单”这个 Task 的。代码如下图。 66 图 iv-3.3.1-3 3.3.2. 审批环节的界 面及代码 审批环节的界 面及代码 审批环节的界 面及代码 审批环节的界 面及代码 在基于流程的业务系统中,有一个较好的实践经验值得推荐,就是以“待办箱”作为业 务界面的统一入口。待办箱集中了各种各样的需要当前系统用户完成的工作。用户点击代办 箱中的一个工作任务(即工作项),便进入了对应业务的操作界面进行操作。 在LeaveApplicationProcess 这个 Example 中 ,“代办箱”如下。 图 iv-3.3.2-1 该界面中,查询待办工作项、签收选定的纪录、完成工作项的代码都在 MyWorkItemServlet.java 中。 有一个问题非常值得探讨,如果所有业务的待办工作都集中在用户的待办箱内,那么如 何做到点击“处理选定的纪录”这个按钮的时候导航到正确的业务界面? Fire Workflow 为FormTask 设计了 EditForm、ViewForm、ListForm 这几个复合属性,里 面分别存放了这个业务相关的编辑界面、只读界面、列表界面的 url。当点击“处理选定的 纪录”时,实际上就是打开这些 url。相关的代码可以参阅 MyWorkItemServlet.openForm()。 工作流引擎并不处理这些 url,所以 url 的具体格式只要你的业务代码“看得懂”即可。 67 4.将将将将FireFireFireFire Workflow Workflow Workflow Workflow 嵌入 你 的 嵌入 你 的 嵌入 你 的 嵌入 你 的 J2EEJ2EEJ2EEJ2EE系统 中 系统 中 系统 中 系统 中 (2)(2)(2)(2) (注:本章对应的 Example 是LeaveApplicationProcessEx。可以从 google 的svn 下载到 该实例,http://fireflow.googlecode.com/svn/trunk/) 上一章的 Example 的待办箱有一个弊病,就是无法体现待办工作项的业务特征。如下图 , 部门经理 1的代办工作项中有两条 WorkItem 记录,很难从界面上区别这两个工作的差异, 比如申请人、请假类型、请假天数等等;WorkItem 对象中也没有这些业务属性。 图iv-4-1 为了使得工作流系统最大限度的独立于业务系统, WorkItem 对象,TaskInstance 对 象 , ProcessInstance对象均没有保存业务信息。与业务相关的数据缺省情况下都存储在流程变量 中。而流程变量是 key-value 形式存储的,不利于查询和统计。除了将业务特征数据保存在 流程变量中之外,还有没有更好的方案呢?我们系统地分析一下流程数据和业务数据。 4.1.工作流数据 VS VS VS VS 业务数据 业务数据和工作流数据的划分以及存储方案一直是一个经典的且令人头痛的问题。业务 数据和工作流数据不可能截然分开,二者有交集。如下图 图iv-4.1-1 68 上图中哪些数据对工作流和业务都有影响呢? 例如:受理编号、审核意见。这些数据本来应该是业务数据,但是流程在流转的过程中 也需要他们。受理编号用于关联流程实例和业务表单,审核意见决定流程的下一步走向。我 们把这种影响流程实例执行路径的和对业务具有标示作用的数据称作“业务特征数据”。 再例如:流程实例的当前环节和环节状态。这些数据本来是流程数据,但是业务系统经 常需要查询这些信息。 上图交集中的数据怎么存储呢?通常有如下几种方案。 方案一、工作流数据侵入业务系统表 在业务系统表结构中不但有业务字段,还有工作流相关的字段,例如:流程实例编号、 当前环节名称、当前环节状态、下一环节名称等等。 这种方法的优点是,查询统计比较方便。例如某业务在当前在那个环节,处于什么状态 , 一目了然。 这种方法的缺点是使得业务数据不“纯净”。而且如果业务审批项目比较多,则在每个 审批项目的主业务表中都需要嵌入流程字段,会使得流程系统和业务系统耦合得很紧。 方案二、将业务特征数据当作流程变量存储到工作流系统中 由于流程变量一般是以 key-value 方式存储的,不利于查询统计。例如“要根据流水号 查询出该业务当前处于哪个环节”实现起来很麻烦。 方案三、FireFireFireFire workflow workflow workflow workflow 的方案 Fire Workflow 首先支持将业务特征数据作为流程变量的存储方式,同时支持将流程变 量这个“纵向表”复制为一个“横向表”,方便查询统计。如下图,以请假流程为例,假设 流程变量里面存储了“请假申请序列号”、“请假人”、“天数”、“审批意见”这几个变量。 图iv-4.1-2 那么这个“复制”得到的横向表如何存储呢? Fire workflow 的方案是扩展任务实例表 T_FF_RT_TaskInstance。这个扩展不是在 T_FF_RT_TaskInstance中增加字段,而是增加一张 新表,假设新表的名称是 T_Biz_LeaveApp_TaskInstance。二者的关系如下图。 图iv-4.1-3 69 上述设计虽然冗余了部分业务特征数据(业务流水号、申请人、天数、审批意见),但 是保持业务表的纯净,不会有工作流数据侵入。 最重要的是,这种设计的使得查询统计非常方便。 例如:如果你想跟踪某个人请假申请现在处于那个环节了,只要联合 T_FF_RT_TaskInstance和T_Biz_MyTaskInstance进行查询即可。而且这两个表的数据是 1 对1的,通过 TaskInstanceId 进行关联,性能也不会有大的问题。 虽然 T_Biz_MyTaskInstanceExtension 可以理解为流程变量表“旋转 90 度”得到,但是 并不要求二者的字段有对应关系。 4.2.如何填充 TaskInstance TaskInstance TaskInstance TaskInstance 扩展表的数据 现在的问题是 T_Biz_LeaveApp_TaskInstance中的数据怎么填入呢? Hibernate 的父子对象映射机制给我带来了便利。我们知道 org.fireflow.engine.impl.TaskInstance 映射到 T_FF_RT_TaskInstance 。那么我们扩展 org.fireflow.engine.impl.TaskInstance 类,得到 org.fireflow.example.Leaveapplication. workflowextension. LeaveApplicationTaskInstanceExtension。他们的继承关系和映射关系如下 图。 图iv-4.2-1 仅仅扩展 org.fireflow.engine.impl.TaskInstance 还是没有解决问题,还需要定制一个 LeaveApplicationTaskInstanceCreator 来创建 LeaveApplicationTaskInstanceExtension 。在文档 第四部分第 3章对应的请假流程 Example 中,我们没有定制 TaskInstanceCreator,工作流引 擎会使用一个缺省的实现。 图iv-4.2-2 70 那么 LeaveApplicationTaskInstanceCreator 注册在哪里才能让工作流引擎正确引用到 呢?首先要在流程定义中设置 TaskInstanceCreator 的类名。 FireWorkflow 引擎按照如下顺序查找 TaskInstanceCreator,首先检查 Task 定义看看有没 有“局部”的TaskInstanceCreator 设置,如果没有则继续检查流程定义文件是否有 “全局” 的TaskInstanceCreator 设置,如果没有则使用确省的 TaskInstanceCreator。(除了可以定制 TaskInstanceCreator 外,还可以定制 TaskInstanceRunner 和TaskInstanceCompletionEvaluator, 请参阅“第三部分 Engine 的设计及扩展”的“第5章TaskInstanceManager”。) 在请假流程中,我们将这个 TaskInstanceCreator 设置为整个流程“全局”的,如下图 图iv-4.2-3 除了在流程定义文件中注册 TaskInstanceCreator 之外,还要在 FireflowContext- beanfactory.xml 中定义 这 个 Bean。如下 图 , 从这 个 spring 配置中我们可以看到,将 LeaveApplicationInfoDAO 注入到了 TaskInstanceCreator 之中,这就建立了工作流和业务系统 之间的数据联系。 图iv-4.2-4 4.3.请假流程实例的扩展以及效果 LeaveApplicationProcessEx 项目在 LeaveApplicationProcess 项目的基础上增加了扩展 TaskInstance和TaskInstanceCreator。主要增加的代码如下图。 71 图iv-4.3-1 有了扩展 LeaveApplicationTaskInstanceExtension,就可以通过类似如下代码从 workitem 直接 get 到业务信息。 ((LeaveApplicationTaskInstanceExtension)workItem.getTaskInstance()).getZYZ();((LeaveApplicationTaskInstanceExtension)workItem.getTaskInstance()).getZYZ();((LeaveApplicationTaskInstanceExtension)workItem.getTaskInstance()).getZYZ();((LeaveApplicationTaskInstanceExtension)workItem.getTaskInstance()).getZYZ(); 在这个 Project 中待办工作项列表表现如下图,较之图iv-4-1,显然明了很多。 图iv-4.3-1 为了展示扩展 TaskInstance对查询的帮助,在这个项目中增加了一个功能“根据姓名查 询”审批情况。该功能的 hibernate 查询代码在 LeaveApplicationInfoDAO(如图图 iv-4.3-2, 而不是通过 Fire Workflow 的PersistenceService(PersistenceService不可能罗列五花八门的复 杂查询)。查询的结果如图 iv-4.3-3。 72 图iv-4.3-2 图iv-4.3-3 5.工作 流应 用中经 典问 题的 解决方 案 工作 流应 用中经 典问 题的 解决方 案 工作 流应 用中经 典问 题的 解决方 案 工作 流应 用中经 典问 题的 解决方 案 5.1.流程定义文件的存储与版本控制 5.1.1. 题外话 题外话 题外话 题外话 我们常常人为地将系统划分成所谓的“源代码(如 java 文 件 )”和“配置文件(或配置 数 据 )”;甚至认为开发“源代码”的工作才是编程工作,编写配置文件、调整配置数据的工 作是“免编程”。 对此,我非常不以为然!且不说二者本来就没实质区别,都需要开发人员去开发;如果 把“配置文件(或配置数据)”发挥至极端,复杂的不行,开发人员需要花更多的精力和时 间去学习的话,“免编程”就没有任何价值! 但是,并不能说 java 文件和 Xml 配置文件(尤其是数据库中的配置数据)没有区别。Java 文件需要编译才能运行,xml 配置文件和数据库中的配置数据修改后立即发挥作用。一般情 况,下修改 Xml 配置文件和配置数据下比修改 java 文件简单。 因此,在我看来 java 文件也好,xml 配置文件也好 .,配置数据也好都是系统的代码。 对他们的开发活动都是“编程”。这几种代码都需要某种形式的版本控制。 73 5.1.2. FireFireFireFire WorkflowWorkflowWorkflowWorkflow流程定义文件 的存储与 版本控制 流程定义文件 的存储与 版本控制 流程定义文件 的存储与 版本控制 流程定义文件 的存储与 版本控制 Fire Workflow 流程定义文件是整个业务系统代码的一部分,以 xml 的形式表现,开发 时存放在/src 目录下,运行时存放在 classpath 中(如/bin 或者/WEB-INF/classes或者某个 jar 包 中 )。 在 运 行 时 通 过 “流程定义服务”的实现类 org.fireflow.engine.definition. DefinitionService4FileSystem 从classpath 中读取定义文件。 这种流程文件的存储方案非常简单,但是没有照顾到如下需求:1)在系统上线运行后, 流程常常需要调整;在流程定义更新后,系统中仍然有旧版本的流程实例在运行。2)最终 用户有自行调整业务流程的需求,存储在 classpath 中的流程定义不能适应这种需求。 因此上述流程文件存储方案适合于系统开发阶段,方便开发人员快速地结合业务数据进 行系统测试。而且,Fire Workflow 提供了和流行的 IDE 结合的很好的设计器,使得开发测 试业务流程就像开发测试一个 java 类一样简单。 Fire Workflow 流程定义文件还可以存储在后台数据库表 T_FF_DF_WORKFLOWDEF 中。该表可以记录同名流程的各个版本。 Fire Workflow 要求每个流程实例只能在自己的流 程版本上运行,不能“迁移”到同名流程的其他版本。这种规定不是 Fire Workflow 想“偷 懒”,而是由于 Fire Workflow 是基于比较严密的 Petri Net 流转算法,同名的流程不同版本间 很可能改变了网络拓扑结构,这种情况下流程实例“迁移”到新的流程版本后无法正确运行 。 以我的经验看,Fire Workflow的这种规定适合绝大多数业务场合。 至于用户自行调整流程,将来可以通过 FireWorkflow 的WEB 工具完成。(该 Web 工具 尚未实现 20090210)。 对于存储在 T_FF_DF_WORKFLOWDEF 中的流程定义文件,需要用 “流程定义服务” 的另外一个实现类 org.fireflow.engine.definition. DefinitionService4DBMS 进行读取。该类最 终调用 IPersistenceService完成实际的数据库 IO操作。 5.2.业务数据 vsvsvsvs工作流数据 (见第四部分,第 4章) 5.3.流程数据存取设计与事务一致性 Fire Workflow 的流程数据的存取都是通过 org.fireflow.engine.persistence. IPersistenceSerivce的实现类完成的,假设这个实现类的名字是 MyPersistenceService。那么 MyPersistenceService其实就是一个存取多张工作流表的 DAO 而已,和你的业务系统的 DAO 处于相同的地位,没有任何区别。示意图如下。 图iv-5.3- 74 1 如果你的系统采用的是申明式事务,将事务申明添加到 MyPersistanceService 上即可以 实现业务操作和流程操作在同一个事务中。 如果你的系统采用的是编码实现的事务,只要将调用流程引擎的代码和和业务逻辑代码 放在同一个事务中即可。在 Fire Workflow Example 中,采用的是编码实现的事务,可以参 考。 Fire Workflow 已经提供了 org.fireflow.engine.persistence. IPersistenceSerivce一个缺省实 现类 org.fireflow.engine.persistence. hibernate.PersistenceServiceHibernateImpl ,该类基于 hibernate 的,你当然也可以自行编写基于 iBATIS,Toplink,甚至 JDBC 的实现类。 5.4.与用户管理系统的接口 Fire workflow 认为,工作流系统是整个业务系统的一部分,不应该有自己独立的用户管 理系统。在 Fire Workflow 中,与用户管理系统接口的地方只有一个,那就是:新产生的工 作项分配给“谁”? Fire Workflow 规定,在流程定义时,所有的 Form 类型的 Task 都必须设置其 Performer 属性。如下图: 业务逻 辑java bean 工作流 逻辑 业务表 DAO MyPersistenceServ ice 数据库(包含业务表和工作流表) 调 用 DAO 层 逻辑层 75 图iv-5.4-1 Performer 是一个复合属性,其本身有如下属性 Assignment Handler 实现类的示例参见 Fire Workflow Example 的org.fireflow.example.ou. AssignmentHandler。代码如下图。 图iv-5.4-2 此处,或许有人有疑问:我们的系统的角色都是用户自己定义的啊,系统发布时没有任 何角色,而 Fire Workflow 要求在设计时就确定角色名称,不合理。 属性名称 说明 Name Performer 的Name,说白了就是角色名称 Display Name 显示名称,即角色的中文名称 Assignment Handler (*这个 Handler 是最 关键的*) 业务系统中一个实现了 org.fireflow.engine.ou. IAssignmentHandler 接口的类。该类的作用就是:在运行时,根 据角色名称返回该角色的所有成员,然后把这些成员的 ID列表 交给工作流系统。工作流系统会根据输入的 ID列表分配工作项。 Description 角色描述。 76 我确实认为角色是用户自己定义的,但是为什么不能在系统设计阶段根据系统的业务特 点预定义一些系统内置的角色呢?在我看来,不但可以这样做,而且理应这样做。业务系统 的角色名称可以五花八门,但是业务的特征是恒定的,不管角色取什么名称,都是指同一个 对象。所以对于这种角色应该预定义在系统中。 5.5.与业务表单的接口 Fire Workflow 不提供所谓的“表单设计器”,提供表单设计器是越俎代庖;Fire Workflow 也不会对业务的表单本身做任何的处理;Fire Workflow 仅仅在定义文件中记录 Form 类型的 Task 各种表单的 Uri,使得系统在运行时可以找到当前 TaskInstance的各种表单的 Uri,然后 对其进行处理。 Form 类型的 Task 有如下属性记录表单信息。 Edit Form,View Form,List Form 都是复合属性,他们各自的属性见下表 表单设置界面如下图 属性名称 说明 Default View 缺省视图,取值为 EditForm、ViewForm、ListForm,缺 省值为 ViewForm。很多情况下,一个 Task 可能对应多个表 单(视图),该属性记录 Task 缺省表单(视图)的类型。 工作流引擎本身不需要使用该属性,仅仅为了方便业务 开发而设置。 Edit Form 可编辑的表单信息,在此填入相关的 url 可以用于链接 业务表单。该属性是一个复合属性,具体见下一个表格。 View Form 只读表单信息。该属性是一个复合属性,具体见下一个 表格。 List Form 列表表单信息。该属性是一个复合属性,具体见下一个 表格。 属性名称 说明 Name 表单的名称 Display Name 表单的显示名 URI 表单的 URI。工作流系统并不关心该 URI,只要业务系 统自己能够该 URI 的含义就可以了。 Description 表单描述。 77 图iv-5.5-1 在请假流程中展示了表单的应用。所有系统用户的“我的待办任务”实际上是同一页面 (当然,不同的操作员看到的内容是不一样的),点击这些任务,系统自动导航到各自的处 理页面。这种自动导航到相应的处理页面有赖于 Task 中存储的表单 URI 信息。相关的代码 可以参考 org.fireflow.example.leaveapplication.bizservlets.openForm()。 5.6.工作项签收与业务实际中的材料移交 在Fire Workflow 中,工作项签收的意义首先是对工作任务的 “认领 ”。因为在 Fire Workflow 会把任务会分配给角色中的所有成员,如果不通过签收的方式 “认领”工作项, 可能会造成多个人干同一件事的情况(非会签的情况下)。工作项被一个操作员签收后,其 他操作员就没有权限处理该单业务了。 工作项签收在某种情况下还代表业务材料的交接,即,我签收工作项代表纸质材料已经 移交到了我的手上。但是这种含义往往与实际情况不太符合,例如:在政府部门的审批业务 中,业务材料通常情况下是由一个部门的内勤收集后统一交到下一个部门的内勤手上,材料 移交在两个内勤之间发生;这种情况下,接受材料的内勤不能够签收工作项,否则业务领导 就看不到本应由他处理的工作任务了。在这种情况下,材料移交不能记录在工作流系统中, 而是记录在业务系统中。 Fire Workflow 也可以用编码的方式设置工作项不需要签收,参见接口 IAssignable. asignToActor(String actorId, boolean needSign)的说明文档。 5.7.流程“自定义”与“自调整” 我反对“流程自定义”这个说法的。尽管很多客户整天嚷嚷着要流程自定义、表单自定 义;也尽管很多公司声称他们的“平台”是免编程的,几乎什么都可以自定义。但是,有几 个最终用户会自己在一个所谓的平台上定义出自己需要的业务呢?我认为几乎没有。所以, “流程自定义”是一个假议题,顶多是一个商业议题而不是一个技术议题。 78 但是,最终用户在某种程度上调整业已存在的流程是一个真实的需求。 Fire Workflow 考虑提供一个 Web 形式的流程“自调整”工具给最终用户,该工具是流程设计器的变种, 可以对系统中已经存在的流程进行某种程度的调整。 5.8.工作流系统的性能问题 我曾经在项目中应用过 Jbpm,发现其性能不佳。首先,Jbpm 表结构太复杂,导致每次 流程操作都要执行大量 SQL 语句。更严重的问题是,JBPM 的很多表数据增长迅速(具体 没有统计,大概是 jbpm_taskactorpool 等),长时间运行后,系统性能肯定有隐患。 以我的经验,工作流系统运行性能主要存在于以下两个方面 � 数据库 IO的数量 在MIS 系统中,多执行一条不必要的语句对性能都有影响!因为 MIS 系统用户众多, 而且每个用户持续不断地办理业务,如果某个业务操作多执行一条不必要的 SQL 语句,那 么一个人一天下来可能执行上百条不必要的语句,100 个人一天要执行上万条不必要的 SQL 语句。这么多无用的 SQL 语句对性能的影响是非常明显的。 因此 Fire Workflow 尽量减少数据库 IO。首先, Fire Workflow 简化表结构,目前 Fire Workflow 总共只有 7张表,除去一张表存储流程定义文件,实际用于引擎计算的表只有 6 张。另一方面,Fire Workflow 尽量优化 SQL 语句,尤其是查询语句,尽量避免 N+1 次查询 的情况。 下表根据 Workflow Example 统计了预览版本的引擎中各种流程操作的 SQL 执行情况。 � 流程表中的数据量 流程系统各个表的数据量是影响性能的另一个关键因素,尤其影响“待办任务”的查询 。 流程操作名称 SQLSQLSQLSQL执行情况 创建流程实例 1条Insert 语句(插入 T_FF_RT_ProcessInstance表) 设置流程实例变量 N条Insert 语句(每个变量 1条) 启动流程实例 1条Update 语句 创建任务实例和工作项 4条Insert 语句 N条插入 T_FF_RT_WorkItem 的语句 查询待办工作项 1条查询语句(将 T_FF_RT_TaskInstance 、 T_FF_RT_WorkItem,T_Biz_MyTaskInstance中的数据一次性 查询出来) 签收工作项 2条Update 语句, 1条Delete 语句 结束工作项并启动下一 个环节 约12 条语句 N条插入 T_FF_RT_WorkItem 的语句 查询已办任务 1条查询语句(将 T_FF_RT_TaskInstance 、 T_FF_RT_WorkItem,T_Biz_MyTaskInstance中的数据一次性 查询出来) 79 Fire Workflow 已经通过初步的优化,将每个表的数据增长速度压缩到最低。 同时,Fire Workflow 正在考虑流程数据分割策略。流程数据有这样一个特点,在业务 稳定的情况下,整个系统中活动的流程实例是比较固定的,而且数据量也不大。然而随着运 行时间的增长,系统中终止的流程实例会越来越多,数据量会越来越大。这种已经终止的流 程实例数据一般情况下不会用到,然而却严重影响数据库操作的速度。因此非常有必要设计 一种策略将这部分数据分割出去。Fire Workflow 接下来需要考虑这个问题。 6.各种 工作 流模式 的实 现 各种 工作 流模式 的实 现 各种 工作 流模式 的实 现 各种 工作 流模式 的实 现 (注:本章对应的流程示例都在 workflow-patterns 这个项目中。可以从 google 的svn 下 载到该项目,http://fireflow.googlecode.com/svn/trunk/) 6.1.概述 本文最初始的标题是“21 种工作流模式的实现”,但是我觉得 21 种工作流模式总结的 并不科学,“21 中工作流模式”只看到了业务现象,没有反应出业务的本质。 例 如 :“并行模式(Parallel Split)”、“单选(Exclusive Choice)”、“多选(Multi Choice)”没 有实质区别,都是从流程的分支中选择若干个执行。具体选择哪几个是由“转移条件”决定 的,如果转移条件计算结果为 true则执行,否则不执行。因此并行模式只是分支选择模式的 一个特例,即所有的转移条件计算结果都是 true。 再例如:21 中工作流模式中的“同步(Synchronization)”、“简单汇聚(Simple Merge)” 也没有实质区别,在 Fire Workflow 中这都是“同步器”节点统一处理。 我甚至认为 21 中工作流模式存在一些谬误,我简单阐述我的看法,欢迎探讨。 例如:21 种工作流模式对“简单汇聚(Simple Merge)”的解释是“流程中的某个节点, 使得两个或者多个流程分支汇聚在一起”,之所以将这种汇聚称为“简单汇聚”,是“假设多 个流程分支不并行执行,因此汇聚点不需要进行复杂的同步操作”。 这种强行的“假设”看似简化了问题,实际上非常不合理。这种假设要求汇聚点“预知” 多个分支中只有一个分支被执行,这种预知是很难实现的;或者限制多个分支中只允许一个 被执行,这种后继节点限制前驱节点的行为是很荒谬的。 因此,本文将工作流模式按照我自己的理解重新组织。 6.2.顺序、分支、汇聚 6.2.1. 顺序分支汇聚 其实是统 一的 顺序分支汇聚 其实是统 一的 顺序分支汇聚 其实是统 一的 顺序分支汇聚 其实是统 一的 首先,Fire Workflow 认 为 ,“分支”和“汇聚”都是一种计算逻辑,这种计算逻辑应该 由“工作流子系统”负责完成。Fire Workflow 用“同步器”节点在模型中表示这种计算逻 辑。如下图,同步器“S”是一个一般意义上的同步器。 80 图iv-6.2.1-1 上图中 “S”首先对 t1、t2、t3执行汇聚操作。在 Fire Workflow 中,不管 t1、t2、t3 中哪个或者哪几个流程分支被执行,S都能正确地进行汇聚。 S汇聚完成后,根据转移条件的计算结果,启动 t_a,t_b,t_c 中的一个或者几个分支。 为什么说顺序、分支、汇聚是统一的呢? A)在上图中,如果 “S”的前驱和后继都是唯一的,则形成一个特例,即顺序流程。 如下图。 图iv-6.2.1-2 B)如果上图中“S”的前驱是唯一的,后继有多个,则形成了“21 中工作流模式”中 的“并行模式(Parallel Split)”、“单选(Exclusive Choice)”、“多选(Multi Choice)”。如下图 图iv-6.2.1-3 81 C)如果上图中“S”的前驱有多个,后继仅有一个分支,则形成“汇聚”,如下图。 图iv-6.2.1-4 不仅顺序、分支、汇聚是统一的,同步器、开始节点、结束节点也是统一的,即开始节 点和结束节点是同步器的特例。如果同步器的前驱为 0,则形成开始节点,如果同步器的后 继节点为 0 ,则形成结束节点。在 Fire Workflow 中分别用 和 表示这两个节点。 6.2.2. 顺序业务流程 举例 顺序业务流程 举例 顺序业务流程 举例 顺序业务流程 举例 在压缩包 workflow-patterns 项目中,有一个简单的顺序流程定义文件 MyFirstProcess.xml,模拟了一个最简单的审批业务。如下图 图iv-6.2.2-1 6.2.3. 并行业务流程 举例 并行业务流程 举例 并行业务流程 举例 并行业务流程 举例 并行业务流程其实是分支选择的一个特例,即所有的转移条件计算结果为 true。在 Fire workflow 中如果 Transition 的转移条件 EL表达是为空,则默认其结果为 true。 业务举例:某商场送货流程。仓库备货完成后流程分拆为两个并行的分支,一个分支通 知送货员送货;另一个线程启动短信发送程序,预约客户在家等候收货。如下图: 82 图iv-6.2.3-1 相关的流程定义文件见:And Split.xml。 流程模拟 通过模拟器可以模拟该流程,如下图。可以看到,在“仓库备货”环节完成后,两个分 支执行线程并行执行。 图iv-6.2.3-2 6.2.4. 分支选择业务 流程举例 分支选择业务 流程举例 分支选择业务 流程举例 分支选择业务 流程举例 在文档《2_通过设计器和模拟器快速了解 Fire Workflow》中,设计了一个典型的请假 流程。如果请假天数小于等于 3天,部门经理审批即可,否则需要送至公司经理审批。该流 程图如下。 83 图iv-6.2.4-1 流程定义文件是:LeaveApplicatonProcess_complex.xml 流程模拟: 请参阅“通过设计器和模拟器快速了解 Fire Workflow”章节。 6.2.5. 汇聚业务流程 举例 汇聚业务流程 举例 汇聚业务流程 举例 汇聚业务流程 举例 在上述“并行业务业务流程举例”中,结束节点实际上进行了汇聚。我们从模拟器可以 看到,当“仓库备货”环节完成后,“手机短信发送程序”被自动执行,此时结束节点的颜 色变成黄色,表示正在等待另一个分支完成。当送货分支完成后,结束节点变成蓝色,整个 流程实例执行完毕。 6.3.子流程 6.3.1. 流程设计 流程设计 流程设计 流程设计 在Fire Workflow 看来,子流程是父流程的一个任务。可以通过 创建一个附带 子流程任务的环节,也可以在已经存在的环节中增加子流程任务。 在本文提供的子流程实例如下图,在父流程和子流程中都定义了一个流程变量 xyz,在 下一节流程模拟中可以看到,父流程的流程变量的值传递给了子流程。 图iv-6.3.1-1 父流程图 84 图iv-6.3.1-2 子流程图 流程定义文件是:parent.xml,child.xml 6.3.2. 流程模拟 流程模拟 流程模拟 流程模拟 首先在父流程的模拟器界面启动父流程,并点击 设置流程变量 xyz 的值为 100,如下 图 图iv-6.3.2-1 签收并通过 Activity1.Task1 后,系统弹出选择文件的对话框,定位到 child.xml 流程定 义文件,然后确定。系统启动 Activity2.Task1,流程执行进程视图切换到 child 流程,同时 85 父流程的流程变量的值赋给了子流程。如下图。你可以通过工具栏右边的下拉列表在执行进 程视图中切换不同的流程。 图iv-6.3.2-2 签收并结束子流程,将视图切换当 parent,可以看到父流程的 Activity2.Task1 也结束了 , 并且启动了父流程的 Activity3.Task1。如下图 图iv-6.3.2-3 6.3.3. 关于 关于 关于 关于 ““““Multi-MergeMulti-MergeMulti-MergeMulti-Merge””””的探讨 的探讨 的探讨 的探讨 在“21 种”工作流模式中有一个种模式叫做“Multi-Merge”,其含义是在“汇聚点”后 的环节可以被执行多次,如下图: S t 1 Activity1 Activity2 Activity3 Activity4 t 2 86 图iv-6.3.3-1 在上图中如果流程分支 t1执行到汇聚点 S时,Activity3 和Activity4 会被执行一遍;同 理当 t2执行到汇聚点 S时,Activity3 和Activity4 也会被执行一遍。显然 S只是图形上的汇 聚,在运行时并未起到同步的作用。 在这种情况下,用Fire workflow 的子流程来建模可能更加合理。将Activity3 和Activity4 置于子流程中(假设子流程名称为 MyChildProcess),父流程如下图。 图iv-6.3.3-2 6.4.“自由流”(Jump)(Jump)(Jump)(Jump) 6.4.1. 流程设计 流程设计 流程设计 流程设计 “自由流”是一种中国特色的业务流程模式,即在某些特殊情况下,流程不按照既定顺 序执行,在环节间自由跳转。 在Fire Workflow 中,跳转操作还不能在设计器中通过图形表达,只能调用接口 IWorkItem.jumpTo(String nextActivityId, DynamicAssignmentHandler dynamicAssignmentHandler, String comments)完成。jumpTo 方法首先结束当前 WorkItem、 TaskInstance 和ActivityInstance,然 后 启 动 目 标 Activity,创 建 相 应 的 TaskInstance 和 WorkItem。 自由跳转是有条件的,其首要条件是当前环节和目标环节在同一个“执行线”上,如果 Fire Workflow 检查到当前环节和目标环节不在同一个执行线上,则拒绝执行跳转操作。 “执行线”是我创造的一个工作流名词,在图论中的定义是 Li(activity1)=Li(activity2)。 即:如果 activity1 的所有前驱节点和后继节点构成的集合等于 activity2 的所有前驱节点和后 继节点构成的集合,则称 activity1 和activity2 在同一个执行线上。如下图, Activity1 和 Activity2 在同一个执行线上, Activity1 和Activity5 也在同一个执行线上;但是, Activity1 和Activity3 不在同一个执行线上,Activity3 和Activity4 也不在同一个执行线上。 87 图iv-6.4.1-1 流程进行跳转的第二个条件是当前环 WorkItem 的结束操作可以触发对应的 TaskInstance 和ActivityInstance也正确结束,否则 Fire workflow 拒绝进行跳转操作。例如, 在会签的情况下,如果还有操作员没有完成他的工作项,则不能执行跳转操作,只有最后一 个完成工作项的操作员才具备执行跳转操作的条件。 自由流相关的流程定义文件见:Jump.xml 6.4.2. 流程模拟 流程模拟 流程模拟 流程模拟 下面模拟从环节 Activity1 直接跳转到 Activity5 的情形。 A)首先创建流程实例,并签收 Activity1.Task1。得到的流程执行进程视图如下 图iv-6.4.2-1 B)然后点击工具栏按钮 ,弹出对话框。在该对话框中输入目标环节的名称和操作员 ID,多个 ID通过逗号分隔。在有多个 ID的情况下,一般都需要签收,因此需要选中“Need Sign”复选框。如下图,有两个操作员,分别是“Zhangsan”和“Lisi”。 图iv-6.4.2-2 确定对话框后,流程跳转到 Activity5;同时从模拟数据视图中可以看到,系统生成了两 个WorkItem,分别赋值给 Zhangsan 和Lisi 如下图。 88 图iv-6.4.2-3 C)签收 Activity5.Task1。 在本例中,如果点击 来签收 Activity5.Task1,系统会报告如下错误:Sign workitem failed, NO todo work items for actor[actorId='Fireflow_Simulator'] and task[taskId='JumpTo.Activity5.Task1']。即,操作员 Fireflow_Simulator 在Activity5.Task1 上没 有待办任务。 必须点击“claim as ...”菜单来执行签收操作。如下图 图iv-6.4.2-4 点击“Sign as...”后,系统弹出对话框,要求输入操作员 ID,此时输入 Zhangsan(或 者Lisi)成功签收任务。如下图: 图iv-6.4.2-5 签收任务后,在模拟数据视图中可以看到“Zhangsan”的工作项变成了 Running 状 态 , 89 而Lisi 的工作项被删除了。这是因为 Acitivity5.Task1 不是的任务分配策略是“ANY”,如下 图;即任何一个操作员完成该任务即可,这种情况下,只要一个操作员签收了他的工作项, 其他的工作项将被删除。 图iv-6.4.2-6 D) 完成 Activity5.Task1 点击“Complete As...”,完成 Activity4.Task1,如下图: 图iv-6.4.2-7 90 6.4.3. 相关 的 相关 的 相关 的 相关 的 APIAPIAPIAPI 在Fire workflow 中,可以通过 IWorkItem 的如下方法实现跳转。 public void jumpTo(String targetActivityId) ; public void jumpTo(String targetActivityId,String comments); public void jumpTo(String targetActivityId, DynamicAssignmentHandler dynamicAssignmentHandler, String comments) ; 在IWorkflowSession 中,由于之相对应的等价的方法,如下 public void completeWorkItemAndJumpTo(String workItemId ,String targetActivityId) ; public void completeWorkItemAndJumpTo(String workItemId,String targetActivityId,String comments) ; public void completeWorkItemAndJumpTo(String workItemId,String targetActivityId, DynamicAssignmentHandler dynamicAssignmentHandler,String comments) ; 6.5.循环(Loop)(Loop)(Loop)(Loop) 在Fire workflow1.0 中,循环可以通过两种方式实现,一种方式把循环看作 “自由流” 的特例,即跳转到已经被执行过的环节上。 Fire Workflow 自动把工作项分派给上一次完成 该环节任务的操作者。 另一种方式是在流程图上用 Loop 元素将循环画出来。下面详细讲一下 Loop 元素的用 法。 6.5.1. 流程设计 流程设计 流程设计 流程设计 在workflow-patterns 项目中,有一个文件名为 Loop.xml 的流程实例,如下图 图iv-6.5.1-1 流程图中,虚线表示循环。循环线只能连接两个 Synchronizer 节点,而且必须是从后继 Synchronizer 节点指向前驱 Synchronizer 节点。 91 循环的执行条件默认为 fasle,即如果循环没有指定 condition,则永远不会被执行,这 个和 transition 恰恰相反。如果一个 synchronizer 上既有输出 transition,也有输出 loop,且执 行条件都为 true ,则 loop 拥有更高的优先级,transition 不会被执行。 如果一个 synchronizer 节点上有多个输出 loop,且执行条件都为 true,则 engine 仅仅会 执行其中一个,具体哪一个则是无法确定的。所以在流程设计时必须避免这种情况。 在一般情况下,进入循环后,循环体内的所有 Task 都会被重新执行一遍,但是业务中 也不能排除例外,即希望某个 Task 在整个流程实例中仅执行一次,即使在循环的情况下也 不被重复执行。在 Fire workflow 1.0 中, Task 增加了一个属性 Loop Strategy,如下图。 LoopStrategy 有3个取值,REDO 表示重做该 Task,对于 FormTask,其 WorkItem 会被自动 分配给上一次完成该 Task 的操作者;SKIP 表示该 Task 仅执行一次,在循环的情况下部被 执行; NONE 也表示重做 Task,对于 FormTask 而言,其 WorkItem 的分配仍然通过解析 AssignmentHandler 完成,而不是分配给上一次完成该 Task 的操作者。 图iv-6.5.1-2 LoopStrategy 对于通过 jumpTo 方法实现的循环也有效。 6.5.2. 流程模拟 流程模拟 流程模拟 流程模拟 Loop 实例的模拟效果如下图,从图中可以看到,在循环条件为 true时,流程实例进入 循环操作。 图iv-6.5.2-1 92 6.6.略过(Skip)(Skip)(Skip)(Skip) 6.6.1. 流程设计 流程设计 流程设计 流程设计 如下业务场景非常常见。 某审批业务规定:当金额小于等于 1万元的情况下需要科领导审批;当大于 1万元小于 等于 10 万元时,除了科领导审批外还要送处领导审批;如果金额大于 10 万元,则需要再往 上送到局领导审批。 对于这种业务模式有三种流程设计方法。 第一种方法是设计一个顺序流程 “申请-->科领导批-->处领导批-->局领导批”,然后由 业务代码根据申请的金额调用 IWorkItem.jumpTo(String nextActivityId, List nextActorIds) 跳过不必要的审批级别。 第二种方法是使用“委派”机制,操作员根据业务管理规则人为地将任务呈送(委派) 给上级领导。 第三种方法是利用“空环节”()实现略过,业务流程如下图 图iv-6.6.1-1 在该流程中预先定义了一个“预算金额”流程变量,在“科长审批”后的相关流程分支 中根据该变量进行判断。这个变量也可以不预先定义,因为 Fire Workflow 允许在任何时候 设置任何名称的流程变量,如果有同名的流程变量,则后值覆盖前值。 流程定义文件是:Skip.xml 6.6.2. 流程模拟 流程模拟 流程模拟 流程模拟 首先启动流程,然后点击 ,设置 流 程 变 量“预算金额”的值。假设我们设置的值为2500, 如下图。 93 图iv-6.6.2-1 在预算金额为 25000 的情况下,流程执行完毕后的视图如下,可以看到“处领导审批” 环节被执行了,而“局领导”审批环节被跳过了。 图iv-6.6.2-2 6.7.会签 6.7.1. 普通会签 普通会签 普通会签 普通会签 通常情况下,会签是通过设置 Task 的Assignment 属性来决定的,如果Assignment 属性 为ALL,则该任务实例必须等到所有的 WorkItem 结束后才会结束。Assignment 属性设置如 下图: 94 图iv-6.7.1-1 另一种情况会导致会签。在跳转的时候,如果下一个环节的 Actor Id有多个,且不需要 签收,则工作流引擎会忽略 Task 的Assignment 属性。读者可以在“自由流”章节按照如下 图所示的参数模拟跳转,即可看到会签的效果。 图iv-6.7.1-2 6.7.2. M:NM:NM:NM:N形式的会签 形式的会签 形式的会签 形式的会签 在实际业务中,有一种会签并不需要所有参与者都发表意见,只要 M个参与者中 N个 表达了某种意见即可结束相应的任务实例,流程实例继续向前流转。这种形式的会签在 fire workflow 中如何实现呢?Fire workflow1.0 提供了较好的实现方案。 在Fire workflow1.0 中,TaskInstance的生命周期划分为“创建”、“运行”、“结束”三个 阶段,每个阶段用一个接口来抽象,这些接口称为“生命周期接口”,如下: ITaskInstanceCreator:创建 task instance。 ITaskInstanceRunner:运行 task instance。 ITaskInstanceCompletionEvaluator:判断 task instance是否可以结束。 上述 M:N 形式的会签,实际上只要给该 Task 提供一个特殊的 ITaskInstanceCompletionEvaluator 实现类即可。该类检查 TaskInstance 是否具备结束条件, 95 如果具备条件,则工作流引擎结束 TaskInstance,继续向前流转。 在Fire workflow Example 中,有一个银行贷款审批流程,其中审核环节便是 M:N 会签。 业务需求如下:在审批环节有 3个审批人员独立审批,如果其中两个或者两个以上同意,则 可以贷款,否则拒绝贷款。在该 Example 中,设置 ITaskInstanceCompletionEvaluator 实现类 的界面如下。 图iv-6.7.2-1 6.8.取回与拒收 取回与拒收和自由流一样也是中国特色的流程特性之一。 取回的含义是:当前操作者在结束工作项后发现有错误,需要将已经发生的流转取消, 以便更正错误。 拒收的含义是:当前操作者因为某种原因拒绝接收工作项,将已经发生的流转退回到前 驱环节。 在预览版中,需要通过 IWorkItem.jumpTo(...)方法实现取回与拒收。1.0 版增加了两个直 接可以使用的API。取回的 API是IWorkItem.withdraw(...);拒收的 API是IWorkItem.reject(...)。 Fire workflow 认为,不能够依靠工作流智能地解决一切问题。曾有网友和我提到,如何 实现取回/拒收中事务补偿机制。我认为这个问题实在太复杂,与其花百倍的功夫去解决它, 还不如绕开它或者不给它发生的机会。因此, Fire workflow 对于取回/拒收做了一些约束, 在设计流程和开发中需要注意。 96 6.8.1. 可以实现取回 的场景举 例 可以实现取回 的场景举 例 可以实现取回 的场景举 例 可以实现取回 的场景举 例 6.8.1.1. 最简单的情形是顺序流转 如下图 图iv-6.8.1-1 Activity1.Task1 取回操作前 图iv-6.8.1-2 Activity1.Task1 取回操作后,当前活动的环节为 Activity1(绿色) 6.8.1.2. And-splitAnd-splitAnd-splitAnd-split 或者Or-split Or-split Or-split Or-split 情况下,亦可取回 如下图,Activity2 完成后,分支到 Activity3 和Activity4,则在 Acitivity2.Task2 上可以 执行取回操作。 图iv-6.8.1-3 Activity2.Task2 取回操作之前 97 图iv-6.8.1-4 Activity2.Task2 取回操作之后,当前活动环节变为 Activity2 6.8.1.3. 当仅有一个TransitionTransitionTransitionTransition被执行的OrOrOrOr汇聚, 如下图 图iv-6.8.1-5 Activity5.Task5 取回操作之前 图iv-6.8.1-6 Activity5.Task5 取回操作之后 98 6.8.1.4. 在自由跳转后,可以取回。 如下图,从 Activity1.Task1 跳转到 Activity7.Task7,则在 Activity1.Task1 可以执行取回 图iv-6.8.1-7Activity1.Task1 执行取回操作之前 图iv-6.8.1-8Activity1.Task1 执行取回操作之后 6.8.2. 不可以实现取 回的场景 举例 不可以实现取 回的场景 举例 不可以实现取 回的场景 举例 不可以实现取 回的场景 举例 6.8.2.1. 如果后继环节的 WorkItem WorkItem WorkItem WorkItem 已经作了签收操作,或者后继 WorkItem WorkItem WorkItem WorkItem 已经结束 取回的本质意义是结束自己的 WorkItem 后立即发现错误,马上更正;如果后继环节的 操作者已经签收,或者结束了后继 WorkItem,则说明他人已经改变了业务数据,这个时候 再收回控制权(做 withdraw 操作),是不合理的。如下图 5.1-1 中,Activity1.Task1 上可以做 取回操作,因为 Activity2.Task2 的工作项还处于 Initialized 状态(粉红色)。而5.1-2 中, Activity1.Task1 上不可以做取回操作,因为 Activity2.Task2 的工作项已经被签收(绿色)。 图5.1-3 中,Activity1.Task1 上也不可以做取回操作,因为 Activity2.Task2 的工作项已经结 束(蓝色)。 图iv-6.8.2-1 99 图iv-6.8.2-2 图iv-6.8.2-3 6.8.2.2. 当后继环节中包含 ToolTaskToolTaskToolTaskToolTask或者SubflowTaskSubflowTaskSubflowTaskSubflowTask,则不可以 取回 如下图, Activity7.Task7 的后继环节中存在 ToolTask,即 Activity9.Task13,此时在 Activity7.Task7 上不可以执行取回操作 。ToolTask 和SubflowTask 的撤销会带来上述所谓的 复杂的事务补偿问题,因此禁止取回。 图iv-6.8.2-4 6.8.2.3. 执行取回操作的Activity Activity Activity Activity 只能有一个TaskTaskTaskTask 例如下图中,Activity10.Task10 或者 Activity10.Task12 都不可以执行取回操作。这个规 定在实际中的含义是这样的:Activity10.Task10 和Activity10.Task12 可能是不同的操作员完 成的;如果允许 Activity10.Task10 取回,那么Fire workflow 会撤销 Activity11 上的所有 Task 100 实例, 并 且 重新 生 成 Activity10.Task10 和Activity10.Task12 的实例 ; 这 样就 “迫使 ” Activity10.Task12 的操作者重复劳动,尽管他之前的操作是正确的,显然这是不合理的。所 以Fire workflow 禁止这种取回操作。 当然在这个图中,因为 Activity11.Task13 是ToolTask,所以也不允许 Activity10 的Task 取回。 图iv-6.8.2-5 6.8.2.4. 如果当前环节之后执行了实质汇聚操作,则不可以取回 如下图,流程已经执行到 Activity5 ,而 Activity3 和Activity4 后执行了汇聚,所以 Activity3.Task3 和Activity4.Task4 上不可以执行取回操作。 图iv-6.8.2-6 那么什么是“实质的汇聚操作”呢?所谓实质就是 Synchronizer 在运行时汇聚了两个或 者两个以上的输入 Transition, 与之相对应的就是非实质的汇聚,即流程图画的是会聚,但 是运行时只有一个输入 transition 真正执行了。 6.8.3. 可以实现拒收 的场景举 例 可以实现拒收 的场景举 例 可以实现拒收 的场景举 例 可以实现拒收 的场景举 例 6.8.3.1. 顺序流转 如下图,顺序流转的情况下可以执行拒收操作。 101 图iv-6.8.3-1 Activity2.Task2 拒收操作前 图iv-6.8.3-2 Activity2.Task2 拒收操作后,当前活动的环节为 Activity1(绿色) 6.8.3.2. 在And-JoinAnd-JoinAnd-JoinAnd-Join 和OrOrOrOr Join Join Join Join 的情况下可以拒收, 如下图 图iv-6.8.3-3 Activity7.Task7 拒收操作之前 图iv-6.8.3-4 Activity7.Task7 拒收操作之后 102 6.8.3.3. 实质的汇聚,也可以拒收 如下图,Activity3、Activity4 实质汇聚到 Activity5,则 Activity5.Task5 可以执行拒收操 作。 图 iv-6.8.3-5 Activity5.Task5 拒收操作之前 图 iv-6.8.3-6 Activity5.Task5 拒收操作之后 6.8.3.4. JumToJumToJumToJumTo操作可以拒收 如下图,从 Activity1.Task1 跳转到 Activity7.Task7 图iv-6.8.3-7Activity7.Task7 执行拒收操作之前 103 图iv-6.8.3-8Activity7.Task7 执行拒收操作之后 6.8.4. 不可以实现拒 收的场景 举例 不可以实现拒 收的场景 举例 不可以实现拒 收的场景 举例 不可以实现拒 收的场景 举例 6.8.4.1. 前驱环节存在ToolTaskToolTaskToolTaskToolTask或者SubflowTaskSubflowTaskSubflowTaskSubflowTask 这种情况涉及到复杂的事务撤销, fire workflow 不允许做拒收操作,如下图;“审批” 环节不可以拒收,因为其前驱是一个 ToolTask。 图iv-6.8.4-1 6.8.4.2. Split Split Split Split 情况下,每个分支上的环节不可以执行拒收操作 如下图,在 Activity3.Task3 或者 Activity4.Task4 上不可以执行拒收操作。 图iv-6.8.4-2 6.8.4.3. 执行拒收操作的环节只可以含有1111个TaskTaskTaskTask 如下图,Activity10.Task10 或者 Activity10.Task12 不可以执行拒收操作。 104 图iv-6.8.4-3 6.9.委派 系统提供了如下接口完成任务委派 IWorkItem.reasignTo(String actorId) , IWorkItem.reasignTo(String actorId,String comments)。 Fire Workflow 仅提供完成委派的接口,并不会按照某种业务规则真正执行委派操作。 业务系统应该按照特定业务规则调用该接口实现委派。 6.10.任务完成期限 6.10.1. 流程设计、模 拟 流程设计、模 拟 流程设计、模 拟 流程设计、模 拟 如果 Task 指定了 Duration 属性,则工作流引擎会自动计算任务完成的期限。以 “Skip.xml”流程为例,如果“科长审批”的工作期限是 2个工作日,则 Duration 属性设置 如下图, 图iv-6.10.1-1 105 启动该流程后,从模拟数据视图可以看到,相关Task Instance的Expired Time 被自动计 算出来。如下图 图iv-6.10.1-2 6.10.2. 相关相关相关相关APIAPIAPIAPI 工作流引擎把获得系统时间、计算任务完成期限等工作委派给日历服务 org.fireflow.engine.calendar.ICalendarService,Fire workflow 提供了该接口的一个缺省实现。 业务系统可以实现该接口或者扩展 org.fireflow.engine.calendar.DefaultCalendarService来解决 用户自行调整上班时间、调整节假日的问题。更多信息请阅读:6_工作流引擎的结构及其扩 展.pdf 6.11.监听工作流事件 Fire Workflow 的流程实例、任务实例在状态发生变化时都会产生相应的事件,业务系 统可以在流程定义文件中注册相应的事件监听器来响应这些事件。 6.11.1. TaskInstance TaskInstance TaskInstance TaskInstance 事件监听器 事件监听器 事件监听器 事件监听器 Task Instance的事件监听器必须实现 org.fireflow.engine.event.ITaskInstanceEventListener 。该接口只有一个方法 public void onTaskInstanceFired(TaskInstanceEvent e)throws EngineException。可以通过 e.getEventType()来确定时间的类型。 事件监听器必须在设计时注册到 Task 的Event Listener 属性中,如下图 106 图iv-6.11.1-1 在模拟器中进行流程模拟时,如果 Task 定义了事件监听器,则会在控制台中打印出监 听器的名称,如下图 图iv-6.11.1-2 6.11.2. ProcessInstanceProcessInstanceProcessInstanceProcessInstance事件监听器 事件监听器 事件监听器 事件监听器 Process Instance 的事件监听器必须实现接口 org.fireflow.engine.event.IProcessInstanceEventListener,该接口也只有一个方法: public void onProcessInstanceFired(ProcessInstanceEvent e)throws EngineException 。可以通过 e.getEventType()来确定事件的类型。 事件监听器必须注册在 Workflow Process 的Event Listener 属性中,如下图 107 图iv-6.11.2-1 注:目前版本的模拟器并没有在模拟时将流程实例的事件监听器名称打印在控制台上。 108 V.附录 1.FireFireFireFire WorkflowWorkflowWorkflowWorkflow Examples Examples Examples Examples 与各 章节 的对应 关系 与各 章节 的对应 关系 与各 章节 的对应 关系 与各 章节 的对应 关系 所有的 Fire workflow Example 都可以从 Google Code 的svn 下载得到, svn 的地址是 httphttphttphttp://fireflow.googlecode.com/svn/trunk 。Fire workflow 在Google Code 的地址是 http://code.google.com/p/fireflow/。下图描述了各个 Example 的用途以及与本文各章节的对应 关系。 图v-1-1
还剩111页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 12 人已下载

下载pdf

pdf贡献者

leolian

贡献于2011-01-30

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf