3131import org .springframework .util .backoff .FixedBackOff ;
3232
3333/**
34- * A basic implementation of {@link RetryOperations} that uses a
35- * {@link RetryPolicy} and a {@link BackOff} to retry a
36- * {@link RetryCallback}. By default, the callback will be called
37- * 3 times with a fixed backoff of 1 second.
34+ * A basic implementation of {@link RetryOperations} that invokes and potentially
35+ * retries a {@link RetryCallback} based on a configured {@link RetryPolicy} and
36+ * {@link BackOff} policy.
3837 *
39- * <p>It is also possible to register a {@link RetryListener} to intercept and inject code
40- * during key retry phases (before a retry attempt, after a retry attempt, etc.).
38+ * <p>By default, a callback will be invoked at most 3 times with a fixed backoff
39+ * of 1 second.
40+ *
41+ * <p>A {@link RetryListener} can be {@linkplain #setRetryListener(RetryListener)
42+ * registered} to intercept and inject behavior during key retry phases (before a
43+ * retry attempt, after a retry attempt, etc.).
4144 *
4245 * <p>All retry operations performed by this class are logged at debug level,
43- * using "org.springframework.core.retry.RetryTemplate" as log category.
46+ * using {@code "org.springframework.core.retry.RetryTemplate"} as the log category.
4447 *
4548 * @author Mahmoud Ben Hassine
49+ * @author Sam Brannen
4650 * @since 7.0
4751 * @see RetryOperations
4852 * @see RetryPolicy
4953 * @see BackOff
5054 * @see RetryListener
55+ * @see RetryCallback
5156 */
5257public class RetryTemplate implements RetryOperations {
5358
5459 protected final LogAccessor logger = new LogAccessor (LogFactory .getLog (getClass ()));
5560
5661 protected RetryPolicy retryPolicy = new MaxRetryAttemptsPolicy ();
5762
58- protected BackOff backOffPolicy = new FixedBackOff ();
63+ protected BackOff backOffPolicy = new FixedBackOff (1000 , Long . MAX_VALUE );
5964
6065 protected RetryListener retryListener = new RetryListener () {
6166 };
6267
6368 /**
64- * Create a new retry template with default settings.
69+ * Create a new {@code RetryTemplate} with maximum 3 retry attempts and a
70+ * fixed backoff of 1 second.
6571 */
6672 public RetryTemplate () {
6773 }
6874
6975 /**
70- * Create a new retry template with a custom {@link RetryPolicy}.
76+ * Create a new {@code RetryTemplate} with a custom {@link RetryPolicy} and a
77+ * fixed backoff of 1 second.
7178 * @param retryPolicy the retry policy to use
7279 */
7380 public RetryTemplate (RetryPolicy retryPolicy ) {
74- Assert .notNull (retryPolicy , "Retry policy must not be null" );
81+ Assert .notNull (retryPolicy , "RetryPolicy must not be null" );
7582 this .retryPolicy = retryPolicy ;
7683 }
7784
7885 /**
79- * Create a new retry template with a custom {@link RetryPolicy} and {@link BackOff}.
86+ * Create a new {@code RetryTemplate} with a custom {@link RetryPolicy} and
87+ * {@link BackOff} policy.
8088 * @param retryPolicy the retry policy to use
8189 * @param backOffPolicy the backoff policy to use
8290 */
@@ -87,88 +95,98 @@ public RetryTemplate(RetryPolicy retryPolicy, BackOff backOffPolicy) {
8795 }
8896
8997 /**
90- * Set the {@link RetryPolicy} to use. Defaults to <code>MaxAttemptsRetryPolicy()</code>.
91- * @param retryPolicy the retry policy to use. Must not be <code>null</code>.
98+ * Set the {@link RetryPolicy} to use.
99+ * <p>Defaults to {@code new MaxRetryAttemptsPolicy()}.
100+ * @param retryPolicy the retry policy to use
101+ * @see MaxRetryAttemptsPolicy
92102 */
93103 public void setRetryPolicy (RetryPolicy retryPolicy ) {
94104 Assert .notNull (retryPolicy , "Retry policy must not be null" );
95105 this .retryPolicy = retryPolicy ;
96106 }
97107
98108 /**
99- * Set the {@link BackOff} to use. Defaults to <code>FixedBackOffPolicy(Duration.ofSeconds(1))</code>.
100- * @param backOffPolicy the backoff policy to use. Must not be <code>null</code>.
109+ * Set the {@link BackOff} policy to use.
110+ * <p>Defaults to {@code new FixedBackOff(1000, Long.MAX_VALUE))}.
111+ * @param backOffPolicy the backoff policy to use
112+ * @see FixedBackOff
101113 */
102114 public void setBackOffPolicy (BackOff backOffPolicy ) {
103115 Assert .notNull (backOffPolicy , "BackOff policy must not be null" );
104116 this .backOffPolicy = backOffPolicy ;
105117 }
106118
107119 /**
108- * Set the {@link RetryListener} to use. Defaults to a <code>NoOp</code> implementation.
109- * If multiple listeners are needed, use a {@link CompositeRetryListener}.
110- * @param retryListener the retry listener to use. Must not be <code>null</code>.
120+ * Set the {@link RetryListener} to use.
121+ * <p>If multiple listeners are needed, use a {@link CompositeRetryListener}.
122+ * <p>Defaults to a <em>no-op</em> implementation.
123+ * @param retryListener the retry listener to use
111124 */
112125 public void setRetryListener (RetryListener retryListener ) {
113126 Assert .notNull (retryListener , "Retry listener must not be null" );
114127 this .retryListener = retryListener ;
115128 }
116129
117130 /**
118- * Call the retry callback according to the configured retry and backoff policies.
119- * If the callback succeeds, its result is returned. Otherwise, a {@link RetryException}
120- * will be thrown to the caller having all attempt exceptions as suppressed exceptions.
131+ * Execute the supplied {@link RetryCallback} according to the configured
132+ * retry and backoff policies.
133+ * <p>If the callback succeeds, its result will be returned. Otherwise, a
134+ * {@link RetryException} will be thrown to the caller.
121135 * @param retryCallback the callback to call initially and retry if needed
122136 * @param <R> the type of the result
123- * @return the result of the callback if any
124- * @throws RetryException thrown if the retry policy is exhausted. All attempt exceptions
125- * are added as suppressed exceptions to the final exception.
137+ * @return the result of the callback, if any
138+ * @throws RetryException if the {@code RetryPolicy} is exhausted; exceptions
139+ * encountered during retry attempts are available as suppressed exceptions
126140 */
127141 @ Override
128142 public <R extends @ Nullable Object > R execute (RetryCallback <R > retryCallback ) throws RetryException {
129- Assert .notNull (retryCallback , "Retry Callback must not be null" );
130143 String callbackName = retryCallback .getName ();
131- // initial attempt
144+ // Initial attempt
132145 try {
133- logger .debug (() -> "About to execute callback '" + callbackName + "'" );
146+ logger .debug (() -> "Preparing to execute callback '" + callbackName + "'" );
134147 R result = retryCallback .run ();
135- logger .debug (() -> "Callback '" + callbackName + "' executed successfully" );
148+ logger .debug (() -> "Callback '" + callbackName + "' completed successfully" );
136149 return result ;
137150 }
138151 catch (Throwable initialException ) {
139- logger .debug (initialException , () -> "Execution of callback '" + callbackName + "' failed, initiating the retry process" );
140- // retry process starts here
152+ logger .debug (initialException ,
153+ () -> "Execution of callback '" + callbackName + "' failed; initiating the retry process" );
154+ // Retry process starts here
141155 RetryExecution retryExecution = this .retryPolicy .start ();
142156 BackOffExecution backOffExecution = this .backOffPolicy .start ();
143157 List <Throwable > suppressedExceptions = new ArrayList <>();
144158
145159 Throwable retryException = initialException ;
146160 while (retryExecution .shouldRetry (retryException )) {
147- logger .debug (() -> "About to retry callback '" + callbackName + "'" );
161+ logger .debug (() -> "Preparing to retry callback '" + callbackName + "'" );
148162 try {
149163 this .retryListener .beforeRetry (retryExecution );
150164 R result = retryCallback .run ();
151165 this .retryListener .onRetrySuccess (retryExecution , result );
152- logger .debug (() -> "Callback '" + callbackName + "' retried successfully" );
166+ logger .debug (() -> "Callback '" + callbackName + "' completed successfully after retry " );
153167 return result ;
154168 }
155169 catch (Throwable currentAttemptException ) {
156170 this .retryListener .onRetryFailure (retryExecution , currentAttemptException );
157171 try {
158172 long duration = backOffExecution .nextBackOff ();
159- logger .debug (() -> "Retry callback '" + callbackName + "' failed for " + currentAttemptException .getMessage () + ", backing off for " + duration + "ms" );
173+ logger .debug (() -> "Retry callback '" + callbackName + "' failed due to '" +
174+ currentAttemptException .getMessage () + "'; backing off for " + duration + "ms" );
160175 Thread .sleep (duration );
161176 }
162177 catch (InterruptedException interruptedException ) {
163178 Thread .currentThread ().interrupt ();
164- throw new RetryException ("Unable to backoff for retry callback '" + callbackName + "'" , interruptedException );
179+ throw new RetryException ("Unable to back off for retry callback '" + callbackName + "'" ,
180+ interruptedException );
165181 }
166182 suppressedExceptions .add (currentAttemptException );
167183 retryException = currentAttemptException ;
168184 }
169185 }
170- // retry policy exhausted at this point, throwing a RetryException with the initial exception as cause and remaining attempts exceptions as suppressed
171- RetryException finalException = new RetryException ("Retry policy for callback '" + callbackName + "' exhausted, aborting execution" , initialException );
186+ // The RetryPolicy has exhausted at this point, so we throw a RetryException with the
187+ // initial exception as the cause and remaining exceptions as suppressed exceptions.
188+ RetryException finalException = new RetryException ("Retry policy for callback '" + callbackName +
189+ "' exhausted; aborting execution" , initialException );
172190 suppressedExceptions .forEach (finalException ::addSuppressed );
173191 this .retryListener .onRetryPolicyExhaustion (retryExecution , finalException );
174192 throw finalException ;
0 commit comments