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:
If a tasks last longer than X seconds, than move to another task (which I guess is the TimerBoundaryEvent )
Wait X seconds between two tasks (which I guess is the TimerCatchingEvent)
To obtain this I created two very simple workflows
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());
}
}
Hi Japelleg.
I did not try your tests, but you can have a look on the working examples
TimerBoundaryEvent:
currentTime = new Date(currentTime.getTime() + twentyFourHours + (60 * 1000));
processEngineConfiguration.getClock().setCurrentTime(currentTime);
managementService.moveTimerToExecutableJob(job.getId());
managementService.executeJob(job.getId());
}
}
@Test
@Deployment
public void testBoundaryTimerEvent() throws Exception {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyy.MM.dd hh:mm");
Date currentTime = simpleDateFormat.parse("2015.10.01 11:01");
processEngineConfiguration.getClock().setCurrentTime(currentTime);
Map<String, Object> vars = new HashMap<>();
vars.put("patient", "kermit");
runtimeService.startProcessInstanceByKey("process1", vars);
// just wait for 2 seconds to run any job if it's the case
TimerCatchingEvent:
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flowable.engine.test.bpmn.event.timer;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
This file has been truncated. show original
Regards
Martin
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.
Hi Jacopo.
japelleg:
Is this assumption true?
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:
asyncExecutor.start();
processEngineConfiguration.setAsyncExecutorActivate(true);
try {
Timer timer = new Timer();
InterruptTask task = new InterruptTask(Thread.currentThread());
timer.schedule(task, maxMillisToWait);
boolean areJobsAvailable = true;
try {
while (areJobsAvailable && !task.isTimeLimitExceeded()) {
Thread.sleep(intervalMillis);
try {
areJobsAvailable = jobsAvailablePredicate.test(managementService);
} catch (Throwable t) {
// Ignore, possible that exception occurs due to locking/updating of table on MSSQL when
// isolation level doesn't allow READ of the table
}
}
} catch (InterruptedException e) {
// ignore
} finally {
Or you can just wait and check expected result after some time.
Regards
Martin
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
Hey Jacopo,
Correct me if I am wrong, but is your question similar to Catch the event of a blocked instance only after a timeout ?
Your example is not OK since you are blocking in your service task? Which is not a wait state and the engine will wait for it’s execution before continuing, this means that the timer task won’t even be created since you are in a running state of the process.
You would have to handle the waiting and proceeding in your own java logic.
Btw, you don’t need to manually execute the job. The reason why it is done like that in the tests is to simulate the events. You can also see that every test that Martin linked has a User task as a wait state. This means that the engine will first do a commit and give back the control of the process execution.
Cheers,
Filip
Hi Filip,
essentially what I need to achieve is:
Introduce a delay between the execution of two tasks: after task 1 wait n seconds before going to task 2
Introduce a timeout for the task execution: if the tasks takes longer than n seconds that should be skipped.
Thanks,
Jacopo
Hi Filip,
ok then let’s focus on the Timer Intermediate event. I created the following workflow:
where the to service tasks simply print out the current time, the timer intermediate event has the time duration set PT10S.
My expectation would be to see the current time and then current time + 10s. Is it sensible?
What I get is the following, so only one of the two tasks is executed and I see it immediately.
Found process definition : My process
2019-06-11T12:54:02.548356600Z
Could you please tell we what am I doing wrong here and how to get the desired behaviour?
Thanks a lot in advanvce for your help!
I attach the complete code for reference
WorkflowExecutor.java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
public class WorkflowExecutor {
private static String filename = "src/main/resources/diagrams/DelayDiagram.bpmn";
public static void main(String[] args) throws FileNotFoundException {
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()
.addInputStream("myProcess.bpmn20.xml", new FileInputStream(filename)).deploy();
// .addClasspathResource("SimpleProcess.bpmn").deploy();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deployment.getId()).singleResult();
System.out.println("Found process definition : " + processDefinition.getName());
// Start process
RuntimeService runtimeService = processEngine.getRuntimeService();
Map<String, Object> variables = new HashMap<String, Object>();
ProcessInstance processInstance =
runtimeService.startProcessInstanceByKey("myProcess", variables);
}
}
DelayTask.java
import java.time.Instant;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
public class DelayTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) throws Exception {
System.out.println(Instant.now());
}
}
DelayDiagram.bpmn
<?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">
<serviceTask id="servicetask1" name="Task1" activiti:class="apackage.DelayTask"></serviceTask>
<serviceTask id="servicetask2" name="Task2" activiti:class="apackage.DelayTask"></serviceTask>
<intermediateCatchEvent id="timerintermediatecatchevent1" name="TimerCatchEvent">
<timerEventDefinition>
<timeDuration>PT10S</timeDuration>
</timerEventDefinition>
</intermediateCatchEvent>
<sequenceFlow id="flow1" sourceRef="servicetask1" targetRef="timerintermediatecatchevent1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="timerintermediatecatchevent1" targetRef="servicetask2"></sequenceFlow>
<startEvent id="startevent1" name="Start"></startEvent>
<sequenceFlow id="flow3" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow4" sourceRef="servicetask2" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1">
<omgdc:Bounds height="55.0" width="105.0" x="104.0" y="20.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask2" id="BPMNShape_servicetask2">
<omgdc:Bounds height="55.0" width="105.0" x="320.0" y="20.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="timerintermediatecatchevent1" id="BPMNShape_timerintermediatecatchevent1">
<omgdc:Bounds height="35.0" width="35.0" x="240.0" y="30.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="24.0" y="30.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="460.0" y="30.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="209.0" y="47.0"></omgdi:waypoint>
<omgdi:waypoint x="240.0" y="47.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="275.0" y="47.0"></omgdi:waypoint>
<omgdi:waypoint x="320.0" y="47.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="59.0" y="47.0"></omgdi:waypoint>
<omgdi:waypoint x="104.0" y="47.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="425.0" y="47.0"></omgdi:waypoint>
<omgdi:waypoint x="460.0" y="47.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
joram
June 20, 2019, 3:38pm
10
It looks like you don’t have the async executor running. Set the asyncExecutorActivate flag to true.