0

I coded a custom implementation of ListItemReader, triying to follow the example in the spring batch's github. Anyway, in my case, I need read a variable from the jobContext, this variable is a path where I have to read the files that contains. I can't use the constructor because the constructors executes before the beforeStep event and I don't have these var at this moment.

Anyway this will work first execution, but if the list never goes again to null I can't execute again the initialize method.

If I tried add an else in the !list.isEmpty() condition that set my list to null. I enter in an infinite loop.

There are other methods to solve this? Maybe I am overcomplicating this.

public class ListItemReader<Path> implements ItemReader<Path>, StepExecutionListener {

    private List<Path> list;
    private org.springframework.batch.core.JobExecution jobExecution;

    public ListItemReader() {
        this.list = new ArrayList<>();
    }

    public void initialize(){

        //Here I made an listdirectories of a path and add all the files to the list
        String pathString = jobExecution.getExecutionContext().getString(Constants.CONTEXT_PATH);
        Path path = Paths.get(pathString );
        ...
        items.add(Paths.get(..path..));
    }

    @Nullable
    @Override
    public T read() {
        if(list == null) initialize();

        if (!list.isEmpty()) {
            return list.remove(0);
        }
        return null;
    }

    @Override
    public ExitStatus afterStep(StepExecution se) {
        return ExitStatus.COMPLETED;
    }

    @Override
    public void beforeStep(StepExecution se) {
        jobExecution = se.getJobExecution();
    }
}
JuniorGuy
  • 107
  • 1
  • 12

1 Answers1

0

I can't use the constructor because the constructors executes before the beforeStep event and I don't have these var at this moment.

Actually, you can delay your bean constructor by using @JobScope and @StepScope. Additionally, you can use @Value and Spring SpEL to inject your jobParameters.

In your case, you might want to rewrite your code, for e.g:

@Configuration
@RequiredArgsConstructor
public class YourJobConfig {
  private final StepBuilderFactory stepBuilder;

  @Bean("yourStep")
  public Step doWhatever() {
    return stepBuilder
      .get("yourStepName")
      .<Path, Path>chunk(50) // <-- replace your chunk size here
      .reader(yourItemReader(null, 0)) // <-- place your default values here
      .process(yourItemProcessor())
      .writer(yourItemWriter())
  }

  @Bean
  public ItemReader<Path> yourItemReader(
    @Value("#{jobParameters['attribute1']}") String attribute1,
    @Value("#{jobParameters['attribute2']}") long attribute2    
  ) {
    return new ListItemReader(attribute1, attribute2) // <-- this is your ListItemReader
  }

  @Bean
  public ItemProcessor<Path, Path> yourItemProcessor(){
    return new YourItemProcessor<>();
  }

  @Bean
  public ItemWriter<Path> yourItemWriter(){
    return new YourItemWriter<>();
  }
}

Before starting your job, you can add some jobParameters:

public JobParameters getJobParams() {
  JobParametersBuilder params = new JobParametersBuilder();

  params.addString("attribute1", "something_cool");
  params.addLong("attribute2", 123456);

  return params.toJobParameters();
}

and add it to your job:

Job yourJob = getYourJob();
jobParameters jobParams = getJobParams();
JobExecution execution = jobLauncher.run(job, jobParams);

References

Minh Dao
  • 827
  • 8
  • 21