Reputation: 48733
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)));
}
}
Upvotes: 0
Views: 8406
Reputation: 10132
No, that is not normal Spring Batch behavior and I have never seen what you are describing.
I think, Spring Batch doesn't make a distinction in exception thrown being checked or unchecked - flow will stop at that very point when exception is thrown for the case of sequential execution.
Obviously, executions of other parts will continue if parallel steps or executions are going on.
Somewhere down the line, exception might have been handled in your code (eaten silently) and that is why execution might have continued to next step.
I have seen both categories of exceptions being thrown in my jobs and job behaves the way I coded skip-retry and other exception handling mechanisms and it has nothing to do with Spring Batch - that is pure Java.
I am not sure if I misread your question but this below sample code doesn't work the way you described. Step-1 throws unchecked exception and execution stopped right there since I am not handling it in anyway.
@SpringBootApplication(exclude = { DataSource.class,
DataSourceAutoConfiguration.class })
@Configuration
@EnableBatchProcessing
public class AppConfiguration {
private static final Logger logger = LoggerFactory.getLogger(AppConfiguration.class);
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Autowired
private JavaMailSender javaMailSender;
@Bean
protected Tasklet tasklet() {
return new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext context) {
MimeMessage message = javaMailSender.createMimeMessage();
javaMailSender.send(message);
return RepeatStatus.FINISHED;
}
};
}
@Bean
protected Tasklet tasklet2() {
return new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext context) {
return RepeatStatus.FINISHED;
}
};
}
@Bean
public Job job() throws Exception {
Flow flow = new FlowBuilder<Flow>("myFlow").from(step1()).next(nextStep()).end();
return this.jobs.get("job").start(flow).end().build();
}
@Bean
protected Step step1() {
return this.steps.get("step1").tasklet(tasklet()).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(AppConfiguration.class, args)));
}
}
Hope it helps !!
Upvotes: 2