简体   繁体   中英

Spring Batch stop job execution from external class

I have an existing spring batch project that have multiple steps. I want to modify a step so I can stop the job : jobExecution.getStatus() == STOPPED .

My step :

@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 implements ItemReadListener, ItemProcessListener, ItemWriteListener and overrides before and after methods that basically write log.

The focus here is on the StepReader class and its read() method that returns a FlatFileItemReader :

@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;
    }
}

I tried to implement StepExecutionListener in StepReader but with no luck, I think because the reader method in StepBuilderFactory is expecting an ItemReader from the reader.read() method and it doesn't care about the rest of the class.

I'm looking for ideas or solution to be able to stop the entire job (not fail it) when NoInputFileException is catched.

I'm looking for ideas or solution to be able to stop the entire job (not fail it) when NoInputFileException is catched.

This is a common pattern and is described in details in the Handling Step Completion When No Input is Found section of the reference documentation. The example in that section shows how to fail a job when no input file is found, but since you want to stop the job instead of failing it, you can use StepExecution#setTerminateOnly(); in the listener and your job will end with status STOPPED . In your example, you would add that listener to the MyStep step.

However, I would suggest to add a pre-validation step and stop the job if there is no file. Here is a quick example:

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());
    }

}

The example prints:

file not found
Job status: STOPPED

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM