activiti学习(二十三)——流程虚拟机源码分析(四)——多实例任务的行为

前言

通过《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方法继续执行下一循环的多实例任务。‘’

 

小结

本文通过跟踪串行多实例任务的进入和离开时的行为,窥探流程引擎是如何处理多实例任务的。多实例任务内部的处理其实不复杂,我们此前文章使用了解过内置变量、动态配置、条件配置等等,都在源码看到了相应的处理方式。关于并行多实例任务,请读者自行分析。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Activiti中,可以使用多实例流程来处理并发任务。以下是一个简单的示例: 假设我们有一个主流程,其中包含一个子流程,子流程中有一个并发多实例任务,需要同时处理多个子任务。 1. 首先,在子流程中创建一个并发多实例任务,可以使用以下XML代码实现: ``` <subProcess id="subProcess1" name="Sub Process"> <multiInstanceLoopCharacteristics isSequential="false"> <loopCardinality>3</loopCardinality> <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 1}</completionCondition> </multiInstanceLoopCharacteristics> <userTask id="subProcessTask" name="Sub Process Task" /> </subProcess> ``` 上面的代码中,`multiInstanceLoopCharacteristics` 元素表示这是一个多实例任务,`isSequential="false"` 表示任务是并行处理的,`loopCardinality` 表示需要处理的子任务数量,这里设置为3。`completionCondition` 表示任务完成的条件,这里设置为当所有子任务都完成时,子流程才算完成。 2. 在主流程中调用子流程,可以使用以下XML代码实现: ``` <callActivity id="subProcessCall" name="Sub Process Call" calledElement="subProcess1" /> ``` 上面的代码中,`calledElement` 属性指定了被调用的子流程的ID,这里为 `subProcess1`。 3. 在子流程中处理多实例任务,可以使用以下Java代码实现: ``` public class SubProcessTaskDelegate implements JavaDelegate { @Override public void execute(DelegateExecution execution) throws Exception { // 获取当前子任务的ID String subTaskId = execution.getCurrentActivityId(); // 处理子任务 System.out.println("Processing sub task " + subTaskId); } } ``` 上面的代码中,`execute` 方法是任务处理的入口。可以通过 `getCurrentActivityId` 方法获取当前子任务的ID,然后处理任务。 以上就是在Activiti中处理并发多实例流程任务的简单示例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值