Skip to content

Commit 41b7df8

Browse files
authored
Introduce java.time.Duration
* Introduce `java.time.Duration` variants into `RetryTemplateBuilder` * Fix non-null assertion messages
1 parent daa04db commit 41b7df8

File tree

2 files changed

+208
-5
lines changed

2 files changed

+208
-5
lines changed

src/main/java/org/springframework/retry/support/RetryTemplateBuilder.java

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.retry.support;
1717

18+
import java.time.Duration;
1819
import java.util.ArrayList;
1920
import java.util.List;
2021

@@ -83,6 +84,7 @@
8384
* @author Aleksandr Shamukov
8485
* @author Artem Bilan
8586
* @author Kim In Hoi
87+
* @author Andreas Ahlenstorf
8688
* @since 1.3
8789
*/
8890
public class RetryTemplateBuilder {
@@ -122,16 +124,43 @@ public RetryTemplateBuilder maxAttempts(int maxAttempts) {
122124
* @param timeout whole execution timeout in milliseconds
123125
* @return this
124126
* @see TimeoutRetryPolicy
127+
* @deprecated Use {@link #withTimeout(long)} instead.
125128
*/
129+
@Deprecated(since = "2.0.2", forRemoval = true)
126130
public RetryTemplateBuilder withinMillis(long timeout) {
127-
Assert.isTrue(timeout > 0, "Timeout should be positive");
131+
return withTimeout(timeout);
132+
}
133+
134+
/**
135+
* Retry until {@code timeout} has passed since the initial attempt.
136+
* @param timeoutMillis timeout in milliseconds
137+
* @return this
138+
* @see TimeoutRetryPolicy
139+
* @throws IllegalArgumentException if timeout is {@literal <=} 0 or if another retry
140+
* policy has already been configured.
141+
* @since 2.0.2
142+
*/
143+
public RetryTemplateBuilder withTimeout(long timeoutMillis) {
144+
Assert.isTrue(timeoutMillis > 0, "timeoutMillis should be greater than 0");
128145
Assert.isNull(this.baseRetryPolicy, "You have already selected another retry policy");
129-
TimeoutRetryPolicy timeoutRetryPolicy = new TimeoutRetryPolicy();
130-
timeoutRetryPolicy.setTimeout(timeout);
131-
this.baseRetryPolicy = timeoutRetryPolicy;
146+
this.baseRetryPolicy = new TimeoutRetryPolicy(timeoutMillis);
132147
return this;
133148
}
134149

150+
/**
151+
* Retry until {@code timeout} has passed since the initial attempt.
152+
* @param timeout duration for how long retries should be attempted
153+
* @return this
154+
* @see TimeoutRetryPolicy
155+
* @throws IllegalArgumentException if timeout is {@code null} or 0, or if another
156+
* retry policy has already been configured.
157+
* @since 2.0.2
158+
*/
159+
public RetryTemplateBuilder withTimeout(Duration timeout) {
160+
Assert.notNull(timeout, "timeout must not be null");
161+
return withTimeout(timeout.toMillis());
162+
}
163+
135164
/**
136165
* Allows infinite retry, do not limit attempts by number or time.
137166
* <p>
@@ -180,6 +209,27 @@ public RetryTemplateBuilder exponentialBackoff(long initialInterval, double mult
180209
return exponentialBackoff(initialInterval, multiplier, maxInterval, false);
181210
}
182211

212+
/**
213+
* Use exponential backoff policy. The formula of backoff period:
214+
* <p>
215+
* {@code currentInterval = Math.min(initialInterval * Math.pow(multiplier, retryNum), maxInterval)}
216+
* <p>
217+
* (for first attempt retryNum = 0)
218+
* @param initialInterval initial sleep duration
219+
* @param multiplier backoff interval multiplier
220+
* @param maxInterval maximum backoff duration
221+
* @return this
222+
* @see ExponentialBackOffPolicy
223+
* @throws IllegalArgumentException if initialInterval is {@code null}, multiplier is
224+
* {@literal <=} 1, or if maxInterval is {@code null}
225+
* @since 2.0.2
226+
*/
227+
public RetryTemplateBuilder exponentialBackoff(Duration initialInterval, double multiplier, Duration maxInterval) {
228+
Assert.notNull(initialInterval, "initialInterval must not be null");
229+
Assert.notNull(maxInterval, "maxInterval must not be null");
230+
return exponentialBackoff(initialInterval.toMillis(), multiplier, maxInterval.toMillis(), false);
231+
}
232+
183233
/**
184234
* Use exponential backoff policy. The formula of backoff period (without randomness):
185235
* <p>
@@ -210,6 +260,31 @@ public RetryTemplateBuilder exponentialBackoff(long initialInterval, double mult
210260
return this;
211261
}
212262

263+
/**
264+
* Use exponential backoff policy. The formula of backoff period (without randomness):
265+
* <p>
266+
* {@code currentInterval = Math.min(initialInterval * Math.pow(multiplier, retryNum), maxInterval)}
267+
* <p>
268+
* (for first attempt retryNum = 0)
269+
* @param initialInterval initial sleep duration
270+
* @param multiplier backoff interval multiplier
271+
* @param maxInterval maximum backoff duration
272+
* @param withRandom adds some randomness to backoff intervals. For details, see
273+
* {@link ExponentialRandomBackOffPolicy}
274+
* @return this
275+
* @see ExponentialBackOffPolicy
276+
* @see ExponentialRandomBackOffPolicy
277+
* @throws IllegalArgumentException if initialInterval is {@code null}, multiplier is
278+
* {@literal <=} 1, or maxInterval is {@code null}
279+
* @since 2.0.2
280+
*/
281+
public RetryTemplateBuilder exponentialBackoff(Duration initialInterval, double multiplier, Duration maxInterval,
282+
boolean withRandom) {
283+
Assert.notNull(initialInterval, "initialInterval most not be null");
284+
Assert.notNull(maxInterval, "maxInterval must not be null");
285+
return this.exponentialBackoff(initialInterval.toMillis(), multiplier, maxInterval.toMillis(), withRandom);
286+
}
287+
213288
/**
214289
* Perform each retry after fixed amount of time.
215290
* @param interval fixed interval in milliseconds
@@ -225,6 +300,24 @@ public RetryTemplateBuilder fixedBackoff(long interval) {
225300
return this;
226301
}
227302

303+
/**
304+
* Perform each retry after fixed amount of time.
305+
* @param interval fixed backoff duration
306+
* @return this
307+
* @see FixedBackOffPolicy
308+
* @throws IllegalArgumentException if another backoff policy has already been
309+
* configured, interval is {@code null} or less than 1 millisecond
310+
* @since 2.0.2
311+
*/
312+
public RetryTemplateBuilder fixedBackoff(Duration interval) {
313+
Assert.notNull(interval, "interval must not be null");
314+
315+
long millis = interval.toMillis();
316+
Assert.isTrue(millis >= 1, "interval is less than 1 millisecond");
317+
318+
return this.fixedBackoff(millis);
319+
}
320+
228321
/**
229322
* Use {@link UniformRandomBackOffPolicy}, see it's doc for details.
230323
* @param minInterval in milliseconds
@@ -244,6 +337,23 @@ public RetryTemplateBuilder uniformRandomBackoff(long minInterval, long maxInter
244337
return this;
245338
}
246339

340+
/**
341+
* Use {@link UniformRandomBackOffPolicy}.
342+
* @param minInterval minimum backoff duration
343+
* @param maxInterval maximum backoff duration
344+
* @return this
345+
* @see UniformRandomBackOffPolicy
346+
* @throws IllegalArgumentException if minInterval is {@code null} or {@literal <} 1,
347+
* maxInterval is {@code null} or {@literal <} 1, maxInterval {@literal >=}
348+
* minInterval or if another backoff policy has already been configured.
349+
* @since 2.0.2
350+
*/
351+
public RetryTemplateBuilder uniformRandomBackoff(Duration minInterval, Duration maxInterval) {
352+
Assert.notNull(minInterval, "minInterval must not be null");
353+
Assert.notNull(maxInterval, "maxInterval must not be null");
354+
return this.uniformRandomBackoff(minInterval.toMillis(), maxInterval.toMillis());
355+
}
356+
247357
/**
248358
* Do not pause between attempts, retry immediately.
249359
* @return this

src/test/java/org/springframework/retry/support/RetryTemplateBuilderTests.java

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.io.FileNotFoundException;
2020
import java.io.IOException;
21+
import java.time.Duration;
2122
import java.util.Arrays;
2223
import java.util.Collections;
2324
import java.util.List;
@@ -29,6 +30,7 @@
2930
import org.springframework.retry.RetryPolicy;
3031
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
3132
import org.springframework.retry.backoff.ExponentialRandomBackOffPolicy;
33+
import org.springframework.retry.backoff.FixedBackOffPolicy;
3234
import org.springframework.retry.backoff.NoBackOffPolicy;
3335
import org.springframework.retry.backoff.UniformRandomBackOffPolicy;
3436
import org.springframework.retry.policy.AlwaysRetryPolicy;
@@ -53,6 +55,7 @@
5355
* @author Aleksandr Shamukov
5456
* @author Kim In Hoi
5557
* @author Gary Russell
58+
* @author Andreas Ahlenstorf
5659
*/
5760
public class RetryTemplateBuilderTests {
5861

@@ -113,10 +116,11 @@ public void testBasicCustomization() {
113116
@Test
114117
public void testFailOnRetryPoliciesConflict() {
115118
assertThatIllegalArgumentException()
116-
.isThrownBy(() -> RetryTemplate.builder().maxAttempts(3).withinMillis(1000).build());
119+
.isThrownBy(() -> RetryTemplate.builder().maxAttempts(3).withTimeout(1000).build());
117120
}
118121

119122
@Test
123+
@SuppressWarnings("removal")
120124
public void testTimeoutPolicy() {
121125
RetryTemplate template = RetryTemplate.builder().withinMillis(10000).build();
122126

@@ -127,6 +131,28 @@ public void testTimeoutPolicy() {
127131
assertThat(((TimeoutRetryPolicy) policyTuple.baseRetryPolicy).getTimeout()).isEqualTo(10000);
128132
}
129133

134+
@Test
135+
public void testTimeoutMillis() {
136+
RetryTemplate template = RetryTemplate.builder().withTimeout(10000).build();
137+
138+
PolicyTuple policyTuple = PolicyTuple.extractWithAsserts(template);
139+
assertDefaultClassifier(policyTuple);
140+
141+
assertThat(policyTuple.baseRetryPolicy).isInstanceOf(TimeoutRetryPolicy.class);
142+
assertThat(((TimeoutRetryPolicy) policyTuple.baseRetryPolicy).getTimeout()).isEqualTo(10000);
143+
}
144+
145+
@Test
146+
public void testTimeoutDuration() {
147+
RetryTemplate template = RetryTemplate.builder().withTimeout(Duration.ofSeconds(3)).build();
148+
149+
PolicyTuple policyTuple = PolicyTuple.extractWithAsserts(template);
150+
assertDefaultClassifier(policyTuple);
151+
152+
assertThat(policyTuple.baseRetryPolicy).isInstanceOf(TimeoutRetryPolicy.class);
153+
assertThat(((TimeoutRetryPolicy) policyTuple.baseRetryPolicy).getTimeout()).isEqualTo(3000);
154+
}
155+
130156
@Test
131157
public void testInfiniteRetry() {
132158
RetryTemplate template = RetryTemplate.builder().infiniteRetry().build();
@@ -190,24 +216,91 @@ public void testFailOnBackOffPolicyConflict() {
190216
.isThrownBy(() -> RetryTemplate.builder().noBackoff().fixedBackoff(1000).build());
191217
}
192218

219+
@Test
220+
public void testFixedBackoff() {
221+
RetryTemplate template = RetryTemplate.builder().fixedBackoff(200).build();
222+
FixedBackOffPolicy policy = getPropertyValue(template, "backOffPolicy", FixedBackOffPolicy.class);
223+
224+
assertThat(policy.getBackOffPeriod()).isEqualTo(200);
225+
}
226+
227+
@Test
228+
public void testFixedBackoffDuration() {
229+
RetryTemplate template = RetryTemplate.builder().fixedBackoff(Duration.ofSeconds(1)).build();
230+
FixedBackOffPolicy policy = getPropertyValue(template, "backOffPolicy", FixedBackOffPolicy.class);
231+
232+
assertThat(policy.getBackOffPeriod()).isEqualTo(1000);
233+
}
234+
193235
@Test
194236
public void testUniformRandomBackOff() {
195237
RetryTemplate template = RetryTemplate.builder().uniformRandomBackoff(10, 100).build();
196238
assertThat(getPropertyValue(template, "backOffPolicy")).isInstanceOf(UniformRandomBackOffPolicy.class);
197239
}
198240

241+
@Test
242+
public void testUniformRandomBackOffDuration() {
243+
RetryTemplate template = RetryTemplate.builder()
244+
.uniformRandomBackoff(Duration.ofSeconds(1), Duration.ofSeconds(2))
245+
.build();
246+
247+
UniformRandomBackOffPolicy policy = getPropertyValue(template, "backOffPolicy",
248+
UniformRandomBackOffPolicy.class);
249+
250+
assertThat(policy.getMinBackOffPeriod()).isEqualTo(1000);
251+
assertThat(policy.getMaxBackOffPeriod()).isEqualTo(2000);
252+
}
253+
199254
@Test
200255
public void testNoBackOff() {
201256
RetryTemplate template = RetryTemplate.builder().noBackoff().build();
202257
assertThat(getPropertyValue(template, "backOffPolicy")).isInstanceOf(NoBackOffPolicy.class);
203258
}
204259

260+
@Test
261+
public void testExponentialBackoff() {
262+
RetryTemplate template = RetryTemplate.builder().exponentialBackoff(10, 2, 500).build();
263+
ExponentialBackOffPolicy policy = getPropertyValue(template, "backOffPolicy", ExponentialBackOffPolicy.class);
264+
265+
assertThat(policy.getInitialInterval()).isEqualTo(10);
266+
assertThat(policy.getMultiplier()).isEqualTo(2);
267+
assertThat(policy.getMaxInterval()).isEqualTo(500);
268+
}
269+
270+
@Test
271+
public void testExponentialBackoffDuration() {
272+
RetryTemplate template = RetryTemplate.builder()
273+
.exponentialBackoff(Duration.ofSeconds(2), 2, Duration.ofSeconds(3))
274+
.build();
275+
276+
ExponentialBackOffPolicy policy = getPropertyValue(template, "backOffPolicy", ExponentialBackOffPolicy.class);
277+
278+
assertThat(policy.getInitialInterval()).isEqualTo(2000);
279+
assertThat(policy.getMultiplier()).isEqualTo(2);
280+
assertThat(policy.getMaxInterval()).isEqualTo(3000);
281+
assertThat(policy.getMaxInterval()).isEqualTo(3000);
282+
}
283+
205284
@Test
206285
public void testExpBackOffWithRandom() {
207286
RetryTemplate template = RetryTemplate.builder().exponentialBackoff(10, 2, 500, true).build();
208287
assertThat(getPropertyValue(template, "backOffPolicy")).isInstanceOf(ExponentialRandomBackOffPolicy.class);
209288
}
210289

290+
@Test
291+
public void testExponentialRandomBackoffDuration() {
292+
RetryTemplate template = RetryTemplate.builder()
293+
.exponentialBackoff(Duration.ofSeconds(2), 2, Duration.ofSeconds(3), true)
294+
.build();
295+
296+
ExponentialRandomBackOffPolicy policy = getPropertyValue(template, "backOffPolicy",
297+
ExponentialRandomBackOffPolicy.class);
298+
299+
assertThat(policy.getInitialInterval()).isEqualTo(2000);
300+
assertThat(policy.getMultiplier()).isEqualTo(2);
301+
assertThat(policy.getMaxInterval()).isEqualTo(3000);
302+
}
303+
211304
@Test
212305
public void testValidateInitAndMax() {
213306
assertThatIllegalArgumentException()

0 commit comments

Comments
 (0)