Hi,
I’m having some issues with Flowable and event listeners and although I think this is a bug in Flowable, it may be that I am doing something wrong. So I’m posting here instead of simply creating a github issue.
My usecase is that I want to send events that occur in the Flowable process to an external system for audit logging purposes.I do this using a FlowableEventListener to hook into specific events, and then sends a message to a Camel route (and onwards using JMS).
In particular I want to extract the business key from the process, as this is the ID used in the external system, but other process variables can also be sent.
The process that I have makes use of an inclusive gateway and when processing the event Flowable seems to get stuck in an infinite recursive loop, that eventually ends with a java.lang.StackOverflowError.I have a unit test that displays the problem, and I have described it below.
I have a simple workaround to avoid this problem by filtering out “inclusiveGateway” events from further processing, but I am concerned that other process contructions can have the same issue.
To reproduce the problem I use a simple process with an inclusive gateway:
<?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">
<process id="inclusive-gateway-stackoverflow" name="inclusive-gateway-stackoverflow" isExecutable="true">
<startEvent id="sid-AFF21E0A-B9AB-48DF-9250-90DF32B2C58A"></startEvent>
<userTask id="sid-9D8F0713-5230-4B5D-B1DC-61B4AFE883F9" name="Review"></userTask>
<endEvent id="sid-80808B51-F8EA-45BA-82BB-AFFD5C86C736"></endEvent>
<inclusiveGateway id="sid-4ECA0909-3A04-4540-BFF9-C46EBF93FC8B" default="sid-727F54C5-AA5B-4578-BEBF-3E1BC273C24D"></inclusiveGateway>
<sequenceFlow id="sid-0CE7A7B3-1F07-4B5E-9A28-21CCCEAC733B" sourceRef="sid-AFF21E0A-B9AB-48DF-9250-90DF32B2C58A" targetRef="sid-4ECA0909-3A04-4540-BFF9-C46EBF93FC8B"></sequenceFlow>
<inclusiveGateway id="sid-54D9D201-B11D-48B1-B348-1329977E46AE"></inclusiveGateway>
<sequenceFlow id="sid-A2414E84-854D-4573-B490-507BEA482D84" sourceRef="sid-9D8F0713-5230-4B5D-B1DC-61B4AFE883F9" targetRef="sid-54D9D201-B11D-48B1-B348-1329977E46AE"></sequenceFlow>
<sequenceFlow id="sid-712AD927-16D4-4EC4-BEA9-538EDEFC96A7" sourceRef="sid-54D9D201-B11D-48B1-B348-1329977E46AE" targetRef="sid-80808B51-F8EA-45BA-82BB-AFFD5C86C736"></sequenceFlow>
<sequenceFlow id="sid-727F54C5-AA5B-4578-BEBF-3E1BC273C24D" sourceRef="sid-4ECA0909-3A04-4540-BFF9-C46EBF93FC8B" targetRef="sid-54D9D201-B11D-48B1-B348-1329977E46AE"></sequenceFlow>
<sequenceFlow id="sid-D804FF17-C59D-4C1C-88F1-1DAF3348159E" sourceRef="sid-4ECA0909-3A04-4540-BFF9-C46EBF93FC8B" targetRef="sid-9D8F0713-5230-4B5D-B1DC-61B4AFE883F9">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${review == true}]]></conditionExpression>
</sequenceFlow>
</process>
</definitions>
And have the following testcase:
package test;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.common.api.delegate.event.FlowableEvent;
import org.flowable.engine.common.api.delegate.event.FlowableEventListener;
import org.flowable.engine.delegate.event.FlowableActivityEvent;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.test.Deployment;
import org.flowable.engine.test.FlowableRule;
import org.junit.Rule;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
public class GetBusinessKeyInEventTest {
@Rule
public FlowableRule flowableRule = new FlowableRule();
class GetBusinessKeyEventListener implements FlowableEventListener {
@Override
public void onEvent(FlowableEvent event) {
if(FlowableActivityEvent.class.isAssignableFrom(event.getClass())) {
String pid = ((FlowableActivityEvent) event).getProcessInstanceId();
System.out.println("Processing Activity ID: " + ((FlowableActivityEvent) event).getActivityId());
if(pid != null) {
ProcessInstance processInstance = flowableRule.getRuntimeService().createProcessInstanceQuery().processInstanceId(pid).singleResult();
if(processInstance != null) {
System.out.println("Business Key: " + processInstance.getBusinessKey());
}
}
}
}
@Override
public boolean isFailOnException() {
return true;
}
}
@Test
@Deployment
public void inclusiveGatewayStackoverflow() {
RuntimeService runtimeService = flowableRule.getRuntimeService();
runtimeService.addEventListener(new GetBusinessKeyEventListener());
Map<String, Object> variables = new HashMap<>();
variables.put("review", true);
ProcessInstance pi1 = runtimeService.startProcessInstanceByKey("inclusive-gateway-stackoverflow", "business-key-123", variables);
assertEquals("business-key-123", runtimeService.createProcessInstanceQuery().processInstanceId(pi1.getProcessInstanceId()).singleResult().getBusinessKey());
}
}
Running this testcase gives a stacktrace like below (I have cut out a lot of the repeated stacktrace).
12:08:19.814 [main] ERROR org.flowable.engine.common.impl.interceptor.AbstractCommandContext - Error while closing command context
org.flowable.engine.common.api.FlowableException: Exception while executing event-listener
at org.flowable.engine.delegate.event.impl.FlowableEventSupport.dispatchEvent(FlowableEventSupport.java:107)
at org.flowable.engine.delegate.event.impl.FlowableEventSupport.dispatchEvent(FlowableEventSupport.java:89)
at org.flowable.engine.delegate.event.impl.FlowableEventDispatcherImpl.dispatchEvent(FlowableEventDispatcherImpl.java:68)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.handleActivityEnd(TakeOutgoingSequenceFlowsOperation.java:111)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.handleFlowNode(TakeOutgoingSequenceFlowsOperation.java:90)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.run(TakeOutgoingSequenceFlowsOperation.java:83)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:75)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:59)
at org.flowable.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:50)
at org.flowable.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:51)
at org.flowable.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:64)
at org.flowable.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:36)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:56)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:51)
at org.flowable.engine.impl.AbstractQuery.singleResult(AbstractQuery.java:123)
at test.GetBusinessKeyInEventTest$GetBusinessKeyEventListener.onEvent(GetBusinessKeyInEventTest.java:31)
at org.flowable.engine.delegate.event.impl.FlowableEventSupport.dispatchEvent(FlowableEventSupport.java:104)
at org.flowable.engine.delegate.event.impl.FlowableEventSupport.dispatchEvent(FlowableEventSupport.java:89)
at org.flowable.engine.delegate.event.impl.FlowableEventDispatcherImpl.dispatchEvent(FlowableEventDispatcherImpl.java:68)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.handleActivityEnd(TakeOutgoingSequenceFlowsOperation.java:111)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.handleFlowNode(TakeOutgoingSequenceFlowsOperation.java:90)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.run(TakeOutgoingSequenceFlowsOperation.java:83)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:75)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:59)
at org.flowable.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:50)
at org.flowable.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:51)
at org.flowable.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:64)
at org.flowable.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:36)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:56)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:51)
at org.flowable.engine.impl.AbstractQuery.singleResult(AbstractQuery.java:123)
at test.GetBusinessKeyInEventTest$GetBusinessKeyEventListener.onEvent(GetBusinessKeyInEventTest.java:31)
at org.flowable.engine.delegate.event.impl.FlowableEventSupport.dispatchEvent(FlowableEventSupport.java:104)
at org.flowable.engine.delegate.event.impl.FlowableEventSupport.dispatchEvent(FlowableEventSupport.java:89)
at org.flowable.engine.delegate.event.impl.FlowableEventDispatcherImpl.dispatchEvent(FlowableEventDispatcherImpl.java:68)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.handleActivityEnd(TakeOutgoingSequenceFlowsOperation.java:111)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.handleFlowNode(TakeOutgoingSequenceFlowsOperation.java:90)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.run(TakeOutgoingSequenceFlowsOperation.java:83)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:75)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:59)
at org.flowable.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:50)
at org.flowable.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:51)
at org.flowable.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:64)
at org.flowable.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:36)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:56)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:51)
at org.flowable.engine.impl.AbstractQuery.singleResult(AbstractQuery.java:123)
at test.GetBusinessKeyInEventTest$GetBusinessKeyEventListener.onEvent(GetBusinessKeyInEventTest.java:31)
...
...
Caused by: java.lang.StackOverflowError: null
at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateIterable(ExpressionEvaluator.java:43)
at org.apache.ibatis.scripting.xmltags.ForEachSqlNode.apply(ForEachSqlNode.java:55)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:33)
at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:41)
at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:292)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:81)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
at org.flowable.engine.impl.db.DbSqlSession.selectListWithRawParameter(DbSqlSession.java:313)
at org.flowable.engine.impl.db.DbSqlSession.selectList(DbSqlSession.java:298)
at org.flowable.engine.impl.persistence.entity.data.impl.MybatisExecutionDataManager.findProcessInstanceByQueryCriteria(MybatisExecutionDataManager.java:243)
at org.flowable.engine.impl.persistence.entity.ExecutionEntityManagerImpl.findProcessInstanceByQueryCriteria(ExecutionEntityManagerImpl.java:117)
at org.flowable.engine.impl.ProcessInstanceQueryImpl.executeList(ProcessInstanceQueryImpl.java:642)
at org.flowable.engine.impl.AbstractQuery.executeSingleResult(AbstractQuery.java:179)
at org.flowable.engine.impl.AbstractQuery.execute(AbstractQuery.java:160)
at org.flowable.engine.impl.interceptor.CommandInvoker$1.run(CommandInvoker.java:39)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:80)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:59)
at org.flowable.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:44)
at org.flowable.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:51)
at org.flowable.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:64)
at org.flowable.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:36)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:56)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:51)
at org.flowable.engine.impl.AbstractQuery.singleResult(AbstractQuery.java:123)
at test.GetBusinessKeyInEventTest$GetBusinessKeyEventListener.onEvent(GetBusinessKeyInEventTest.java:31)
at org.flowable.engine.delegate.event.impl.FlowableEventSupport.dispatchEvent(FlowableEventSupport.java:104)
at org.flowable.engine.delegate.event.impl.FlowableEventSupport.dispatchEvent(FlowableEventSupport.java:89)
at org.flowable.engine.delegate.event.impl.FlowableEventDispatcherImpl.dispatchEvent(FlowableEventDispatcherImpl.java:68)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.handleActivityEnd(TakeOutgoingSequenceFlowsOperation.java:111)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.handleFlowNode(TakeOutgoingSequenceFlowsOperation.java:90)
at org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation.run(TakeOutgoingSequenceFlowsOperation.java:83)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:75)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:59)
at org.flowable.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:50)
at org.flowable.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:51)
at org.flowable.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:64)
at org.flowable.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:36)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:56)
at org.flowable.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:51)
at org.flowable.engine.impl.AbstractQuery.singleResult(AbstractQuery.java:123)
at test.GetBusinessKeyInEventTest$GetBusinessKeyEventListener.onEvent(GetBusinessKeyInEventTest.java:31)
...
...
Does this look like a bug? I can create a GitHub issue if it is.
Is my workaround of excluding inclusive gateways good enough? Or are there other BPMN components that can have a similar issue?
Thanks for any help!
Edit: Forgot to say that this is using Flowable 6.0.0.