Add configurable recursion depth limit for Call Activities
Call Activities can create infinite loops through direct recursion (Process A calls itself) or indirect cycles (Process A → Process B → Process A), causing StackOverflowError and database exhaustion.
Changes
-
Configuration: Added
maxCallActivityRecursionDepthproperty toProcessEngineConfigurationImpl(default: 10, disable with ≤0) -
Cycle detection: Before subprocess instantiation,
CallActivityBehavior.checkCallActivityRecursion()traverses thesuperExecutionchain to detect if the target process definition key already exists in the call hierarchy -
Depth enforcement: Rejects calls when
currentDepth + 1 > maxDepthto prevent excessively deep non-cyclic hierarchies -
Case calls: Applied identical logic to
CaseCallActivityBehaviorfor CMMN case invocations
Error messages
Cycles include full call chain for debugging:
Recursive Call Activity detected: Process 'processA' is already present in the call hierarchy.
Current call chain: processB -> processA -> processB (cycle detected at depth 2).
Configure 'maxCallActivityRecursionDepth' in ProcessEngineConfiguration to adjust the limit.
Depth violations show what would exceed limit:
Call Activity recursion depth limit exceeded: Maximum depth is 3, current depth is 3.
Attempting to call: processD. Current call chain: processA -> processB -> processC.
Configuration example
ProcessEngineConfiguration config = ...
config.setMaxCallActivityRecursionDepth(5); // Allow up to 5 processes in hierarchy
config.setMaxCallActivityRecursionDepth(0); // Disable check entirely
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
-
artifacts.cibseven.org- Triggering command:
/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.11/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.11/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.11 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.11/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/cibseven/cibseven org.codehaus.plexus.classworlds.launcher.Launcher clean test -Dtest=CallActivityRecursionTest -DfailIfNoTests=false(dns block) - Triggering command:
/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.11/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.11/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.11 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.11/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/cibseven/cibseven org.codehaus.plexus.classworlds.launcher.Launcher clean install -DskipTests -pl engine -am(dns block)
- Triggering command:
If you need me to access, download, or install something from one of these locations, you can either:
- Configure Actions setup steps to set up my environment, which run before the firewall is enabled
- Add the appropriate URLs or hosts to the custom allowlist in this repository's Copilot coding agent settings (admins only)
Original prompt
Problem
When using Call Activities in BPMN processes, it's possible to create recursive or cyclic process calls that lead to infinite loops:
- Direct recursion: Process A calls itself via a Call Activity
- Indirect recursion: Process A → calls Process B → calls Process A → calls Process B → ... (endless cycle)
Currently, the CIB Seven engine does not detect or prevent such recursive call chains. This can lead to:
StackOverflowErrorcrashes- Database exhaustion (endless process instance creation)
- System instability
Proposed Solution
Implement a configurable recursion depth limit for Call Activities that:
- Tracks the call hierarchy by walking up the
superExecutionchain when a Call Activity is about to start a subprocess- Detects cycles by checking if the target
ProcessDefinition(by key) already exists in the current call chain- Enforces a maximum depth to prevent excessively deep (even non-cyclic) call hierarchies
- Is configurable via
ProcessEngineConfigurationImplementation Details
1. New Configuration Property
Add a new property to
ProcessEngineConfigurationImpl:protected int maxCallActivityRecursionDepth = 10; // default value, 0 or -1 = disabled public int getMaxCallActivityRecursionDepth() { ... } public void setMaxCallActivityRecursionDepth(int maxCallActivityRecursionDepth) { ... }2. Recursion Check in CallActivityBehavior
Modify
CallActivityBehavior.startInstance()inengine/src/main/java/org/cibseven/bpm/engine/impl/bpmn/behavior/CallActivityBehavior.java:Before starting the subprocess, traverse the
superExecutionchain and:
- Count the current depth
- Collect all
ProcessDefinitionkeys in the chain- Throw a
ProcessEngineExceptionif:
- The target process key is already in the chain (cycle detected), OR
- The maximum recursion depth is exceeded
3. Similar Check for CaseCallActivityBehavior
Apply the same logic to
CaseCallActivityBehaviorfor CMMN case calls if applicable.4. Unit Tests
Create tests that verify:
- Direct recursion (Process A → Process A) is detected and prevented
- Indirect recursion (Process A → Process B → Process A) is detected and prevented
- Deep but non-cyclic call chains work up to the configured limit
- The limit can be disabled by setting it to 0 or -1
- Appropriate error messages are thrown
Example Error Message
ProcessEngineException: Recursive Call Activity detected: Process 'processA' is already present in the call hierarchy. Current call chain: processB -> processA -> processB (cycle detected at depth 2). Configure 'maxCallActivityRecursionDepth' in ProcessEngineConfiguration to adjust the limit.Acceptance Criteria
New configuration property maxCallActivityRecursionDepthadded toProcessEngineConfigurationImplRecursion check implemented in CallActivityBehavior.startInstance()Clear and helpful exception message when recursion is detected Unit tests for direct recursion, indirect recursion, and depth limit Documentation updated (if applicable) Related Code Locations
engine/src/main/java/org/cibseven/bpm/engine/impl/bpmn/behavior/CallActivityBehavior.java- Main implementationengine/src/main/java/org/cibseven/bpm/engine/impl/cfg/ProcessEngineConfigurationImpl.java- Configurationengine/src/main/java/org/cibseven/bpm/engine/impl/persistence/entity/ExecutionEntity.java-getSuperExecution()for hierarchy traversalengine/src/test/java/org/cibseven/bpm/engine/test/bpmn/callactivity/CallActivityTest.java- Existing tests to extend
This pull request was created as a result of the following prompt from Copilot chat.
Problem
When using Call Activities in BPMN processes, it's possible to create recursive or cyclic process calls that lead to infinite loops:
- Direct recursion: Process A calls itself via a Call Activity
- Indirect recursion: Process A → calls Process B → calls Process A → calls Process B → ... (endless cycle)
Currently, the CIB Seven engine does not detect or prevent such recursive call chains. This can lead to:
StackOverflowErrorcrashes- Database exhaustion (endless process instance creation)
- System instability
Proposed Solution
Implement a configurable recursion depth limit for Call Activities that:
- Tracks the call hierarchy by walking up the
superExecutionchain when a Call Activity is about to start a subprocess- Detects cycles by checking if the target
ProcessDefinition(by key) already exists in the current call chain- Enforces a maximum depth to prevent excessively deep (even non-cyclic) call hierarchies
- Is configurable via
ProcessEngineConfigurationImplementation Details
1. New Configuration Property
Add a new property to
ProcessEngineConfigurationImpl:protected int maxCallActivityRecursionDepth = 10; // default value, 0 or -1 = disabled public int getMaxCallActivityRecursionDepth() { ... } public void setMaxCallActivityRecursionDepth(int maxCallActivityRecursionDepth) { ... }2. Recursion Check in CallActivityBehavior
Modify
CallActivityBehavior.startInstance()inengine/src/main/java/org/cibseven/bpm/engine/impl/bpmn/behavior/CallActivityBehavior.java:Before starting the subprocess, traverse the
superExecutionchain and:
- Count the current depth
- Collect all
ProcessDefinitionkeys in the chain- Throw a
ProcessEngineExceptionif:
- The target process key is already in the chain (cycle detected), OR
- The maximum recursion depth is exceeded
3. Similar Check for CaseCallActivityBehavior
Apply the same logic to
CaseCallActivityBehaviorfor CMMN case calls if applicable.4. Unit Tests
Create tests that verify:
- Direct recursion (Process A → Process A) is detected and prevented
- Indirect recursion (Process A → Process B → Process A) is detected and prevented
- Deep but non-cyclic call chains work up to the configured limit
- The limit can be disabled by setting it to 0 or -1
- Appropriate error messages are thrown
Example Error Message
ProcessEngineException: Recursive Call Activity detected: Process 'processA' is already present in the call hierarchy. Current call chain: processB -> processA -> processB (cycle detected at depth 2). Configure 'maxCallActivityRecursionDepth' in ProcessEngineConfiguration to adjust the limit.Acceptance Criteria
New configuration property maxCallActivityRecursionDepthadded toProcessEngineConfigurationImplRecursion check implemented in CallActivityBehavior.startInstance()Clear and helpful exception message when recursion is detected Unit tests for direct recursion, indirect recursion, and depth limit Documentation updated (if applicable) Related Code Locations
engine/src/main/java/org/cibseven/bpm/engine/impl/bpmn/behavior/CallActivityBehavior.java- Main implementationengine/src/main/java/org/cibseven/bpm/engine/impl/cfg/ProcessEngineConfigurationImpl.java- Configurationengine/src/main/java/org/cibseven/bpm/engine/impl/persistence/entity/ExecutionEntity.java-getSuperExecution()for hierarchy traversalengine/src/test/java/org/cibseven/bpm/engine/test/bpmn/callactivity/CallActivityTest.java- Existing tests to extend