2

I have a simple annotation configuration for a Spring batch job as follows :

@Configuration
@EnableBatchProcessing
public abstract class AbstractFileLoader<T> {

    private static final String FILE_PATTERN = "*.dat";


    @Bean
    @StepScope
    @Value("#{stepExecutionContext['fileName']}")
    public FlatFileItemReader<T> reader(String file) {
        FlatFileItemReader<T> reader = new FlatFileItemReader<T>();
        String path = file.substring(file.indexOf(":") + 1, file.length());
        FileSystemResource resource = new FileSystemResource(path);
        reader.setResource(resource);
        DefaultLineMapper<T> lineMapper = new DefaultLineMapper<T>();
        lineMapper.setFieldSetMapper(getFieldSetMapper());
        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer(",");
        tokenizer.setNames(getColumnNames());
        lineMapper.setLineTokenizer(tokenizer);
        reader.setLineMapper(lineMapper);
        reader.setLinesToSkip(1);
        return reader;
    }

    @Bean
    public ItemProcessor<T, T> processor() {
        // TODO add transformations here
        return null;
    }

    //Exception when using JobScope for the writer
    @Bean

    public ItemWriter<T> writer() {
        ListItemWriter<T> writer = new ListItemWriter<T>();
        return writer;
    }


    @Bean
    public Job loaderJob(JobBuilderFactory jobs, Step s1,
            JobExecutionListener listener) {
        return jobs.get(getLoaderName()).incrementer(new RunIdIncrementer())
                .listener(listener).start(s1).build();
    }

    @Bean
    public Step readStep(StepBuilderFactory stepBuilderFactory,
            ItemReader<T> reader, ItemWriter<T> writer,
            ItemProcessor<T, T> processor, TaskExecutor taskExecutor,
            ResourcePatternResolver resolver) {

        final Step readerStep = stepBuilderFactory
                .get(getLoaderName() + " ReadStep:slave").<T, T> chunk(25254)
                .reader(reader).processor(processor).writer(writer)
                .taskExecutor(taskExecutor).throttleLimit(16).build();

        final Step partitionedStep = stepBuilderFactory
                .get(getLoaderName() + " ReadStep:master")
                .partitioner(readerStep)
                .partitioner(getLoaderName() + " ReadStep:slave",
                        partitioner(resolver)).taskExecutor(taskExecutor)
                .build();

        return partitionedStep;

    }


    @Bean
    public TaskExecutor taskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }

    @Bean
    public Partitioner partitioner(
            ResourcePatternResolver resourcePatternResolver) {
        MultiResourcePartitioner partitioner = new MultiResourcePartitioner();
        Resource[] resources;
        try {
            resources = resourcePatternResolver.getResources("file:"
                    + getFilesPath() + FILE_PATTERN);
        } catch (IOException e) {
            throw new RuntimeException(
                    "I/O problems when resolving the input file pattern.", e);
        }
        partitioner.setResources(resources);
        return partitioner;
    }

    @Bean
    public JobExecutionListener listener(ItemWriter<T> writer) {
        /* org.springframework.batch.core.scope.StepScope scope; */
        return new JobCompletionNotificationListener<T>(writer);
    }

    public abstract FieldSetMapper<T> getFieldSetMapper();

    public abstract String getFilesPath();

    public abstract String getLoaderName();

    public abstract String[] getColumnNames();

}

When I run the same instance of the job with two different job parameters, both instances run sequentially instead of running in parallel. I have a SimpleAysncTaskExecutor bean configured which I assume should cause the jobs to be triggered asynchronously.

Do I need to add any more configuration to this class to have the job instances execute in parallel?

Chetan Kinger
  • 15,069
  • 6
  • 45
  • 82

1 Answers1

1

You have to configure the jobLauncher that you're using to launch jobs to use your TaskExecutor (or a separate pool). The simplest way is to override the bean:

@Bean
JobLauncher jobLauncher(JobRepository jobRepository) {
    new SimpleJobLauncher(
            taskExecutor: taskExecutor(),
            jobRepository: jobRepository)
}

Don't be confused by the warning that will be logged saying that a synchronous task executor will be used. This is due to an extra instance that is created owing to the very awkward way Spring Batch uses to configure the beans it provides in SimpleBatchConfiguration (long story short, if you want to get rid of the warning you'll need to provide a BatchConfigurer bean and specify how 4 other beans are to be created, even if you want to change just one).

Note that it being the same job is irrelevant here. The problem is that by default the job launcher will launch the job on the same thread.

Artefacto
  • 96,375
  • 17
  • 202
  • 225
  • This results in a `NullPointerException`... I am trying to figure out why! – Chetan Kinger Feb 25 '16 at 07:39
  • @CKing you have an example here: https://gist.github.com/cataphract/d4575729e7f57e226f02 – Artefacto Feb 25 '16 at 08:23
  • I am using spring batch 3.0.6 and there doesn't seem to be a `SimpleJobLauncher` constructor that takes two arguments. Also, I am using java 1.6 so this syntax doesn't seem to work for me. – Chetan Kinger Feb 25 '16 at 09:37
  • @CKing That is Groovy. The equivalent in Java would be `SimpleJobLauncher sjl = new SimpleJobLauncher(); sjl.setTaskExecutor(taskExecutor()); sjl.setJobRepository(jobRepository); return sjl;` – Artefacto Feb 25 '16 at 09:54
  • @CKing what's the variable that's null? – Artefacto Feb 25 '16 at 10:05
  • My apologies. It doesn't result in a NPE but the following exception `org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'Loader': Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: com.sbpoc.batch.Loader.setBeanFactory(Lorg/springframework/beans/factory/BeanFactory;)V` If I comment out the `jobLauncher` method, everything works fine. – Chetan Kinger Feb 25 '16 at 10:24
  • What class is that `com.sbpoc.batch.Loader`? – Artefacto Feb 26 '16 at 14:02