From 51b6e8cc9f439c981b7dba57654c05f41685c71b Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Tue, 10 Jun 2025 18:26:52 +0200 Subject: [PATCH] Rename RetryCallback to Retryable See gh-34716 --- .../core/retry/RetryListener.java | 6 +-- .../core/retry/RetryOperations.java | 12 ++--- .../core/retry/RetryTemplate.java | 54 ++++++++++--------- .../{RetryCallback.java => Retryable.java} | 18 ++++--- .../core/retry/RetryTemplateTests.java | 16 +++--- 5 files changed, 56 insertions(+), 50 deletions(-) rename spring-core/src/main/java/org/springframework/core/retry/{RetryCallback.java => Retryable.java} (68%) diff --git a/spring-core/src/main/java/org/springframework/core/retry/RetryListener.java b/spring-core/src/main/java/org/springframework/core/retry/RetryListener.java index 9586e778fd..107c818583 100644 --- a/spring-core/src/main/java/org/springframework/core/retry/RetryListener.java +++ b/spring-core/src/main/java/org/springframework/core/retry/RetryListener.java @@ -40,7 +40,7 @@ public interface RetryListener { /** * Called after the first successful retry attempt. * @param retryExecution the retry execution - * @param result the result of the callback + * @param result the result of the {@link Retryable} */ default void onRetrySuccess(RetryExecution retryExecution, Object result) { } @@ -48,7 +48,7 @@ public interface RetryListener { /** * Called every time a retry attempt fails. * @param retryExecution the retry execution - * @param throwable the exception thrown by the callback + * @param throwable the exception thrown by the {@link Retryable} */ default void onRetryFailure(RetryExecution retryExecution, Throwable throwable) { } @@ -56,7 +56,7 @@ public interface RetryListener { /** * Called if the {@link RetryPolicy} is exhausted. * @param retryExecution the retry execution - * @param throwable the last exception thrown by the {@link RetryCallback} + * @param throwable the last exception thrown by the {@link Retryable} */ default void onRetryPolicyExhaustion(RetryExecution retryExecution, Throwable throwable) { } diff --git a/spring-core/src/main/java/org/springframework/core/retry/RetryOperations.java b/spring-core/src/main/java/org/springframework/core/retry/RetryOperations.java index a731b8a357..c72c6f05e8 100644 --- a/spring-core/src/main/java/org/springframework/core/retry/RetryOperations.java +++ b/spring-core/src/main/java/org/springframework/core/retry/RetryOperations.java @@ -31,16 +31,16 @@ import org.jspecify.annotations.Nullable; public interface RetryOperations { /** - * Execute the given callback (according to the {@link RetryPolicy} configured - * at the implementation level) until it succeeds, or eventually throw an - * exception if the {@code RetryPolicy} is exhausted. - * @param retryCallback the callback to call initially and retry if needed + * Execute the given {@link Retryable} (according to the {@link RetryPolicy} + * configured at the implementation level) until it succeeds, or eventually + * throw an exception if the {@code RetryPolicy} is exhausted. + * @param retryable the {@code Retryable} to execute and retry if needed * @param the type of the result - * @return the result of the callback, if any + * @return the result of the {@code Retryable}, if any * @throws RetryException if the {@code RetryPolicy} is exhausted; exceptions * encountered during retry attempts should be made available as suppressed * exceptions */ - R execute(RetryCallback retryCallback) throws RetryException; + R execute(Retryable retryable) throws RetryException; } 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 1285fe8480..9506266db3 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 @@ -32,11 +32,11 @@ import org.springframework.util.backoff.FixedBackOff; /** * A basic implementation of {@link RetryOperations} that invokes and potentially - * retries a {@link RetryCallback} based on a configured {@link RetryPolicy} and - * {@link BackOff} policy. + * retries a {@link Retryable} operation based on a configured {@link RetryPolicy} + * and {@link BackOff} policy. * - *

By default, a callback will be invoked at most 3 times with a fixed backoff - * of 1 second. + *

By default, a retryable operation will be invoked at most 3 times with a + * fixed backoff of 1 second. * *

A {@link RetryListener} can be {@linkplain #setRetryListener(RetryListener) * registered} to intercept and inject behavior during key retry phases (before a @@ -52,7 +52,7 @@ import org.springframework.util.backoff.FixedBackOff; * @see RetryPolicy * @see BackOff * @see RetryListener - * @see RetryCallback + * @see Retryable */ public class RetryTemplate implements RetryOperations { @@ -128,29 +128,30 @@ public class RetryTemplate implements RetryOperations { } /** - * Execute the supplied {@link RetryCallback} according to the configured - * retry and backoff policies. - *

If the callback succeeds, its result will be returned. Otherwise, a - * {@link RetryException} will be thrown to the caller. - * @param retryCallback the callback to call initially and retry if needed + * Execute the supplied {@link Retryable} according to the configured retry + * and backoff policies. + *

If the {@code Retryable} succeeds, its result will be returned. Otherwise, + * a {@link RetryException} will be thrown to the caller. + * @param retryable the {@code Retryable} to execute and retry if needed * @param the type of the result - * @return the result of the callback, if any + * @return the result of the {@code Retryable}, if any * @throws RetryException if the {@code RetryPolicy} is exhausted; exceptions * encountered during retry attempts are available as suppressed exceptions */ @Override - public R execute(RetryCallback retryCallback) throws RetryException { - String callbackName = retryCallback.getName(); + public R execute(Retryable retryable) throws RetryException { + String retryableName = retryable.getName(); // Initial attempt try { - logger.debug(() -> "Preparing to execute callback '" + callbackName + "'"); - R result = retryCallback.run(); - logger.debug(() -> "Callback '" + callbackName + "' completed successfully"); + logger.debug(() -> "Preparing to execute retryable operation '%s'".formatted(retryableName)); + R result = retryable.run(); + logger.debug(() -> "Retryable operation '%s' completed successfully".formatted(retryableName)); return result; } catch (Throwable initialException) { logger.debug(initialException, - () -> "Execution of callback '" + callbackName + "' failed; initiating the retry process"); + () -> "Execution of retryable operation '%s' failed; initiating the retry process" + .formatted(retryableName)); // Retry process starts here RetryExecution retryExecution = this.retryPolicy.start(); BackOffExecution backOffExecution = this.backOffPolicy.start(); @@ -158,25 +159,27 @@ public class RetryTemplate implements RetryOperations { Throwable retryException = initialException; while (retryExecution.shouldRetry(retryException)) { - logger.debug(() -> "Preparing to retry callback '" + callbackName + "'"); + logger.debug(() -> "Preparing to retry operation '%s'".formatted(retryableName)); try { this.retryListener.beforeRetry(retryExecution); - R result = retryCallback.run(); + R result = retryable.run(); this.retryListener.onRetrySuccess(retryExecution, result); - logger.debug(() -> "Callback '" + callbackName + "' completed successfully after retry"); + logger.debug(() -> "Retryable operation '%s' completed successfully after retry" + .formatted(retryableName)); return result; } catch (Throwable currentAttemptException) { this.retryListener.onRetryFailure(retryExecution, currentAttemptException); try { long duration = backOffExecution.nextBackOff(); - logger.debug(() -> "Retry callback '" + callbackName + "' failed due to '" + - currentAttemptException.getMessage() + "'; backing off for " + duration + "ms"); + logger.debug(() -> "Retryable operation '%s' failed due to '%s'; backing off for %dms" + .formatted(retryableName, currentAttemptException.getMessage(), duration)); Thread.sleep(duration); } catch (InterruptedException interruptedException) { Thread.currentThread().interrupt(); - throw new RetryException("Unable to back off for retry callback '" + callbackName + "'", + throw new RetryException( + "Unable to back off for retryable operation '%s'".formatted(retryableName), interruptedException); } suppressedExceptions.add(currentAttemptException); @@ -185,8 +188,9 @@ public class RetryTemplate implements RetryOperations { } // The RetryPolicy has exhausted at this point, so we throw a RetryException with the // initial exception as the cause and remaining exceptions as suppressed exceptions. - RetryException finalException = new RetryException("Retry policy for callback '" + callbackName + - "' exhausted; aborting execution", initialException); + RetryException finalException = new RetryException( + "Retry policy for operation '%s' exhausted; aborting execution".formatted(retryableName), + initialException); suppressedExceptions.forEach(finalException::addSuppressed); this.retryListener.onRetryPolicyExhaustion(retryExecution, finalException); throw finalException; diff --git a/spring-core/src/main/java/org/springframework/core/retry/RetryCallback.java b/spring-core/src/main/java/org/springframework/core/retry/Retryable.java similarity index 68% rename from spring-core/src/main/java/org/springframework/core/retry/RetryCallback.java rename to spring-core/src/main/java/org/springframework/core/retry/Retryable.java index 05463440a1..f90f294018 100644 --- a/spring-core/src/main/java/org/springframework/core/retry/RetryCallback.java +++ b/spring-core/src/main/java/org/springframework/core/retry/Retryable.java @@ -17,30 +17,32 @@ package org.springframework.core.retry; /** - * Callback interface for a retryable block of code. + * {@code Retryable} is a functional interface that can be used to implement any + * generic block of code that can potentially be retried. * *

Used in conjunction with {@link RetryOperations}. * * @author Mahmoud Ben Hassine + * @author Sam Brannen * @since 7.0 * @param the type of the result * @see RetryOperations */ @FunctionalInterface -public interface RetryCallback { +public interface Retryable { /** * Method to execute and retry if needed. - * @return the result of the callback - * @throws Throwable if an error occurs during the execution of the callback + * @return the result of the operation + * @throws Throwable if an error occurs during the execution of the operation */ R run() throws Throwable; /** - * A unique, logical name for this callback, used to distinguish retries for - * different business operations. - *

Defaults to the fully-qualified class name. - * @return the name of the callback + * A unique, logical name for this retryable operation, used to distinguish + * between retries for different business operations. + *

Defaults to the fully-qualified class name of the implementation class. + * @return the name of this retryable operation */ default String getName() { return getClass().getName(); diff --git a/spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java b/spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java index 1e44f37d78..565bea4050 100644 --- a/spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java +++ b/spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java @@ -37,7 +37,7 @@ class RetryTemplateTests { @Test void retryWithSuccess() throws Exception { - RetryCallback retryCallback = new RetryCallback<>() { + Retryable retryable = new Retryable<>() { int failure; @@ -57,14 +57,14 @@ class RetryTemplateTests { retryTemplate.setBackOffPolicy(new FixedBackOff(100, Long.MAX_VALUE)); - assertThat(retryTemplate.execute(retryCallback)).isEqualTo("hello world"); + assertThat(retryTemplate.execute(retryable)).isEqualTo("hello world"); } @Test void retryWithFailure() { Exception exception = new Exception("Error while invoking greeting service"); - RetryCallback retryCallback = new RetryCallback<>() { + Retryable retryable = new Retryable<>() { @Override public String run() throws Exception { throw exception; @@ -79,8 +79,8 @@ class RetryTemplateTests { retryTemplate.setBackOffPolicy(new FixedBackOff(100, Long.MAX_VALUE)); assertThatExceptionOfType(RetryException.class) - .isThrownBy(() -> retryTemplate.execute(retryCallback)) - .withMessage("Retry policy for callback 'greeting service' exhausted; aborting execution") + .isThrownBy(() -> retryTemplate.execute(retryable)) + .withMessage("Retry policy for operation 'greeting service' exhausted; aborting execution") .withCause(exception); } @@ -96,7 +96,7 @@ class RetryTemplateTests { TechnicalException technicalException = new TechnicalException("Error while invoking greeting service"); - RetryCallback retryCallback = new RetryCallback<>() { + Retryable retryable = new Retryable<>() { @Override public String run() throws TechnicalException { throw technicalException; @@ -126,8 +126,8 @@ class RetryTemplateTests { retryTemplate.setBackOffPolicy(new FixedBackOff(100, Long.MAX_VALUE)); assertThatExceptionOfType(RetryException.class) - .isThrownBy(() -> retryTemplate.execute(retryCallback)) - .withMessage("Retry policy for callback 'greeting service' exhausted; aborting execution") + .isThrownBy(() -> retryTemplate.execute(retryable)) + .withMessage("Retry policy for operation 'greeting service' exhausted; aborting execution") .withCause(technicalException); }