Programmatically configuring fault tolerance for a chunk works kind of as follows:
stepBuilders.get("step")
.<Partner,Partner>chunk(1)
.reader(reader())
.processor(processor())
.writer(writer())
.listener(logProcessListener())
.faultTolerant()
.skipLimit(10)
.skip(UnknownGenderException.class)
.listener(logSkipListener())
.build();
The trick is, that with adding "chunk", the chain switches to a SimpleStepBuilder which offers the "faultTolerant" method.
My question is how to do that if you just have a tasklet (no reader, processor, writer)?
Defining a tasklet works as follows:
stepBuilders.get("step")
.tasklet(tasklet())
.build();
The usage of "tasklet" switches to a TaskletStepBuilder which does not offer a "faultTolerant" method. Therefore I see no way how to define properties like skipLimit and that like.
Any ideas?
A Tasklet
has no notion of "items" to skip, so fault tolerance only makes sense for a chunk-oriented step. I suggest you use Spring Retry in the raw (1.1.0.RELEASE is out now, just, and you have some fluent builder options and a @Retryable
annotation there).
Based on the guidance given by @DaveSyer and using org.springframework.retry:spring-retry:1.1.0.RELEASE this is what I have ended up with:
Tasklet tasklet = // whatever
ProxyFactory proxyFactory = new ProxyFactory(Tasklet, new SingletonTargetSource(tasklet));
proxyFactory.addAdvice(RetryInterceptorBuilder.stateless()
.maxAttempts(3)
.build());
Step step = stepBuilderFactory.get("taskName")
.tasklet((Tasklet)proxyFactory.proxy)
.build();
The only thing I'm struggling with still is if I want to swallow the exception that causes the retry after maxAttempts
is exceeded. If I add an ExceptionHandler
to the step then the step is never retried. I guess that means the exception handler is inside the pointcut which I find a little surprising.
//you need to have a bean in order to define the retry policies
@Configuration
@EnableAutoConfiguration
public class MyRetryBean{
@Autowired
private RetryProperties properties;
private RetryTemplate retryTemplate;
@Bean
public RetryTemplate initializeRetryTemplate() {
Map<Class<? extends Throwable>, Boolean> retryExceptions = Collections.singletonMap(IOException.class,
Boolean.TRUE);
SimpleRetryPolicy policy = new SimpleRetryPolicy(properties.getMaxAttempt(), retryExceptions);
retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(policy);
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(properties.getBackoffPeriod());
retryTemplate.setBackOffPolicy(backOffPolicy);
return retryTemplate;
}
public RetryTemplate getRetryTemplate() {
return retryTemplate;
}
}
// in spring batch steps config
@Autowire
private MyRetryBean retryBean;
stepBuilders.get("step")
.tasklet(new MyTasklet(retryBean))
.build();
// in your tasklet
public class MyTasklet implements Tasklet{
private MyRetryBean retry;
public MyTasklet (MyRetryBean aRetry) {
this.retry= aRetry;
}
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws IOException {
retry.getRetryTemplate().execute(new RetryCallback<RepeatStatus, IOException>() {
@Override
public RepeatStatus doWithRetry(RetryContext context) throws IOException {
throw new IOException();
}
});
return null;
}
}
来源:https://stackoverflow.com/questions/23523130/how-to-configure-fault-tolerance-programmatically-for-a-spring-tasklet-not-a-ch