Timer Boundary and Catching Event usage from Java code

#1

Dear all,

I’m new to Flowable so it may sounds like a trivial question but I actually don’t understand how to proper use the TimerBoundaryEvent and TimerCatchingEvent.

There are 2 behaviours I would like to achieve in my workflow:

  1. If a tasks last longer than X seconds, than move to another task (which I guess is the TimerBoundaryEvent )
  2. Wait X seconds between two tasks (which I guess is the TimerCatchingEvent)

To obtain this I created two very simple workflows
SampleWorkflow1 SampleWorkflow2

In the first case I set the time duration to PT2S, in the first task I have a thread.sleep(5000) which should make the task last more than the timeout. What I get here is that the process terminates without the second task to be executed.
I the second case I get the same behaviour like whatever comes after the first task is ignored.

I add below the diagram xml and the java classes (task delegates and test class).

I tried having a look at other topics or examples but they did not help me.
Could you please help me with that?

Thanks a lot,
Jacopo

Diagram 1

<?xml version="1.0" encoding="UTF-8"?>
      <process id="myProcess" name="My process" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <serviceTask id="sampleWorkflow" name="Task1" activiti:class="workflows.SampleTask"></serviceTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="sampleWorkflow"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow2" sourceRef="servicetask1" targetRef="endevent1"></sequenceFlow>
    <serviceTask id="servicetask1" name="Task2" activiti:class="workflows.SampleTask2"></serviceTask>
    <boundaryEvent id="boundarytimer1" name="Timer" attachedToRef="sampleWorkflow" cancelActivity="true">
      <timerEventDefinition>
        <timeDuration>PT2S</timeDuration>
      </timerEventDefinition>
    </boundaryEvent>
    <sequenceFlow id="flow3" sourceRef="boundarytimer1" targetRef="servicetask1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
    <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="70.0" y="70.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sampleWorkflow" id="BPMNShape_sampleWorkflow">
        <omgdc:Bounds height="55.0" width="105.0" x="180.0" y="60.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="boundarytimer1" id="BPMNShape_boundarytimer1">
        <omgdc:Bounds height="30.0" width="30.0" x="271.0" y="74.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="540.0" y="71.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1">
        <omgdc:Bounds height="55.0" width="105.0" x="350.0" y="61.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="105.0" y="87.0"></omgdi:waypoint>
        <omgdi:waypoint x="180.0" y="87.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="455.0" y="88.0"></omgdi:waypoint>
        <omgdi:waypoint x="540.0" y="88.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="301.0" y="89.0"></omgdi:waypoint>
        <omgdi:waypoint x="350.0" y="88.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

Diagram 2

<?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" 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">
    <startEvent id="startevent1" name="Start"></startEvent>
    <serviceTask id="sampleWorkflow" name="Task1" activiti:class="workflows.SampleTask"></serviceTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="sampleWorkflow"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow2" sourceRef="servicetask1" targetRef="endevent1"></sequenceFlow>
    <serviceTask id="servicetask1" name="Task2" activiti:class="workflows.SampleTask2"></serviceTask>
    <eventBasedGateway id="eventgateway1" name="Event Gateway"></eventBasedGateway>
    <sequenceFlow id="flow3" sourceRef="sampleWorkflow" targetRef="eventgateway1"></sequenceFlow>
    <intermediateCatchEvent id="timerintermediatecatchevent1" name="TimerCatchEvent">
      <timerEventDefinition>
        <timeDuration>PT2S</timeDuration>
      </timerEventDefinition>
    </intermediateCatchEvent>
    <sequenceFlow id="flow4" sourceRef="eventgateway1" targetRef="timerintermediatecatchevent1"></sequenceFlow>
    <sequenceFlow id="flow5" sourceRef="timerintermediatecatchevent1" targetRef="servicetask1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
    <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="70.0" y="70.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sampleWorkflow" id="BPMNShape_sampleWorkflow">
        <omgdc:Bounds height="55.0" width="105.0" x="180.0" y="60.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="650.0" y="70.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1">
        <omgdc:Bounds height="55.0" width="105.0" x="460.0" y="60.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="eventgateway1" id="BPMNShape_eventgateway1">
        <omgdc:Bounds height="40.0" width="40.0" x="310.0" y="67.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="timerintermediatecatchevent1" id="BPMNShape_timerintermediatecatchevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="390.0" y="70.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="105.0" y="87.0"></omgdi:waypoint>
        <omgdi:waypoint x="180.0" y="87.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="565.0" y="87.0"></omgdi:waypoint>
        <omgdi:waypoint x="650.0" y="87.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="285.0" y="87.0"></omgdi:waypoint>
        <omgdi:waypoint x="310.0" y="87.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="350.0" y="87.0"></omgdi:waypoint>
        <omgdi:waypoint x="390.0" y="87.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
        <omgdi:waypoint x="425.0" y="87.0"></omgdi:waypoint>
        <omgdi:waypoint x="460.0" y="87.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

Tester class
package workflows;

import java.util.HashMap;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;

public class TimerTester {

  public static void main(String[] args) {
    ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
        .setJdbcUrl("jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1").setJdbcUsername("sa")
        .setJdbcPassword("").setJdbcDriver("org.h2.Driver")
        .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

    ProcessEngine processEngine = cfg.buildProcessEngine();

    RepositoryService repositoryService = processEngine.getRepositoryService();
    Deployment deployment = repositoryService.createDeployment()
        .addClasspathResource("workflows/Sampleworkflow.bpmn").deploy();

    ProcessDefinition processDefinition =
        repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId())
        .singleResult();

    RuntimeService runtimeService = processEngine.getRuntimeService();

    ProcessInstance processInstance =
        runtimeService.startProcessInstanceByKey("myProcess", new HashMap<String, Object>());
  }
}

Delegate class
package workflows;

import java.time.LocalTime;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;

public class SampleTask implements JavaDelegate {

  @Override
  public void execute(DelegateExecution execution) {
    System.out.println("doing something at " + LocalTime.now());
    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("finished something at " + LocalTime.now());
  }
}
#2

Hi Japelleg.

I did not try your tests, but you can have a look on the working examples
TimerBoundaryEvent:

TimerCatchingEvent:

Regards
Martin

#3

Dear Martin,

thanks for your answer. I had a look at the tests but I still have doubts.
First of all I see calls to many methods which are available through org.flowable.engine.test.Deployment which I guess I won’t use in my application.

The class I provided is on purpose not a test class. This is because I want my java application to execute the workflow when asked to.
What I expected is that once I start the process with runtimeService.startProcessInstanceByKey then the workflow starts and proceeds according to the machine time. Is this assumption true?

Then why should the timers be triggered manually?

Thanks a lot,

Jacopo

[Edit]
I added the following lines in my class:

ProcessInstance processInstance =
        runtimeService.startProcessInstanceByKey("myProcess", variables);
    
    ManagementService managementService = cfg.getManagementService();
    TimerJobQuery tjq = managementService.createTimerJobQuery().processInstanceId(processInstance.getId());
    System.out.println("Timer Job Query: " + tjq.count());
    
    //timerintermediatecatchevent1
    List<Job> list = managementService.createTimerJobQuery().list();    
    Job job = list.get(0);
    
    System.out.println(job.getDuedate());
    
    managementService.moveTimerToExecutableJob(job.getId());
    managementService.executeJob(job.getId());

And what I get is:

doing something at 15:52:16.423184600
finished something at 15:52:21.425871500
Timer Job Query: 1
Thu May 16 15:55:00 CEST 2019
doing something else at 15:52:22.118266600
finished something else at 15:52:24.119064800

So it looks like the due date is properly set, but why does the workflow immediately goes to the next task instead of waiting for the due date?

What I need to achieve is essentially to let some time pass between the two tasks.

#4

Hi Jacopo.

Yes, it is.

    managementService.moveTimerToExecutableJob(job.getId());
    managementService.executeJob(job.getId());

Is not needed when you put some Thread.sleep with the check whether the job was executed.
Similar to the following code:

Or you can just wait and check expected result after some time.

Regards
Martin

#5

Hi Martin,

thanks for the reply.

This sounds good to me:

But then I don’t see what is wrong with my example. Please let me know if I should provide more information.

Thanks,
Jacopo