Osworkflow是完全用java语言编写的开放源代码的工作流引擎,具有显著的灵活性及完全面向有技术背景的用户的特点。用户可以根据自身的需求利 用这款开源软件设计简单或是复杂的工作流。通过使用,用户就可以把工作中心放在业务和规则的定义上,而不需通过硬编码的方式实现一个Petri网或是一个 有穷自动机。用户可以以最小的代价把osworkflow整合到自己的程序中来。Osworkflow几乎提供了所有用户可能在实际流程定义中需要用到的 工作流构成元素,如:环节(step)、条件(conditions)、循环(loops)、分支(spilts)、合并(joins)、角色 (roles)等等。(假如读者对这些概念还不熟悉,笔者将在Osworkflow基本概念一节中进行简单描述。)
但是,这款开源软件的文档十分匮乏,而且在大多数现实情形中并不适用。本文将尝试为读者填平实际的用例需求与十分简单的说明文档间的鸿沟。
用户可以在OpenSymphony的网站上下载osworkflow的发布。当前的最高版本是2.7(译者注:最新版本为2.8). 解压缩发布的软件包,即得到二进制程序、源代码、API文档、说明文档等。用户可以在软件的论坛和维基上获得进一步的帮助。
什么是工作流?
维基百科(Wikipedia,WP)把工作流定义为“一份工作的操作过程”:任务如何组成、如何操作、相关顺序如何、如何同步、信息如何流动以支持这些任务、以及任务如何被跟踪等。
一个工作流引擎实现了业务的流程处理。用户应可以自动跟踪过程,这将使得引擎更具效率。同时用户可以对工作流进行建模,监控及统计引擎数据等。
示例业务过程:贷款程序
本文的示例业务过程研究一个贷款应用程序的实例。我们将通过一个利用osworkflow工作流引擎的工作流来实现它。这个过程会在每家银行及金融机构中 出现, 其区别仅仅体现为有更过的部门或更多地文档需要处理. 在本示例中, 我们会用尽量简单的方法来实现这个业务流程以便于用户理解。图一描述了这个 业务流程
图一 贷款业务流程
过程非常简单,分为如下4步,如下所示:
1) 填写表格:银行客户填写表格申请贷款。
2) 风险分析:一位风险分析家评估不良贷款的风险。
3) 财务历史审查:财务管理官员负责检查客户历史贷款、应付账单、信用卡历史纪录等信息。
4) 最终决定(同意/拒绝):银行部门主管根据风险分析情况及财务历史审查情况最终决定是否贷款给该客户。
正如我们之前看到的,每个工作流都包含角色,每个角色都包含被分配的任务。下文说明了业务流程中涉及到的角色:
1) 前台职员:在某个银行部门向顾客提供信贷申请表的雇员。
2) 财务官员:负责检查申请者历史财务情况(往期贷款、未支付帐单等等)的职员。
3) 风险分析家:负责分析将钱给予借贷申请者的外部因素(比如社会经济情况等)及借贷者本人的个人情况。
4) 银行部门经理:负责最终决定是否给予借贷者贷款的经理。
请记住,“信贷申请表”是一个重要的概念(我们将在系统实现一节看到其重要性)因为它是流经整个工作流的业务数据。
基本工作流概念
笔者在开篇曾介绍osworkflow提供了一些特有的构造,现在笔者将逐一介绍它们。
首先,在osworkflow中读者需要了解得最重要的概念是环节,每个工作流包含了多个环节,读者可以把环节想象成工作流中每一个重要的活动。每个环节 可以有一些诸如“已完成”、“正在处理”、“已添加至处理队列”、“未处理”等的状态,设计工作流的人可以根据需要自己定义状态。
在每个环节,动作被用户指定为自动或手动地执行。每个动作执行后,都有一个结果(result)。结果决定了工作流的流转方向:可以停留在同一环节,跳转到另一环节,跳转到一个分支,或者汇集到一个合并等。
最后两个概念涉及用户对业务流程的并发执行,分支把工作流分解为两个并行的环节,合并则在用户满足一定条件后,把两个并行的环节合并成一个。
动 作的执行代表了业务流程的执行,每个动作都有一组预处理功能(pre-functions)和一组后处理功能(post-functions)。其作用正 如读者想象的那样,一个在动作触发之前执行,一个在动作触发之后执行。一个简单的例子是:可以在预处理功能中检验申请表格数据的正确性,而后在后处理贡功 能中把经检验的数据保存至数据库。
动作的执行结果可以是有条件的(conditional)或无条件的(unconditional)。对于有条 件的结果,引擎将首先检查是否条件被满足,然后再交给工作流来处理。如果条件不满足的话,引擎将进一步判断下一个有条件结果是否得到满足,以此类推,直到 系统最终执行到无条件结果进行处理。
读者可能会问,如果所有的条件结果都没有得到满足会如何呢?事实上,每个动作都强制要求具有唯一一个无条件结果。与此对应的,可以有多个有条件的结果。
业务规则常常在最终结果中带有条件判断,比如,“如果申请来自于一个老客户,则流转到环节1”或者“如果当前系统的用户的角色是经理的话,直接流转道最后一个环节”。
最 后一个重要的概念是步骤状态(process state),在osworkflow中,当前步骤状态是所有当前环节状态的集合。读者可能会认为工作流在 运行过程中只能有一个状态,但现实的情况是:因为对分支和合并的支持,引擎能够做到对环节的并发控制,因此工作流的当前状态就可能出现:“等待风险分析及 已核查财务历史”的情况。
激活动作的用户被顺理成章地称为触发者(caller),每个环节都有一个所有者(owner),以代表在当前环节中负责执行动作的角色或用户。
当用户在环节中运转流程的时候,已完成的环节被保存至历史表中(history),用户当前所处的环节成为当前环节(current steps)。
最 后,读者可能注意到,在osworkflow中并不存在其他工作流引擎中所包含的工作项(workitem)的概念。这是因为osworkflow是“十 分底层”的工作流实现,怎样实现或定义工作项完全交由用户来决定。笔者认为工作项的概念太过抽象,用业务数据来称呼它或许更为贴切一些。
Osworkflow 的文档中介绍了更多的构造元素,如寄存器(Registers),共用方法(common functions)等,但笔者建议在建立好第一个工作流以后 再去研究它们。它们是osworkflow基本元素外的高级特性,而我们前面所认识的元素则是osworkflow的根本所在。
Osworkflow体系结构
我们将在本节分析控制osworkflow的体系结构,我们需要理解它是怎样适用到我们的程序中来的。如我们所猜想的,Osworkflow最主要的接口是Workflow,这个接口也是整个工作流引擎的入口点。是整个系统的门面(facade)。
接口的实现主要关注具体的业务操作能力,这个接口定义了工作流查询,获取当前的可执行动作,执行动作,显示历史环节等。
工作流被持久化在工作流存储体(Workflow Store)中,osworkflow提供了几种持久化的方法,包括Hibernate持久化集成,JDBC持久化集成等。一个存储体包含了环节信息,变量,工作流自身的描述信息等等。
用户可能遇到的最常见的应用模式如下所示:
1) 通过给定的状态在工作流存储体中查询工作流信息,通常还根据某一个工作流程中具有需执行动作的用户来进行查询。这种查询时通过WorkflowQuery对象中的Workflow.query()方法实现的。
2) 通过getAvailableActions()方法列出所有在满足条件查询结果中可执行的操作。
3) 通过doAction()方法执行用户选择的动作。在执行动作的时候一些执行参数可以以java.util.Map的形式传递,以实现在工作流定义的运行期进行信息的传递。
4) 用户可以有选择地通过调用initialize()实例化一个工作流。
在理想情况下,由业务逻辑层负责调用osworkflow中的方法,如图二所示:
图二 在业务逻辑中集成工作流
在osworkflow 中,业务逻辑描述在一个XML文件中,称为工作流描述符(workflow descriptor.)。我们将在实现小节中建立一个简单的描述符。在工作 流描述符中的功能(functions)和条件(conditions)中,用户可以定义自己的业务逻辑。笔者将在把工作流集成到应用程序中一节中进行论 述。
实现
本小节介绍如何把一个业务逻辑抽象成一个工作流。首先我们要在业务流程图中识别出工作流的环节。如图一所示,显然,我们共有四个环节,同时包含一个分支及一个合并。在下面的bank.xml文件中,读者将看到它们在描述符中是如何被表示的。
建立好环节以后,必须在每个环节中添加一些动作以便于工作流运转。每个动作有唯一的无条件结果,条件结果由读者有选择地来实现。
<step id="1" name="Form Filling">
<actions>
<action id="2" name="Fill Form">
<results>
<unconditional-result old-status="Finished" split="1"/>
</results>
</action>
</actions>
</step>
接下来要并行地执行风险分析和财务历史核查,这里是放置分支的最理想地点。
<splits>
<split id="1">
<unconditional-result old-status="Finished"
status="Underway" owner="Risk Analyst" step="2"/>
<unconditional-result old-status="Finished"
status="Underway" owner="Financial Officer" step="3"/>
</split>
</splits>
在部门经理最终确认以前,并发的工作流环节必须得到合并。我们可以通过应用一个合并(join)来实现它,合并通过一个条件告诉工作流引擎,是否可以合并 并进行到下一环节。在本例中,我们假设这个条件为:前面两个环节都已具有“Finished”的结束状态。即当风险分析或财务审核任何一个未完成前,不能 进行到下一步:
<joins>
<join id="1">
<conditions type="AND">
<condition type="beanshell">
<arg name="script"><![CDATA[
"Finished".equals(jn.getStep(2).getStatus()) &&
"Finished".equals(jn.getStep(3).getStatus())
]]></arg>
</condition>
</conditions>
<unconditional-result old-status="Finished"
status="Underway" owner="Manager" step="4"/>
</join>
</joins>
下面在描述符中加入每个环节的所有者,正如我们在基本概念一节看到的那样,所有者通常代表了环节间交互的角色,角色的引入默认情况下通过osuser框架来实现。
用户既可以手写XML描述符文件,也可以通过osworkflow提供的设计器来实现。读者可以在OpenSymphony的网站上试用这个工具。
测试实现
测试时,我们可以应用osworkflow提供的例子,把bank.xml放到WEB-INF/classes文件夹下,在文件workflows.xml中添加一行以使引擎能够识别这个工作流。
把工作流集成到应用程序
在建模及测试工作流之后,我们即可以通过下面的几行代码把osworkflow集成到我们的程序中。
Workflow wf = new BasicWorkflow(username);
HashMap inputs = new HashMap();
inputs.put("docTitle", request.getParameter("title"));
wf.initialize("workflowName", 1, inputs);
inputs 哈希表包含了初始工作流动作中需要传出的参数,有几个实现了Workflow接口的类,其中BasicWorkflow是不支持事务的简单实现。在工作流 执行过程中可以在流程中调用外部的方法,这种方法应该是实现了FunctionProvider接口的方法类。然后我们就可以用以下的方式调用它:
<action id="1" name="Execute business rule">
<pre-functions>
<function type="class">
<arg name="class.name">java.net.DroolsExecutorFunction</arg>
<arg name="ruleBaseName">BusinessRules.drl</arg>
</function>
</pre-functions>
...
用户可以通过实现Condition接口,添加自己的条件控制。FunctionProvider及Condition接口可以调用工作流中的已知方法,这两个接口都可以接受来自于XML描述符文件中的参数。
结论
把一个业务流程抽象成一个工作流的任务并不容易,需要好的方法和合适的工具,osworkflow是一个为我们提供了许多可重用结构的理想工具。希望通过 对本文的阅读,读者能够理解最基本的osworkflow概念。本文论述过程中所采用的方法非常基础和简单,但却值得借鉴。