From 8b9e620084483caa08f8658c68892143311fc068 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Wed, 11 Jun 2025 13:26:16 +0200 Subject: [PATCH] Allow FixedBackOff to be constructed with only a custom interval This commit introduces two new constructors: - FixedBackOff(long) - FixedBackOff(Duration) Closes gh-35028 --- .../core/retry/RetryTemplate.java | 3 ++- .../util/backoff/FixedBackOff.java | 24 +++++++++++++++++++ .../core/retry/RetryTemplateTests.java | 8 ++++--- .../DefaultMessageListenerContainer.java | 20 +++++++++------- 4 files changed, 42 insertions(+), 13 deletions(-) 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 546fe8ad2ea..38cd22882b8 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 @@ -16,6 +16,7 @@ package org.springframework.core.retry; +import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -60,7 +61,7 @@ public class RetryTemplate implements RetryOperations { protected RetryPolicy retryPolicy = new MaxRetryAttemptsPolicy(); - protected BackOff backOffPolicy = new FixedBackOff(1000, Long.MAX_VALUE); + protected BackOff backOffPolicy = new FixedBackOff(Duration.ofSeconds(1)); protected RetryListener retryListener = new RetryListener() { }; diff --git a/spring-core/src/main/java/org/springframework/util/backoff/FixedBackOff.java b/spring-core/src/main/java/org/springframework/util/backoff/FixedBackOff.java index 959b04fbb37..b08b4aa9a86 100644 --- a/spring-core/src/main/java/org/springframework/util/backoff/FixedBackOff.java +++ b/spring-core/src/main/java/org/springframework/util/backoff/FixedBackOff.java @@ -16,6 +16,8 @@ package org.springframework.util.backoff; +import java.time.Duration; + /** * A simple {@link BackOff} implementation that provides a fixed interval * between two attempts and a maximum number of retries. @@ -50,6 +52,28 @@ public class FixedBackOff implements BackOff { public FixedBackOff() { } + /** + * Create an instance with the supplied interval and an unlimited number of + * attempts. + * @param interval the interval between two attempts in milliseconds + * @since 7.0 + * @see #setMaxAttempts(long) + */ + public FixedBackOff(long interval) { + this.interval = interval; + } + + /** + * Create an instance with the supplied interval and an unlimited number of + * attempts. + * @param interval the interval between two attempts + * @since 7.0 + * @see #setMaxAttempts(long) + */ + public FixedBackOff(Duration interval) { + this.interval = interval.toMillis(); + } + /** * Create an instance with the supplied interval and maximum number of attempts. * @param interval the interval between two attempts in milliseconds 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 565bea40504..7e17efbbdc1 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 @@ -16,6 +16,8 @@ package org.springframework.core.retry; +import java.time.Duration; + import org.junit.jupiter.api.Test; import org.springframework.core.retry.support.MaxRetryAttemptsPolicy; @@ -55,7 +57,7 @@ class RetryTemplateTests { } }; - retryTemplate.setBackOffPolicy(new FixedBackOff(100, Long.MAX_VALUE)); + retryTemplate.setBackOffPolicy(new FixedBackOff(Duration.ofMillis(10))); assertThat(retryTemplate.execute(retryable)).isEqualTo("hello world"); } @@ -76,7 +78,7 @@ class RetryTemplateTests { } }; - retryTemplate.setBackOffPolicy(new FixedBackOff(100, Long.MAX_VALUE)); + retryTemplate.setBackOffPolicy(new FixedBackOff(Duration.ofMillis(10))); assertThatExceptionOfType(RetryException.class) .isThrownBy(() -> retryTemplate.execute(retryable)) @@ -123,7 +125,7 @@ class RetryTemplateTests { } }; retryTemplate.setRetryPolicy(retryPolicy); - retryTemplate.setBackOffPolicy(new FixedBackOff(100, Long.MAX_VALUE)); + retryTemplate.setBackOffPolicy(new FixedBackOff(Duration.ofMillis(10))); assertThatExceptionOfType(RetryException.class) .isThrownBy(() -> retryTemplate.execute(retryable)) diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java index ac26a7089dc..b9d856a5c1b 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -194,7 +194,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe private boolean virtualThreads = false; - private BackOff backOff = new FixedBackOff(DEFAULT_RECOVERY_INTERVAL, Long.MAX_VALUE); + private BackOff backOff = new FixedBackOff(DEFAULT_RECOVERY_INTERVAL); private int cacheLevel = CACHE_AUTO; @@ -278,8 +278,8 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe * between recovery attempts. If the {@link BackOffExecution} implementation * returns {@link BackOffExecution#STOP}, this listener container will not further * attempt to recover. - *

The {@link #setRecoveryInterval(long) recovery interval} is ignored - * when this property is set. + *

Note that setting the {@linkplain #setRecoveryInterval(long) recovery + * interval} overrides this property. * @since 4.1 */ public void setBackOff(BackOff backOff) { @@ -288,15 +288,17 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe /** * Specify the interval between recovery attempts, in milliseconds. - * The default is 5000 ms, that is, 5 seconds. This is a convenience method - * to create a {@link FixedBackOff} with the specified interval. - *

For more recovery options, consider specifying a {@link BackOff} - * instance instead. + *

The default is 5000 ms, that is, 5 seconds. + *

This is a convenience method to create a {@link FixedBackOff} with the + * specified interval. For more recovery options, consider specifying a + * {@link #setBackOff(BackOff) BackOff} instance instead. Note, however, that + * explicitly setting the {@link #setBackOff(BackOff) BackOff} overrides this + * property. * @see #setBackOff(BackOff) * @see #handleListenerSetupFailure */ public void setRecoveryInterval(long recoveryInterval) { - this.backOff = new FixedBackOff(recoveryInterval, Long.MAX_VALUE); + this.backOff = new FixedBackOff(recoveryInterval); } /**