cancel
Showing results for 
Search instead for 
Did you mean: 

Event listener with async service task does not update service

mishamo
Champ in-the-making
Champ in-the-making
Hi,

I am trying to use an event listener to update my UI when an unclaimed task becomes available in the workflow. While events are coming through, this does not seem to be reflected in the services when queried. This seems to be an issue that is more pertinent when using async tasks and the job executor. I have created a test case to prove this:


import java.util.List;

import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.ActivitiRule;
import org.activiti.engine.test.Deployment;
import org.junit.Rule;
import org.junit.Test;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class UpdateOnEventFailTest {

  @Rule
  public ActivitiRule activitiRule = new ActivitiRule("processupdatebug.activiti.cfg.xml");

  private volatile boolean passed;
  private List<Task> unclaimedTasks;

  @Test
  @Deployment(resources = "UpdateFailProcess.bpmn20.xml")
  public void testName() throws Exception {
    RuntimeService runtimeService = activitiRule.getRuntimeService();
    runtimeService.addEventListener(eventListener);

    runtimeService.startProcessInstanceByKey("myProcess");

    //required to let the job executor finish anything that it was doing
    Thread.sleep(2000);

    runtimeService.removeEventListener(eventListener);

    assertThat(passed, is(true));
  }

  private final ActivitiEventListener eventListener = new ActivitiEventListener() {
    @Override
    public void onEvent(ActivitiEvent activitiEvent) {
      System.err.println(activitiEvent.getType());
      doUpdate();
    }

    @Override
    public boolean isFailOnException() {
      return false;
    }
  };

  private void doUpdate() {
    TaskService taskService = activitiRule.getTaskService();
    unclaimedTasks = taskService.createTaskQuery()
                                 .taskUnassigned()
                                 .active()
                                 .list();

    if(unclaimedTasks.size() > 0) {
      passed = true;
      System.err.println("TEST PASSED");
    }
  }
}



<?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="myProcess" name="My process" isExecutable="true">
    <userTask id="usertask1" name="Unclaimed Task"></userTask>
    <serviceTask id="servicetask1" name="Service Task" activiti:async="true" activiti:class="process.processupdatebug.ProcessUpdateTaskDelegate"></serviceTask>
    <sequenceFlow id="flow1" sourceRef="servicetask1" targetRef="usertask1"></sequenceFlow>
    <startEvent id="startevent1" name="Start"></startEvent>
    <sequenceFlow id="flow2" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow3" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
    <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="682.0" y="290.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1">
        <omgdc:Bounds height="55.0" width="105.0" x="480.0" y="290.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="330.0" y="300.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="900.0" y="300.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="585.0" y="317.0"></omgdi:waypoint>
        <omgdi:waypoint x="682.0" y="317.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="365.0" y="317.0"></omgdi:waypoint>
        <omgdi:waypoint x="480.0" y="317.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="787.0" y="317.0"></omgdi:waypoint>
        <omgdi:waypoint x="900.0" y="317.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>



<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

    <property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
    <property name="jdbcDriver" value="org.h2.Driver" />
    <property name="jdbcUsername" value="sa" />
    <property name="jdbcPassword" value="" />

    <property name="jobExecutorActivate" value="true" />

    <property name="databaseSchemaUpdate" value="true" />

  </bean>

</beans>



import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;

public class ProcessUpdateTaskDelegate implements JavaDelegate {

  @Override
  public void execute(DelegateExecution execution) throws Exception {

  }
}


Points to notice are that the service task has "async=true" set, the config has "jobExecutorActivate = true" set and the test requires a sleep for a second or two to allow the job executor to complete anything in the pipeline before shutting the engine down. Basically, the state I am expecting is for the unclaimed tasks to be of size 1 as the workflow should have moved on to the user task (having finished the empty service task). The interesting thing is that all the appropriate events are fired (ACTIVITY_STARTED, ACTIVITY_COMPLETED and JOB_EXECUTION_SUCCESS) but that the list of unclaimed tasks never reflects this.
7 REPLIES 7

mishamo
Champ in-the-making
Champ in-the-making
Any ideas on this?

jbarrez
Star Contributor
Star Contributor
The state is only flushed at the end of the transaction. When you execute the query, the task is completed in memory, but only persisted to the db afterwards. Hence why you are seeing this behaviour.

mishamo
Champ in-the-making
Champ in-the-making
Hi, thanks for your reply.

That's pretty much what I figured was going on. Is there something in the roadmap for an "end of transaction" event? This seems like a fairly common use case (update UI when a task state is changed), are we approaching this use case in an unexpected manner?

trademak
Star Contributor
Star Contributor
Hi,

Yes makes sense. Could you create a JIRA issue for this feature request?

Thanks,

mishamo
Champ in-the-making
Champ in-the-making
A colleague of mine created an issue: https://jira.codehaus.org/browse/ACT-2040.

Thanks.

hipodrom
Champ in-the-making
Champ in-the-making
any news about that?
I need to maintain reporting system of acitivity events, but they are not in DB yet while finding them in ActivitiEventListener

jbarrez
Star Contributor
Star Contributor
@hipodrom : can you expand on your use case?
If I'm understanding you correctly … one way to solve this would be to add the logic in a TransactionListener that acts on the 'committed' event.