简体   繁体   中英

How to skip a step execution from beforeStep method in StepExecutionListener?

I´d like to implement a generic StepExecutionListener for any step so in the beforeStep method (that is to say, before the current step tasklet started) it could check some database related conditions: if not succeeded, it would skip the current step and continue with the next one.

Under the:

public void beforeStep(StepExecution stepExecution)

at the moment I didn´t find anything in "stepExecution" object to do that. I just get to change the exit status to COMPLETED, but still the corresponding tasklet executes.

Is there a way to achieve this? I´ve also heard about deciders, but I don´t like adding too much verbose configuration on my XML.

Thanks in advance.

Deciders are a better option in this case, as their sole purpose is to decide wether to execute your step or not. They do make the xml verbose but that is better as someone else ca understand at the first glance.

The only way I think beforeStep can handle this is if you throuw an exception whene the task name is of the one you want to ignore. Your next step however may need to be configured to run even if previous step fails (Not recommended if that is not exactly what you want when the step is not skipped).

Although, handling in beforeStep will become a lot cumbersome if your task expands and you need to decide for 3-4 steps.

Let me know what you go with!

EDIT:

Example on usage of deciders along with steps

<batch:decision id="decider1" decider="deciderClass1">
        <batch:next on="FAILED" to="decider2" />
        <batch:next on="COMPLETED" to="task1"/>
    </batch:decision>
    <batch:step id="step1" next="decider2" parent="step1Class"/>

    <batch:decision id="decider2" decider="deciderClass2">
        <batch:next on="FAILED" to="step3" />
        <batch:next on="COMPLETED" to="step2"/>
    </batch:decision>
    <batch:step id="step2" parent="step2Class" next="step3"/>
.....

I´ll answer to myself in order to introduce some code.

Following the proposal of Priteesh I defined one Decider like this:

    <decision id="deciderId" decider="deciderDeclaration">
        <next on="CONTINUE" to="step1" />
        <next on="SKIP" to="finalStep" />
    </decision>

Putting it at first place in my context.xml. This is important because step1 doesn´t execute ever if the conditions are not met.

And my decider is something like this:

public class StepExecutionDecider implements JobExecutionDecider{

    /** LOGGER. */
    private Logger LOGGER = ...

    private static final String FLOW_STATUS_CONTINUE = "CONTINUE";
    private static final String FLOW_STATUS_SKIP = "SKIP";

    @Override
    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {

        String status = FLOW_STATUS_CONTINUE;

        String condition = ....

        if (Constants.NO.equals(condition)){

            LOGGER.warn(jobExecution.getJobInstance().getJobName(), " -> The Step execution is disabled. Continuing with the next Step.");

            status = FLOW_STATUS_SKIP;
        }

        return new FlowExecutionStatus(status);
    }

    ...
}

The strange thing is that, whenever my condition is not met, the LOGGER message is printed twice in my log, with exactly the same timestamp...

Anyway, thank you very much Priteesh for your kind help.

I had the same goal: use beforeStep() in the StepExecutionListener to check non-spring batch related conditions to decide if the step should run or not.

What doesn't work

Using a decider ( JobExecutionDecider ) does not work because JobExecutionDecider.decide() receives as parameter the step previously executed . The purpose of the decider is to allow you to check the status of the previous step execution to choose what to do next. If I'm testing a condition that is specific to StepXYZ , I need to have that step execution at hand before it's executed .

Well, StepExecutionListener.execute() puts StepXyz execution on your hand prior to execution. It sounds promising... However, the API does not have any mechanism to tell spring batch not to run StepXyz . To illustrate the point, none of the statements below will do what we want.

stepExecution.status = BatchStatus.ABANDONED
stepExecution.exitStatus = ExitStatus.NOOP
stepExecution.setTerminateOnly()   

Conclusion

After searching all around the framework code, SO, and the spring documentation, my conclusion is:

One cannot use beforeStep() to gracefully tell spring batch not to run that step.

(I'm using Spring Boot 2.3.9.)

My simple solution

In my case the condition to be tested is one or more feature flags . I already had a FeatureFlags Component , so I injected it into the tasklets and tested the pre-condition at the start of execute() :

override fun execute(contribution: StepContribution, chunkContext: ChunkContext): RepeatStatus? {
    if (!featureFlags.stepShouldRun) {
        contribution.exitStatus = ExitStatus("SKIPPED BASED ON FEATURE FLAGS")
        return RepeatStatus.FINISHED
    }
    return execute()
}

fun execute(): RepeatStatus {
    // the actual step logic goes here
    // add StepContribution as a param if necessary
}

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