Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected exception type thrown instead of actual CHECKED exception when noRetryFor is used #405

Closed
yilativs opened this issue Nov 23, 2023 · 3 comments
Assignees
Labels
Milestone

Comments

@yilativs
Copy link

Unexpected exception type thrown instead of actual CHECKED exception when noRetryFor is used
however when notRecoverable is used actual checked exception is thrown.

For RuntimeException the issue does not happen.

Here is the stack trace:

Caused by: java.lang.reflect.UndeclaredThrowableException
	at org.springframework.util.ReflectionUtils.rethrowRuntimeException(ReflectionUtils.java:147)
	at org.springframework.util.ReflectionUtils.handleInvocationTargetException(ReflectionUtils.java:126)
	at org.springframework.util.ReflectionUtils.handleReflectionException(ReflectionUtils.java:110)
	at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:284)
	at org.springframework.retry.annotation.RecoverAnnotationRecoveryHandler.recover(RecoverAnnotationRecoveryHandler.java:100)
	at org.springframework.retry.interceptor.RetryOperationsInterceptor$ItemRecovererCallback.recover(RetryOperationsInterceptor.java:159)
	at org.springframework.retry.support.RetryTemplate.handleRetryExhausted(RetryTemplate.java:543)
	at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:389)
	at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:225)
	at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:124)
	at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:160)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
	at org.example.foo.service.FooService$$SpringCGLIB$$0.throwsException(<generated>)
	at org.example.foo.service.FooServiceTest.lambda$0(FooServiceTest.java:106)
	at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:53)
	... 73 more
Caused by: org.example.foo.NonRecoverableException
	at org.example.foo.service.FooService.throwsException(FooService.java:46)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756)
	at org.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:102)
	at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
	... 82 more
@artembilan
Copy link
Member

Any chances to see the code which can reproduce the problem and we can play with?
Thanks

yilativs added a commit to yilativs/spring-retry-sample that referenced this issue Nov 28, 2023
@yilativs
Copy link
Author

Sure Artem, you can find a test case here https://github.com/yilativs/spring-retry-sample/compare/fix-broken-no-retry?expand=1

PS I hope you guys will add a unit test for that case to your project to avoid regression.

@artembilan
Copy link
Member

So, the logic is hidden in the ReflectionUtils:

    public static void rethrowRuntimeException(Throwable ex) {
        if (ex instanceof RuntimeException runtimeException) {
            throw runtimeException;
        } else if (ex instanceof Error error) {
            throw error;
        } else {
            throw new UndeclaredThrowableException(ex);
        }
    }

That is what confirmed by your stack trace.

So, I believe that you realign with other expectations we need to fix RetryTemplate to catch an UndeclaredThrowableException on the T recovered = recoveryCallback.recover(context); and re-throw its cause.

Will issue PR shortly.

@artembilan artembilan added this to the 2.0.5 milestone Dec 11, 2023
@artembilan artembilan self-assigned this Dec 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants