diff --git a/spring-core/src/main/java/org/springframework/core/retry/RetryException.java b/spring-core/src/main/java/org/springframework/core/retry/RetryException.java index eef16f68ef1..9a862d543d2 100644 --- a/spring-core/src/main/java/org/springframework/core/retry/RetryException.java +++ b/spring-core/src/main/java/org/springframework/core/retry/RetryException.java @@ -22,13 +22,22 @@ import java.util.Objects; /** * Exception thrown when a {@link RetryPolicy} has been exhausted. * - *

A {@code RetryException} will contain the last exception thrown by the - * {@link Retryable} operation as the {@linkplain #getCause() cause} and any - * exceptions from previous attempts as {@linkplain #getSuppressed() suppressed + *

A {@code RetryException} will typically contain the last exception thrown + * by the {@link Retryable} operation as the {@linkplain #getCause() cause} and + * any exceptions from previous attempts as {@linkplain #getSuppressed() suppressed * exceptions}. * + *

However, if an {@link InterruptedException} is encountered while + * {@linkplain Thread#sleep(long) sleeping} for the current + * {@link org.springframework.util.backoff.BackOff BackOff} duration, a + * {@code RetryException} will contain the {@code InterruptedException} as the + * {@linkplain #getCause() cause} and any exceptions from previous attempts to + * invoke the {@code Retryable} operation as {@linkplain #getSuppressed() + * suppressed exceptions}. + * * @author Mahmoud Ben Hassine * @author Juergen Hoeller + * @author Sam Brannen * @since 7.0 * @see RetryOperations */ @@ -41,7 +50,9 @@ public class RetryException extends Exception { /** * Create a new {@code RetryException} for the supplied message and cause. * @param message the detail message - * @param cause the last exception thrown by the {@link Retryable} operation + * @param cause the last exception thrown by the {@link Retryable} operation, + * or an {@link InterruptedException} thrown while sleeping for the current + * {@code BackOff} duration */ public RetryException(String message, Throwable cause) { super(message, Objects.requireNonNull(cause, "cause must not be null")); @@ -49,7 +60,9 @@ public class RetryException extends Exception { /** - * Get the last exception thrown by the {@link Retryable} operation. + * Get the last exception thrown by the {@link Retryable} operation, or an + * {@link InterruptedException} thrown while sleeping for the current + * {@code BackOff} duration. */ @Override public final Throwable getCause() { diff --git a/spring-core/src/main/java/org/springframework/core/retry/RetryTemplate.java b/spring-core/src/main/java/org/springframework/core/retry/RetryTemplate.java index 74be5041832..bf55b724c67 100644 --- a/spring-core/src/main/java/org/springframework/core/retry/RetryTemplate.java +++ b/spring-core/src/main/java/org/springframework/core/retry/RetryTemplate.java @@ -164,9 +164,11 @@ public class RetryTemplate implements RetryOperations { } catch (InterruptedException interruptedException) { Thread.currentThread().interrupt(); - throw new RetryException( + RetryException retryException = new RetryException( "Unable to back off for retryable operation '%s'".formatted(retryableName), interruptedException); + exceptions.forEach(retryException::addSuppressed); + throw retryException; } logger.debug(() -> "Preparing to retry operation '%s'".formatted(retryableName)); try {