Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ public TransitionBuilder<Q> on(String pattern) {
return new TransitionBuilder<>(this, pattern);
}

/**
* Start a transition to a new state if the exit status from the previous state
* matches the status given.
* @param status the exit status on which to take this transition
* @return a builder to enable fluent chaining
*/
public TransitionBuilder<Q> on(ExitStatus status) {
return on(status.getExitCode());
}

/**
* A synonym for {@link #build()} which callers might find useful. Subclasses can
* override build to create an object of the desired type (e.g. a parent builder or an
Expand Down Expand Up @@ -437,6 +447,16 @@ public TransitionBuilder<Q> on(String pattern) {
return new TransitionBuilder<>(parent, pattern);
}

/**
* Start a transition to a new state if the exit status from the previous state
* matches the status given.
* @param status the exit status on which to take this transition
* @return a TransitionBuilder
*/
public TransitionBuilder<Q> on(ExitStatus status) {
return on(status.getExitCode());
}

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import org.jspecify.annotations.NullUnmarked;

import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.job.Job;
import org.springframework.batch.core.step.Step;
import org.springframework.batch.core.job.SimpleJob;
Expand Down Expand Up @@ -99,6 +100,15 @@ public FlowBuilder.TransitionBuilder<FlowJobBuilder> on(String pattern) {
return builder.on(pattern);
}

/**
* Branch into a flow conditional on the outcome of the current step.
* @param status the exit status of the current step
* @return a builder for fluent chaining
*/
public FlowBuilder.TransitionBuilder<FlowJobBuilder> on(ExitStatus status) {
return on(status.getExitCode());
}

/**
* Start with this decider. Returns a flow builder and when the flow is ended a job
* builder will be returned to continue the job configuration if needed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,49 @@ public void execute(StepExecution stepExecution) throws UnexpectedJobExecutionEx
assertFalse(stepExecutions.hasNext());
}

@Test
void testOnExitStatus() throws Exception {
FlowBuilder<Flow> builder = new FlowBuilder<>("transitionsFlow");
JobRepository jobRepository = new ResourcelessJobRepository();
JobParameters jobParameters = new JobParameters();
JobInstance jobInstance = jobRepository.createJobInstance("foo", jobParameters);
JobExecution jobExecution = jobRepository.createJobExecution(jobInstance, jobParameters,
new ExecutionContext());

StepSupport stepA = new StepSupport("stepA") {
@Override
public void execute(StepExecution stepExecution) throws UnexpectedJobExecutionException {
stepExecution.setExitStatus(ExitStatus.FAILED);
}
};

StepSupport stepB = new StepSupport("stepB") {
@Override
public void execute(StepExecution stepExecution) throws UnexpectedJobExecutionException {
}
};

StepSupport stepC = new StepSupport("stepC") {
@Override
public void execute(StepExecution stepExecution) throws UnexpectedJobExecutionException {
}
};

FlowExecution flowExecution = builder.start(stepA)
.on("*")
.to(stepB)
.from(stepA)
.on(ExitStatus.FAILED)
.to(stepC)
.end()
.start(new JobFlowExecutor(jobRepository, new SimpleStepHandler(jobRepository), jobExecution));

Iterator<StepExecution> stepExecutions = jobExecution.getStepExecutions().iterator();
assertEquals("stepA", stepExecutions.next().getStepName());
assertEquals("stepC", stepExecutions.next().getStepName());
assertFalse(stepExecutions.hasNext());
}

private static StepSupport createCompleteStep(String name) {
return new StepSupport(name) {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,18 @@ void testBuildOverTwoLines() throws JobInterruptedException {
assertEquals(2, execution.getStepExecutions().size());
}

@Test
void testBuildOnExitStatus() throws JobInterruptedException {
FlowJobBuilder builder = new JobBuilder("flow", jobRepository).start(step1)
.on(ExitStatus.COMPLETED)
.to(step2)
.end();
builder.preventRestart();
builder.build().execute(execution);
assertEquals(BatchStatus.COMPLETED, execution.getStatus());
assertEquals(2, execution.getStepExecutions().size());
}

@Test
void testBuildSubflow() throws JobInterruptedException {
Flow flow = new FlowBuilder<Flow>("subflow").from(step1).end();
Expand Down Expand Up @@ -278,6 +290,24 @@ public FlowExecutionStatus decide(JobExecution jobExecution, @Nullable StepExecu
assertEquals(1, execution.getStepExecutions().size());
}

@Test
void testBuildWithDeciderAtStartOnExitStatus() throws JobInterruptedException {
JobExecutionDecider decider = new JobExecutionDecider() {
private int count = 0;

@Override
public FlowExecutionStatus decide(JobExecution jobExecution, @Nullable StepExecution stepExecution) {
count++;
return count < 2 ? new FlowExecutionStatus("ONGOING") : FlowExecutionStatus.COMPLETED;
}
};
JobFlowBuilder builder = new JobBuilder("flow", jobRepository).start(decider);
builder.on(ExitStatus.COMPLETED).end().from(decider).on("*").to(step1).end();
builder.build().preventRestart().build().execute(execution);
assertEquals(BatchStatus.COMPLETED, execution.getStatus());
assertEquals(1, execution.getStepExecutions().size());
}

@Test
void testBuildWithDeciderPriorityOnWildcardCount() throws JobInterruptedException {
JobExecutionDecider decider = (jobExecution, stepExecution) -> new FlowExecutionStatus("COMPLETED_PARTIALLY");
Expand Down