Post

afterStep 에서 Exception을 던져도 다음 Step이 이어서 실행된다.

afterStep에서 검증 로직 돌린 후, 다음 Step 실행하지 않고 배치를 종료하고 싶은 경우가 있다. 배치 애플리케이션을 아예 종료해버리는 방법도 있지만, 보다 graceful 하게 처리하고 싶은 경우, 다음 Step이 실행되지 않도록 하려면?

종료 상태를 나타내는 Status는 BatchStatus와 ExitStatus 두개가 있다.

어떤 Step에서 문제가 발생했을 때, 다음 Step이 실행되지 않도록 하는 방법은 2가지가 있다.

1. 비정상 ExitStatus를 반환하고 on() 으로 분기해주는 방법
1
2
3
4
5
return jobBuilderFactory.get(jobName)
    .start(step1)
    .on(ExitStatus.FAILED.getExitCode()).end()
    ...

2. BatchStatus가 COMPLETED가 아니도록 하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
// BatchStatus가 COMPLETED가 아니면 다음 step을 실행하지 않는다
class SimpleJob {
    protected void doExecute(JobExecution execution) throws JobInterruptedException, JobRestartException, StartLimitExceededException {
        while(steps.hasNext()) {
            Step step = (Step)steps.next();
            stepExecution = this.handleStep(step, execution);
            if (stepExecution.getStatus() != BatchStatus.COMPLETED) {
                break;
            }
        }
        ...

기본적으로 일일히 on()을 이용해 비정상 ExitCode에 대해 분기처리 해주지 않아도, 예외가 발생하면 BatchStatus가 비정상으로 세팅되어 다음 Step 실행이 되지 않게끔 되어 있다.

beforeStep, tasklet에서 발생한 Exception에 대해서는 그렇다. 하지만, afterStep에서 Exception이 발생하는 경우에는 다르다.

afterStep에서의 Exception은 배치를 종료시키지 않는다.

AbstractStep::execute 부분 확인해보면, [beforeStep, Tasklet]에서 Exception 던지는 경우 BatchStatus.FAILED로 변경하기 때문에 배치가 종료된다. 하지만 [afterStep]에서 Exception throw 하는 경우 로그만 찍고 넘어가버려 다음 Step이 실행된다.

afterStep에서의

setTerminateOnly는 배치를 종료시키지 않는다. AbstractStep::execute 부분 확인해보면, [beforeStep, Tasklet]에서 setTerminateOnly 하는 경우 BatchStatus.STOPPED로 변경하기 때문에 배치가 종료된다. 하지만 [afterStep]에서 setTerminateOnly 하는 경우 동작에 아무런 차이가 없다. afterStep 호출 후 isTerminateOnly 체크 하지 않는다. 그냥 다음 Step이 실행된다.

참고) afterStep 실행부는 finally block에 있기 때문에 beforeStep, tasklet에서 Exception이 일어나도 항상 실행된다.


afterStep에서 직접 BatchStatus를 변경하면, on 분기를 사용하지 않아도 다음 Step 실행을 막을 수 있다.

afterStep에서 검증 로직 돌린 후, 다음 Step 실행하지 않고 배치를 종료하고 싶다면? (on 분기를 사용하지 않으면서) BatchStatus를 STOPPED | FAILED 로 세팅하면 된다.

1
2
3
4
5
6
override fun afterStep(stepExecution: StepExecution): ExitStatus {
    stepExecution.upgradeStatus(BatchStatus.STOPPED)
    stepExecution.exitStatus = ExitStatus.STOPPED
    return stepExecution.exitStatus
}

This post is licensed under CC BY 4.0 by the author.