Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repeating a Step x times in Spring Batch

I'm using Spring Batch 3.0.3 configured with annotation to create a batch job that repeats a step an undetermined number of times.

My first step will read into memory a list of items used during the repeating step. I'd like the repeating steps to iterate through this job-scoped list.

How can I configure my job to run the same step x times? I've seen examples in xml of a step specifying the next step to run. I'm thinking I could point two steps to each other in an infinite loop until the list has been iterated. Would this work and is there a way to do it with annotations? Below is my main configuration file with some code that wasn't working.

@ComponentScan(excludeFilters = @Filter(IgnoreDuringScan.class))
@EnableAutoConfiguration
@EnableBatchProcessing
@Loggable
public class BatchCrudConfiguration
{
    @Bean
    public Job batchCRUDJob(JobBuilderFactory jobBuilderFactory, Step[] processSheetSteps)
    {
        JobBuilder jobBuilder = jobBuilderFactory.get("batchCRUDJob").incrementer(new RunIdIncrementer());
        FlowBuilder<FlowJobBuilder> jobFlowBuilder = jobBuilder.flow(processSheetSteps[0]);
        for (int i = 1; i < processSheetSteps.length; i++)
        {
            jobFlowBuilder = jobFlowBuilder.next(processSheetSteps[i]);
        }
        return jobFlowBuilder.end().build();
    }

    @Bean
    public Step[] processSheetSteps(
            StepBuilderFactory stepBuilderFactory,
            RawDataReader[] readers,
            DelegatingWriter writer,
            DelegatingProcessor processor,
            @Value("${batchcrud.chunkSize}") int chunkSize)
    {
        int numberOfReaders = readers.length;
        Step[] steps = new Step[numberOfReaders];
        for (int i = 0; i < numberOfReaders; i++)
        {
            steps[i] = stepBuilderFactory.get("processSheet" + i + "Step").<RawData, DataItem>
                    chunk(chunkSize).reader(readers[i]).processor(processor).writer(writer).build();
        }
        return steps;
    }
like image 581
Chris Oppedal Avatar asked Mar 26 '15 18:03

Chris Oppedal


1 Answers

The way I'd approach this is via the following:

  1. First step loads the list.
  2. Second step processes an item in the list.
  3. The second step has a StepExecutionListener that evaluates if there are more items in the list to process. If so, it returns an ExitStatus that maps to the same step. If not, it returns an ExitStatus that maps to end the job or continue the job (based on the rest of the flow).

For example:

StepExecutionListener

public class MyListener {

    @Autowired
    private List myItems;

    @AfterStep
    public ExitStatus afterStep(StepExecution stepExecution) {
        if(myItems.size() > 0) {
            return new ExitStatus("CONTINUE");
        }
        else {
            return new ExitStatus("FINISHED");
        }
    }
}

Job Configuration

...
@Bean
public Step step1() {...}

@Bean
public MyListener listener() {..}

@Bean
public Step step2(MyListener listener) {
    return stepBuilder.get("step2")
                .tasklet(myTasklet()) // Replace this piece as needed
                .listener(listener).build();
}

@Bean
public Job job1(Step step1, Step step2) {
    return jobBuilder.get("job1")
                     .start(step1)
                     .next(step2).on("CONTINUE").to(step2).on("FINISHED").end()
                     .build();
}
...

Note I have not tested this code so there may be typos, etc.

like image 74
Michael Minella Avatar answered Oct 23 '22 01:10

Michael Minella