Activiti 5.3提供了子流程的实现,包括两种基于子流程的实现:
一种是内嵌子流程:子流程元素<subProcess>内嵌在主流程元素<process>之内,只能在该流程中使用该子流程,外部是无法访问到的。这种子流程一般作为局部通用逻辑处理,或者因为特定业务需要,使得比较复杂的单个主流程设计清晰直观;
另一种是调用子流程:首先实现一个流程,在另一个流程中可以调用该流程,通常可以定义一些通用的流程作为这种调用子流程,供其他多个流程定义复用。这种子流程使用<callActivity>元素来进行调用,间接地嵌入到主流程中,用起来比较方便。
内嵌子流程
实现的基于内嵌子流程的流程,示例如图所示:
对应的流程定义文件SubProcessTest.MySubprocess.bpmn20.xml,内容如下所示:
<?xml version="1.0" encoding="UTF-8"?> <definitions id="definitions" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="Examples"> <process id="MySubprocess" name="My Subprocess"> <startEvent id="theStart" /> <sequenceFlow id="flow1" sourceRef="theStart" targetRef="subProcess" /> <subProcess id="subProcess"> <startEvent id="subProcessStart" /> <sequenceFlow id="flow2" sourceRef="subProcessStart" targetRef="subProcessFork" /> <parallelGateway id="subProcessFork" /> <sequenceFlow id="flow3" sourceRef="subProcessFork" targetRef="task1" /> <sequenceFlow id="flow4" sourceRef="subProcessFork" targetRef="task2" /> <userTask id="task1" name="Check bank" activiti:candidateGroups="engineering"> <extensionElements> <activiti:taskListener event="complete" class="org.shirdrn.workflow.activiti.subprocess.CheckBankTask" /> </extensionElements> </userTask> <sequenceFlow id="flow5" sourceRef="task1" targetRef="subProcessJoin" /> <userTask id="task2" name="Investigate software" activiti:candidateGroups="engineering" > <extensionElements> <activiti:taskListener event="complete" class="org.shirdrn.workflow.activiti.subprocess.CheckMerchantTask" /> </extensionElements> </userTask> <sequenceFlow id="flow6" sourceRef="task2" targetRef="subProcessJoin" /> <parallelGateway id="subProcessJoin" /> <sequenceFlow id="flow7" sourceRef="subProcessJoin" targetRef="subProcessEnd" /> <endEvent id="subProcessEnd" /> </subProcess> <sequenceFlow id="flow10" sourceRef="subProcess" targetRef="taskAfterSubProcess" /> <userTask id="taskAfterSubProcess" name="Collect message" activiti:candidateGroups="engineering" > <extensionElements> <activiti:taskListener event="complete" class="org.shirdrn.workflow.activiti.subprocess.CollectMessageTask" /> </extensionElements> </userTask> <sequenceFlow id="flow11" sourceRef="taskAfterSubProcess" targetRef="theEnd" /> <endEvent id="theEnd" /> </process> </definitions>
流程定义中,实现了3个TaskListener,对应的代码分别如下所示:
package org.shirdrn.workflow.activiti.subprocess; import java.util.HashMap; import java.util.logging.Logger; import org.activiti.engine.delegate.DelegateTask; import org.activiti.engine.impl.pvm.delegate.TaskListener; public class CheckBankTask implements TaskListener { private final Logger log = Logger.getLogger(CheckBankTask.class.getName()); @SuppressWarnings("unchecked") public void notify(DelegateTask delegateTask) { log.info("i am CheckBankTask."); System.out.println("in : " + delegateTask.getVariables()); ((HashMap<String, Object>)delegateTask.getVariables().get("in")).put("next", "CheckBankTask"); ((HashMap<String, Object>)delegateTask.getVariables().get("out")).put("reponse", "subprocess:CheckBankTask->CheckMerchantTask"); } }
package org.shirdrn.workflow.activiti.subprocess; import java.util.HashMap; public class CheckMerchantTask implements TaskListener { private final Logger log = Logger.getLogger(CheckMerchantTask.class.getName()); @SuppressWarnings("unchecked") public void notify(DelegateTask delegateTask) { log.info("i am CheckMerchantTask."); System.out.println("in : " + delegateTask.getVariables()); ((HashMap<String, Object>)delegateTask.getVariables().get("in")).put("previous", "CheckMerchantTask"); } }
package org.shirdrn.workflow.activiti.subprocess; import java.util.Map; public class CollectMessageTask implements TaskListener { private final Logger log = Logger.getLogger(CollectMessageTask.class.getName()); @SuppressWarnings("unchecked") public void notify(DelegateTask delegateTask) { log.info("i am CollectMessageTask."); System.out.println("out : " + (Map<String, Object>)delegateTask.getVariables().get("out")); System.out.println("all : " + delegateTask.getVariables()); } }
测试用例,代码如下所示:
package org.shirdrn.workflow.activiti.subprocess; import java.util.HashMap; import java.util.List; import java.util.Map; import org.activiti.engine.repository.Deployment; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.shirdrn.workflow.activiti.AbstractTest; /** * <a href="http://my.oschina.net/arthor" class="referer" target="_blank">@author</a> shirdrn */ public class MySubProcessTest extends AbstractTest { @Override protected void initialize() throws Exception { Deployment deployment = repositoryService .createDeployment() .addClasspathResource( "diagrams/SubProcessTest.MySubprocess.bpmn20.xml") .deploy(); deploymentId = deployment.getId(); } @Override protected void destroy() throws Exception { repositoryService.deleteDeployment(deploymentId, true); } public void testSubProcess() { // prepare data packet Map<String, Object> variables = new HashMap<String, Object>(); Map<String, Object> subVariables = new HashMap<String, Object>(); variables.put("maxTransCount", 1000000); variables.put("merchant", new Merchant("ICBC")); variables.put("protocol", "UM32"); variables.put("repository", "10.10.38.99:/home/shirdrn/repository"); variables.put("in", subVariables); variables.put("out", new HashMap<String, Object>()); // start process instance ProcessInstance pi = runtimeService.startProcessInstanceByKey("MySubprocess", variables); // enter subprocess List<Task> tasks = taskService.createTaskQuery().processInstanceId(pi.getId()).orderByTaskName().asc().list(); assertEquals(2, tasks.size()); for(Task task : tasks) { taskService.complete(task.getId()); } // leave subprocess Task collectTask = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult(); assertEquals("Collect message", collectTask.getName()); Map<String, Object> taskVariables = new HashMap<String, Object>(); taskVariables.put("att", "anything you need"); taskService.setVariable(collectTask.getId(), "oper", "shirdrn"); taskService.complete(collectTask.getId(), taskVariables); } }
运行结果信息,如下所示:
2011-3-24 17:36:36 org.shirdrn.workflow.activiti.subprocess.CheckBankTask notify 信息: i am CheckBankTask. in : {protocol=UM32, repository=10.10.38.99:/home/shirdrn/repository, merchant=Merchant[ICBC], maxTransCount=1000000, in={}, out={}} 2011-3-24 17:36:36 org.shirdrn.workflow.activiti.subprocess.CheckMerchantTask notify 信息: i am CheckMerchantTask. in : {protocol=UM32, repository=10.10.38.99:/home/shirdrn/repository, merchant=Merchant[ICBC], maxTransCount=1000000, in={next=CheckBankTask}, out={reponse=subprocess:CheckBankTask->CheckMerchantTask}} 2011-3-24 17:36:36 org.shirdrn.workflow.activiti.subprocess.CollectMessageTask notify 信息: i am CollectMessageTask. out : {reponse=subprocess:CheckBankTask->CheckMerchantTask} all : {protocol=UM32, repository=10.10.38.99:/home/shirdrn/repository, merchant=Merchant[ICBC], oper=shirdrn, att=anything you need, maxTransCount=1000000, in={previous=CheckMerchantTask, next=CheckBankTask}, out={reponse=subprocess:CheckBankTask->CheckMerchantTask}}
实现的子流程,示例如图所示:
对应的流程定义文件Subprocess.Check.bpmn20.xml,如下所示:
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"> <process id="CheckSubprocess" name="CheckSubprocess"> <startEvent id="startevent4" name="Start"></startEvent> <serviceTask id="servicetask4" name="Check User" activiti:class="org.shirdrn.workflow.activiti.subprocess.BusinessCheck"></serviceTask> <endEvent id="endevent4" name="End"></endEvent> <sequenceFlow id="flow7" name="" sourceRef="startevent4" targetRef="servicetask4"></sequenceFlow> <sequenceFlow id="flow8" name="" sourceRef="servicetask4" targetRef="endevent4"></sequenceFlow> </process> </definitions>
上面的流程定义,和通常定义流程的方式是相同的。流程中,对应的JavaDelegate的实现类,代码如下所示:
package org.shirdrn.workflow.activiti.subprocess; import java.util.logging.Logger; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.JavaDelegate; public class BusinessCheck implements JavaDelegate { private static final Logger log = Logger.getLogger(BusinessCheck.class.getName()); @Override public void execute(DelegateExecution execution) throws Exception { // varOutFromMainprocess<->varInSubprocess String varInSubprocess = (String)execution.getVariable("varInSubprocess"); log.info("in subprocess get(varInSubprocess): " + varInSubprocess); log.info("variavles=" + execution.getVariables()); execution.setVariable("s:bc", "Subprocess:BusinessCheck"); log.info("I am BusinessCheck in subprocess."); execution.setVariable("varInSubprocess", "BBBB"); log.info("in subprocess set(varInSubprocess): " + varInSubprocess); } }
该BusinessCheck类简单用来校验用户数据。
下面看一下主流程,如图所示:
上面的callSubprocess结点对应的就是流程定义中的<callActivity>元素,调用了外部流程。对应的流程定义文件Subprocess.Mainprocess.bpmn20.xml,如下所示:
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"> <process id="Mainprocess" name="Main Process"> <startEvent id="startevent1" name="Start"></startEvent> <sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow> <serviceTask id="servicetask1" name="System Initialization" activiti:class="org.shirdrn.workflow.activiti.subprocess.Initialization"></serviceTask> <sequenceFlow id="flow2" name="" sourceRef="servicetask1" targetRef="callSubprocess"></sequenceFlow> <callActivity id="callSubprocess" calledElement="CheckSubprocess" > <extensionElements> <activiti:in source="varOutFromMainprocess" target="varInSubprocess" /> <activiti:out source="varInSubprocess" target="varOutFromSubprocess" /> </extensionElements> </callActivity> <sequenceFlow id="flow3" name="" sourceRef="callSubprocess" targetRef="servicetask2"></sequenceFlow> <serviceTask id="servicetask2" name="Do Transaction" activiti:class="org.shirdrn.workflow.activiti.subprocess.DoTransaction"></serviceTask> <sequenceFlow id="flow4" name="" sourceRef="servicetask2" targetRef="endevent1"></sequenceFlow> <endEvent id="endevent1" name="End"></endEvent> </process> </definitions>
需要说明的是,对于主流程与子流程之间进行数据交换,需要在<callActivity>元素中进行配置,通过执行“传入”、“传出”变量的方式来进行。片段如下所示:
<callActivity id="callSubprocess" calledElement="CheckSubprocess" > <extensionElements> <activiti:in source="varOutFromMainprocess" target="varInSubprocess" /> <activiti:out source="varInSubprocess" target="varOutFromSubprocess" /> </extensionElements> </callActivity>
我们可以通过在主流程中设置一个变量为varOutFromMainprocess,并指定对应的输入数据,根据流程定义,会将变量 varOutFromMainprocess对应的值,传递给子流程中的变量varInSubprocess,只需要在子流程中获取变量 varInSubprocess的值即可。从子流程中传出数据的道理是类似的。上面实现了2个JavaDelegate处理类,代码分别如下所示:
package org.shirdrn.workflow.activiti.subprocess; import java.util.logging.Logger; public class Initialization implements JavaDelegate { private static final Logger log = Logger.getLogger(Initialization.class.getName()); @Override public void execute(DelegateExecution execution) throws Exception { log.info("variavles=" + execution.getVariables()); execution.setVariable("m:i", "Mainprocess:Initialization"); log.info("I am Initialization in mainprocess."); execution.setVariable("varOutFromMainprocess", "AAAA"); log.info("in mainprocess set(varOutFromMainprocess): " + execution.getVariable("varOutFromMainprocess")); } }
package org.shirdrn.workflow.activiti.subprocess; import java.util.logging.Logger; public class DoTransaction implements JavaDelegate { private static final Logger log = Logger.getLogger(DoTransaction.class.getName()); @Override public void execute(DelegateExecution execution) throws Exception { // varInSubprocess<->varOutFromSubprocess String varOutFromSubprocess = (String)execution.getVariable("varOutFromSubprocess"); log.info("in mainprocess get(varOutFromSubprocess): " + varOutFromSubprocess); log.info("variavles=" + execution.getVariables()); execution.setVariable("m:dt", "Mainprocess:DoTransaction"); log.info("I am DoTransaction in mainprocess."); } }
实现的测试用例,代码如下所示:
package org.shirdrn.workflow.activiti.subprocess; import java.util.HashMap; import java.util.Map; import org.activiti.engine.repository.Deployment; import org.activiti.engine.runtime.ProcessInstance; import org.shirdrn.workflow.activiti.AbstractTest; /** * <a href="http://my.oschina.net/arthor" class="referer" target="_blank">@author</a> shirdrn */ public class MainprocessTest extends AbstractTest { @Override protected void initialize() throws Exception { Deployment deployment = repositoryService .createDeployment() .addClasspathResource( "diagrams/Subprocess.Check.bpmn20.xml") .deploy(); deploymentIdList.add(deployment.getId()); deployment = repositoryService .createDeployment() .addClasspathResource( "diagrams/Subprocess.Mainprocess.bpmn20.xml") .deploy(); deploymentIdList.add(deployment.getId()); } @Override protected void destroy() throws Exception { for(String deployment : deploymentIdList) { repositoryService.deleteDeployment(deployment, true); } } public void testSubProcess() { // prepare data packet Map<String, Object> variables = new HashMap<String, Object>(); Map<String, Object> subVariables = new HashMap<String, Object>(); variables.put("protocol", "UM32"); variables.put("repository", "10.10.38.99:/home/shirdrn/repository"); variables.put("in", subVariables); variables.put("out", new HashMap<String, Object>()); // start process instance ProcessInstance pi = runtimeService.startProcessInstanceByKey("Mainprocess", variables); assertEquals(true, pi.isEnded()); } }
代码中org.shirdrn.workflow.activiti.AbstractTest类可以参考: Activiti 5.3:流程活动自动与手工触发执行里面。上述测试程序运行结果如下所示:
2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.Initialization execute 信息: variavles={protocol=UM32, repository=10.10.38.99:/home/shirdrn/repository, in={}, out={}} 2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.Initialization execute 信息: I am Initialization in mainprocess. 2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.Initialization execute 信息: in mainprocess set(varOutFromMainprocess): AAAA 2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.BusinessCheck execute 信息: in subprocess get(varInSubprocess): AAAA 2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.BusinessCheck execute 信息: variavles={varInSubprocess=AAAA} 2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.BusinessCheck execute 信息: I am BusinessCheck in subprocess. 2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.BusinessCheck execute 信息: in subprocess set(varInSubprocess): AAAA 2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.DoTransaction execute 信息: in mainprocess get(varOutFromSubprocess): BBBB 2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.DoTransaction execute 信息: variavles={protocol=UM32, repository=10.10.38.99:/home/shirdrn/repository, varOutFromSubprocess=BBBB, varOutFromMainprocess=AAAA, in={}, out={}, m:i=Mainprocess:Initialization} 2011-3-24 17:52:13 org.shirdrn.workflow.activiti.subprocess.DoTransaction execute 信息: I am DoTransaction in mainprocess.