Boundary Signal Event on a multi instance user task inside an embedded sub process

I am having a multi instance user task MT inside an embedded sub process. The multi instance user task MT has task listener set on completion of task. The listener evaluates a condition and if true triggers the boundary signal event. The boundary signal event moves the flow to another user task UT inside the same sub process.

Here is a snippet from my process definition,

  <signal id="signal_start_final_verification" name="SIGNAL_START_FINAL_VERIFICATION" flowable:scope="processInstance"></signal>
   ...
   ...
  <boundaryEvent id="..." name="..." attachedToRef="MT" cancelActivity="false">
    <documentation>...</documentation>
    <signalEventDefinition signalRef="signal_start_final_verification"></signalEventDefinition>
  </boundaryEvent>

In order to signal UT, I use the following snippet in my on completion task listener of MT. It is executed only when a certain condition evaluates to true.

 Execution execution = runtimeService.createExecutionQuery()
                .processInstanceId(delegateExecution.getProcessInstanceId())
                .signalEventSubscriptionName("SIGNAL_START_FINAL_VERIFICATION")
                .singleResult();

 runtimeService.signalEventReceived("SIGNAL_START_FINAL_VERIFICATION", execution.getId());

Lets say that the multi instance loops 5 times and the condition evaluates to true at 2nd, 4th and 5th instance. The MT task instances are completed but UT is not created or spawned though at 2nd and 4th instance. During execution of onComplete listener of 5th instance task, UT is again not created and the MT task instance rolls backs with following exception.

org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: org.postgresql.util.PSQLException: ERROR: update or delete on table "act_ru_execution" violates foreign key constraint "act_fk_var_exe" on table "act_ru_variable"
  Detail: Key (id_)=(695101) is still referenced from table "act_ru_variable".
### The error may involve org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl.deleteExecution-Inline
### The error occurred while setting parameters
### SQL: delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ?
### Cause: org.postgresql.util.PSQLException: ERROR: update or delete on table "act_ru_execution" violates foreign key constraint "act_fk_var_exe" on table "act_ru_variable"
  Detail: Key (id_)=(695101) is still referenced from table "act_ru_variable".
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:200)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.delete(DefaultSqlSession.java:213)
    ...
    ...

I am failing to debug why the boundary signal event is not being triggered and why the last instance of multi instance user task raises exception while executing its onComplete task listener.

@martin.grofcik @joram If you could please provide some help or directions here ?

If I sum it up in a few lines, the problem is that the on complete task listener on a multiinstance user task inside a subprocess doesn’t works for the last iteration (and rollbacks). I have tried with a dummy on complete task listener (method signature with no body) too but it also throws the same previous exception on last iteration.

If I remove the on complete task listener, it works fine. But, in my case an on complete task listener is must to have.

I moved away from using boundary signal event to event subprocess.

forum

The problem was with duplicate name variables being stored in ACT_RU_VARIABLE. Renaming duplicates fixed the problem. A issue still is that if I complete like one of the instance of MT as soon as it is created (or may be a delay of 3-5s), the event subprocess signal is fired but the UT inside event subprocess is not created. Delaying completion of further instances also act the same.

If I complete the first MT instance task within a delay of say 15s+, it works fine then for all MTs.

Finally, I changed

<signal id="signal_start_final_verification" name="SIGNAL_START_FINAL_VERIFICATION" flowable:scope="processInstance"></signal>

to

<signal id="signal_start_final_verification" name="SIGNAL_START_FINAL_VERIFICATION" flowable:scope="global"></signal>

and it all works great now. I can complete any of the MT task at the very instant it is created and the signal triggers the UT task at that very instant if the condition set evaluates to true.

I din’t get why processInstance scope dint work flawlessly as my signal is restricted to the process instance. In my case, I have been triggering the event using that very process execution Id (subscribed to the signal) and hence the global scope isn’t a problem for me.

Hi,

could you share a jUnit test, please?

mvn archetype:generate -DarchetypeGroupId=org.flowable -DarchetypeArtifactId=flowable-archetype-unittest -DarchetypeVersion=6.0.1 -DgroupId=org.flowable -DartifactId=junittest

Regads
Martin

@martin.grofcik I created a stub using the mvn command, But, not sure how to add onComplete task listener within the test case itself. Is it possible using only the dependencies in stub pom.

If I create onComplete task listener as a separate class, injection of RuntimeService or TaskService fails.

You can use process engine configuration in the same way as your real system is doing it.

@martin.grofcik So, I tried to create and test within my existing source code:

Here is the bpmn diagram:
0

Here is the process definition with signal_start_ut globally scoped:

<?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:flowable="http://flowable.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.flowable.org/processdef">
  <signal id="signal_start_ut" name="Signal Start UT" flowable:scope="global"></signal>
  <process id="my-process" name="my-process" isExecutable="true">
    <startEvent id="start" name="Start"></startEvent>
    <subProcess id="sid-39EC39C9-1C3B-4EEE-AAAE-836D94108B4D" name="subProcess">
      <subProcess id="sid-1981A22B-4D3A-47EC-A57F-5082915C3B79" name="subProcess" triggeredByEvent="true">
        <startEvent id="start_ut" name="Start UT" isInterrupting="false">
          <signalEventDefinition signalRef="signal_start_ut"></signalEventDefinition>
        </startEvent>
        <userTask id="ut" name="UT"></userTask>
        <endEvent id="end_ut" name="End UT"></endEvent>
        <sequenceFlow id="sid-27A2EE9F-5284-431E-B3B0-7D3F5B031F7E" sourceRef="ut" targetRef="end_ut"></sequenceFlow>
        <sequenceFlow id="sid-26F3AB3D-A392-4C0E-9599-FBDF7AC1B14F" sourceRef="start_ut" targetRef="ut"></sequenceFlow>
      </subProcess>
      <startEvent id="start_mt" name="Start MT"></startEvent>
      <userTask id="mt" name="MT">
        <extensionElements>
          <flowable:taskListener event="complete"
              delegateExpression="${checkUT}"></flowable:taskListener>
        </extensionElements>
        <multiInstanceLoopCharacteristics isSequential="false">
          <loopCardinality>5</loopCardinality>
        </multiInstanceLoopCharacteristics>
      </userTask>
      <endEvent id="end_mt" name="End MT"></endEvent>
      <sequenceFlow id="sid-0BC2B5C4-7752-43C8-84FE-C37750F9B597" sourceRef="start_mt" targetRef="mt"></sequenceFlow>
      <sequenceFlow id="sid-37AB8C90-5337-41F9-80E9-96D6B6E47FB0" sourceRef="mt" targetRef="end_mt"></sequenceFlow>
    </subProcess>
    <sequenceFlow id="sid-E7087910-0B58-4C2C-827F-F0AF54C1F57B" sourceRef="start" targetRef="sid-39EC39C9-1C3B-4EEE-AAAE-836D94108B4D"></sequenceFlow>
    <endEvent id="end" name="End"></endEvent>
    <sequenceFlow id="sid-2B59204D-B5FB-45AE-960B-43DD49392017" sourceRef="sid-39EC39C9-1C3B-4EEE-AAAE-836D94108B4D" targetRef="end"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_my-process">
    <bpmndi:BPMNPlane bpmnElement="my-process" id="BPMNPlane_my-process">
      <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
        <omgdc:Bounds height="30.0" width="30.0" x="120.0" y="170.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-39EC39C9-1C3B-4EEE-AAAE-836D94108B4D" id="BPMNShape_sid-39EC39C9-1C3B-4EEE-AAAE-836D94108B4D">
        <omgdc:Bounds height="220.0" width="475.0" x="225.0" y="75.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-1981A22B-4D3A-47EC-A57F-5082915C3B79" id="BPMNShape_sid-1981A22B-4D3A-47EC-A57F-5082915C3B79">
        <omgdc:Bounds height="100.0" width="443.0" x="238.0" y="180.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="start_ut" id="BPMNShape_start_ut">
        <omgdc:Bounds height="30.0" width="30.0" x="285.0" y="212.00000149011612"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="ut" id="BPMNShape_ut">
        <omgdc:Bounds height="70.0" width="104.0" x="407.5" y="195.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="end_ut" id="BPMNShape_end_ut">
        <omgdc:Bounds height="28.0" width="28.0" x="615.0" y="216.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="start_mt" id="BPMNShape_start_mt">
        <omgdc:Bounds height="30.0" width="30.0" x="285.0" y="110.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="mt" id="BPMNShape_mt">
        <omgdc:Bounds height="70.0" width="104.0" x="410.5" y="90.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="end_mt" id="BPMNShape_end_mt">
        <omgdc:Bounds height="28.0" width="28.0" x="615.0" y="111.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
        <omgdc:Bounds height="28.0" width="28.0" x="765.0" y="171.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="sid-26F3AB3D-A392-4C0E-9599-FBDF7AC1B14F" id="BPMNEdge_sid-26F3AB3D-A392-4C0E-9599-FBDF7AC1B14F">
        <omgdi:waypoint x="314.9973474306012" y="227.28208311985915"></omgdi:waypoint>
        <omgdi:waypoint x="407.5" y="229.02194405947358"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-37AB8C90-5337-41F9-80E9-96D6B6E47FB0" id="BPMNEdge_sid-37AB8C90-5337-41F9-80E9-96D6B6E47FB0">
        <omgdi:waypoint x="514.5" y="125.0"></omgdi:waypoint>
        <omgdi:waypoint x="615.0" y="125.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-2B59204D-B5FB-45AE-960B-43DD49392017" id="BPMNEdge_sid-2B59204D-B5FB-45AE-960B-43DD49392017">
        <omgdi:waypoint x="700.0" y="185.0"></omgdi:waypoint>
        <omgdi:waypoint x="765.0" y="185.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-27A2EE9F-5284-431E-B3B0-7D3F5B031F7E" id="BPMNEdge_sid-27A2EE9F-5284-431E-B3B0-7D3F5B031F7E">
        <omgdi:waypoint x="511.5" y="230.0"></omgdi:waypoint>
        <omgdi:waypoint x="615.0" y="230.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-0BC2B5C4-7752-43C8-84FE-C37750F9B597" id="BPMNEdge_sid-0BC2B5C4-7752-43C8-84FE-C37750F9B597">
        <omgdi:waypoint x="315.0" y="125.0"></omgdi:waypoint>
        <omgdi:waypoint x="410.5" y="125.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-E7087910-0B58-4C2C-827F-F0AF54C1F57B" id="BPMNEdge_sid-E7087910-0B58-4C2C-827F-F0AF54C1F57B">
        <omgdi:waypoint x="150.0" y="185.0"></omgdi:waypoint>
        <omgdi:waypoint x="225.0" y="185.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

The MT onComplete Task Listener class CheckUT:

package org.flowable.listeners;

import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.DelegateTask;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.runtime.Execution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("checkUT")
public class CheckUT implements TaskListener {

  @Autowired
  private RuntimeService runtimeService;

  public void notify(DelegateTask delegateTask) {

    DelegateExecution delegateExecution = delegateTask.getExecution();

    Execution execution = runtimeService.createExecutionQuery()
        .signalEventSubscriptionName("Signal Start UT")
        .processInstanceId(delegateExecution.getProcessInstanceId())
        .singleResult();

    runtimeService.signalEventReceived("Signal Start UT", execution.getId());
  }
}

The provided test passes with global scope:
<signal id="signal_start_ut" name="Signal Start UT" flowable:scope="global"></signal>
but fails with processInstance scope.
<signal id="signal_start_ut" name="Signal Start UT" flowable:scope="processInstance"></signal>

package org.flowable.bpmn;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;

import com.ocrolus.cura.CuraApp;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Task;
import org.flowable.engine.test.FlowableRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
@ActiveProfiles("test")
public class MyUnitTest {

  @Autowired
  private RuntimeService runtimeService;

  @Autowired
  private TaskService taskService;

  @Test
  public void test() throws Exception {

    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("my-process");
    assertNotNull(processInstance);

    List<Task> tasks = taskService.createTaskQuery().list();
    assertEquals(tasks.size(), 5);

    Task firstTask = tasks.get(0);
    taskService.complete(firstTask.getId());

    List<Task> utTasks = taskService.createTaskQuery().taskDefinitionKey("ut").list();
    // utTasks.size() = 0 when scoped to processInstance
    Assertions.assertThat(utTasks.size()).isEqualTo(1);
  }
}

Flowable team - Any thoughts on this?

HI Zoheb,

I am sorry I do not have a time now to build a jUnit test from your sources.
Martin

Hi @girirajsharma

I am also getting the same exception even after making all my signal events as global.

Could you please help me on how to fix this issue ?