So I've created a sample batch run to learn how to make a job repeat itself until a condition is met. For the most part, it's working as intended, but I'm getting the following log entry when I execute the program
org.springframework.batch.core.job.SimpleStepHandler: Duplicate step [getStartingStep] detected in execution of job=[infinateLoopJob]. If either step fails, both will be executed again on restart.
While I've been told by other developers it's not that big of a deal, Id rather do it right and not have anything weird happen down the road, when I want to rely on this knowledge for production code. I tried to append a timestamp generation to the name of the step, but no luck (it only appended it once upon initial creation of the Step).
So in a nutshell, how can I have it so that each step isn't using the same name over and over, and still perform the action of looping through the entire job?
The code currently being used:
AppStart.java
@SpringBootApplication(scanBasePackages="com.local.testJobLoop")
public class AppStart {
private static final Logger logger = LoggerFactory.getLogger(com.local.testJobLoop.AppStart.class);
AppStart() {
super();
}
public static void main(String[] args) {
SpringApplication application = new SpringApplication(AppStart.class);
application.setWebEnvironment(false);
ApplicationContext context = application.run(args);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job infinateLoopJob = context.getBean("infinateLoopJob", Job.class);
try {
JobParameters jobParameters = new JobParametersBuilder()
.addLong("timestamp", System.currentTimeMillis())
.toJobParameters();
JobExecution execution = jobLauncher.run(infinateLoopJob, jobParameters);
BatchStatus status = execution.getStatus();
logger.debug("Exit Status : {}", status);
if (!BatchStatus.COMPLETED.equals(status)) {
List<Throwable> exceptions = execution.getAllFailureExceptions();
for (Throwable throwable : exceptions) {
logger.error("Batch Failure Exceptions:", throwable);
}
}
} catch (JobExecutionAlreadyRunningException e) {
logger.error("Job execution already running:", e);
} catch (JobRestartException e) {
logger.error("Illegal attempt to restart a job:", e);
} catch (JobInstanceAlreadyCompleteException e) {
logger.error("Illegal attempt to restart a job that was already completed successfully", e);
} catch (JobParametersInvalidException e) {
logger.error("Invalid job parameter:", e);
}
logger.debug("Done");
}
}
AppJobConfig.java
@Configuration
@EnableBatchProcessing
public class AppJobConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Bean
public Job infinateLoopJob(@Qualifier("getStartingStep") final Step startingStep,
@Qualifier("getSecondStep") final Step secondStep) {
return jobBuilderFactory.get("infinateLoopJob")
.start(startingStep).on(Constants.STEP_EXIT_STATUS_CONTINUE).to(secondStep)
.from(startingStep).on(Constants.STEP_EXIT_STATUS_COMPLETED).end()
.from(secondStep).next(startingStep).build()
.build();
}
}
AppStepConfig.java
@Configuration
@EnableBatchProcessing
public class AppStepConfig {
@Autowired
private StepBuilderFactory stepBuilderFactory;
private static final Logger logger = LoggerFactory.getLogger(AppStepConfig.class);
@Bean
public Step getStartingStep(@Qualifier("startingActionTasklet") final StartingActionTasklet tasklet,
@Qualifier("startingActionListener") final StartingActionListener listener) {
return stepBuilderFactory.get("getStartingStep")
.tasklet(tasklet)
.listener(listener)
.build();
}
@Bean
public Step getSecondStep(@Qualifier("secondActionTasklet") final SecondActionTasklet tasklet,
@Qualifier("secondActionListener") final SecondActionListener listener) {
return stepBuilderFactory.get("getSecondStep")
.tasklet(tasklet)
.listener(listener)
.build();
}
StartingActionTasklet.java
@Component
public class StartingActionTasklet implements Tasklet, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(StartingActionTasklet.class);
public StartingActionTasklet() { super(); }
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
int number = (int) chunkContext.getStepContext()
.getStepExecution()
.getJobExecution()
.getExecutionContext()
.get("incrementNumber");
LOGGER.info("STARTING ACTION: Number is {number}");
return RepeatStatus.FINISHED;
}
public void afterPropertiesSet() throws Exception { /* do nothing */ }
}
SecondActionTasklet.java
@Component
public class SecondActionTasklet implements Tasklet, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(SecondActionTasklet.class);
public SecondActionTasklet() { super(); }
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
int number = (int) chunkContext.getStepContext()
.getStepExecution()
.getJobExecution()
.getExecutionContext()
.get("incrementNumber");
LOGGER.info("SECOND ACTION: Number is " + number);
number++;
LOGGER.info("SECOND ACTION: Number is now " + number);
chunkContext.getStepContext()
.getStepExecution()
.getJobExecution()
.getExecutionContext()
.put("incrementNumber", number);
return RepeatStatus.FINISHED;
}
public void afterPropertiesSet() throws Exception {
//do nothing
}
}
StartingActionListener.java
@Component
public class StartingActionListener implements StepExecutionListener {
private static final Logger LOGGER = LoggerFactory.getLogger(StartingActionListener.class);
public StartingActionListener() {
super();
}
public void beforeStep(StepExecution stepExecution) {
LOGGER.debug("StartingActionListener - beforeStep");
// Get incrementNumber from job execution context
JobExecution jobExecution = stepExecution.getJobExecution();
ExecutionContext jobContext = jobExecution.getExecutionContext();
Integer incrementNumber = (Integer) jobContext.get("incrementNumber");
if (incrementNumber == null) {
jobContext.put("incrementNumber", 0);
}
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
LOGGER.debug("StartingActionListener - afterStep");
// Get incrementNumber from job execution context
JobExecution jobExecution = stepExecution.getJobExecution();
ExecutionContext jobContext = jobExecution.getExecutionContext();
Integer incrementNumber = (Integer) jobContext.get("incrementNumber");
// Continue job execution if have more feed, stop otherwise
if (incrementNumber == null || incrementNumber > 10) {
return new ExitStatus(Constants.STEP_EXIT_STATUS_COMPLETED);
} else {
return new ExitStatus(Constants.STEP_EXIT_STATUS_CONTINUE);
}
}
}
SecondActionListener.java
@Component
public class SecondActionListener implements StepExecutionListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SecondActionListener.class);
public void beforeStep(StepExecution stepExecution) {
}
public ExitStatus afterStep(StepExecution stepExecution) {
LOGGER.debug("SecondActionListener - afterStep");
return null;
}
}