cancel
Showing results for 
Search instead for 
Did you mean: 

problem with jump to a multi-instance user task

windfighter
Champ in-the-making
Champ in-the-making
my workflow model is: start->user task1->user task2(multi-instance)->user task3-> end

all the assignee I set is using flow variables.
at runtime of the flow ,the assignee are as below.

user task1 : assignee is `zhangsan`
user task2: assignee are `lisi` and `wanger`
user task3: assignee is `mazi`

I write a test function,
and hope the flow can jump from `user task3` to `user task2`,and I set the assignees of `user task2` in the `jumpAcitivtyCmd` before jumping.

but the result is : jumping work correctly, but when the second task user `wanger` complete the task of `user task2`, pop up execption.

it tasks me long time on it, and I don't kown how to resolve it. and very exhausted.

Hope you can help me!!!
thanks.

the flow mode is :

<code>
<?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:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlnsSmiley Surprisedmgdc="http://www.omg.org/spec/DD/20100524/DC" xmlnsSmiley Surprisedmgdi="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="simpleFlow" name="任务发起" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="User Task1" activiti:assignee="${user}"></userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <userTask id="usertask2" name="User Task2" activiti:assignee="${user}">
      <multiInstanceLoopCharacteristics isSequential="true" activiti:collection="${users}" activiti:elementVariable="user"></multiInstanceLoopCharacteristics>
    </userTask>
    <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
    <userTask id="usertask3" name="User Task3" activiti:assignee="${user}"></userTask>
    <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_simpleFlow">
    <bpmndi:BPMNPlane bpmnElement="simpleFlow" id="BPMNPlane_simpleFlow">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="140.0" y="150.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="220.0" y="140.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
        <omgdc:Bounds height="55.0" width="105.0" x="370.0" y="140.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
        <omgdc:Bounds height="55.0" width="105.0" x="520.0" y="140.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="680.0" y="150.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="175.0" y="167.0"></omgdi:waypoint>
        <omgdi:waypoint x="220.0" y="167.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="325.0" y="167.0"></omgdi:waypoint>
        <omgdi:waypoint x="370.0" y="167.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="475.0" y="167.0"></omgdi:waypoint>
        <omgdi:waypoint x="520.0" y="167.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="625.0" y="167.0"></omgdi:waypoint>
        <omgdi:waypoint x="680.0" y="167.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>
<code>

and my test code is :
<code>
@Test
   public void testJump() {
       //flow template model is: start->usertask1->usertask2->usertask3->end
       //user task2 is multi-instance user task, and multi-instance collection is ${users}, and element is user;
       String processDefId = "simpleFlow:2:1587504";


       Map<String, Object> variables = new HashMap<>();

       //set user task1 assignee
       variables.put("user", "zhangsan");

       ProcessInstance pi = runtimeService.startProcessInstanceById(processDefId, variables);
       printExecutions(pi.getProcessInstanceId(), "after start flow");
       logger.info("流程实例Id:{}", pi.getProcessInstanceId());


       //set user task2 assignee
       List<String> u2Assignees =  Arrays.asList(new String[]{"lisi", "wanger"});
       runtimeService.setVariable(pi.getProcessInstanceId(), "users",u2Assignees);
       //complete user task 1
       completeTask(pi.getProcessInstanceId(), null);
       printExecutions(pi.getProcessInstanceId(), "after complete user task1");

       //set user task3 assignee
       runtimeService.setVariable(pi.getProcessInstanceId(), "user", "mazi");
       //complete user task2
       completeTask(pi.getProcessInstanceId(), null);
       printExecutions(pi.getProcessInstanceId(), "after complete user task2-instance1");

       //complete user task2
       completeTask(pi.getProcessInstanceId(), null);
       printExecutions(pi.getProcessInstanceId(), "after complete user task2-instance2");

       //complete user task3
       //completeTask(pi.getProcessInstanceId(), null);
       //printExecutions(pi.getProcessInstanceId(), "after complete user task3");

       //jump to user task2
       JumpActivityCmd cmd = new JumpActivityCmd(pi.getProcessInstanceId(),"usertask2","disapprove",u2Assignees,u2Assignees.get(0));
      
       managementService.executeCommand(cmd);
/*

       TaskFlowControlService taskFlowControlService = new TaskFlowControlService(processEngine,pi.getProcessInstanceId());
       try {
           taskFlowControlService.jump("usertask2");
       }catch (Exception e){
           logger.error("",e);
       }
*/

       printExecutions(pi.getProcessInstanceId(), "after jump to user task2");

       completeTask(pi.getProcessInstanceId(), null);
       printExecutions(pi.getProcessInstanceId(), "after complete user task2-instance1");


       completeTask(pi.getProcessInstanceId(), null);
       printExecutions(pi.getProcessInstanceId(), "after complete user task2-instance2");
      
   }

private void printExecutions(String processInstanceId,String messageTitle){
        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
        if(executions != null && executions.size() > 0){
            for(Execution e : executions){
                logger.info(messageTitle + ":current executionId is {},activityId is {}, parent executionId is {}",e.getId(),e.getActivityId(),e.getParentId());
            }
        }
    }

    private  void completeTask(String processInstanceId,Map variables) {
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();

        if (tasks != null && tasks.size() > 0) {

            for (Task e : tasks) {
                logger.info("****************************complete task:{},task assignee {} ,execution id {}****************************", e.getId(),e.getAssignee(), e.getExecutionId());
                taskService.complete(e.getId());
            }


        }

    }

<code>

and the jumpActivityCmd class is :
<code>
public class JumpActivityCmd implements Command<Object> {
    Logger logger = org.slf4j.LoggerFactory.getLogger(JumpActivityCmd.class);

    private String activityId;
    private String processInstanceId;
    private String jumpOrigin;

    //task assignees,if the target activity is multi-instance
    private List<String> taskUsers;

    //task assignee, if not multi-instance
    private String taskUser;


    public JumpActivityCmd(String processInstanceId, String activityId,String jumpOrigin,
                           List<String> taskUsers,String taskUser) {
        this(processInstanceId, activityId, jumpOrigin);
        this.taskUsers = taskUsers;
        this.taskUser = taskUser;
    }

    public JumpActivityCmd(String processInstanceId, String activityId) {
        this(processInstanceId, activityId, "jump");
    }

    public JumpActivityCmd(String processInstanceId, String activityId, String jumpOrigin) {
        this.activityId = activityId;
        this.processInstanceId = processInstanceId;
        this.jumpOrigin = jumpOrigin;
    }

    public Object execute(CommandContext commandContext) {


        ExecutionEntity executionEntity = commandContext.getExecutionEntityManager().findExecutionById(processInstanceId);
        executionEntity.destroyScope(jumpOrigin);

        ProcessDefinitionImpl processDefinition = executionEntity.getProcessDefinition();
        ActivityImpl activity = processDefinition.findActivity(activityId);

        if(!activityId.equals("end") && jumpOrigin.equals("disapprove")) {
            executionEntity.setVariable("users",taskUsers);
            executionEntity.setVariable("user",taskUser);
        }

        executionEntity.executeActivity(activity);

        return executionEntity;

    }

}
<code>
9 REPLIES 9

hari
Star Contributor
Star Contributor
Hi,

Instead of using JumpActiviti(Doesn't look to be from Activiti) why dont you have a sequence flow from user task3 to user task2 on disapproval/rejection ?

windfighter
Champ in-the-making
Champ in-the-making
because,  which task node should jump to depends on configuration data in the non-activiti data tables, and I don't want the draw exclusive-gateway to realize the disapproval. cause any  task node would be a target node when disapproval/rejection happened.

faizal-manan
Champ in-the-making
Champ in-the-making
Hi,
I've make some changes to JumpActivitiCmd.java to fix the error, but I'm not really sure it's a proper solution or it may introduce another error later

<a href="https://gist.github.com/faizal-manan/6a3a077c52b13f596e18e1cb211207a2">JumpActivitiCmd.java Changes</a>

<a href="https://gist.github.com/faizal-manan/bc340ac8c6309954990655fa38abf2c7">Origin stacktrace</a>

<a href="https://gist.github.com/faizal-manan/a9c5c5b8d40d4e6e42d23711ffa70049">Success log</a>

Even though it was successfully (or properly ended), some potential bug or issue need to address
1. refer to successLog
1a. line 24, userTask3 for mazi was created, but TaskCompleteListener wasn't triggered
1b. line 41, instead of mazi, wanger was assigned to userTask3

thanks , faizal-manan.

code is userful for me!

and the additional error could be resolved with other ways..

for the 1a line 24, I can't reproduce the error. it work well for my case.

for the 1b. line 41: we can set the assignee to mazi before completing the user Task2

faizal-manan
Champ in-the-making
Champ in-the-making
hi windfighter,
you're right, (1a) doesn't trigger any error, but since I register completeTaskListener to every single user task, it should appear in a log upon calling taskService.complete()
As you can see from the log, completeTaskListener for userTask3 is fired during 2nd iteration (refer to line 45) and the question is where's the 1st completeTaskListener log for userTask3?

hi, faizal-manan


in the log line 25:  (TaskCreatedListener.java:18) - [5-21] Task Created: usertask3(User Task3)

task was created , and assigned to `mazi`.

then we execute the jump command (   managementService.executeCommand(cmd); )

when the jump command executed, the activiti actually jump from `user task 3` to `user task2`,it does't complete the complete the `user task3` , so there is no completeTaskListener log for the user Task3.









faizal-manan
Champ in-the-making
Champ in-the-making
hi windfighter,

did you have any idea which part of incomplete complete task?

hi faizal-manan,

there is no code to complete the `user task3` in the testJump function.


could you please indicate which line to complete the `user task3` ?

faizal-manan
Champ in-the-making
Champ in-the-making
just duplicate the last 2 LOC
<code>
  printExecutions(pi.getProcessInstanceId(), "after jump to user task2");

        completeTask(pi.getProcessInstanceId(), null);
        printExecutions(pi.getProcessInstanceId(), "after complete user task2-instance1");


        completeTask(pi.getProcessInstanceId(), null);
        printExecutions(pi.getProcessInstanceId(), "after complete user task2-instance2");

        completeTask(pi.getProcessInstanceId(), null);
        printExecutions(pi.getProcessInstanceId(), "after complete user task3");
</code>