在《step2详解》中,我们介绍了split和join的使用,其实还是有一些细节的东西存在遗漏,所以决定增加着一个小节作为补充,在开始之前,我们先来思考两个问题
第一:在初始化之后是否就可以进行split操作
第二:split和join是否存在一些逻辑的影响,也就是说当某个条件成立执行split,当某个条件不成立则执行其他的step。
上述两种情况,在工作流中是最合理不过的需求了比如,我们就希望某个工作任务开启的瞬间,需要两个前置条件才能让他向下流转,我们也存在当提交某个申请时,通过与拒绝存在较大的差异,通过则需要split,拒绝则继续到某个step中。这样说也许有一点不太具象,我们通过状态图和配置文件以及测试代码来进行进一步的分析。
上面的图描述了一个从一开始就进入split的情况,然后当两个动作执行完毕之后,再一次合并,到了一个新的动作状态,我们通过配置文件和junit测试代码实现以下;
workflow的配置文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.7//EN" "http://www.opensymphony.com/osworkflow/workflow_2_6_1.dtd">
<workflow>
<initial-actions>
<action id="100" name="初始化">
<results>
<unconditional-result old-status="Finished"
status="Underway" split="1" />
</results>
</action>
</initial-actions>
<steps>
<step id="1" name="第一个步骤">
<actions>
<action id="1" name="你一个步骤的动作">
<restrict-to>
<conditions type="AND">
<condition type="class">
<arg name="class.name">
com.opensymphony.workflow.util.StatusCondition
</arg>
<arg name="status">Underway</arg>
<arg name="stepId">2</arg>
</condition>
</conditions>
</restrict-to>
<results>
<unconditional-result old-status="Finished"
status="Underway" join="1" />
</results>
</action>
</actions>
</step>
<step id="2" name="第二个步骤">
<actions>
<action id="2" name="第二个步骤的动作">
<restrict-to>
<conditions type="AND">
<condition type="class">
<arg name="class.name">
com.opensymphony.workflow.util.StatusCondition
</arg>
<arg name="status">Underway</arg>
<arg name="stepId">2</arg>
</condition>
</conditions>
</restrict-to>
<results>
<unconditional-result old-status="Finished"
status="Underway" join="1" />
</results>
</action>
</actions>
</step>
<step id="3" name="结束了"></step>
</steps>
<splits>
<split id="1">
<unconditional-result old-status="Finished"
status="Underway" step="1" />
<unconditional-result old-status="Finished"
status="Underway" step="2" />
</split>
</splits>
<joins>
<join id="1">
<conditions type="AND">
<condition type="beanshell">
<arg name="script"><![CDATA[
"Finished".equals(jn.getStep(1).getStatus())
&& "Finished".equals(jn.getStep(2).getStatus())
]]></arg>
</condition>
</conditions>
<unconditional-result old-status="Finished"
status="Underway" owner="test" step="3" />
</join>
</joins>
</workflow>
Junit测试代码如下所示:
package com.wangwenjun.osworkflow;
import java.util.Collections;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.opensymphony.workflow.InvalidActionException;
import com.opensymphony.workflow.InvalidEntryStateException;
import com.opensymphony.workflow.InvalidInputException;
import com.opensymphony.workflow.InvalidRoleException;
import com.opensymphony.workflow.Workflow;
import com.opensymphony.workflow.WorkflowException;
import com.opensymphony.workflow.basic.BasicWorkflow;
import com.opensymphony.workflow.spi.SimpleStep;
import com.opensymphony.workflow.spi.WorkflowEntry;
/**
* This class is for test step element on flow configuration.
* @author Alex(QQ:532500648)
*/
public class StepTest {
private Workflow wf = null;
@Before
public void init() {
wf = new BasicWorkflow("step");
}
@Test
public void testJoinInInitial()
{
long id;
try {
id = wf.initialize("joinsplit2", 100, null);
List currentSteps=wf.getCurrentSteps(id);
/*由于一开始就拆分了两个,所以应该有两个可用的step*/
Assert.assertEquals(2, currentSteps.size());
/*执行其中的一个步骤动作*/
wf.doAction(id, 1, null);
/*当前的历史执行步骤有一个step对象*/
List historySteps = wf.getHistorySteps(id);
Assert.assertEquals(1, historySteps.size());
/*执行完一个,应该还剩下另外一个*/
currentSteps=wf.getCurrentSteps(id);
Assert.assertEquals(1, currentSteps.size());
wf.doAction(id, 2, null);
/*
* 当前的历史执行步骤有两个step对象?
* 你错了,其实是三个,因为执行完毕2之后有进入了合并,然后理所当然的到了step3
* */
historySteps = wf.getHistorySteps(id);
Assert.assertEquals(3, historySteps.size());
} catch (InvalidActionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidRoleException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidInputException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidEntryStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (WorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
public void testJoinAndSplit()
{
long id ;
try {
id = wf.initialize("joinsplit", 100, null);
List currentSteps = wf.getCurrentSteps(id);
/*系统初始化之后只有一个step可以执行,那就是step,也就是请假单发起的步骤*/
Assert.assertEquals(1, currentSteps.size());
Assert.assertEquals(1,((SimpleStep)currentSteps.get(0)).getId());
/*执行请假申请流程*/
wf.doAction(id, 1, Collections.EMPTY_MAP);
/*接下来应该到直接上级*/
currentSteps = wf.getCurrentSteps(id);
Assert.assertEquals(1, currentSteps.size());
/*直接上级的step为2*/
Assert.assertEquals(2,((SimpleStep)currentSteps.get(0)).getId());
/*获取执行历史*/
List historySteps = wf.getHistorySteps(id);
/*只有一个执行历史*/
Assert.assertEquals(1, currentSteps.size());
/*直接上级审批通过*/
wf.doAction(id, 2, Collections.EMPTY_MAP);
/*接下来应该到了两位部门领导哪里,因此需要执行的步骤应该有两个才对*/
currentSteps = wf.getCurrentSteps(id);
Assert.assertEquals(2, currentSteps.size());
/*部门领导A审批*/
wf.doAction(id, 4, Collections.EMPTY_MAP);
/*部门领导B审批*/
wf.doAction(id, 5, Collections.EMPTY_MAP);
/*接下来应该到了最高领导哪里,因此需要执行的步骤应该有1个才对*/
currentSteps = wf.getCurrentSteps(id);
Assert.assertEquals(1, currentSteps.size());
/*最高领导审批*/
wf.doAction(id, 6, Collections.EMPTY_MAP);
/*流程执行完毕*/
Assert.assertEquals(WorkflowEntry.COMPLETED, wf.getEntryState(id));
historySteps = wf.getHistorySteps(id);
Assert.assertEquals(5, historySteps.size());
} catch (InvalidActionException e) {
e.printStackTrace();
} catch (InvalidRoleException e) {
e.printStackTrace();
} catch (InvalidInputException e) {
e.printStackTrace();
} catch (InvalidEntryStateException e) {
e.printStackTrace();
} catch (WorkflowException e) {
e.printStackTrace();
}
}
/*@Test
public void testPreFunctionOnStep()
{
long id;
try {
id = wf.initialize("step", 1, null);
Determine the prekey value whether is equal
Assert.assertEquals("prevalue",wf.getPropertySet(id).getString("prekey"));
wf.doAction(id, 2, null);
after execute action
Assert.assertEquals("prevalue",wf.getPropertySet(id).getString("prekey"));
} catch (InvalidActionException e) {
e.printStackTrace();
} catch (InvalidRoleException e) {
e.printStackTrace();
} catch (InvalidInputException e) {
e.printStackTrace();
} catch (InvalidEntryStateException e) {
e.printStackTrace();
} catch (WorkflowException e) {
e.printStackTrace();
}
}*/
@Test
public void testPostFunctionOnStep()
{
long id;
try {
id = wf.initialize("step", 1, null);
/*Determine the prekey value whether is equal*/
Assert.assertEquals("prevalue",wf.getPropertySet(id).getString("prekey"));
wf.doAction(id, 2, null);
/*after execute action value not equals*/
Assert.assertFalse("prevalue".equals(wf.getPropertySet(id).getString("prekey")));
/*prevalue changed is our excepted.*/
Assert.assertEquals("changed",wf.getPropertySet(id).getString("prekey"));
} catch (InvalidActionException e) {
e.printStackTrace();
} catch (InvalidRoleException e) {
e.printStackTrace();
} catch (InvalidInputException e) {
e.printStackTrace();
} catch (InvalidEntryStateException e) {
e.printStackTrace();
} catch (WorkflowException e) {
e.printStackTrace();
}
}
}
好了,再来写一个Step结合split进行流程跳转的例子吧,如果大致懂了配置文件,下面的配置信息还是比较简单的,您一看就能明白
<?xml version="1.0" encoding="UTF-8"?>执行的测试代码如下所示:
<!DOCTYPE workflow PUBLIC
"-//OpenSymphony Group//DTD OSWorkflow 2.8//EN"
"http://www.opensymphony.com/osworkflow/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 Part">
<actions>
<action id="2" name="Finish First Part">
<results>
<unconditional-result old-status="Finished" status="Queued" split="1"/>
</results>
</action>
</actions>
</step>
<step id="2" name="Second Part">
<actions>
<action id="3" name="Back to First Part">
<results>
<unconditional-result old-status="Rejected" status="Queued" step="1"/>
</results>
</action>
<action id="4" name="Finish">
<results>
<unconditional-result old-status="Finished" status="Queued" join="1"/>
</results>
</action>
</actions>
</step>
<step id="3" name="Another Second Part">
<actions>
<action id="5" name="Back to First Part">
<results>
<unconditional-result old-status="Rejected" status="Queued" step="1"/>
</results>
</action>
<action id="6" name="Finish">
<results>
<unconditional-result old-status="Finished" status="Queued" join="1"/>
</results>
</action>
</actions>
</step>
<step id="4" name="End">
<actions>
<action id="7" name="Finish" finish="true">
<results>
<unconditional-result old-status="Finished" status="Game Over" step="4"/>
</results>
</action>
</actions>
</step>
</steps>
<splits>
<split id="1">
<unconditional-result old-status="Finished" status="Queued" step="2"/>
<unconditional-result old-status="Finished" status="Queued" step="3"/>
</split>
</splits>
<joins>
<join id="1">
<conditions type="AND">
<condition type="beanshell">
<arg name="script">
"Finished".equals(jn.getStep(2).getStatus())
</arg>
</condition>
<condition type="beanshell">
<arg name="script">
"Finished".equals(jn.getStep(3).getStatus())
</arg>
</condition>
</conditions>
<unconditional-result old-status="Finished" status="Queued" step="4"/>
</join>
</joins>
</workflow>
public void testJoinNodesOrder() throws Exception {
long id = workflow.initialize("orders", 1, null);
workflow.doAction(id, 2, null);
workflow.doAction(id, 3, null);
workflow.doAction(id, 2, null);
workflow.doAction(id, 4, null);
workflow.doAction(id, 6, null);
workflow.doAction(id, 7, null);
}