RetryListener Example

In my previous post, I explained how to configure job retry if job fails in first attempt.

In this post under Spring Batch, I will explain with example how to configure RetryListener.

With the help of RetryListener you can listen to error events and respond appropriately.

To create an exception that will force the job to retry, I will use ExceptionSimulator class as shown below

ExceptionSimulator


package package32;

import org.springframework.batch.item.ItemProcessor;

public class ExceptionSimulator implements ItemProcessor {
	int count = 0;
	
	@Override
	public Employee process(Employee employee) throws Exception {
		Employee employee1 = null;
		
		if (count % 2 == 0) {
			employee1 = employee;
			count = count + 1;
		} else if (count %2 == 1) {
			count = count + 1;
			throw new IllegalArgumentException();
		}
		return employee1;
	}
}

Employee records at even position are returned as it is but records at odd position will throw IllegalArgumentException.

Next we will create a listener class that implements org.springframework.retry.RetryListener interface as shown below

CustomRetryListener


1  package package32;
2  
3  import org.springframework.retry.RetryCallback;
4  import org.springframework.retry.RetryContext;
5  import org.springframework.retry.RetryListener;
6  
7  public class CustomRetryListener implements RetryListener {
8  	@Override
9  	public  void close(RetryContext retryContext, RetryCallback retryCallback, Throwable throwable) {
10 	}
11 
12 	@Override
13 	public  void onError(RetryContext retryContext, RetryCallback retryCallback, Throwable throwable) {
14 		System.out.println("Retry Count: " + retryContext.getRetryCount());
15 	}
16 
17 	@Override
18 	public  boolean open(RetryContext retryContext, RetryCallback retryCallback) {
19 		return true;
20 	}
21 }

As shown in the above code, we create CustomRetryListener class implementing RetryListener interface. The interface has three methods “close”, “onError”, and “open”. We need to focus on “onError” and “open” method.

We need to implement the “open” method and its return value should always be true if we want the listener “onError” method to be called. If false, “onError” method will not be called. The method “onError” will be called when an error happens.

Next we have to configure the listener with the job as shown in the below xml


1  <bean id="customRetryListener" class="package32.CustomRetryListener"/>
2  
3  <batch:job id="importEmployees">
4  	<batch:step id="readWriteEmployees">
5  		<batch:tasklet start-limit="2">
6  			<batch:chunk reader="reader" writer="writer" commit-interval="50" retry-limit="5" processor="exceptionSimulator">
7  				<batch:retryable-exception-classes>
8  					<batch:include class="java.lang.IllegalArgumentException"/>
9  				</batch:retryable-exception-classes>
10 				<batch:retry-listeners>
11 					<batch:listener ref="customRetryListener"/>
12 				</batch:retry-listeners>
13 			</batch:chunk>
14 		</batch:tasklet>
15 	</batch:step>
16 </batch:job>

At line 1 we create a bean named “customRetryListener” and at line 11 we set the bean as a retry listener.

Below is the complete xml configuration for your reference


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:batch="http://www.springframework.org/schema/batch"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd">
	
	<bean id="employee" class="package32.Employee" scope="prototype"/>
	
	<bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
		<property name="resource" value="file:FileInput.txt"/>
		<property name="lineMapper">
			<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
				<property name="lineTokenizer">
					<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
						<property name="names" value="id,name,status,salary"/>
					</bean>
				</property>
				<property name="fieldSetMapper">
					<bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
						<property name="prototypeBeanName" value="employee"/>
					</bean>
				</property>
			</bean>
		</property> 
	</bean>
	
	<bean id="writer" class="org.springframework.batch.item.file.FlatFileItemWriter">
		<property name="resource" value="file:FileOutput.txt"/>
		<property name="lineAggregator">
			<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
				<property name="fieldExtractor">
					<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
						<property name="names" value="id,name,status,salary"/>
					</bean>
				</property>
			</bean>
		</property>
	</bean>
	
	<bean id="exceptionSimulator" class="package32.ExceptionSimulator" />
	
	<bean id="customRetryListener" class="package32.CustomRetryListener"/>
	
	<batch:job id="importEmployees">
		<batch:step id="readWriteEmployees">
			<batch:tasklet start-limit="2">
				<batch:chunk reader="reader" writer="writer" commit-interval="50" retry-limit="5" processor="exceptionSimulator">
					<batch:retryable-exception-classes>
						<batch:include class="java.lang.IllegalArgumentException"/>
					</batch:retryable-exception-classes>
					<batch:retry-listeners>
						<batch:listener ref="customRetryListener"/>
					</batch:retry-listeners>
				</batch:chunk>
			</batch:tasklet>
		</batch:step>
	</batch:job>
	
	<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
	
	<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
		<property name="transactionManager" ref="transactionManager"/>
	</bean>
	
	<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
		<property name="jobRepository" ref="jobRepository"/>
	</bean>
</beans>

Leave a Reply