1. 从哪儿可以得到OSWorkflow的有关工作流定义的资料?
答:下载之后,在OSWorkflow的docs目录下会有一些dtd文件。其中workflow_2_7.dtd就是2.7的工作流xml使用的dtd。在这个文件中,作者使用注释详细说明了每个元素的含义和作用。
2. 简单的描述一下OSWorkflow工作流定义的特点?
答:OSWorkflow把工作流视为一组关联的Step,通过执行与Step相关的Action,实现从一个Step到另一个Step的转换。从而引起工作流状态的转换,推动工作的前进。目前OSWorkflow并不支持子流程,这一点可以从它的dtd文件看出来。另一个主要的特点就是脚本(如beanshell)的支持,OSWorkflow的主要组件,如function、validator等,都支持使用脚本来编程。
3. 如何定义顺序结构?
答:Step1到Step2的转换,可以这样来看:在Step1上执行了一个Action,这个Action的结果是转向到Step2。因此,可以描述成:
<steps> <step name="Step1" id="1"> <actions> <action name="Step1_Action" id="11"> <results> <unconditional-result old-status="Finished" status="Waiting" step="2"/> </results> </action> </actions> </step> <step name="Step2" id="2"> ...... </step> <steps> |
注意,在OSWorkflow中基本上都是使用id,而不是name。对于status和old-status,前者表示转向到Step2后的状态值(Step2的状态),后者表示离开Step1后状态值(Step1的状态)。
另外,当转向的Step是类似于<step name="end" id="2"/>时,则流程会自动终止。当转向的Step中包含的Action集合中存在一个auto属性为true的action时,转向之后Action自动触发。如:
<step name="Step2" id="2"> <actions> <action name="Step2_Action" id="21" auto="true"> <results> <unconditional-result old-status="Finished" status="Ready1" split="100"/> </results> </action> </actions> </step> |
进入Step2后,Step2_Action将自动执行。
4. 如何定义并行工作中的Split和Join?
答:在状态机中表示并行工作的存在着2个特别的元素:分拆和合并。在OSWorkflow中与之对应的则是:<split>和<join>。从Step2分拆成2个并行任务(Step31,Step32),再合并并转向Step4。可以如此理解:在Step2上执行一个Action,其结果会导致Step31和Step32的产生;分别执行Step31和Step32的Action,转向合并点;合并点判断Step31和Step32都完成后,那么转向Step4。因此,可以描述为:
<steps> ...... <step name="Step2" id="2"> <actions> <action name="Step2_Action" id="21"> <results> <unconditional-result old-status="Finished" status="Ready1" split="100"/> </results> </action> </actions> </step> <step name="Split_Step" id="31"> <actions> <action name="Split_Step_Action" id="311"> <results> <unconditional-result old-status="finished" status="Ready2" join="200"/> </results> </action> </actions> </step> <step name="Split_Step" id="32"> <actions> <action name="Split_Step_Action" id="321"> <results> <unconditional-result old-status="finished" status="Ready3" join="200"/> </results> </action> </actions> </step> <step name="Join_Step" id="4"> ...... </step> </steps> <splits> <split id="100"> <unconditional-result old-status="finished" status="Ready5" step="31" /> <unconditional-result old-status="finished" status="Ready6" step="32" /> </split> </splits> <joins> <join id="200"> <conditions> <condition type="beanshell"> <arg name="script"> "finished".equals( jn.getStep(31).getStatus()) &&"finished".equals( jn.getStep(32).getStatus()); </arg> </condition> </conditions> <unconditional-result old-status="finished" status="Ready7" step="4" /> </join> </joins> |
注意:<steps>、<splits>和<joins>可以理解为<step>、<split>和<join>对应的集合。关键是<result>(或<unconditional-result>),使得这些离散的建立了关联,确定了状态转换的目标。
5. 如何建立分支和循环结构?
答:对于循环结构可以由分支结构来模拟,让我们主要来看看分支结构的实现。通常有2种方法:
- 使用外部程序,因为状态改变是Action的结果,而每个Step可以有多个Action。通过在外部程序中决定使用哪个Action,可以达到分支的效果。这个方法的本质实际是将分支判断放到外部程序中。如:
<steps> <step name="Step1" id="1"> <actions> <action name="Step1_Action1" id="11"> <results> <unconditional-result old-status="Finished" status="Waiting" step="2"/> </results> </action> <action name="Step1_Action2" id="12"> <results> <unconditional-result old-status="Finished" status="Waiting" step="3"/> </results> </action> </actions> </step> <step name="Step2" id="2"> ...... </step> <step name="Step3" id="3"> ...... </step> <steps> |
当处于Step1时,在外部程序如果调用action11,那么下一步就是Step2;如果是action12,下一步就是Step3。
- 使用带条件的结果,相对于上个方法而言,这个是使用OSWorkflow的内部机制。当然,内部条件使用的数据仍然还是由外部调用决定的。如:
<step name="Step2" id="2"> <actions> <action name="Step2_Action" id="21"> <results> <result old-status="Finished" status="Ready1" step="5"> <conditions> <condition type="beanshell"> <arg name="script"> null== transientVars.get("flag"); </arg> </condition> </conditions> </result> <unconditional-result old-status="Finished" status="Ready1" split="100"/> </results> </action> </actions> </step> |
上述描述相当于:
if(){
转向step5;
}else{
转向split100;
}
从OSWorkflow的DTD中,可以知道<results>的有关资料:<!ELEMENT results (result*, unconditional-result)>。当有多个带条件的result时,满足条件的第一个<result>起作用,当没有条件满足时< unconditional-result >起作用。
6. 如何实现自定义的功能?
答:OSWorkflow在工作流描述文件中提供了自定义功能的地方:
- function,主要在状态改变时发生作用,如状态改变时发送电子邮件通知。对应的元素<function>,用于<pre-functions>和<post-functions>中。针对<step>和<action>都可以使用,pre-function是进入<step>或<action>时起作用;post-functions是离开时起作用。例子:
- condition,自定义条件,如执行action的所需要的条件,在<conditions>中定义。针对<join>、< restrict-to >和<result>起作用。如果在<conditions>包含<conditions>,表示内嵌条件。
- validator,自定义验证,如验证参数或流程的状态,在<validators>中定义。针对<action>、<result>和<unconditional-result>起作用。
- register,为工作流的组件,如function,提供了存取另一个实体的方便操作。可以理解为这个实体的创建工厂,在<registers>中定义。针对工作流的其他组件都起作用。
上述组件都支持三种类型的定义:Java-based、beanshell和bsf,使用方法基本一致。以function为例:
<function type="beanshell"> <arg name="script"> System.out.println("prefunction 2 of action 11"); </arg> </function> | <function type="class"> <!-- 实现类,参见javadoc --> <arg name="class.name"> workflow.Prefunction1OfStep1 </arg> <arg name="msg">foxgem is ok!</arg> </function> |
同时OSWorkflow还提供了对应的一些预置组件,请参见相关的javadoc。为了方便数据的交互,OSWorkflow提供了一些内置的对象(对于脚本,直接在脚本中按下面的名字使用即可):
- transientVars,临时变量Map,包括:register、用户输入和当前的工作流上下文等信息。
- args,在文件的<arg>中定义的参数Map。
- propertySet,用于持久化的变量。
- jn,com.opensymphony.workflow.JoinNodes,表示合并的步骤集合。
上述对象,前三个可以使用在前面列出的所有组件中使用。最后一个主要在join中定义的conditions中使用,见前例。
7. 外部程序如何使用OSWorkflow?
答:外部程序使用OSWorkflow的基本步骤:
1) 定义工作流文件,并配置。OSWorkflow的配置文件基本上是三个层次:
osworkflow.xml,配置osworkflow的全局信息。如创建工厂,持久化机制等。
workflows.xml,指明工作流文件的集合
具体workflow定义,定义具体的工作流描述。
2) 创建工作流:Workflow flow= new BasicWorkflow("tester");。
3) 初始化指定的工作流。
//最后一个参数,可以传进一些数据供OSWorkflow的组件使用
long id= flow.initialize( "mytest", 1, null);
其中mytest,是在workflows.xml中定义的名字,对应具体的工作流定义文件。在OSWorkflow中有一组特定的Action,<initial-actions>,它的目的就是用来初始化流程的。典型的<initial-actions>定义如下:
<initial-actions> <action name="Start Workflow" id="1"> <results> <unconditional-result old-status="Finished" status="Queued" step="1"/> </results> </action> <action name="Another Entry" id="2"> <results> <unconditional-result old-status="Finished" status="Underway" step="2"/> </results> </action> </initial-actions> |
这其中的每个Action代表了不同的流程启动点。
4) 执行指定的动作,完成流程状态的切换。flow.doAction( flowid, 11, null);。其中flowid是初始化时得到的流程id;11是<action>的id,这个id应该是全局唯一的;最后一个参数,是用来给OSWorkflow传递参数用的外部输入。
以上就是OSWorkflow的基本使用,实际上最常用的就是2个方法:initialize和doAction。对于其他的方法和对象,请参见对应的javadoc。
8. 如何将持久化机制用于流程数据?
答:对于持久化的支持,OSWorkflow提供了WorkflowStore接口,可以让使用者自己实现自定义的持久化机制。对于数据库,使用JDBCStore即可。关于JDBCStore的配置主要集中于osworkflow.xml中,具体的配置方法,参见文档:1.4 Persistence Options。
9. 如何查询工作流?
答:主要的类型:
- 获得当前步骤和历史步骤列表:Workflow的getCurrentSteps和getHistorySteps。
- 获得当前可用的action:Workflow的getAvailableActions。
- 自定义查询:首先构造WorkflowExpressionQuery,然后使用Workflow的query。具体例子参见:文档5.4 Queries。