一、工作流的概念
1. 步骤、状态和动作
工作流要描述步骤(step)、步骤的状态(status)、各个步骤之间的关系以及执行各个步骤的条件和权限,每个步骤中可以含有一个或多个动作(action),动作将会使一个步骤的状态发生改变。
对于一个执行的工作流来讲,步骤的切换是不可避免的。一个工作流在某一时刻会有一个或多个当前步骤,每个当前步骤都有一个状态值,当前步骤的状态值组成了工作流实例的状态值。一旦完成了一个步骤,那么这个步骤将不再是当前步骤(而是切换到一个新的步骤),通常一个新的当前步骤将随之建立起来,以保证工作流继续执行。完成了的步骤的最终状态值是用old-status属性指定的,这个状态值的设定将发生在切换到其他步骤之前。old-status的值可以是任意的,但在一般情况下,我们设置为Finished。
切换本身是一个动作(action)的执行结果。每个步骤可以含有多个动作,究竟要载入哪个动作是由最终用户、外部事件或者triggerd的自动调用决定的。随着动作的完成,一个特定的步骤切换也将发生。动作可以被限制在用户、用户组或当前状态。每一个动作都必须包含一个unconditional result和0个或多个conditional results。
所以,总体来说,一个工作流由多个步骤组成。每个步骤有一个当前状态(例如:Queued, Underway, or Finished),一个步骤包含多个动作。每个步骤含有多个可以执行的动作。每个动作都有执行的条件,也有要执行的函数。动作包含有可以改变状态和当前工作流步骤的results。
2. Results, Joins, and Splits
(1) Unconditional Result
对于每一个动作来讲,必须存在一个Unconditional Result。一个result是一系列指令,这些指令将告诉OSWorkFlow下一个任务要做什么。这包括使工作流从一个状态切换到另一个状态。
(2) conditional Result
conditional Result是unconditional Result的一个扩展。它需要一个或多个condition子标签。第一个为true的conditional(使用AND或OR类型),会指明发生切换的步骤,这个切换步骤的发生是由于某个用户执行了某个动作的结果导致的。
(3) 三种不同的Results(conditional or unconditional)
--一个新的、单一的步骤和状态的组合。
--一个分裂成两个或多个步骤和状态的组合。
--将这个和其他的切换组合成一个新的单一的步骤和状态的组合。
每种不同的result对应了不同的xml描述,你可以阅读http://www.opensymphony.com/osworkflow/workflow_2_6.dtd,获取更多的信息。
注意:通常,一个split或一个join不会再导致一个split 或 join的发生。
① 单一步骤和状态的结果可以这样描述:
<unconditional-result old-status="Finished" step="2" status="Underway" owner="${someOwner}"/>
如果状态不是Queued的话,那么第三个必要条件就是新步骤的所有者(owner)。除了可以指明下一个状态的信息之外,result也可以指定validators和 post-functions,这将在下面讨论。
② 从一个状态分裂成多个状态可以这样描述:
<unconditional-result split="1"/>
...
<splits>
<split id="1">
<unconditional-result old-status="Finished" step="2"
status="Underway" owner="${someOwner}"/>
<unconditional-result old-status="Finished" step="2"
status="Underway" owner="${someOtherOwner}"/>
</split>
</splits>
③ 将多个状态合并为一个状态可以这样描述:
<!-- for step id 6 ->
<unconditional-result join="1"/>
...
<!- for step id 8 ->
<unconditional-result join="1"/>
...
<joins>
<join id="1">
<conditions type="AND">
<condition type="beanshell">
<arg name="script">
"Finished".equals(jn.getStep(6).getStatus()
&& "Finished".equals(jn.getStep(8).getStatus())
</arg>
</condition>
</conditions>
<unconditional-result old-status="Finished" status="Underway" owner="test" step="2"/>
</join>
</joins>
上面的描述也许有点含糊,但是你最应该关注的是condition标签,它使用一个"jn"的变量,利用这个变量,你可以组成表达式来决定合并动作发生的条件。这个表达式的意思是说:“当id=6和id=8的步骤的状态都变成Finished的状态并且他们要发生的切换都是join="1"时,这个合并动作才发生”。
3. 外部函数
OSWorkFlow为要定义和执行的外部业务逻辑定义了一个标准的解决方法。这是通过使用"functions"来实现的。OSWorkFlow有两种function,pre和post step functions。
Pre functions是在流程的步骤发生切换之前执行的。一个例子是取得调用者名字的函数,将取得的调用者的名字作为要发生状态切换的result使用。另外一个例子是pre-function用来更新大多数的动作的最近调用者。这两个函数是做为标准的工具函数提供的,在实际的工作流中非常实用。
Post functions与Pre functions有着同样的适用范围,不同点在于Post functions是在状态发生变化之后执行的。一个典型的Post functions的例子是:在某个动作执行完毕后,给某些人发送电子邮件。例如:当一个文档处于'research'步骤,并且执行了'markReadyForReview'动作的时候,要发送邮件给所有的reviewers。
使用Post functions与Pre functions可能有很多原因。一个是如果用户点击完成按钮两次而且发送了两次执行动作的指令,而且这个动作的Pre function会执行很长时间,那么这个长时间执行的函数可能会被执行多次,因为切换动作还没有发生,这时OSWorkflow会认为第二次执行动作是合理的。所以要将这个函数改变成Post function。一般情况下,Pre function应该是简单的、能快速执行的函数。
函数可以定义在两个独立的位置:步骤(step)和动作(action)。
通常情况下,Post functions与Pre functions被定义在action中。在action中定义的Pre-functions是指在该动作发生之前要执行的函数。当工作流发生切换的时候,函数用来做某些事情,不管是发送E_mail也好,还是为以后的使用设置变量。
当Post functions与Pre functions被定义在step中的时候,用途就有点不同了。在step中定义的Pre-functions是指在切换到这个step之前要执行
的函数。注意:这些函数将被应用到这个步骤的所有的切换,即使是由于这个步骤本身发起的切换,例如:在同一个步骤内,由Queued状态转移到Underway状态时,也将调用所有的 Pre functions。与此相同,在step中的Post functions将在流程离开这个步骤之前。
4. Trigger Functions
Trigger Functions与其他函数一样,不同点在于他们不是与一个动作相联系的。他们也是通过一个唯一的ID来标识。这些函数通常是运行在系统级用户或非正规用户的环境中。Trigger functions是通过OSWorkflow的API来使用。
5. Validators
validator 是用来校验一个动作的输入的有效性的。如果输入符合条件,那么将执行这个动作。如果输入不符合条件,那么将抛出InvalidInputException异常。
6. Registers
A register 是一个辅助函数,这个函数可以返回一个对象,这个对象可以用在函数中去访问其他的普通对象,特别是出现在workflow中的实体。被注册的对象可以是任何类型,典型的注册对象的例子是:Document, Metadata, Issue, 和Task。
下面是一个registers的例子:
<registers>
<register name="doc" class="com.acme.DocumentRegister"/>
</registers>
...
<results>
<result condition="doc.priority == 1" step="1" status="Underway"
owner="${someManager}"/>
<unconditional-result step="1" status="Queued"/>
</results>
...
7. Conditions
Conditions就象validators,registers和functions一样,可以用不同的语言和技术来实现。Conditions可以用AND或OR逻辑运算符来组织。Conditions通常是与conditional results联系的,只有当条件成立,result才会执行。
Conditions与函数很相似,唯一不同的是Conditions返回的是boolean值,而不是void。
8. Variable Interpolation
在所有的functions、conditions、validators和registers中,可能要提供一系列的参数。这些参数将会被转化为参数Map,这将在后面讨论。同样,在workflow描述符中的status,old-status和owner标签也将被动态的解析成变量。一个变量是这样被识别的:${foo}。当OSWorkflow识别出这种格式的时候,它首先到transientVars中去找关键字为foo的对象,如果没有找到,那么就到propertySet中去找,如果也没找到,那么变量foo将被转换为一个空字串。
One thing of particular importance is that in the case of args, if the variable is the only argument, the argument will not be of type String, but instead whatever the variable type is. However, if the arg is a mix of characters and variables, the entire argument is converted to String no matter what. That means the two arguments below are very different in that foo is a Date object and bar is a String:
<arg name="foo">${someDate}</arg>
<arg name="bar"> ${someDate} </arg> <!-- 注意多余的空格 -->
9. Permissions and Restrictions
Permissions can be assigned to users and/or groups based on the state of the workflow instance. These permissions are unrelated to the functionality of the workflow engine, but they are useful to have for applications that implement OSWorkflow. For example, a document management system might have the permission name "file-write-permission" enabled for a particular group only during the "Document Edit" stage of the workflow. That way your application can use the API to determine if files can be modified or not. This is useful as there could be a number of states within the workflow where the "file-write-permission" is applicable, so instead of checking for specific steps or conditions, the check can simply be made for a particular permission.
10. Auto actions
有的时候,我们需要一些动作可以基于一些条件自动地执行。为了达到这个目的,你可以在action中加入auto="true"属性。流程将考察这个动作的条件和限制,如果条件符合,那么将执行这个动作。 Auto action是由当前的调用者执行的,所以将对该动作的调用者执行权限检查。
11. Integrating with Abstract Entities
建议在你的核心实体中,例如"Document" 或 "Order",在内部创建一个新的属性:workflowId。这样,当新的"Document" 或 "Order"被创建的时候,它能够和一个workflow实例关联起来。那么,你的代码可以通过OSWorkflow API查找到这个workflow实例并且得到这个workflow的信息和动作。
12. Workflow Instance State
有的时候,为整个workflow实例指定一个状态是很有帮助的,它独立于流程的执行步骤。OSWorkflow提供一些workflow实例中可以包含的"meta-states"。这些"meta-states"可以是CREATED, ACTIVATED, SUSPENDED, KILLED 和COMPLETED。当一个工作流实例被创建的时候,它将处于CREATED状态。然后,只要一个动作被执行,它就会自动的变成ACTIVATED状态。如果调用者没有明确地改变实例的状态,工作流将一直保持这个状态直到工作流结束。当工作流不可能再执行任何其他的动作的时候,工作流将自动的变成COMPLETED状态。
然而,当工作流处于ACTIVATED状态的时候,调用者可以终止或挂起这个工作流(设置工作流的状态为KILLED 或 SUSPENDED)。一个终止了的工作流将不能再执行任何动作,而且将永远保持着终止状态。一个被挂起了的工作流会被冻结,他也不能执行任何的动作,除非它的状态再变成ACTIVATED。