I send email in Spring Batch Tasklet
.
SMTP server is down so unchecked MailSendException
exception occurred.
Next step in transition is declared as (from email sending):
FlowBuilder<Flow> flowBuilder = new FlowBuilder<Flow>("myFlow")
.from(sendNotificationStep()).next(nextStep());
and nextStep()
is executed even in case of unchecked exception.
Is that normal behavior of Spring Batch Framework to ignore unchecked exceptions?
The problem is that this exception is silently ignored and is not logged (I set root
logger to WARN
).
Somewhat opposite behavior reported in why does transaction roll back on RuntimeException but not SQLException
UPDATE After stepping with debugger I end inside:
public class SimpleFlow implements Flow, InitializingBean {
public FlowExecution resume(String stateName, FlowExecutor executor) throws FlowExecutionException {
state = nextState(stateName, status, stepExecution);
status
is FAILED
, state
is sendNotificationStep
and nextState()
return nextStep
.
There is catch
in resume
:
catch (Exception e) {
executor.close(new FlowExecution(stateName, status));
throw new FlowExecutionException(String.format("Ended flow=%s at state=%s with exception", name,
stateName), e);
}
but exception handled previously by:
public abstract class AbstractStep implements Step, InitializingBean, BeanNameAware {
public final void execute(StepExecution stepExecution) throws JobInterruptedException,
catch (Throwable e) {
stepExecution.upgradeStatus(determineBatchStatus(e));
exitStatus = exitStatus.and(getDefaultExitStatusForFailure(e));
stepExecution.addFailureException(e);
if (stepExecution.getStatus() == BatchStatus.STOPPED) {
logger.info(String.format("Encountered interruption executing step %s in job %s : %s", name, stepExecution.getJobExecution().getJobInstance().getJobName(), e.getMessage()));
if (logger.isDebugEnabled()) {
logger.debug("Full exception", e);
}
}
else {
logger.error(String.format("Encountered an error executing step %s in job %s", name, stepExecution.getJobExecution().getJobInstance().getJobName()), e);
}
}
Batch admin lists problem step as ABANDONED
.
UPDATE 3 Fully functional example to reproduce behavior (thanks to Sabir Khan for providing stab!):
@SpringBootApplication
@Configuration
@EnableBatchProcessing
public class X {
private static final Logger logger = LoggerFactory.getLogger(X.class);
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Bean
protected Tasklet tasklet1() {
return (StepContribution contribution, ChunkContext context) -> {
logger.warn("Inside tasklet1");
throw new IllegalStateException("xxx");
//return RepeatStatus.FINISHED;
};
}
@Bean
protected Tasklet tasklet2() {
return (StepContribution contribution, ChunkContext context) -> {
logger.warn("Inside tasklet2");
return RepeatStatus.FINISHED;
};
}
@Bean
public Job job() throws Exception {
Flow flow = new FlowBuilder<Flow>("myFlow").from(firstStep()).on("*").to(nextStep()).end();
return this.jobs.get("job").start(flow).end().build();
}
@Bean
protected Step firstStep() {
return this.steps.get("firstStep").tasklet(tasklet1()).build();
}
@Bean
protected Step nextStep() {
return this.steps.get("nextStep").tasklet(tasklet2()).build();
}
public static void main(String[] args) throws Exception {
System.exit(SpringApplication.exit(SpringApplication.run(X.class, args)));
}
}