Spring Batch: Can't quite work out Conditional Flow - spring

Edited to update my latest configuration: Is this on the right track for my use-case?
I have a flow that's supposed to go like this:
The FileRetrievingTasklet retrieves a remote file and places the
"type" of that file in the execution context.
If the file is of type "YEARLY", proceed to the yearlyStep().
If the file is of type "QUARTERLY", proceed to the quarterlyStep().
Finish.
This seems so simple, but what I have doesn't work. The job finishes with FAILED after the tasklet step.
Here's my job config:
#Bean
public Job fundsDistributionJob() {
return jobBuilderFactory
.get("fundsDistributionJob")
.start(retrieveFileStep(stepBuilderFactory))
.on("YEARLY").to(yearEndStep())
.from(retrieveFileStep(stepBuilderFactory))
.on("QUARTERLY").to(quarterlyStep())
.end()
.listener(new FileWorkerJobExecutionListener())
.build();
}
And one of the steps:
#Bean
public Step quarterlyStep() {
return stepBuilderFactory.get("quarterlyStep")
.<Item, Item>chunk(10)
.reader(quarterlyReader())
.processor(processor())
.writer(writer())
.listener(new StepItemReadListener())
.faultTolerant()
.skipPolicy(new DistSkipPolicy())
.build();
}
Can someone tell me what's missing?

The approach with a decider (before your edit) is the way to go. You just had an issue with your flow definition. Here is an example that works as you described:
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.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 {
private final JobBuilderFactory jobs;
private final StepBuilderFactory steps;
public MyJob(JobBuilderFactory jobs, StepBuilderFactory steps) {
this.jobs = jobs;
this.steps = steps;
}
#Bean
public Step retrieveFileStep() {
return steps.get("retrieveFileStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("Downloading file..");
chunkContext.getStepContext().getStepExecution()
.getExecutionContext().put("type", Type.YEARLY);
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public JobExecutionDecider fileMapperDecider() {
return (jobExecution, stepExecution) -> {
Type type = (Type) stepExecution.getExecutionContext().get("type");
return new FlowExecutionStatus(type == Type.YEARLY ? "yearly" : "quarterly");
};
}
#Bean
public Step yearlyStep() {
return steps.get("yearlyStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("running yearlyStep");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step quarterlyStep() {
return steps.get("quarterlyStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("running quarterlyStep");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Job job() {
return jobs.get("job")
.start(retrieveFileStep())
.next(fileMapperDecider())
.from(fileMapperDecider()).on("yearly").to(yearlyStep())
.from(fileMapperDecider()).on("quarterly").to(quarterlyStep())
.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());
}
enum Type {
YEARLY, QUARTERLY
}
}
It prints:
Downloading file..
running yearlyStep
If you change the type attribute in the execution context to Type.QUARTERLY in retrieveFileStep, it prints:
Downloading file..
running quarterlyStep

Related

The method next(Step) is undefined for the type FlowJobBuilder

I am working on Spring Boot and Batch Project and using spring Batch decider.
Error
The method next(Step) is undefined for the type FlowJobBuilder
return jobBuilderFactory.get("sampleJob")
.incrementer(new RunIdIncrementer())
.start(abcStep(stepBuilderFactory, abcReader))
.next(sampleDecider())
.from(sampleDecider())
.on(MDSConst.SUCCESS).to(xyxStep(stepBuilderFactory, xyzReader))
.from(sampleDecider())
.on(MDSConst.FAILED).end().build()
.next(mnoStep(stepBuilderFactory, mnoReder))
.build();
If sampleDecider gives SUCCESS, xyxStep should execute
If sampleDecider gives FAILED, batch Job should stop
If sampleDecider gives SUCCESS, xyxStep should execute and then mnoStep should execute
I hope this is the write way of doing things even for the chunk based Steps.
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.core.launch.support.RunIdIncrementer;
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 step1() {
return steps.get("step1")
.tasklet((contribution, chunkContext) -> {
System.out.println("hello");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public JobExecutionDecider decider() {
return (jobExecution, stepExecution) -> new FlowExecutionStatus("YES"); // or NO
}
#Bean
public Step step2() {
return steps.get("step2")
.tasklet((contribution, chunkContext) -> {
System.out.println("world");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step step3() {
return steps.get("step3")
.tasklet((contribution, chunkContext) -> {
System.out.println("!!");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step step5() {
return steps.get("step5")
.tasklet((contribution, chunkContext) -> {
System.out.println("Step 5");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step step4() {
return steps.get("step4")
.tasklet((contribution, chunkContext) -> {
System.out.println("Step 4");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Job job() {
return jobs.get("job")
.incrementer(new RunIdIncrementer())
.start(step1())
.next(decider())
.on("YES").to(step2())
.next(step4())
.next(step5())
.from(decider())
.on("NO").to(step3())
.end()
.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());
}
}

Dynamic step creationin Spring Batch using custom parameter for decision making

Perfect solution for dynamic step creationin Spring Batch.
Just that I am not able to get parameters into this , which will decide what step need to be executed or how can pass steps Array ?
<pre>#Bean
public Job job() {
Step[] stepsArray = // create your steps array or pass it as a parameter
SimpleJobBuilder jobBuilder = jobBuilderFactory.get("mainCalculationJob")
.incrementer(new RunIdIncrementer())
.start(truncTableTaskletStep());
for (Step step : stepsArray) {
jobBuilder.next(step);
}
return jobBuilder.build();
}</pre>
Thanks
i am looking how to pass this step array as parameter and get in above function
Here is an example of how to pass the steps array as a parameter:
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.builder.SimpleJobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.repeat.RepeatStatus;
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 MyJobConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
public MyJobConfiguration(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory;
}
public Step initialStep() {
return stepBuilderFactory.get("initialStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("initial step");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step[] dynamicSteps() {
// load steps sequence from db and create steps here
Step step1 = stepBuilderFactory.get("step1")
.tasklet((contribution, chunkContext) -> {
System.out.println("hello");
return RepeatStatus.FINISHED;
})
.build();
Step step2 = stepBuilderFactory.get("step2")
.tasklet((contribution, chunkContext) -> {
System.out.println("world");
return RepeatStatus.FINISHED;
})
.build();
return new Step[]{step1, step2};
}
#Bean
public Job job(Step[] dynamicSteps) {
SimpleJobBuilder jobBuilder = jobBuilderFactory.get("job")
.start(initialStep());
for (Step step : dynamicSteps) {
jobBuilder.next(step);
}
return jobBuilder.build();
}
public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(MyJobConfiguration.class);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job job = context.getBean(Job.class);
jobLauncher.run(job, new JobParameters());
}
}
Nothing related to Spring Batch here, this is Spring dependency injection: passing an array of beans of type Step as a parameter to a another bean definition method (of type Job).

Spring Batch With Annotation and Caching

Does anyone have good example of Spring Batch (Using Annotation) to cache a reference table which will be accessible to processor ?
I just need a simple cache, run a query which returns some byte[] and keep it in memory till the time job is executing.
Appreciate any help on this topic.
Thanks !
A JobExecutionListener can be used to populate the cache with reference data before the job is executed and clear the cache after the job is finished.
Here is an example:
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
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.launch.JobLauncher;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
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 {
private JobBuilderFactory jobs;
private StepBuilderFactory steps;
public MyJob(JobBuilderFactory jobs, StepBuilderFactory steps) {
this.jobs = jobs;
this.steps = steps;
}
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager(); // return the implementation you want
}
#Bean
public Tasklet tasklet() {
return new MyTasklet(cacheManager());
}
#Bean
public Step step() {
return steps.get("step")
.tasklet(tasklet())
.build();
}
#Bean
public JobExecutionListener jobExecutionListener() {
return new CachingJobExecutionListener(cacheManager());
}
#Bean
public Job job() {
return jobs.get("job")
.start(step())
.listener(jobExecutionListener())
.build();
}
class MyTasklet implements Tasklet {
private CacheManager cacheManager;
public MyTasklet(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
String name = (String) cacheManager.getCache("referenceData").get("foo").get();
System.out.println("Hello " + name);
return RepeatStatus.FINISHED;
}
}
class CachingJobExecutionListener implements JobExecutionListener {
private CacheManager cacheManager;
public CachingJobExecutionListener(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
#Override
public void beforeJob(JobExecution jobExecution) {
// populate cache as needed. Can use a jdbcTemplate to query the db here and populate the cache
cacheManager.getCache("referenceData").put("foo", "bar");
}
#Override
public void afterJob(JobExecution jobExecution) {
// clear cache when the job is finished
cacheManager.getCache("referenceData").clear();
}
}
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());
}
}
When executed, it prints:
Hello bar
which means data is correctly retrieved from the cache. You would need to adapt the sample to query the database and populate the cache (See comments in code).
Hope this helps.
You can use ehcache-jsr107 implementation. Very quick to setup.
Spring and ehcache integration example is available here.
You should be able to setup same with spring batch also.
Hope this hleps

Spring Batch two Steps with one Decider

I'm having following job:
#Bean
fun createCsvJob(
jobs: JobBuilderFactory,
validateCsvHeaderStep: Step,
processCsvStep: Step,
moveCsvStep: Step,
markCsvAsFailedStep: Step,
moveFailedCsvStep: Step
) = jobs.get(PROCESS_CSV_JOB)
.start(validateCsvHeaderStep)
.next(processCsvStep)
.on("*").to(decider())
.from(decider()).on(ExitStatus.COMPLETED.exitCode).to(moveCsvStep)
.from(decider()).on(ExitStatus.FAILED.exitCode).to(markCsvAsFailedStep).next(moveFailedCsvStep)
.build()
.build()!!
There are two steps that can fail validateCsvHeaderStep and processCsvStep. I would like to have a flow when error in processing will go to markCsvAsFailedStep and moveFailedCsvStep, but when everything works fine it should go to moveCsvStep.
Currently if validateCsvHeaderStep fails, the whole job fails.
When I try to add decider like this:
.start(validateCsvHeaderStep)
.on(ExitStatus.FAILED.exitCode).to(decider())
.on(ExitStatus.COMPLETED.exitCode).to(processCsvStep)
.on("*").to(decider())
.from(decider()).on(ExitStatus.COMPLETED.exitCode).to(moveCsvStep)
.from(decider()).on(ExitStatus.FAILED.exitCode).to(markCsvAsFailedStep).next(moveFailedCsvStep)
I'm getting:
Next state not found in flow=myJob for state=myJob.validateCsvStep with exit status=COMPLETED
Is there a way to achieve error handling for both steps without duplicating logic?
You need to define the flow starting from validateCsvHeaderStep to the decider on COMPLETED like follows:
.start(validateCsvHeaderStep)
.on(ExitStatus.COMPLETED.exitCode).to(processCsvStep)
.from(validateCsvHeaderStep)on("*").to(decider())
here are two steps that can fail validateCsvHeaderStep and processCsvStep. I would like to have a flow when error in processing will go to markCsvAsFailedStep and moveFailedCsvStep, but when everything works fine it should go to moveCsvStep.
Here is an example:
import org.springframework.batch.core.ExitStatus;
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.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 validateCsvHeaderStep() {
return steps.get("validateCsvHeaderStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("validateCsvHeaderStep");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step processCsvStep() {
return steps.get("processCsvStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("processCsvStep");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step markCsvAsFailedStep() {
return steps.get("markCsvAsFailedStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("markCsvAsFailedStep");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step moveFailedCsvStep() {
return steps.get("moveFailedCsvStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("moveFailedCsvStep");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step moveCsvStep() {
return steps.get("moveCsvStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("moveCsvStep");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Job job() {
return jobs.get("job")
.flow(validateCsvHeaderStep())
.on(ExitStatus.FAILED.getExitCode()).to(markCsvAsFailedStep())
.from(validateCsvHeaderStep()).on("*").to(processCsvStep())
.from(processCsvStep()).on(ExitStatus.FAILED.getExitCode()).to(markCsvAsFailedStep())
.from(processCsvStep()).on("*").to(moveCsvStep())
.from(markCsvAsFailedStep()).on("*").to(moveFailedCsvStep())
.from(moveFailedCsvStep()).end()
.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());
}
}
it prints:
validateCsvHeaderStep
processCsvStep
moveCsvStep
If for example validateCsvHeaderStep fails:
#Bean
public Step validateCsvHeaderStep() {
return steps.get("validateCsvHeaderStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("validateCsvHeaderStep");
chunkContext.getStepContext().getStepExecution().setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
})
.build();
}
It prints:
validateCsvHeaderStep
markCsvAsFailedStep
moveFailedCsvStep
If processCsvStep fails:
#Bean
public Step processCsvStep() {
return steps.get("processCsvStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("processCsvStep");
chunkContext.getStepContext().getStepExecution().setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
})
.build();
}
it prints:
validateCsvHeaderStep
processCsvStep
markCsvAsFailedStep
moveFailedCsvStep
Hope this helps.

Spring batch conditional flow creates infinite loop

I have a simple 3-step flow:
public Job myJob() {
Step extract = extractorStep();
Step process = filesProcessStep();
Step cleanup = cleanupStep();
return jobBuilderFactory.get("my-job")
.flow(echo("Starting job"))
.next(extract)
.next(process)
.next(cleanup)
.next(echo("Ending job"))
.end()
.build();
}
Now I want to add error processing using ExitStatus from StepExecutionListener.afterStep(). Any error should forward flow to cleanup step. So I changed to the code below:
public Job myJob() {
Step extract = extractorStep();
Step process = filesProcessStep();
Step cleanup = cleanupStep();
return jobBuilderFactory.get("my-job")
.start(echo("Starting batch job"))
.next(extract).on(ExitStatus.FAILED.getExitCode()).to(cleanup)
.from(extract).on("*").to(process)
.next(process).on(ExitStatus.FAILED.getExitCode()).to(cleanup)
.from(process).on("*").to(cleanup)
.next(echo("End batch job"))
.end()
.build();
}
Now I have an infinite loop to the cleanup step.
I would like some help to correct this flow.
In your example, the flow is undefined starting from cleanup. You should precise that from cleanup the flow must continue to echo using .from(cleanup).to(echo("End batch job")).end(). Here is an example:
import org.springframework.batch.core.ExitStatus;
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.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 extractorStep() {
return steps.get("extractorStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("extractorStep");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step filesProcessStep() {
return steps.get("filesProcessStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("filesProcessStep");
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Step cleanupStep() {
return steps.get("cleanupStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("cleanupStep");
return RepeatStatus.FINISHED;
})
.build();
}
public Step echo(String message) {
return steps.get("echo-" + message)
.tasklet((contribution, chunkContext) -> {
System.out.println(message);
return RepeatStatus.FINISHED;
})
.build();
}
#Bean
public Job job() {
Step start = echo("Starting batch job");
Step extract = extractorStep();
Step process = filesProcessStep();
Step cleanup = cleanupStep();
Step stop = echo("End batch job");
return jobs.get("job")
.start(start).on("*").to(extract)
.from(extract).on(ExitStatus.FAILED.getExitCode()).to(cleanup)
.from(extract).on("*").to(process)
.from(process).on("*").to(cleanup)
.from(cleanup).next(stop)
.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());
}
}
it prints:
Starting batch job
extractorStep
filesProcessStep
cleanupStep
End batch job
if the extractorStep fails, for example:
#Bean
public Step extractorStep() {
return steps.get("extractorStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("extractorStep");
chunkContext.getStepContext().getStepExecution().setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
})
.build();
}
the flow will skip filesProcessStep and go to cleanup:
Starting batch job
extractorStep
cleanupStep
End batch job
Hope this helps.

Resources