6

I'm setting up a job server using Spring Batch. My JdbcCursorItemReader needs to be configured with sql that changes on a per-job run basis. Because the sql changes, I want the reader to have @StepScope so I don't need to worry about sql statefulness.

So I've setup a class like this:

public class ParameterSettingJdbcCursorItemReader extends JdbcCursorItemReader implements StepExecutionListener {

    @Override
    public void beforeStep(StepExecution stepExecution) {

        JobParameters jobParameters = stepExecution.getJobParameters();

        if (jobParameters != null) {
            List<Object> args = new ArrayList<Object>();
            for (JobParameter jobParameter : jobParameters.getParameters().values()) {
                args.add(jobParameter.getValue());
            }

            Object[] arrayArgs = args.toArray(new Object[args.size()]);
            String sql = String.format(getSql(), arrayArgs);
            setSql(sql);
        }
    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        return null;
    }
}

The idea is that I pass sql parameters to the object via JobParameters and use String.format to fill in the dynamically-changing sql.

I am using Java-based configuration throughout my server. My bean for one instance of my ItemReader looks like:

@Bean
@StepScope
public ItemReader<MyInputObject> myInputObjectItemReader() {
    ParameterSettingJdbcCursorItemReader itemReader = new ParameterSettingJdbcCursorItemReader();
    itemReader.setDataSource(myDataSource());
    itemReader.setSql("SELECT * FROM my_table WHERE date = '%1$s'");
    itemReader.setRowMapper(myInputObjectMapper);
    return itemReader;
}

When I start up my server and run the Spring Batch job, I get this error: java.lang.IllegalStateException: No Scope registered for scope 'step'

I've read elsewhere that in order to be able to use StepScope, one needs to first add it to the xml app configuration like this : <bean class="org.springframework.batch.core.scope.StepScope" />

But since I'm using Java-based configuration, that's not an option.

So how do I register the StepScope via a Java-based config? I've tried this:

@Bean
public org.springframework.batch.core.scope.StepScope stepScope() {
    return new org.springframework.batch.core.scope.StepScope();
}

... but when I do I get all sorts of NPEs during app startup on beans that have no relation to StepScope.

Thanks in advance.

Alex
  • 855
  • 7
  • 21
Samuel Dupont
  • 63
  • 1
  • 1
  • 3
  • [This](https://github.com/codecentric/spring-batch-javaconfig/blob/master/src/test/java/de/codecentric/batch/FlatfileToDbWithParametersAutowiringJobTests.java) seems to be similar to what you're trying to do. The author wrote a [blog](https://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-2-jobparameters-executioncontext-and-stepscope/) to explain. – Alex Mar 01 '14 at 01:29

2 Answers2

3

You have to register the scope with the ApplicationContext. Normally that would be done for you when you use @EnableBatchProcessing. Did you do that (add that annotation to one of your @Configurations)?

Dave Syer
  • 56,583
  • 10
  • 155
  • 143
  • I'm not using the `@EnableBatchProcessing` annotation. I tried it initially, but ran into issues, because I have 4 data sources in the app. I tried creating a unique BatchConfigurer for each data source - with `@EBP` on each - but I still get the same error about multiple data sources. So I guess what I'm looking for is to see how StepScope is enabled underneath the covers when `@EBP` is set and then just add that to my config instead of `@EBP`. Alternatively, if you know how I can get around the multiple data sources issue created by `@EBP`, that'd be fine too. – Samuel Dupont Mar 03 '14 at 19:25
  • Why would you need 4 `BatchConfigurers`? One of the `DataSources` has to be for the Batch repository. If you can identify it somehow (e.g. call that "dataSource" or mark it `@Primary`) you can write one `BatchConfigurer` that creates a `JobRepository` from it. – Dave Syer Mar 03 '14 at 21:56
  • A little investigation into DefaultBatchConfigurer revealed that it needs the data source to store the job repo. So I used an answer to [this question](http://stackoverflow.com/questions/17090739/spring-batch-2-2-javaconfig) - though using a `@Resource(name="myDataSource")` annotation instead of `@Autowired` - to help set that up. That gave me use of the `@StepScope` on my ItemReader beans. I still had to provide a proxyMode of TARGET_CLASS on the scope annotation, else Tomcat wouldn't start. – Samuel Dupont Mar 03 '14 at 21:56
  • yeah, I discovered the same once I realized why it needed the data source. I should have dug into the DefaultBatchConfigurer source earlier on. – Samuel Dupont Mar 03 '14 at 22:01
1

+1 to Dave's point about using @EanbleBatchProcesssing. That will add a StepScope to your context. However, your code still won't work once you've done that because you're returning an ItemReader for "myInputObjectItemReader" instead of ParameterSettingJdbcCursorItemReader. See the issue here for details on why that matters: Spring-batch @BeforeStep does not work with @StepScope

Community
  • 1
  • 1
Michael Minella
  • 20,843
  • 4
  • 55
  • 67