0

After finding an accepted answer from a Spring Batch dev here, as well as the accompanying JavaConfig code below that, I am still left a bit confused as to how to use stepScope(). I am trying to scope the multiResourceItemReader below, but simply adding the bean definition of stepScope() to the top or bottom of the file causes this error:

Exception encountered during context initialization - cancelling refresh attempt: 
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'readFiles' defined in class path resource [com/onlinephotosubmission/csvImporter/service/BatchJobService.class]: 
    Bean instantiation via factory method failed; 
nested exception is org.springframework.beans.BeanInstantiationException: 
    Failed to instantiate [java.lang.Object]: Factory method 'readFiles' threw exception; 
nested exception is java.lang.NullPointerException: 
    Cannot invoke method get() on null object

All I know is that stepScope() has to be in a @Configuration file, other than that, I'm thoroughly confused as to what needs to be done.


BatchJobService.groovy

@Configuration
@EnableBatchProcessing
class BatchJobService {

@Autowired
JobBuilderFactory jobBuilderFactory
@Autowired
StepBuilderFactory stepBuilderFactory
@Autowired
JobLauncher jobLauncher
@Autowired
AfterJobListener afterJobListener

//  Set values from properties file

@Value('${input.directory:file:inputs/*.csv}')
Resource[] resources

@Value('${report.directory:output}')
String reportsDir

@Value('${completed.directory:completed}')
String completedDir

@Value('${report.name.prepend:people}')
String prependName

@Value('${timestamp.format:dd_MM_yyyy_HH_mm_ss}')
String timestampFormat

//  End set properties
@Bean
StepScope stepScope() {
    final StepScope stepScope = new StepScope()
    stepScope.setAutoProxy(true)
    return stepScope
}

@Bean
Job readFiles() {
    return jobBuilderFactory
            .get("readFiles")
            .incrementer(new RunIdIncrementer())
            .flow(step1())
            .end()
            .listener(afterJobListener)
            .build()
}

@Bean
Step step1() {
    return stepBuilderFactory
            .get("step1")
    //NOTE: may need to adjust chunk size larger (say 1000 to take all transacions at once)
    // or smaller (say 1 to take each transaction individually).
    // Bigger is usually better, though.
            .<Person, Person>chunk(1000)
            .reader(multiResourceItemReader())
            .processor(modifier())
            .writer(writer())
            .build()
}

@Bean
MultiResourceItemReader<Person> multiResourceItemReader() {
    MultiResourceItemReader<Person> resourceItemReader = new MultiResourceItemReader<Person>()
    resourceItemReader.setResources(resources)
    resourceItemReader.setDelegate(reader())
    return resourceItemReader
}

@Bean
FlatFileItemReader<Person> reader() {
    FlatFileItemReader<Person> reader = new FlatFileItemReader<Person>()
    reader.setLinesToSkip(1)    //skips header line
    reader.setLineMapper(new DefaultLineMapper()
    {{
        setLineTokenizer(new DelimitedLineTokenizer(",")
        {{
            setNames(["email", "identifier"] as String[])
        }})
        setFieldSetMapper(new BeanWrapperFieldSetMapper<Person>()   // BeanWrapperFieldSetMapper maps the line token values to a POJO directly by name
        {{
            setTargetType(Person.class)
        }})
    }})

    return reader
}


@Bean
PersonItemProcessor modifier(){
    return new PersonItemProcessor()
}


@Bean
FlatFileItemWriter<Person> writer() {
    FlatFileItemWriter<Person> writer = new FlatFileItemWriter<>()
    writer.setAppendAllowed(true)
    writer.setResource(new FileSystemResource(reportsDir + "/" + prependName + getTime() + ".csv"))
    writer.setLineAggregator(new DelimitedLineAggregator<Person>()
    {{
        setDelimiter(",")
        setFieldExtractor(new BeanWrapperFieldExtractor<Person>()
        {{
            setNames(["status", "email", "identifier"] as String[])
        }})
    }})
    return writer
}

}

Numilani
  • 346
  • 4
  • 16

1 Answers1

1

The @EnableBatchProcessing automatically imports the StepScope, so you don't need to declare it as a bean in your application context. The issue you are linking to happens when there is a mix between XML and Java Config. In your case here, I see only Java Config so the issue should not happen.

I am trying to scope the multiResourceItemReader below

All I know is that stepScope() has to be in a @Configuration file, other than that, I'm thoroughly confused as to what needs to be done.

Just declaring the step scope is not enough, you need to add the @StepScope annotation on the bean definition.

You can find more details about the StepScope in the reference documentation here: https://docs.spring.io/spring-batch/4.0.x/reference/html/step.html#step-scope

Community
  • 1
  • 1
Mahmoud Ben Hassine
  • 28,519
  • 3
  • 32
  • 50