32

I read SO related questions but the solutions don't work for me.

I get the org.springframework.batch.item.ReaderNotOpenException: Reader must be open before it can be read exception.

Below is my configuration:

@Bean
@StepScope
public ItemReader<Player> reader(@Value("#{jobParameters[inputZipfile]}") String inputZipfile) {
                final String [] header = { .. this part omitted for brevity ... };
                FlatFileItemReader<Player> reader = new FlatFileItemReader<Player>();


                System.out.println("\t\t\t\t\t"+inputZipfile);

                reader.setResource(new ClassPathResource(inputZipfile));
                reader.setLineMapper(new DefaultLineMapper<Player>() {{
                    setLineTokenizer(new DelimitedLineTokenizer() {{
                        setNames( header );
                    }});
                    setFieldSetMapper(new BeanWrapperFieldSetMapper<Player>() {{
                        setTargetType(Player.class);
                    }});
                }});
                reader.setComments( header );
                return reader;
}

@Bean
@StepScope
public ItemProcessor<Player, PlayersStats> processor(@Value("#{jobParameters[statType]}") String statType,
                                                                 @Value("#{jobParameters[season]}") String season){
                PlayersStatsProcessor psp = new PlayersStatsProcessor();
                psp.setStatisticType( StatisticType.valueOf(statType) );
                psp.setSeason( season );
                return psp;
}


@Bean
@StepScope
public ItemWriter<PlayersStats> writer(){
            return new CustomWriter();
}


@Bean
public Job generateStatisticsJob() {

        return this.jobs.get("generateStatisticsJob")
                .incrementer(new RunIdIncrementer())
                .start(processPlayerStats())
                //.end()
                .build();
}

@Bean
public Step processPlayerStats() {
           return this.steps.get("processPlayerStats")        
                        .<Player, PlayersStats> chunk(10)
                        .reader(reader(null))
                        .processor(processor(null,null))
                        .writer(writer())
                        .build();
}

The inputZipFile variable is set properly and the file exists on the drive. I checked in the FlatFileItemReader code and the ReaderNotOpenException occurs when the reader member of the reader class is not set. The reader member is set in doOpen method. It looks that doOpen is not called. The question is why ?

2
  • But is the FlatFileItemReader able to read zip files? May 25, 2014 at 15:20
  • inputZipFile is a leftover. I am passing txt file.
    – Luke
    May 25, 2014 at 22:44

8 Answers 8

38

The issue disappeared when I change the return type of my reader bean from Item to FlatFileItemReader. It is still not clear to me why this is a problem since chunk().reader() accepts ItemReader as an input. I assume that there is some AOP magic under the hood which does FlatFileReader init and matches by the return type.

2
31

Since you put the reader in StepScope, the bean return type should be the implementing type FlatFileItemReader:

@Bean
@StepScope
public FlatFileItemReader<Player> reader(@Value("#{jobParameters[inputZipfile]}") String inputZipfile) {
            ...
            return reader;
}

If you specify the interface, the Spring proxy only has access to the methods and annotations specified on the interface ItemReader and is missing important annotations. There is also a warning (with a typo) in the logs:

2015-05-07 10:40:22,733 WARN  [main] org.springframework.batch.item.ItemReader is an interface.  The implementing class will not be queried for annotation based listener configurations.  If using @StepScope on a @Bean method, be sure to return the implementing class so listner annotations can be used.
2015-05-07 10:40:22,748 WARN  [main] org.springframework.batch.item.ItemReader is an interface.  The implementing class will not be queried for annotation based listener configurations.  If using @StepScope on a @Bean method, be sure to return the implementing class so listner annotations can be used. 

Currently the Spring Boot Batch example is also returning the ItemReader, so I guess other people will struggle with the same issues.

1
  • It works for me. 1. Add @StepScope 2. For each ItemReader, ItemWriter method where return type is ItemReader, ItemWriter, change return type to actual implementation class such as JdbcCursorItemReader, JdbcCursorItemWriter. Also, if you have Exception of "close" of reader, add @Bean(destroyMethod="") Mar 2, 2020 at 15:46
11

It is because ItemReader don't have the open method, using hte StepScope will create a proxy class based on the return type. It is also ok to return ItemStreamReader

5

I have fixed it by:

reader.open(new ExecutionContext());
3
  • This actually worked for me after struggling for 1.5 days. Oct 15, 2019 at 10:43
  • 1
    This is a bad trick, not the right solution. Spring calls reader.open() automatically if you give him the possibility to do so: your @Bean method must return the implementing class as recommended by other answers.
    – Pino
    Mar 27, 2020 at 13:50
  • Thanks to this hack, I could recycle a Chunk step reader into a Tasklet step. Oct 8, 2021 at 9:48
1

I think you should increase your chunk size in processPlayerStats() step bean class, i.e from chunk(10) to chunk(100/more may be).

1

The method I defined is like below:

    @Bean
    @StepScope
    public ItemReader<BP> BPReader(){
       
        JdbcCursorItemReader<BP> itemReader = new JdbcCursorItemReader<BP>();
        ...
        return itemReader;
    }

The type I defined in the method is ItemReader which is an interface, the return type is JdbcCursorItemReader which is a sub-class of it. By changing the return type defination to JdbcCursorItemReader solved my problem

1

Instead of returning ItemReader, return ItemStreamReader, since it's a combination of ItemReader and ItemStream interfaces, so it recognizes that there is the open method that needs to be called.

0

Same problem here. Changing return type of my reader to the real implementation and adding to the reader

implements ItemStream

Did the trick for me

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.