Configuring StatisticsListener (using annotations)

In this post under Spring Retry, I will show with example how to configure StatisticsListener using annotations.

As explained in previous posts, StatisticsListener class provided by Spring Retry framework collects statistics of a particular retry operations.

The statistics information involves
1) Abort Count –> How many times the retry was aborted
2) Complete Count –> How many times the retry completed successful
3) Error Count –> How many times exception was thrown
4) Recovery Count –> How many times the application recovered from the exception
5) Started Count –> How many retry attempts were involved including first attempt

First for our example, we will create a Service class as shown below

Service11


1  import org.springframework.retry.annotation.Retryable;
2  import org.springframework.stereotype.Service;
3  
4  @Service
5  public class Service11 {
6      private int i = 0;
7  
8      @Retryable(label="Service11", listeners="retryListener")
9      public void executeWithException() {
10         i = i + 1;
11         System.out.println("Executing method 'executeWithException' : " + i);
12         throw new NullPointerException();
13     }
14 }

In the above Service class, we annotated method which has to be retried using “Retryable” annotation. We are also specifying name under which this method’s statistics has to be stored, using “label” annotation attribute. We are also specifying the listeners using “listeners” annotation attribute.

In the above example, we are specifying the listener name as “retryListener”. So we should have bean with the same name in the application.

Next we will see the main class.

Main Class


1  import java.util.Iterator;
2  
3  import org.springframework.beans.factory.annotation.Qualifier;
4  import org.springframework.context.ApplicationContext;
5  import org.springframework.context.annotation.AnnotationConfigApplicationContext;
6  import org.springframework.context.annotation.Bean;
7  import org.springframework.context.annotation.Configuration;
8  import org.springframework.retry.RetryListener;
9  import org.springframework.retry.RetryStatistics;
10 import org.springframework.retry.annotation.EnableRetry;
11 import org.springframework.retry.stats.DefaultStatisticsRepository;
12 import org.springframework.retry.stats.StatisticsListener;
13 import org.springframework.retry.stats.StatisticsRepository;
14 
15 @Configuration
16 @EnableRetry
17 public class Example32 {
18     public static void main(String[] args) {
19         ApplicationContext context = new AnnotationConfigApplicationContext(Example32.class);
20         Service11 service11 = (Service11) context.getBean("service11");
21         
22         try {
23             service11.executeWithException();
24         } catch(Exception excep) {
25             excep.printStackTrace();
26         }
27         
28         StatisticsRepository statisticsRepository = (StatisticsRepository) context.getBean("statisticsRepository");
29         Iterator<RetryStatistics> retryStatistics = statisticsRepository.findAll().iterator();
30         
31         while(retryStatistics.hasNext()) {
32             RetryStatistics entry = retryStatistics.next();
33             System.out.println(entry);
34         }
35     }
36     
37     @Bean(name="service11")
38     public Service11 getService() {
39         return new Service11();
40     }
41     
42     @Bean(name="retryListener")
43     public RetryListener getStatisticsListener(@Qualifier("statisticsRepository") StatisticsRepository statisticsRepository) {
44         return new StatisticsListener(statisticsRepository);
45     }
46     
47     @Bean("statisticsRepository")
48     public StatisticsRepository getStatisticsRepository() {
49         return new DefaultStatisticsRepository();
50     }
51 }

In the above code, we have bean definitions for “RetryListener” and “StatisticsRepository”. The “RetryListener” bean name “retryListener” matches with the name provided in “Service11” class “Retryable” annotation. At line 44, we are providing an instance of “StatisticsRepository” to an instance of “StatisticsListener” as constructor argument.

At line 23, we call the service class method that will throw exception and will be retried.

At line 28, we get the instance of “StatisticsRepository”.

From line 31 to 34, we loop through the instance of “StatisticsRepository” printing each statistics entry.

Below is the output

Output


[INFO] AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6193b845: startup date [Sat Jul 09 09:13:59 IST 2022]; root of context hierarchy
Executing method 'executeWithException' : 1
Executing method 'executeWithException' : 2
Executing method 'executeWithException' : 3
java.lang.NullPointerException
    at Service11.executeWithException(Service11.java:12)
    at Service11$$FastClassBySpringCGLIB$$6bdf1375.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:736)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:93)
    at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
    at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:209)
    at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:119)
    at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:163)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)
    at Service11$$EnhancerBySpringCGLIB$$c36aa23b.executeWithException(<generated>)
    at Example32.main(Example32.java:23)
DefaultRetryStatistics [name=Service11, startedCount=3, completeCount=0, recoveryCount=0, errorCount=3, abortCount=1]

Leave a Reply