31
31
import org .springframework .util .backoff .FixedBackOff ;
32
32
33
33
/**
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.
38
37
*
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.).
41
44
*
42
45
* <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.
44
47
*
45
48
* @author Mahmoud Ben Hassine
49
+ * @author Sam Brannen
46
50
* @since 7.0
47
51
* @see RetryOperations
48
52
* @see RetryPolicy
49
53
* @see BackOff
50
54
* @see RetryListener
55
+ * @see RetryCallback
51
56
*/
52
57
public class RetryTemplate implements RetryOperations {
53
58
54
59
protected final LogAccessor logger = new LogAccessor (LogFactory .getLog (getClass ()));
55
60
56
61
protected RetryPolicy retryPolicy = new MaxRetryAttemptsPolicy ();
57
62
58
- protected BackOff backOffPolicy = new FixedBackOff ();
63
+ protected BackOff backOffPolicy = new FixedBackOff (1000 , Long . MAX_VALUE );
59
64
60
65
protected RetryListener retryListener = new RetryListener () {
61
66
};
62
67
63
68
/**
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.
65
71
*/
66
72
public RetryTemplate () {
67
73
}
68
74
69
75
/**
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.
71
78
* @param retryPolicy the retry policy to use
72
79
*/
73
80
public RetryTemplate (RetryPolicy retryPolicy ) {
74
- Assert .notNull (retryPolicy , "Retry policy must not be null" );
81
+ Assert .notNull (retryPolicy , "RetryPolicy must not be null" );
75
82
this .retryPolicy = retryPolicy ;
76
83
}
77
84
78
85
/**
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.
80
88
* @param retryPolicy the retry policy to use
81
89
* @param backOffPolicy the backoff policy to use
82
90
*/
@@ -87,88 +95,98 @@ public RetryTemplate(RetryPolicy retryPolicy, BackOff backOffPolicy) {
87
95
}
88
96
89
97
/**
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
92
102
*/
93
103
public void setRetryPolicy (RetryPolicy retryPolicy ) {
94
104
Assert .notNull (retryPolicy , "Retry policy must not be null" );
95
105
this .retryPolicy = retryPolicy ;
96
106
}
97
107
98
108
/**
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
101
113
*/
102
114
public void setBackOffPolicy (BackOff backOffPolicy ) {
103
115
Assert .notNull (backOffPolicy , "BackOff policy must not be null" );
104
116
this .backOffPolicy = backOffPolicy ;
105
117
}
106
118
107
119
/**
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
111
124
*/
112
125
public void setRetryListener (RetryListener retryListener ) {
113
126
Assert .notNull (retryListener , "Retry listener must not be null" );
114
127
this .retryListener = retryListener ;
115
128
}
116
129
117
130
/**
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.
121
135
* @param retryCallback the callback to call initially and retry if needed
122
136
* @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
126
140
*/
127
141
@ Override
128
142
public <R extends @ Nullable Object > R execute (RetryCallback <R > retryCallback ) throws RetryException {
129
- Assert .notNull (retryCallback , "Retry Callback must not be null" );
130
143
String callbackName = retryCallback .getName ();
131
- // initial attempt
144
+ // Initial attempt
132
145
try {
133
- logger .debug (() -> "About to execute callback '" + callbackName + "'" );
146
+ logger .debug (() -> "Preparing to execute callback '" + callbackName + "'" );
134
147
R result = retryCallback .run ();
135
- logger .debug (() -> "Callback '" + callbackName + "' executed successfully" );
148
+ logger .debug (() -> "Callback '" + callbackName + "' completed successfully" );
136
149
return result ;
137
150
}
138
151
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
141
155
RetryExecution retryExecution = this .retryPolicy .start ();
142
156
BackOffExecution backOffExecution = this .backOffPolicy .start ();
143
157
List <Throwable > suppressedExceptions = new ArrayList <>();
144
158
145
159
Throwable retryException = initialException ;
146
160
while (retryExecution .shouldRetry (retryException )) {
147
- logger .debug (() -> "About to retry callback '" + callbackName + "'" );
161
+ logger .debug (() -> "Preparing to retry callback '" + callbackName + "'" );
148
162
try {
149
163
this .retryListener .beforeRetry (retryExecution );
150
164
R result = retryCallback .run ();
151
165
this .retryListener .onRetrySuccess (retryExecution , result );
152
- logger .debug (() -> "Callback '" + callbackName + "' retried successfully" );
166
+ logger .debug (() -> "Callback '" + callbackName + "' completed successfully after retry " );
153
167
return result ;
154
168
}
155
169
catch (Throwable currentAttemptException ) {
156
170
this .retryListener .onRetryFailure (retryExecution , currentAttemptException );
157
171
try {
158
172
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" );
160
175
Thread .sleep (duration );
161
176
}
162
177
catch (InterruptedException interruptedException ) {
163
178
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 );
165
181
}
166
182
suppressedExceptions .add (currentAttemptException );
167
183
retryException = currentAttemptException ;
168
184
}
169
185
}
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 );
172
190
suppressedExceptions .forEach (finalException ::addSuppressed );
173
191
this .retryListener .onRetryPolicyExhaustion (retryExecution , finalException );
174
192
throw finalException ;
0 commit comments