Andy
Andy

Reputation: 1994

How to end a job when no input read

We read most of our data from a DB. Sometimes the result-set is empty, and for that case we want the job to stop immediately, and not hand over to a writer. We don't want to create a file, if there is no input.

Currently we achieve this goal with a Step-Listener that returns a certain String, which is the input for a transition to either the next business-step or a delete-step, which deletes the file we created before (the file contains no real data).

I'd like the job to end after the reader realizes that there is no input?

Upvotes: 3

Views: 4664

Answers (5)

Raphael Stobbe
Raphael Stobbe

Reputation: 1

NiksVij Answer works for me, i implemented it like this:

@Component
public class FileValidatorTasklet implements Tasklet {

    private final ImportProperties importProperties;

    @Autowired
    public FileValidatorTasklet(ImportProperties importProperties) {
        this.importProperties = importProperties;
    }

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {

        String folderPath = importProperties.getPathInput();
        String itemName = importProperties.getItemName();
        File currentItem = new File(folderPath + File.separator + itemName);

        if (currentItem.exists()) {
            contribution.setExitStatus(new ExitStatus("FILE_FOUND", "FILE_FOUND"));
        } else {
            contribution.setExitStatus(new ExitStatus("NO_FILE_FOUND", "NO_FILE_FOUND"));
        }
        return RepeatStatus.FINISHED;
    }
}

and in the Batch Configuration:

    @Bean
    public Step fileValidatorStep() {
        return this.stepBuilderFactory.get("step1")
                .tasklet(fileValidatorTasklet)
                .build();
    }

    @Bean
    public Job tdZuHostJob() throws Exception {

        return jobBuilderFactory.get("tdZuHostJob")
                .incrementer(new RunIdIncrementer())
                .listener(jobCompletionNotificationListener)
                .start(fileValidatorStep()).on("NO_FILE_FOUND").end()
                .from(fileValidatorStep()).on("FILE_FOUND").to(testStep()).end()
                .build();
    }

Upvotes: 0

NiksVij
NiksVij

Reputation: 183

New edit (more elegant way)

This approach is to elegantly move to the next step or end the batch application when the file is not found and prevent unwanted steps to execute (and their listeners too).

-> Check for the presence of file in a tasklet, say FileValidatorTasklet.

-> When the file is not found set some exit status (enum or final string) , here we have set EXIT_CODE

sample tasklet

public class FileValidatorTasklet implements Tasklet {
    static final String EXIT_CODE = "SOME_EXIT_CODE";
    static final String EXIT_DESC = "SOME_EXIT_DESC";
    @Override
    public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
        boolean isFileFound = false;
        //do file check and set isFileFound
        if(!isFileFound){
            stepContribution.setExitStatus(new ExitStatus(EXIT_CODE, EXIT_DESC));
        }
        return RepeatStatus.FINISHED;
    }
}

-> In the job configuration of this application after executing FileValidatorTasklet, check for the presence of the EXIT_CODE.

-> Provide the subsequent path for this job if the code is found else the normal flow of the job.( Here we are simply terminating the job if the EXIT_CODE is found else continue with the next steps)

sample config

public Job myJob(JobBuilderFactory jobs) {
    return jobs.get("offersLoaderJob")
            .start(fileValidatorStep).on(EXIT_CODE).end() // if EXIT_CODE is found , then end the job
            .from(fileValidatorStep) // else continue the job from here, after this step 
            .next(step2)
            .next(finalStep)
            .end()
            .build();

}

Here we have taken advantage of conditional step flow in spring batch. We have to define two separate path from step A. The flow is like A->B->C or A->D->E.

Old answer:

I have been through this and hence I am sharing my approach. It's better to

throw new RunTimeException("msg");.

It will start to terminate the Spring Application , rather than exact terminate at that point. All methods like close() in ( reader/writer) would be called and destroy method of all the beans would be called.

Note: While executing this in Listener, remember that by this point all the beans would have been initialized and code in their initialization (like afterPropertySet() ) would have executed.

I think above is the correct way, but if you are willing to terminate at that point only, you can try System.exit(1);

Upvotes: 1

MattC
MattC

Reputation: 6334

I have the same question as the OP. I am using all annotations, and if the reader returns as null when no results (in my case a File) are found, then the Job bean will fail to be initialized with an UnsatisfiedDependencyException, and that exception is thrown to stdout.

If I create a Reader and then return it w/o a File specified, then the Job will be created. After that an ItemStreamException is thrown, but it is thrown to my log, as I am past the Job autowiring and inside the Step at that point. That seems preferable, at least for what I am doing.

Any other solution would be appreciated.

Upvotes: 0

Kenston Choi
Kenston Choi

Reputation: 2942

Joshua's answer addresses the stopping of the job instead of transitioning to the next business step.

Your file writer might still create the file unnecessarily. You can create something like a LazyItemWriter with a delegate (FlatFileItemWriter) and it will only call delegate.open (once) if there's a call to write method. Of course you have to check if delegate.close() needs to be called only if the delegate was previously opened. This makes sure that no empty file is created and deleting it is no longer a concern.

Upvotes: 0

Joshua Moore
Joshua Moore

Reputation: 24776

It would likely be cleaner to use a JobExecutionDecider and based on the read count from the StepExecution set a new FlowExecutionStatus and route it to the end of the job.

Upvotes: 0

Related Questions