@ -18,7 +18,7 @@ package org.springframework.resilience;
import java.io.IOException ;
import java.io.IOException ;
import java.lang.reflect.InvocationTargetException ;
import java.lang.reflect.InvocationTargetException ;
import java.lang.reflect.Method ;
import java.nio.charset.MalformedInputException ;
import java.nio.file.AccessDeniedException ;
import java.nio.file.AccessDeniedException ;
import java.time.Duration ;
import java.time.Duration ;
import java.util.Properties ;
import java.util.Properties ;
@ -42,15 +42,16 @@ import org.springframework.resilience.annotation.ConcurrencyLimit;
import org.springframework.resilience.annotation.EnableResilientMethods ;
import org.springframework.resilience.annotation.EnableResilientMethods ;
import org.springframework.resilience.annotation.RetryAnnotationBeanPostProcessor ;
import org.springframework.resilience.annotation.RetryAnnotationBeanPostProcessor ;
import org.springframework.resilience.annotation.Retryable ;
import org.springframework.resilience.annotation.Retryable ;
import org.springframework.resilience.retry.MethodRetryPredicate ;
import org.springframework.resilience.retry.MethodRetrySpec ;
import org.springframework.resilience.retry.MethodRetrySpec ;
import org.springframework.resilience.retry.SimpleRetryInterceptor ;
import org.springframework.resilience.retry.SimpleRetryInterceptor ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.assertThatIOException ;
import static org.assertj.core.api.Assertions.assertThatIOException ;
import static org.assertj.core.api.Assertions.assertThatRuntimeException ;
/ * *
/ * *
* @author Juergen Hoeller
* @author Juergen Hoeller
* @author Sam Brannen
* @since 7 . 0
* @since 7 . 0
* /
* /
class RetryInterceptorTests {
class RetryInterceptorTests {
@ -187,12 +188,22 @@ class RetryInterceptorTests {
AnnotatedClassBean proxy = bf . getBean ( AnnotatedClassBean . class ) ;
AnnotatedClassBean proxy = bf . getBean ( AnnotatedClassBean . class ) ;
AnnotatedClassBean target = ( AnnotatedClassBean ) AopProxyUtils . getSingletonTarget ( proxy ) ;
AnnotatedClassBean target = ( AnnotatedClassBean ) AopProxyUtils . getSingletonTarget ( proxy ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : retryOperation ) . withMessage ( "3" ) ;
// 3 = 1 initial invocation + 2 retry attempts
// Not 3 retry attempts, because RejectMalformedInputException3Predicate rejects
// a retry if the last exception was a MalformedInputException with message "3".
assertThatIOException ( ) . isThrownBy ( proxy : : retryOperation ) . withMessageContaining ( "3" ) ;
assertThat ( target . counter ) . isEqualTo ( 3 ) ;
assertThat ( target . counter ) . isEqualTo ( 3 ) ;
// 7 = 3 + 1 initial invocation + 3 retry attempts
assertThatRuntimeException ( )
. isThrownBy ( proxy : : retryOperationWithNestedException )
. havingCause ( )
. isExactlyInstanceOf ( IOException . class )
. withMessage ( "7" ) ;
assertThat ( target . counter ) . isEqualTo ( 7 ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : otherOperation ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : otherOperation ) ;
assertThat ( target . counter ) . isEqualTo ( 4 ) ;
assertThat ( target . counter ) . isEqualTo ( 8 ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : overrideOperation ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : overrideOperation ) ;
assertThat ( target . counter ) . isEqualTo ( 6 ) ;
assertThat ( target . counter ) . isEqualTo ( 10 ) ;
}
}
@Test
@Test
@ -212,7 +223,10 @@ class RetryInterceptorTests {
AnnotatedClassBeanWithStrings proxy = ctx . getBean ( AnnotatedClassBeanWithStrings . class ) ;
AnnotatedClassBeanWithStrings proxy = ctx . getBean ( AnnotatedClassBeanWithStrings . class ) ;
AnnotatedClassBeanWithStrings target = ( AnnotatedClassBeanWithStrings ) AopProxyUtils . getSingletonTarget ( proxy ) ;
AnnotatedClassBeanWithStrings target = ( AnnotatedClassBeanWithStrings ) AopProxyUtils . getSingletonTarget ( proxy ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : retryOperation ) . withMessage ( "3" ) ;
// 3 = 1 initial invocation + 2 retry attempts
// Not 3 retry attempts, because RejectMalformedInputException3Predicate rejects
// a retry if the last exception was a MalformedInputException with message "3".
assertThatIOException ( ) . isThrownBy ( proxy : : retryOperation ) . withMessageContaining ( "3" ) ;
assertThat ( target . counter ) . isEqualTo ( 3 ) ;
assertThat ( target . counter ) . isEqualTo ( 3 ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : otherOperation ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : otherOperation ) ;
assertThat ( target . counter ) . isEqualTo ( 4 ) ;
assertThat ( target . counter ) . isEqualTo ( 4 ) ;
@ -237,7 +251,10 @@ class RetryInterceptorTests {
AnnotatedClassBeanWithStrings proxy = ctx . getBean ( AnnotatedClassBeanWithStrings . class ) ;
AnnotatedClassBeanWithStrings proxy = ctx . getBean ( AnnotatedClassBeanWithStrings . class ) ;
AnnotatedClassBeanWithStrings target = ( AnnotatedClassBeanWithStrings ) AopProxyUtils . getSingletonTarget ( proxy ) ;
AnnotatedClassBeanWithStrings target = ( AnnotatedClassBeanWithStrings ) AopProxyUtils . getSingletonTarget ( proxy ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : retryOperation ) . withMessage ( "3" ) ;
// 3 = 1 initial invocation + 2 retry attempts
// Not 3 retry attempts, because RejectMalformedInputException3Predicate rejects
// a retry if the last exception was a MalformedInputException with message "3".
assertThatIOException ( ) . isThrownBy ( proxy : : retryOperation ) . withMessageContaining ( "3" ) ;
assertThat ( target . counter ) . isEqualTo ( 3 ) ;
assertThat ( target . counter ) . isEqualTo ( 3 ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : otherOperation ) ;
assertThatIOException ( ) . isThrownBy ( proxy : : otherOperation ) ;
assertThat ( target . counter ) . isEqualTo ( 4 ) ;
assertThat ( target . counter ) . isEqualTo ( 4 ) ;
@ -267,6 +284,7 @@ class RetryInterceptorTests {
int counter = 0 ;
int counter = 0 ;
@Override
public void retryOperation ( ) throws IOException {
public void retryOperation ( ) throws IOException {
counter + + ;
counter + + ;
throw new IOException ( Integer . toString ( counter ) ) ;
throw new IOException ( Integer . toString ( counter ) ) ;
@ -314,16 +332,24 @@ class RetryInterceptorTests {
@Retryable ( delay = 10 , jitter = 5 , multiplier = 2 . 0 , maxDelay = 40 ,
@Retryable ( delay = 10 , jitter = 5 , multiplier = 2 . 0 , maxDelay = 40 ,
includes = IOException . class , excludes = AccessDeniedException . class ,
includes = IOException . class , excludes = AccessDeniedException . class ,
predicate = Custom Predicate. class )
predicate = RejectMalformedInputException3 Predicate. class )
static class AnnotatedClassBean {
static class AnnotatedClassBean {
int counter = 0 ;
int counter = 0 ;
public void retryOperation ( ) throws IOException {
public void retryOperation ( ) throws IOException {
counter + + ;
counter + + ;
if ( counter = = 3 ) {
throw new MalformedInputException ( counter ) ;
}
throw new IOException ( Integer . toString ( counter ) ) ;
throw new IOException ( Integer . toString ( counter ) ) ;
}
}
public void retryOperationWithNestedException ( ) {
counter + + ;
throw new RuntimeException ( new IOException ( Integer . toString ( counter ) ) ) ;
}
public void otherOperation ( ) throws IOException {
public void otherOperation ( ) throws IOException {
counter + + ;
counter + + ;
throw new AccessDeniedException ( Integer . toString ( counter ) ) ;
throw new AccessDeniedException ( Integer . toString ( counter ) ) ;
@ -340,13 +366,16 @@ class RetryInterceptorTests {
@Retryable ( delayString = "${delay}" , jitterString = "${jitter}" ,
@Retryable ( delayString = "${delay}" , jitterString = "${jitter}" ,
multiplierString = "${multiplier}" , maxDelayString = "${maxDelay}" ,
multiplierString = "${multiplier}" , maxDelayString = "${maxDelay}" ,
includes = IOException . class , excludes = AccessDeniedException . class ,
includes = IOException . class , excludes = AccessDeniedException . class ,
predicate = Custom Predicate. class )
predicate = RejectMalformedInputException3 Predicate. class )
static class AnnotatedClassBeanWithStrings {
static class AnnotatedClassBeanWithStrings {
int counter = 0 ;
int counter = 0 ;
public void retryOperation ( ) throws IOException {
public void retryOperation ( ) throws IOException {
counter + + ;
counter + + ;
if ( counter = = 3 ) {
throw new MalformedInputException ( counter ) ;
}
throw new IOException ( Integer . toString ( counter ) ) ;
throw new IOException ( Integer . toString ( counter ) ) ;
}
}
@ -363,15 +392,6 @@ class RetryInterceptorTests {
}
}
private static class CustomPredicate implements MethodRetryPredicate {
@Override
public boolean shouldRetry ( Method method , Throwable throwable ) {
return ! "3" . equals ( throwable . getMessage ( ) ) ;
}
}
static class DoubleAnnotatedBean {
static class DoubleAnnotatedBean {
AtomicInteger current = new AtomicInteger ( ) ;
AtomicInteger current = new AtomicInteger ( ) ;