Добавить параметр в контекст задания с шага тарелки и использовать его на последующих этапах в Spring Пакет

В настоящее время я использую параметры jobParameters для получения имен файлов для моего FlatFileItemReader и FlatFileItemWriter. Это нормально для тестирования моей партии, но моя цель - прочитать файл в каком-то каталоге (есть только этот файл в этом каталоге), и имя файла может измениться. Имя выходного файла должно зависеть от имени входного файла.

Поэтому я подумал о добавлении нового шага к моей работе, и на этом шаге будут установлены как выходные, так и входные имена файлов, путем поиска в хорошем каталоге и поиска файла в нем. Я прочитал Передача данных в будущие шаги из Spring Doc и эту тему от SO, но я не могу заставить его работать, файлы всегда "null".

Сначала я определил следующую Tasklet

public class SettingFilenamesTasklet implements Tasklet {

    private StepExecution stepExecution;

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        // TODO Search folder and set real filenames
        String inputFilename = "D:/TestInputFolder/dataFile.csv";
        String outputFilename = "D:/TestOutputFolder/dataFile-processed.csv";
        ExecutionContext stepContext = stepExecution.getExecutionContext();
        stepContext.put("inputFile", inputFilename);
        stepContext.put("outputFile", outputFilename);
        return RepeatStatus.FINISHED;
    }

    @BeforeStep
    public void saveStepExecution(StepExecution stepExec) {
        stepExecution = stepExec;
    }
}

Затем я добавил элемент promotionListener bean

@Bean
public ExecutionContextPromotionListener promotionListener() {
    ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
    listener.setKeys(new String[]{
            "inputFile", "outputFile"
    });
    return listener;
}

Я изменил параметры jobParameters на jobExecutionContext в моем определении FlatFileItemWriter (я не изменил ни одной строки на сам код)

@Bean
@StepScope
public FlatFileItemWriter<RedevableCRE> flatFileWriter(@Value("#{jobExecutionContext[outputFile]}") String outputFile) {
    FlatFileItemWriter<Employee> flatWriter = new FlatFileItemWriter<Employee>();
    FileSystemResource isr;
    isr = new FileSystemResource(new File(outputFile));
    flatWriter.setResource(isr);
    DelimitedLineAggregator<RedevableCRE> aggregator = new DelimitedLineAggregator<RedevableCRE>();
    aggregator.setDelimiter(";");
    BeanWrapperFieldExtractor<RedevableCRE> beanWrapper = new BeanWrapperFieldExtractor<RedevableCRE>();
    beanWrapper.setNames(new String[]{
        "id", "firstName", "lastName", "phone", "address"
    });
    aggregator.setFieldExtractor(beanWrapper);
    flatWriter.setLineAggregator(aggregator);
    flatWriter.setEncoding("ISO-8859-1");
    return flatWriter;
}

Я добавил свою Tasklet bean

@Bean
public SettingFilenamesTasklet settingFilenames() {
    return new SettingFilenamesTasklet();
}

И я создал новый шаг для добавления в объявление моей работы

@Bean
public Step stepSettings(StepBuilderFactory stepBuilderFactory, SettingFilenamesTasklet tasklet, ExecutionContextPromotionListener listener) {
    return stepBuilderFactory.get("stepSettings").tasklet(tasklet).listener(listener).build();
}

В настоящее время FlatFileItemReader по-прежнему использует значение jobParameters, я хочу, чтобы мой FlatFileItemWriter работал в первую очередь. Я получаю следующую ошибку:

[...]    
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.batch.item.file.FlatFileItemWriter]: Factory method 'flatFileWriter' threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:591)
    ... 87 common frames omitted
Caused by: java.lang.NullPointerException: null
    at java.io.File.<init>(Unknown Source)
    at batchTest.BatchConfiguration.flatFileWriter(BatchConfiguration.java:165)
    at batchTest.BatchConfiguration$$EnhancerBySpringCGLIB$$5d415889.CGLIB$flatFileWriter$1(<generated>)
    at batchTest.BatchConfiguration$$EnhancerBySpringCGLIB$$5d415889$$FastClassBySpringCGLIB$$969a8527.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:312)
    at batchTest.BatchConfiguration$$EnhancerBySpringCGLIB$$5d415889.flatFileWriter(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
    ... 88 common frames omitted

Я попытался заменить аннотацию @StepScope на @JobScope; поместить мои параметры непосредственно в jobExecutionContext (+ JobExecutionListener) вместо использования StepContext + promotionListener... Ничего не работает. Файл ресурсов всегда имеет значение null, когда я пытаюсь создать FlatFileItemWriter.

Что мне не хватает?

Спасибо за вашу помощь.

Ответ 1

В tasklet у вас есть ChunkContext в вашем распоряжении, поэтому вам не нужно @BeforeStep, вы можете удалить его (в моей конфигурации он вообще не вызывается, и когда вы думаете об этом как о одном шаге действия, но я не вижу, чтобы NPE так догадывался, что часть работает). Мы решили это одним из двух подходов:

  • С помощью chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("inputFile", inputFilename);

    вы можете поместить любой параметр из задачи на задание
ExecutionContext

Вы можете добавить ExecutionContextPromotionListener к шагу вашей тарелки, а затем сделать chunkContext.getStepContext().getStepExecution().getExecutionContext().put("inputFile", inputFilename);