Description
Bug description
In Spring Batch, when defining a Flow with multiple transitions based on a JobExecutionDecider, the order of .from(decider).on(...) declarations is impacting the flow execution unexpectedly.
Environment
Spring Batch version: 5.0.3
Java version: 17
Database (if any): postgresql
Any other relevant framework/tool versions
Steps to reproduce
Configure a Job with a Flow as defined in the provided example.
Use a JobExecutionDecider to determine the flow execution based on a job parameter.
Notice that the execution path depends incorrectly on the order of the .from(routeDecider).on(...) method calls.
Expected behavior
The flow execution decision should solely depend on the output of the JobExecutionDecider and not on the order of the .from().on() method calls in the flow definition. Each condition ("node", "can", "both") should correctly lead to its corresponding flow regardless of the order these conditions are defined in the flow builder except the * case
Minimal Complete Reproducible example
A simple setup can be provided that includes:
public class RouteDecider implements JobExecutionDecider {
@Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
String route = jobExecution.getJobParameters().getString("route").trim();
log.info("Route decision : {}",route);
return new FlowExecutionStatus(route);
}
}
My flow is configured like bellow ;
@Bean
public Flow testFlow(Step archiveCanStep, Step archiveNodeStep, Step archiveAllStep, Step next, Step expStep, Step canStep, RouteDecider routeDecider) {
return new FlowBuilder<SimpleFlow>("testFlow")
.start(routeDecider).on("node").to(expStep).next(nodeStep).next(archiveNodeStep)
.from(routeDecider).on("can").to(canStep).next(archiveCanStep)
.from(routeDecider).on("both").to(expStep).next(nodeStep).next(canStep).next(archiveBothStep)
.from(routeDecider).on("*").end()
.end();
}
Case of "node" -> OK
Case of "can" -> OK
Case of "both" -> execute always (node) (expStep).next(nodeStep).next(archiveNodeStep)
Case "*" -> OK
if i change order definition like this
@Bean
public Flow testFlow(Step archiveCanStep, Step archiveNoeStep, Step archiveAllStep, Step next, Step expStep, Step canStep, RouteDecider routeDecider) {
return new FlowBuilder<SimpleFlow>("testFlow")
.start(routeDecider).on("both").to(expStep).next(nodeStep).next(canStep).next(archiveBothStep)
.from(routeDecider).on("can").to(canStep).next(archiveCanStep)
.from(routeDecider).on("node").to(expStep).next(nodeStep).next(archiveNodeStep)
.from(routeDecider).on("*").end()
.end();
}
Case of "node" -> KO execute always (both) .to(expStep).next(nodeStep).next(canalisationStep).next(archiveBothStep)
Case of "can" -> OK
Case of "both" -> OK
Case "*" -> OK
So I tried something else to bypass this issue by defining single flow for each case like this now so I can simplify the decider flow ...
@Bean
public Flow routeDeciderFlow(Flow NodeFlow, Flow canFlow, Flow AllFlow,routeDecider routeDecider) {
return new FlowBuilder<SimpleFlow>(FLOW_JOB_BERG_DECIDER)
.start(routeDecider).on("can").to(canFlow)
.from(routeDecider).on("node").to(NodeFlow)
.from(routeDecider).on("*").to(AllFlow)
.end();
}
@Bean
public Flow NodeFlow(Step nodeStep, Step expStep, Step archiveNoeStep) {
return new FlowBuilder<SimpleFlow>(FLOW_JOB_BERG_DECIDER)
.start(expStep)
.next(nodeStep)
.next(archiveNoeStep)
.end();
}
@Bean
public Flow canFlow(Step canStep, Step archiveCanStep) {
return new FlowBuilder<SimpleFlow>(FLOW_JOB_BERG_DECIDER)
.start(canStep)
.next(archiveCanStep)
.end();
}
@Bean
public Flow AllFlow(Step archiveAllStep, Step nodeStep, Step expStep, Step canStep) {
return new FlowBuilder<SimpleFlow>(FLOW_JOB_BERG_DECIDER)
.start(expStep)
.next(nodeStep)
.next(canStep)
.next(archiveAllStep)
.end();
}