Creating Custom RetryListener (using xml configuration)

In this post under Spring Retry, I will show with example how to add RetryListener to our Spring Retry code.

RetryListener interface will help us to react to events like (react before first retry attempt, react after every failed retry attempt, react after final retry attempt).

RetryListener interface has the below three interface methods.
1) void open(RetryContext context, RetryCallback callback)
–> Called before the first attempt in a retry.
2) void onError(RetryContext context, RetryCallback callback, Throwable throwable)
–> Called after every unsuccessful attempt at a retry.
3) boolean close(RetryContext context, RetryCallback callback, Throwable throwable)
–> Called after the final attempt (successful or not).

For this example we will use the below service class

Service1


public class Service1 {
    private int i = 0;
    private int j = 0;

    public void executeWithException() {
        i = i + 1;
        System.out.println("Executing method 'executeWithException' : " + i);
        throw new NullPointerException();
    }

    public void executeWithoutException() {
        j = j + 1;
        System.out.println("Executing method 'executeWithoutException' : " + j);
    }

    public String recovery() {
        return "Hello";
    }
}

We will create a class “CustomRetryListener1” which implements the “RetryListener” interface. Below is the code for the class

CustomRetryListener1


import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;

public class CustomRetryListener1 implements RetryListener {
    @Override
    public <T, E extends Throwable> void close(RetryContext arg0, RetryCallback<T, E> arg1, Throwable arg2) {
        System.out.println("Calling 'close' method");
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext arg0, RetryCallback<T, E> arg1, Throwable arg2) {
        System.out.println("Calling 'onError' method");
    }

    @Override
    public <T, E extends Throwable> boolean open(RetryContext arg0, RetryCallback<T, E> arg1) {
        System.out.println("Calling 'open' method");
        return true;
    }
}

Please note the “open” method must return a boolean. If “true” is returned, retry attempt will proceed as usual otherwise the retry attempt is aborted.

Next I will show how to add the listener to RetryTemplate using xml configuration

XML


1  <xml version="1.0" encoding="UTF-8"/>
2  <beans xmlns="http://www.springframework.org/schema/beans"
3  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd>
5      <bean id="customRetryListener" class="CustomRetryListener1"/>
6      
7      <bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
8          <property name="listeners">
9              <list>
10                 <ref bean="customRetryListener"/>
11             </list>
12         </property>
13     </bean>
14     
15     <bean id="service1" class="Service1">
16 </beans>

In the above xml code, at line 5, I have created a bean definition for our “CustomRetryListener1”

From line 7 to 13, we create a “RetryTemplate” bean with the listener created at line 5 added in “RetryTemplate’s” “listeners” property.

At line 15, I create a bean definition for our “Service1” class.

In this way we configure “RetryTemplate” instance to use “CustomRetryListener1” listener

Below is the main class for your reference

Main


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.retry.support.RetryTemplate;

public class Example2 {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Example2.xml");
        Service1 service1 = (Service1)context.getBean("service1");
        RetryTemplate retryTemplate =  (RetryTemplate)context.getBean("retryTemplate");

        retryTemplate.execute(retryContext -> { service1.executeWithException(); return null; });
    }
}

Output

[INFO] ClassPathXmlApplicationContext – Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6576fe71: startup date [Sat Nov 27 10:45:48 IST 2021]; root of context hierarchy
[INFO] XmlBeanDefinitionReader – Loading XML bean definitions from class path resource [Example2.xml]
Calling ‘open’ method
Executing method ‘executeWithException’ : 1
Calling ‘onError’ method
Executing method ‘executeWithException’ : 2
Calling ‘onError’ method
Executing method ‘executeWithException’ : 3
Calling ‘onError’ method
Exception in thread “main” java.lang.NullPointerException
at Service1.executeWithException(Service1.java:8)
at Example2.lambda$0(Example2.java:11)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:209)
at Example2.main(Example2.java:11)
Calling ‘close’ method

Leave a Reply