函数
接下来,我们想在一个用户开始撰写草稿以后,设置他为“owner”。为了达到这样的目的,我们需要做2件事情:
1) 通过一个函数设置“caller”变量在当前的环境设置里。
2) 根据“caller”变量来设置“owner”属性。
函数是Workflow的一个功能强大的特性。函数基本上是一个在工作流程中的工作单位,他不会影响到流程本身。举例来说,你可能有一个“SendEmail”的函数,用来在某些特定的流程流转发生时来发送email提醒。
函数也可以用来添加变量到当前的环境设置里。变量是一个指定名称的对象,可以用来在工作流中被以后的函数或者脚本使用。
Workflow提供了一些内置的常用函数。其中一个称为“Caller”,这个函数会获得当前调用工作流的用户,并把它放入一个名为“caller”的字符型变量中。
因为我们需要追踪是哪个用户开始了编写草稿,所以可以使用这个函数来修改我们的动作定义:
<action id="2" name="Start First Draft">
<pre-functions>
<function type="type">
<arg name="type.name">DotNetTools.Workflow.Util.Caller,DotNetTools.Workflow</arg>
</function>
</pre-functions>
<results>
<unconditional-result old-status="Finished" status="Underway"
step="1" owner="${caller}"/>
</results>
</action>
组合起来
把这些概念都组合起来,这样我们就有了动作2:
<action id="2" name="Start First Draft">
<restrict-to>
<conditions>
<condition type="type">
<arg name="type.name">
DotNetTools.Workflow.Util.StatusCondition,DotNetTools.Workflow
</arg>
<arg name="status">Queued</arg>
</condition>
</conditions>
</restrict-to>
<pre-functions>
<function type="type">
<arg name="type.name">
DotNetTools.Workflow.Util.Caller,DotNetTools.Workflow
</arg>
</function>
</pre-functions>
<results>
<unconditional-result old-status="Finished" status="Underway"
step="1" owner="${caller}"/>
</results>
</action>
我们使用类似想法来设置动作3:
<action id="3" name="Finish First Draft">
<restrict-to>
<conditions type="AND">
<condition type="type">
<arg name="type.name">
DotNetTools.Workflow.Util.StatusCondition,DotNetTools.Workflow
</arg>
<arg name="status">Underway</arg>
</condition>
<condition type="type">
<arg name="type.name">
DotNetTools.Workflow.Util.AllowOwnerOnlyCondition,DotNetTools.Workflow
</arg>
</condition>
</conditions>
</restrict-to>
<results>
<unconditional-result old-status="Finished" status="Queued" step="2"/>
</results>
</action>
在这里我们指定了一个新的条件:“allow owner only”。这样能够保证只有开始撰写这份草稿的用户才能完成它。而状态条件确保了只有在“Underway”状态下的流程才能调用“finish first draft”动作。
把他们组合在一起,我们就有了第一个流程定义:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE workflow PUBLIC "-//dotnettools.org//DTD Workflow 2.8//EN" "http://www.dotnettools.org/workflow/workflow_2_8.dtd">
<workflow>
<initial-actions>
<action id="1" name="Start Workflow">
<results>
<unconditional-result old-status="Finished" status="Queued" step="1"/>
</results>
</action>
</initial-actions>
<steps>
<step id="1" name="First Draft">
<actions>
<action id="2" name="Start First Draft">
<restrict-to>
<conditions>
<condition type="type">
<arg name="type.name">
DotNetTools.Workflow.Util.StatusCondition,DotNetTools.Workflow
</arg>
<arg name="status">Queued</arg>
</condition>
</conditions>
</restrict-to>
<pre-functions>
<function type="type">
<arg name="type.name">
DotNetTools.Workflow.Util.Caller,DotNetTools.Workflow
</arg>
</function>
</pre-functions>
<results>
<unconditional-result old-status="Finished" status="Underway"
step="1" owner="${caller}"/>
</results>
</action>
<action id="3" name="Finish First Draft">
<restrict-to>
<conditions type="AND">
<condition type="type">
<arg name="type.name">
DotNetTools.Workflow.Util.StatusCondition,DotNetTools.Workflow
</arg>
<arg name="status">Underway</arg>
</condition>
<condition type="type">
<arg name="type.name">
DotNetTools.Workflow.Util.AllowOwnerOnlyCondition,DotNetTools.Workflow
</arg>
</condition>
</conditions>
</restrict-to>
<results>
<unconditional-result old-status="Finished" status="Queued" step="2"/>
</results>
</action>
</actions>
</step>
<step id="2" name="finished" />
</steps>
</workflow>
现在这个工作流的定义已经完整了,让我们来测试和检查它的运行。
现在我们已经完成了一个完整的工作流定义,下一步是检验它是否按照我们预想的方式执行。
在一个快速开发环境中,最简单的方法就是写一个测试案例。通过测试案例调用工作流,根据校验结果和捕捉可能发生的错误,来保证流程定义的正确性。
我们假设你已经熟悉Junit和了解怎样编写测试案例。如果你对这些知识还不了解的话,可以去JUnit的网站查找、阅读相关文档。编写测试案例会成为你的一个非常有用的工具。
在开始载入流程定义、调用动作以前,我们需要配置Workflow的数据存储方式和定义文件的位置等。