【问题标题】:Spring Batch stop job execution from external classSpring Batch 从外部类停止作业执行
【发布时间】:2019-04-04 16:30:55
【问题描述】:

我有一个现有的包含多个步骤的春季批处理项目。我想修改一个步骤,以便停止工作:jobExecution.getStatus() == STOPPED

我的步骤:

@Autowired
public StepBuilderFactory stepBuilderFactory;
@Autowired
private StepReader reader;
@Autowired
private StepProcessor processor;
@Autowired
private StepWriter writer;
@Autowired
public GenericListener listener;
@Bean
@JobScope
@Qualifier("mystep")
public Step MyStep() throws ReaderException {
    return stepBuilderFactory.get("mystep")
            .reader(reader.read())
            .listener(listener)
            .processor(processor)
            .writer(writer)
            .build();
}

GenericListener 实现了ItemReadListener, ItemProcessListener, ItemWriteListener 并覆盖了之前和之后基本上写日志的方法。

这里的重点是StepReader 类及其返回FlatFileItemReaderread() 方法:

@Component
public class StepReader {
    public static final String DELIMITER = "|";
    @Autowired
    private ClassToAccessProperties classToAccessProperties;
    private Logger log = Logger.create(StepReader.class);
    @Autowired
    private FlatFileItemReaderFactory<MyObject> flatFileItemReaderFactory;

    public ItemReader<MyObject> read() throws ReaderException {
        try {
            String csv = classToAccessProperties.getInputCsv();
            FlatFileItemReader<MyObject> reader = flatFileItemReaderFactory.create(csv, getLineMapper());
            return reader;
        } catch (ReaderException | EmptyInputfileException | IOException e) {
            throw new ReaderException(e);
        } catch (NoInputFileException e) {
            log.info("Oh no !! No input file");
            // Here I want to stop the job
            return null;
        }
    }
    private LineMapper<MyObject> getLineMapper () {
        DefaultLineMapper<MyObject> mapper = new DefaultLineMapper<>();
        DelimitedLineTokenizer delimitedLineTokenizer = new DelimitedLineTokenizer();
        delimitedLineTokenizer.setDelimiter(DELIMITER);
        mapper.setLineTokenizer(delimitedLineTokenizer);
        mapper.setFieldSetMapper(new MyObjectFieldSetMapper());
        return mapper;
    }
}

我试图在StepReader 中实现StepExecutionListener 但没有运气,我认为是因为StepBuilderFactory 中的reader 方法期望ItemReader 来自reader.read() 方法,它并不关心其余的班级。

我正在寻找能够在NoInputFileException 被捕获时停止整个工作(而不是失败)的想法或解决方案。

【问题讨论】:

标签: java spring spring-batch


【解决方案1】:

我正在寻找能够在捕获 NoInputFileException 时停止整个工作(而不是失败)的想法或解决方案。

这是一种常见模式,在参考文档的Handling Step Completion When No Input is Found 部分中有详细描述。该部分中的示例显示了在找不到输入文件时如何使作业失败,但由于您想停止作业而不是失败,您可以在侦听器中使用StepExecution#setTerminateOnly();,您的作业将以STOPPED 状态结束.在您的示例中,您可以将该侦听器添加到 MyStep 步骤。

但是,如果没有文件,我建议添加预验证步骤并停止作业。这是一个简单的例子:

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing
public class MyJob {

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Bean
    public Step fileValidationStep() {
        return steps.get("fileValidationStep")
                .tasklet((contribution, chunkContext) -> {
                    // TODO add code to check if the file exists
                    System.out.println("file not found");
                    chunkContext.getStepContext().getStepExecution().setTerminateOnly();
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step fileProcessingStep() {
        return steps.get("fileProcessingStep")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("processing file");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Job job() {
        return jobs.get("job")
                .start(fileValidationStep())
                .next(fileProcessingStep())
                .build();
    }

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean(Job.class);
        JobExecution jobExecution = jobLauncher.run(job, new JobParameters());
        System.out.println("Job status: " + jobExecution.getExitStatus().getExitCode());
    }

}

示例打印:

file not found
Job status: STOPPED

希望这会有所帮助。

【讨论】:

  • 虽然这不是我希望的解决方案,因为MyStep 设计负责验证和读取输入文件,但您的解决方案帮助我避免了批处理的技术问题。谢谢
  • MyStep is by design responsible for validation and reading the input file: So much complexity in software comes from trying to make one thing do two things。这个评论是建设性的,我只是想在不质疑你的设计的情况下提供帮助。但我真的建议让每一步都做一件事(并且做好)。
  • 我完全同意你的看法,但这是一个旧的现有项目,我正在努力将维护成本降至最低,因此不触及现有设计。但是,我调整了您的解决方案,现在效果很好。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-10
  • 1970-01-01
  • 2019-06-10
  • 2019-11-21
  • 1970-01-01
  • 2021-06-25
  • 1970-01-01
相关资源
最近更新 更多