前言
通过《activiti学习(十七)——多实例任务的使用(会签功能)》一文,相信读者已经大概了解多实例任务的含义,以及流程行进中数据库的变化。本文会对流程虚拟机离开多实例任务的过程进行剖析。
对象解析阶段
在对象解析阶段,多实例任务便需要设置区别于普通任务的行为。我们回顾对象解析阶段源码BpmnParseHandlers.java:
public class BpmnParseHandlers {
//......
public void parseElement(BpmnParse bpmnParse, BaseElement element) {
if (element instanceof DataObject) {
return;
}
if (element instanceof FlowElement) {
bpmnParse.setCurrentFlowElement((FlowElement) element);
}
List<BpmnParseHandler> handlers = parseHandlers.get(element.getClass());
if (handlers == null) {
LOGGER.warn("Could not find matching parse handler for + " + element.getId() + " this is likely a bug.");
} else {
for (BpmnParseHandler handler : handlers) {
handler.parse(bpmnParse, element);
}
}
}
}
回忆一下,这段代码主要是根据bpmnModel中元素的类型,获取元素对应的解析器,然后在25行进行解析。然后会调用AbstractActivityBpmnParseHandler的parse方法:
public abstract class AbstractActivityBpmnParseHandler<T extends FlowNode> extends AbstractFlowNodeBpmnParseHandler<T> {
public void parse(BpmnParse bpmnParse, BaseElement element) {
super.parse(bpmnParse, element);
if (element instanceof Activity
&& ((Activity) element).getLoopCharacteristics() != null) {
createMultiInstanceLoopCharacteristics(bpmnParse, (Activity) element);
}
}
protected void createMultiInstanceLoopCharacteristics(BpmnParse bpmnParse, Activity modelActivity) {
MultiInstanceLoopCharacteristics loopCharacteristics = modelActivity.getLoopCharacteristics();
// Activity Behavior
MultiInstanceActivityBehavior miActivityBehavior = null;
ActivityImpl activity = bpmnParse.getCurrentScope().findActivity(modelActivity.getId());
if (activity == null) {
throw new ActivitiException("Activity " + modelActivity.getId() + " needed for multi instance cannot bv found");
}
if (loopCharacteristics.isSequential()) {
miActivityBehavior = bpmnParse.getActivityBehaviorFactory().createSequentialMultiInstanceBehavior(
activity, (AbstractBpmnActivityBehavior) activity.getActivityBehavior());
} else {
miActivityBehavior = bpmnParse.getActivityBehaviorFactory().createParallelMultiInstanceBehavior(
activity, (AbstractBpmnActivityBehavior) activity.getActivityBehavior());
}
// ActivityImpl settings
activity.setScope(true);
activity.setProperty("multiInstance", loopCharacteristics.isSequential() ? "sequential" : "parallel");
activity.setActivityBehavior(miActivityBehavior);
ExpressionManager expressionManager = bpmnParse.getExpressionManager();
BpmnModel bpmnModel = bpmnParse.getBpmnModel();
//......设置miActivityBehavior属性
}
}
之前我们直接在第4行代码,便深入进去,解析对象,并为活动类设置行为。现在我们要分析若元素属于多实例任务时,执行6-8行的情况。接下来跳转到12行,14行获取loopCardinality属性,即多任务循环次数。18行获取第4行已经解析并创建的当前ActivityImpl。23-29行若为串行多实例任务则执行24-25创建对应串行多实例行为,否则创建并行多实例行为。34行把新建的多实例行为设置为当前活动的行为类。39行设置miActivityBehavior各项属性,例如loopcardinality、completionCondition、collection、elementVariable等多实例任务的属性。
关于多实例的行为类
串行多实例行为类SequentialMultiInstanceBehavior和并行多实例行为类ParallelMultiInstanceBehavior的共同父类是MultiInstanceActivityBehavior,我们先看一下这个类的初始化:
public abstract class MultiInstanceActivityBehavior extends FlowNodeActivityBehavior
implements CompositeActivityBehavior, SubProcessActivityBehavior {
//......
public MultiInstanceActivityBehavior(ActivityImpl activity, AbstractBpmnActivityBehavior innerActivityBehavior) {
this.activity = activity;
setInnerActivityBehavior(innerActivityBehavior);
}
//......
public void setInnerActivityBehavior(AbstractBpmnActivityBehavior innerActivityBehavior) {
this.innerActivityBehavior = innerActivityBehavior;
this.innerActivityBehavior.setMultiInstanceActivityBehavior(this);
}
//......
}
这个类的初始化会设置一个多实例行为类和一个内部的行为类,例如userTask的行为类。这样当处理完多实例的行为后,再调用原来活动的行为类。MultiInstanceActivityBehavior及其子类就是负责这些事情。
执行串行多实例任务
这一小节以串行多实例为例进行代码的跟踪。回忆《activiti学习(二十一)——流程虚拟机源码分析(三)——从进入到离开userTask》,当流程进入userTask前,会经过AtomicOperationTransitionCreateScope类,回顾一下:
public class AtomicOperationTransitionCreateScope implements AtomicOperation {
//......
public void execute(InterpretableExecution execution) {
InterpretableExecution propagatingExecution = null;
ActivityImpl activity = (ActivityImpl) execution.getActivity();
if (activity.isScope()) {
propagatingExecution = (InterpretableExecution) execution.createExecution();
propagatingExecution.setActivity(activity);
propagatingExecution.setTransition(execution.getTransition());
execution.setTransition(null);
execution.setActivity(null);
execution.setActive(false);
log.debug("create scope: parent {} continues as execution {}", execution, propagatingExecution);
propagatingExecution.initialize();
} else {
propagatingExecution = execution;
}
propagatingExecution.performOperation(AtomicOperation.TRANSITION_NOTIFY_LISTENER_START);
}
}
对于一般的userTask,第8行activity.isScope()为false,对于多实例任务,这里的判断为true,有疑问的读者请回顾本文前面对象解析阶段的代码。第9行会创建一个子execution,10-14行为父execution和子execution设置属性。
流程虚拟机继续运转,会调用其行为类的execute方法,我们看看SequentialMultiInstanceBehavior类的execute方法:
public class SequentialMultiInstanceBehavior extends MultiInstanceActivityBehavior {
//......
public void execute(ActivityExecution execution) throws Exception {
super.execute(execution);
if(innerActivityBehavior instanceof SubProcessActivityBehavior) {
if(!execution.isActive() && execution.isEnded() && (execution.getExecutions() == null || execution.getExecutions().isEmpty())) {
execution.setActive(true);
}
}
}
}
第6行会调用父类即MultiInstanceActivityBehavior的execute方法。第8行判断内部行为类是否为子进程行为,按现在的假设这个内部行为类是UserTaskActivityBehavior,该判断为false。接下来进入MultiInstanceActivityBehavior的execute方法:
public abstract class MultiInstanceActivityBehavior extends FlowNodeActivityBehavior
implements CompositeActivityBehavior, SubProcessActivityBehavior {
//......
public void execute(ActivityExecution execution) throws Exception {
if (getLocalLoopVariable(execution, getCollectionElementIndexVariable()) == null) {
try {
createInstances(execution);
} catch (BpmnError error) {
ErrorPropagation.propagateError(error, execution);
}
if (resolveNrOfInstances(execution) == 0) {
leave(execution);
}
} else {
innerActivityBehavior.execute(execution);
}
}
//......
protected Integer getLocalLoopVariable(ActivityExecution execution, String variableName) {
return (Integer) execution.getVariableLocal(variableName);
}
//......
}
第7行先判断本地流程变量loopCounter是否已设置,刚进入多实例任务的话,这里应该还未设置,为null,所以执行第9行。第9行调用SequentialMultiInstanceBehavior的createInstances,跟踪下去:
public class SequentialMultiInstanceBehavior extends MultiInstanceActivityBehavior {
//......
protected void createInstances(ActivityExecution execution) throws Exception {
int nrOfInstances = resolveNrOfInstances(execution);
if (nrOfInstances < 0) {
throw new ActivitiIllegalArgumentException("Invalid number of instances: must be a non-negative integer value"
+ ", but was " + nrOfInstances);
}
setLoopVariable(execution, NUMBER_OF_INSTANCES, nrOfInstances);
setLoopVariable(execution, NUMBER_OF_COMPLETED_INSTANCES, 0);
setLoopVariable(execution, getCollectionElementIndexVariable(), 0);
setLoopVariable(execution, NUMBER_OF_ACTIVE_INSTANCES, 1);
logLoopDetails(execution, "initialized", 0, 0, 1, nrOfInstances);
if (nrOfInstances>0) {
executeOriginalBehavior(execution, 0);
}
}
//......
}
第6行调用resolveNrOfInstances获取多任务实例总数nrOfInstances。resolveNrOfInstances这个函数首先获取loopCardinality的值,若获取不到则取Collection集合的大小作为实例总数,若loopCardinality和Collection都未设置,则取CollectionVariable(通过DataInputItem设置)的大小。12-15行分别设置nrOfInstances、nrOfCompletedInstances、loopCounter和nrOfActiveInstances的流程变量。19行准备执行原有的行为,这里是userTask的行为。跟踪MultiInstanceActivityBehavior的executeOriginalBehavior方法:
public abstract class MultiInstanceActivityBehavior extends FlowNodeActivityBehavior
implements CompositeActivityBehavior, SubProcessActivityBehavior {
//......
protected void executeOriginalBehavior(ActivityExecution execution, int loopCounter) throws Exception {
if (usesCollection() && collectionElementVariable != null) {
Collection collection = null;
if (collectionExpression != null) {
collection = (Collection) collectionExpression.getValue(execution);
} else if (collectionVariable != null) {
collection = (Collection) execution.getVariable(collectionVariable);
}
Object value = null;
int index = 0;
Iterator it = collection.iterator();
while (index <= loopCounter) {
value = it.next();
index++;
}
setLoopVariable(execution, collectionElementVariable, value);
}
if (loopCounter == 0) {
callCustomActivityStartListeners(execution);
innerActivityBehavior.execute(execution);
} else {
execution.executeActivity(activity);
}
}
//......
}
第6行判断是否设置collection和collectionElement,若有,则从collection集合中迭代,因为入参loopCounter是0,所以只会取第一个对象,并设置到本地流程变量中。因为入参loopCounter是0,24行判断后走25行,callCustomActivityStartListeners函数会调用执行监听器的start方法。26行执行UserTaskActivityBehavior的execute方法。userTask的情况参考《activiti学习(二十一)》即可。如果loopCounter不等于0,则表示已经完成过的数次任务,然而仍未满足离开条件,进入多实例任务的下一次任务,等同于再次进入一个任务并执行其行为类,因此使用execution.executeActivity。
完成和离开串行多实例任务
《activiti学习(二十一)》文中我们知道任务完成时调用taskService.complete,会调用行为类的signal方法,因此对于多实例任务,会调用MultiInstanceActivityBehavior的signal方法,我们看下具体的实现:
public abstract class MultiInstanceActivityBehavior extends FlowNodeActivityBehavior
implements CompositeActivityBehavior, SubProcessActivityBehavior {
//......
public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception {
innerActivityBehavior.signal(execution, signalName, signalData);
}
//......
}
该方法只是单纯调用UserTaskActivityBehavior的signal方法,UserTaskActivityBehavior的signal方法前文也也分析过,会调用AbstractBpmnActivityBehavior的leave方法,我们再次分析该方法:
public class AbstractBpmnActivityBehavior extends FlowNodeActivityBehavior {
//......
protected void leave(ActivityExecution execution) {
if(hasCompensationHandler(execution)) {
createCompensateEventSubscription(execution);
}
if (!hasLoopCharacteristics()) {
super.leave(execution);
} else if (hasMultiInstanceCharacteristics()){
multiInstanceActivityBehavior.leave(execution);
}
}
//......
}
之前文章的分析,我们假设任务节点不是多实例任务,所以走第10行,这次我们是多实例的情况,走12行。会调用SequentialMultiInstanceBehavior的leave方法:
public class SequentialMultiInstanceBehavior extends MultiInstanceActivityBehavior {
//......
public void leave(ActivityExecution execution) {
int loopCounter = getLoopVariable(execution, getCollectionElementIndexVariable()) + 1;
int nrOfInstances = getLoopVariable(execution, NUMBER_OF_INSTANCES);
int nrOfCompletedInstances = getLoopVariable(execution, NUMBER_OF_COMPLETED_INSTANCES) + 1;
int nrOfActiveInstances = getLoopVariable(execution, NUMBER_OF_ACTIVE_INSTANCES);
if (loopCounter != nrOfInstances && !completionConditionSatisfied(execution)) {
callActivityEndListeners(execution);
}
setLoopVariable(execution, getCollectionElementIndexVariable(), loopCounter);
setLoopVariable(execution, NUMBER_OF_COMPLETED_INSTANCES, nrOfCompletedInstances);
logLoopDetails(execution, "instance completed", loopCounter, nrOfCompletedInstances, nrOfActiveInstances, nrOfInstances);
if (loopCounter >= nrOfInstances || completionConditionSatisfied(execution)) {
super.leave(execution);
} else {
try {
executeOriginalBehavior(execution, loopCounter);
} catch (BpmnError error) {
throw error;
} catch (Exception e) {
throw new ActivitiException("Could not execute inner activity behavior of multi instance behavior", e);
}
}
}
//......
}
6-9行获取多实例任务内置流程变量并对nrOfCompletedInstances、loopCounter加一。11行如果计数未达到任务总数并且未满足完成任务条件,则执行12行触发执行监听器end事件。15-16行更新loopCounter和nrOfCompletedInstances流程变量值。若满足计数达到任务总数,或者满足完成任务条件,则同非多实例任务那样调用AbstractBpmnActivityBehavior的leave方法离开,否则调用MultiInstanceActivityBehavior 的executeOriginalBehavior方法继续执行下一循环的多实例任务。‘’
小结
本文通过跟踪串行多实例任务的进入和离开时的行为,窥探流程引擎是如何处理多实例任务的。多实例任务内部的处理其实不复杂,我们此前文章使用了解过内置变量、动态配置、条件配置等等,都在源码看到了相应的处理方式。关于并行多实例任务,请读者自行分析。