简体   繁体   中英

Spring Batch Conditional Flow Not executing the else part

I am trying to achieve the flow shown in the image below using Spring batch. I was referring to java configuration on page 85 of https://docs.spring.io/spring-batch/4.0.x/reference/pdf/spring-batch-reference.pdf where it talks about Java Configuration.

要求

For some reason, when the Decider returns TYPE2, the batch ends with Failed State without any error message. Following is the java configuration of my job:

jobBuilderFactory.get("myJob")
            .incrementer(new RunIdIncrementer())
            .preventRestart()
            .start(firstStep())
            .next(typeDecider()).on("TYPE1").to(stepType1()).next(lastStep())
            .from(typeDecider()).on("TYPE2").to(stepType2()).next(lastStep())
            .end()
            .build();

I think something not right with the java configuration though it matches with the Spring document. A flow can be useful here but I am sure there would be a way without it. Any idea on how to achieve this?

You need to define the flow not only from the decider to next steps but also starting from stepType1 and stepType2 to lastStep . Here is an example:

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.batch.core.job.flow.JobExecutionDecider;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing
public class MyJob {

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Bean
    public Step firstStep() {
        return steps.get("firstStep")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("firstStep");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public JobExecutionDecider decider() {
        return (jobExecution, stepExecution) -> new FlowExecutionStatus("TYPE1"); // or TYPE2
    }

    @Bean
    public Step stepType1() {
        return steps.get("stepType1")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("stepType1");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step stepType2() {
        return steps.get("stepType2")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("stepType2");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step lastStep() {
        return steps.get("lastStep")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("lastStep");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Job job() {
        return jobs.get("job")
                .start(firstStep())
                .next(decider())
                    .on("TYPE1").to(stepType1())
                    .from(decider()).on("TYPE2").to(stepType2())
                    .from(stepType1()).on("*").to(lastStep())
                    .from(stepType2()).on("*").to(lastStep())
                    .build()
                .build();
    }

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean(Job.class);
        jobLauncher.run(job, new JobParameters());
    }

}

This prints:

firstStep
stepType1
lastStep

If the decider returns TYPE2 , the sample prints:

firstStep
stepType2
lastStep

Hope this helps.

Ran into the similar issue where else part is not being called (technically only first configured on() is being called)

Almost all the websites related to the flow and decider examples have the similar job configurations and was not able to figure it out what was the issue.

After some research, found the way how spring maintains the deciders and decisions. At high level, while initializing the application, based on the job configuration spring maintains a list of decisions for a decider object (like decsion0, decision1, so on).

when we call the decider() method it always returns a new object for the decider. As it is returning a new object, the list contains only one mapping for each object (ie, decision0 ) and since it is a list, it always return the first configured decision.So this is the reason why only the first configured transition only being called.

Solution: Instead of making a method call to the decider, create a single-ton bean for the decider and use it in the job configuration

Example:

@Bean
public JobExecutionDecider stepDecider() {
    return new CustomStepDecider();
}

inject it and use it in the job creation bean

@Bean
public Job sampleJob(Step step1, Step step2,Step step3,
JobExecutionDecider stepDecider) {

return jobBuilderFactory.get("sampleJob")                    
        .start(step1)
        .next(stepDecider).on("TYPE1").to(step2)
        .from(stepDecider).on("TYPE2").to(step3)


}

Hope this helps.

Create a dummyStep which will return the FINISH status and jump to next decider. you need to redirect flow cursor to next decider or virtual step after finishing the current step

.next(copySourceFilesStep())
.next(firstStepDecider).on(STEP_CONTINUE).to(executeStep_1())
.from(firstStepDecider).on(STEP_SKIP).to(virtualStep_1())

//-executeStep_2
.from(executeStep_1()).on(ExitStatus.COMPLETED.getExitCode())
.to(secondStepDecider).on(STEP_CONTINUE).to(executeStep_2())
.from(secondStepDecider).on(STEP_SKIP).to(virtualStep_3())

.from(virtualStep_1()).on(ExitStatus.COMPLETED.getExitCode())
.to(secondStepDecider).on(STEP_CONTINUE).to(executeStep_2())
.from(secondStepDecider).on(STEP_SKIP).to(virtualStep_3())

//-executeStep_3
.from(executeStep_2()).on(ExitStatus.COMPLETED.getExitCode())
.to(thirdStepDecider).on(STEP_CONTINUE).to(executeStep_3())
.from(thirdStepDecider).on(STEP_SKIP).to(virtualStep_4())

.from(virtualStep_3()).on(ExitStatus.COMPLETED.getExitCode())
.to(thirdStepDecider).on(STEP_CONTINUE).to(executeStep_3())
.from(thirdStepDecider).on(STEP_SKIP).to(virtualStep_4())

@Bean
public Step virtulaStep_2() {
    return stepBuilderFactory.get("continue-virtualStep2")
            .tasklet((contribution, chunkContext) -> {
                return RepeatStatus.FINISHED;
            })
            .build();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM