Fix infinite loop in FlowBuilder#next()

Resolves #4432
This commit is contained in:
injae.kim
2023-10-25 20:26:27 +09:00
committed by Mahmoud Ben Hassine
parent 7fd34829f9
commit 4a67b22e90
2 changed files with 94 additions and 22 deletions

View File

@@ -48,6 +48,7 @@ import org.springframework.core.task.TaskExecutor;
* @author Dave Syer
* @author Michael Minella
* @author Mahmoud Ben Hassine
* @author Injae Kim
* @since 2.2
* @param <Q> the type of object returned by the builder (by default a Flow)
*
@@ -107,7 +108,8 @@ public class FlowBuilder<Q> {
/**
* Transition to the next step on successful completion of the current step. All other
* outcomes are treated as failures.
* outcomes are treated as failures. If no steps are registered yet, then this method
* will behave in the same way as {@link #start(Step)}.
* @param step the next step
* @return this to enable chaining
*/
@@ -250,26 +252,32 @@ public class FlowBuilder<Q> {
if (this.currentState == null) {
doStart(input);
}
State next = createState(input);
addTransition("COMPLETED", next);
addTransition("*", failedState);
this.currentState = next;
else {
State next = createState(input);
addTransition("COMPLETED", next);
addTransition("*", failedState);
this.currentState = next;
}
}
private void doStart(Object input) {
if (this.currentState != null) {
doFrom(input);
}
this.currentState = createState(input);
else {
this.currentState = createState(input);
}
}
private void doFrom(Object input) {
if (currentState == null) {
doStart(input);
}
State state = createState(input);
tos.put(currentState.getName(), currentState);
this.currentState = state;
else {
State state = createState(input);
tos.put(currentState.getName(), currentState);
this.currentState = state;
}
}
private State createState(Object input) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import java.util.Iterator;
import org.junit.jupiter.api.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInterruptedException;
@@ -34,26 +35,79 @@ import org.springframework.batch.core.step.JobRepositorySupport;
import org.springframework.batch.core.step.StepSupport;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
/**
* @author Dave Syer
* @author Michael Minella
* @author Mahmoud Ben Hassine
* @author Injae Kim
*
*/
class FlowBuilderTests {
@Test
void test() throws Exception {
void testNext() throws Exception {
FlowBuilder<Flow> builder = new FlowBuilder<>("flow");
JobRepository jobRepository = new JobRepositorySupport();
JobExecution execution = jobRepository.createJobExecution("foo", new JobParameters());
builder.start(new StepSupport("step") {
@Override
public void execute(StepExecution stepExecution)
throws JobInterruptedException, UnexpectedJobExecutionException {
}
}).end().start(new JobFlowExecutor(jobRepository, new SimpleStepHandler(jobRepository), execution));
builder.next(createCompleteStep("stepA"))
.end()
.start(new JobFlowExecutor(jobRepository, new SimpleStepHandler(jobRepository), execution));
Iterator<StepExecution> stepExecutions = execution.getStepExecutions().iterator();
assertEquals(stepExecutions.next().getStepName(), "stepA");
assertFalse(stepExecutions.hasNext());
}
@Test
void testMultipleNext() throws Exception {
FlowBuilder<Flow> builder = new FlowBuilder<>("flow");
JobRepository jobRepository = new JobRepositorySupport();
JobExecution execution = jobRepository.createJobExecution("foo", new JobParameters());
builder.next(createCompleteStep("stepA"))
.next(createCompleteStep("stepB"))
.next(createCompleteStep("stepC"))
.end()
.start(new JobFlowExecutor(jobRepository, new SimpleStepHandler(jobRepository), execution));
Iterator<StepExecution> stepExecutions = execution.getStepExecutions().iterator();
assertEquals(stepExecutions.next().getStepName(), "stepA");
assertEquals(stepExecutions.next().getStepName(), "stepB");
assertEquals(stepExecutions.next().getStepName(), "stepC");
assertFalse(stepExecutions.hasNext());
}
@Test
void testStart() throws Exception {
FlowBuilder<Flow> builder = new FlowBuilder<>("flow");
JobRepository jobRepository = new JobRepositorySupport();
JobExecution execution = jobRepository.createJobExecution("foo", new JobParameters());
builder.start(createCompleteStep("stepA"))
.end()
.start(new JobFlowExecutor(jobRepository, new SimpleStepHandler(jobRepository), execution));
Iterator<StepExecution> stepExecutions = execution.getStepExecutions().iterator();
assertEquals(stepExecutions.next().getStepName(), "stepA");
assertFalse(stepExecutions.hasNext());
}
@Test
void testFrom() throws Exception {
FlowBuilder<Flow> builder = new FlowBuilder<>("flow");
JobRepository jobRepository = new JobRepositorySupport();
JobExecution execution = jobRepository.createJobExecution("foo", new JobParameters());
builder.from(createCompleteStep("stepA"))
.end()
.start(new JobFlowExecutor(jobRepository, new SimpleStepHandler(jobRepository), execution));
Iterator<StepExecution> stepExecutions = execution.getStepExecutions().iterator();
assertEquals(stepExecutions.next().getStepName(), "stepA");
assertFalse(stepExecutions.hasNext());
}
@Test
@@ -66,7 +120,7 @@ class FlowBuilderTests {
@Override
public void execute(StepExecution stepExecution)
throws JobInterruptedException, UnexpectedJobExecutionException {
stepExecution.setExitStatus(new ExitStatus("FAILED"));
stepExecution.setExitStatus(ExitStatus.FAILED);
}
};
@@ -94,10 +148,20 @@ class FlowBuilderTests {
.start(new JobFlowExecutor(jobRepository, new SimpleStepHandler(jobRepository), execution));
Iterator<StepExecution> stepExecutions = execution.getStepExecutions().iterator();
StepExecution stepExecutionA = stepExecutions.next();
assertEquals(stepExecutionA.getStepName(), "stepA");
StepExecution stepExecutionC = stepExecutions.next();
assertEquals(stepExecutionC.getStepName(), "stepC");
assertEquals(stepExecutions.next().getStepName(), "stepA");
assertEquals(stepExecutions.next().getStepName(), "stepC");
assertFalse(stepExecutions.hasNext());
}
private static StepSupport createCompleteStep(String name) {
return new StepSupport(name) {
@Override
public void execute(StepExecution stepExecution)
throws JobInterruptedException, UnexpectedJobExecutionException {
stepExecution.upgradeStatus(BatchStatus.COMPLETED);
stepExecution.setExitStatus(ExitStatus.COMPLETED);
}
};
}
}