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
。