@ -17,7 +17,9 @@
@@ -17,7 +17,9 @@
package org.springframework.core.retry ;
import java.time.Duration ;
import java.util.concurrent.atomic.AtomicInteger ;
import org.junit.jupiter.api.BeforeEach ;
import org.junit.jupiter.api.Test ;
import org.springframework.util.backoff.FixedBackOff ;
@ -36,95 +38,99 @@ class RetryTemplateTests {
@@ -36,95 +38,99 @@ class RetryTemplateTests {
private final RetryTemplate retryTemplate = new RetryTemplate ( ) ;
@Test
void retryWithSuccess ( ) throws Exception {
Retryable < String > retryable = new Retryable < > ( ) {
int failure ;
@BeforeEach
void configureTemplate ( ) {
this . retryTemplate . setBackOffPolicy ( new FixedBackOff ( Duration . ofMillis ( 10 ) ) ) ;
}
@Override
public String execute ( ) throws Exception {
if ( failure + + < 2 ) {
throw new Exception ( "Error while invoking greeting service" ) ;
}
return "hello world " ;
}
@Test
void retryWithImmediateSuccess ( ) throws Exception {
AtomicInteger invocationCount = new AtomicInteger ( ) ;
Retryable < String > retryable = ( ) - > {
invocationCount . incrementAndGet ( ) ;
return "always succeeds " ;
} ;
@Override
public String getName ( ) {
return "greeting service" ;
assertThat ( invocationCount ) . hasValue ( 0 ) ;
assertThat ( retryTemplate . execute ( retryable ) ) . isEqualTo ( "always succeeds" ) ;
assertThat ( invocationCount ) . hasValue ( 1 ) ;
}
@Test
void retryWithSuccessAfterInitialFailures ( ) throws Exception {
AtomicInteger invocationCount = new AtomicInteger ( ) ;
Retryable < String > retryable = ( ) - > {
if ( invocationCount . incrementAndGet ( ) < = 2 ) {
throw new Exception ( "Boom!" ) ;
}
return "finally succeeded" ;
} ;
retryTemplate . setBackOffPolicy ( new FixedBackOff ( Duration . ofMillis ( 10 ) ) ) ;
assertThat ( retryTemplate . execute ( retryable ) ) . isEqualTo ( "hello world" ) ;
assertThat ( invocationCount ) . hasValue ( 0 ) ;
assertThat ( retryTemplate . execute ( retryable ) ) . isEqualTo ( "finally succeeded" ) ;
assertThat ( invocationCount ) . hasValue ( 3 ) ;
}
@Test
void retryWithFailure ( ) {
Exception exception = new Exception ( "Error while invoking greeting service" ) ;
void retryWithExhaustedPolicy ( ) {
AtomicInteger invocationCount = new AtomicInteger ( ) ;
RuntimeException exception = new RuntimeException ( "Boom!" ) ;
Retryable < String > retryable = new Retryable < > ( ) {
@Override
public String execute ( ) throws Exception {
public String execute ( ) {
invocationCount . incrementAndGet ( ) ;
throw exception ;
}
@Override
public String getName ( ) {
return "greeting service " ;
return "test " ;
}
} ;
retryTemplate . setBackOffPolicy ( new FixedBackOff ( Duration . ofMillis ( 10 ) ) ) ;
assertThat ( invocationCount ) . hasValue ( 0 ) ;
assertThatExceptionOfType ( RetryException . class )
. isThrownBy ( ( ) - > retryTemplate . execute ( retryable ) )
. withMessage ( "Retry policy for operation 'greeting service ' exhausted; aborting execution" )
. withMessage ( "Retry policy for operation 'test ' exhausted; aborting execution" )
. withCause ( exception ) ;
// 4 = 1 initial invocation + 3 retry attempts
assertThat ( invocationCount ) . hasValue ( 4 ) ;
}
@Test
void retrySpecificException ( ) {
@SuppressWarnings ( "serial" )
class TechnicalException extends Exception {
public TechnicalException ( String message ) {
super ( message ) ;
}
}
TechnicalException technicalException = new TechnicalException ( "Error while invoking greeting service" ) ;
void retryWithFailingRetryableAndCustomRetryPolicy ( ) {
AtomicInteger invocationCount = new AtomicInteger ( ) ;
RuntimeException exception = new NumberFormatException ( ) ;
Retryable < String > retryable = new Retryable < > ( ) {
@Override
public String execute ( ) throws TechnicalException {
throw technicalException ;
public String execute ( ) {
invocationCount . incrementAndGet ( ) ;
throw exception ;
}
@Override
public String getName ( ) {
return "greeting service" ;
}
} ;
RetryPolicy retryPolicy = ( ) - > new RetryExecution ( ) {
int retryAttempts ;
@Override
public boolean shouldRetry ( Throwable throwable ) {
return ( this . retryAttempts + + < 3 & & throwable instanceof TechnicalException ) ;
return "always fails" ;
}
} ;
AtomicInteger retryCount = new AtomicInteger ( ) ;
// Custom RetryPolicy that only retries for a NumberFormatException and max 5 retry attempts.
RetryPolicy retryPolicy = ( ) - > throwable - > ( retryCount . incrementAndGet ( ) < = 5 & & throwable instanceof NumberFormatException ) ;
retryTemplate . setRetryPolicy ( retryPolicy ) ;
retryTemplate . setBackOffPolicy ( new FixedBackOff ( Duration . ofMillis ( 10 ) ) ) ;
assertThat ( invocationCount ) . hasValue ( 0 ) ;
assertThat ( retryCount ) . hasValue ( 0 ) ;
assertThatExceptionOfType ( RetryException . class )
. isThrownBy ( ( ) - > retryTemplate . execute ( retryable ) )
. withMessage ( "Retry policy for operation 'greeting service' exhausted; aborting execution" )
. withCause ( technicalException ) ;
. withMessage ( "Retry policy for operation 'always fails' exhausted; aborting execution" )
. withCause ( exception ) ;
// 6 = 1 initial invocation + 5 retry attempts
assertThat ( invocationCount ) . hasValue ( 6 ) ;
assertThat ( retryCount ) . hasValue ( 6 ) ;
}
}