Skip to content

Conditionnel Flow #4478

Open
Open
@elfelli

Description

@elfelli

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();
    }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions