5

The below @Retryable code works where there is a direct call to method, but retryable method call through @Async annotated method then throws an exception. Any suggestions ?

Here is my Service Class

@Service
public class RetryAndRecoverService {

    int counter = 0;
    String str = null;
    @Retryable(value = {FooException.class, BarException.class}, maxAttempts = 5,  backoff = @Backoff(delay = 1000, multiplier = 1))
    public String retryWithException() {
        System.out.println("retryWithException - "+(counter++));
        String value = getMapperValue();
        if(value == null){
            throw new FooException();
        }
        return value;
    }

     private String getMapperValue() {
        return null;
    }

    @Async
    public String testRetry(){
        return retryWithException();
    }

}

Here is Junit Test class

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = RetryExampleApplication.class)
public class RetryTest {

    @Autowired
    private RetryAndRecoverService retryAndRecoverService;

    @Test
    public void retryWithException() {
        Stream.of(IntStream.range(0, 3)).forEach(index -> {
            String str = retryAndRecoverService.testRetry();
            System.out.println(str);

        }); 
    }
}

Here is Spring Boot application Class

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableRetry
@EnableAsync
@SpringBootApplication
public class RetryExampleApplication {


    @Autowired
    RetryAndRecoverService retryAndRecoverService;

    public static void main(String[] args){
        SpringApplication.run(RetryExampleApplication.class, args);
    }

    private void retryAndRecoverService() {
        retryAndRecoverService.retryWithException();

    }
}

Exception as follows. Any suggestions on this please.

retryWithException - 0

2018-04-05 12:45:17.644 ERROR 23052 --- [cTaskExecutor-1] .a.i.SimpleAsyncUncaughtExceptionHandler : Unexpected error occurred invoking async method 'public java.lang.String com.mscharhag.springretrydemo.RetryAndRecoverService.testRetry()'.

com.mscharhag.springretrydemo.RetryAndRecoverService$FooException: null
    at com.mscharhag.springretrydemo.RetryAndRecoverService.retryWithException(RetryAndRecoverService.java:19) ~[classes/:na]
    at com.mscharhag.springretrydemo.RetryAndRecoverService.testRetry(RetryAndRecoverService.java:47) ~[classes/:na]
    at com.mscharhag.springretrydemo.RetryAndRecoverService$$FastClassBySpringCGLIB$$f31442b9.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:121) ~[spring-retry-1.1.2.RELEASE.jar:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:108) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_141]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_141]
Martin Schröder
  • 4,176
  • 7
  • 47
  • 81
Sabarish.K
  • 109
  • 1
  • 2
  • 7
  • 2
    Spring uses proxies to apply AOP and `@Async` and `@Retryable` are both applied through AOP. Only method calls into the object pass through the proxy internal method calls won't pass through the proxy. So basically this will only work if the `@Retryable` method is in a different bean or is on the `@Async` method. – M. Deinum Apr 05 '18 at 10:05
  • Absolutely correct !! It is working fine after move these two methods in two different classes. Thank you. – Sabarish.K Apr 06 '18 at 06:21

1 Answers1

1

A similar problem was the case and fixed with spring version 4.3.14 Please see https://jira.spring.io/browse/SPR-16196

Selim Ok
  • 1,141
  • 1
  • 7
  • 21