Skip to content

Commit ca4bc84

Browse files
committed
Address code review
- Make retry policy implementations private - Remove listener subpackage - Use LogAccessor API where appropriate - Update nullability specification for return types - Fix thread interruption and listener calls in RetryTemplate - Update code style
1 parent b7a7ca6 commit ca4bc84

15 files changed

+100
-101
lines changed

spring-core/src/main/java/org/springframework/core/retry/RetryExecution.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
/**
2020
* Strategy interface to define a retry execution.
2121
*
22-
* <p>Implementations do not need to be thread-safe.
22+
* <p>Implementations may be stateful but do not need to be thread-safe.
2323
*
2424
* @author Mahmoud Ben Hassine
2525
* @since 7.0

spring-core/src/main/java/org/springframework/core/retry/RetryListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package org.springframework.core.retry;
1818

19-
import org.springframework.core.retry.support.listener.CompositeRetryListener;
19+
import org.springframework.core.retry.support.CompositeRetryListener;
2020

2121
/**
2222
* An extension point that allows to inject code during key retry phases.

spring-core/src/main/java/org/springframework/core/retry/RetryOperations.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public interface RetryOperations {
3939
* @throws RetryException thrown if the retry policy is exhausted. All attempt exceptions
4040
* should be added as suppressed exceptions to the final exception.
4141
*/
42-
<R> @Nullable R execute(RetryCallback<R> retryCallback) throws RetryException;
42+
<R extends @Nullable Object> R execute(RetryCallback<R> retryCallback) throws RetryException;
4343

4444
}
4545

spring-core/src/main/java/org/springframework/core/retry/RetryTemplate.java

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
import java.util.ArrayList;
2020
import java.util.List;
2121

22-
import org.apache.commons.logging.Log;
2322
import org.apache.commons.logging.LogFactory;
2423
import org.jspecify.annotations.Nullable;
2524

25+
import org.springframework.core.log.LogAccessor;
26+
import org.springframework.core.retry.support.CompositeRetryListener;
2627
import org.springframework.core.retry.support.MaxRetryAttemptsPolicy;
27-
import org.springframework.core.retry.support.listener.CompositeRetryListener;
2828
import org.springframework.util.Assert;
2929
import org.springframework.util.backoff.BackOffExecution;
3030
import org.springframework.util.backoff.BackOffPolicy;
@@ -51,13 +51,13 @@
5151
*/
5252
public class RetryTemplate implements RetryOperations {
5353

54-
protected final Log logger = LogFactory.getLog(getClass());
54+
protected final LogAccessor logger = new LogAccessor(LogFactory.getLog(getClass()));
5555

56-
private RetryPolicy retryPolicy = new MaxRetryAttemptsPolicy();
56+
protected RetryPolicy retryPolicy = new MaxRetryAttemptsPolicy();
5757

58-
private BackOffPolicy backOffPolicy = new FixedBackOffPolicy(1000);
58+
protected BackOffPolicy backOffPolicy = new FixedBackOffPolicy(1000);
5959

60-
private RetryListener retryListener = new RetryListener() {
60+
protected RetryListener retryListener = new RetryListener() {
6161
};
6262

6363
/**
@@ -125,53 +125,42 @@ public void setRetryListener(RetryListener retryListener) {
125125
* are added as suppressed exceptions to the final exception.
126126
*/
127127
@Override
128-
public <R> @Nullable R execute(RetryCallback<R> retryCallback) throws RetryException {
128+
public <R extends @Nullable Object> R execute(RetryCallback<R> retryCallback) throws RetryException {
129129
Assert.notNull(retryCallback, "Retry Callback must not be null");
130130
String callbackName = retryCallback.getName();
131131
// initial attempt
132132
try {
133-
if (logger.isDebugEnabled()) {
134-
logger.debug("About to execute callback '" + callbackName + "'");
135-
}
133+
logger.debug("About to execute callback '" + callbackName + "'");
136134
R result = retryCallback.run();
137-
if (logger.isDebugEnabled()) {
138-
logger.debug("Callback '" + callbackName + "' executed successfully");
139-
}
135+
logger.debug("Callback '" + callbackName + "' executed successfully");
140136
return result;
141137
}
142138
catch (Throwable initialException) {
143-
if (logger.isDebugEnabled()) {
144-
logger.debug("Execution of callback '" + callbackName + "' failed, initiating the retry process", initialException);
145-
}
139+
logger.debug(initialException, () ->"Execution of callback '" + callbackName + "' failed, initiating the retry process");
146140
// retry process starts here
147141
RetryExecution retryExecution = this.retryPolicy.start();
148142
BackOffExecution backOffExecution = this.backOffPolicy.start();
149143
List<Throwable> suppressedExceptions = new ArrayList<>();
150144

151145
Throwable retryException = initialException;
152146
while (retryExecution.shouldRetry(retryException)) {
153-
if (logger.isDebugEnabled()) {
154-
logger.debug("About to retry callback '" + callbackName + "'");
155-
}
147+
logger.debug("About to retry callback '" + callbackName + "'");
156148
try {
157149
this.retryListener.beforeRetry(retryExecution);
158150
R result = retryCallback.run();
159151
this.retryListener.onRetrySuccess(retryExecution, result);
160-
if (logger.isDebugEnabled()) {
161-
logger.debug("Callback '" + callbackName + "' retried successfully");
162-
}
152+
logger.debug("Callback '" + callbackName + "' retried successfully");
163153
return result;
164154
}
165155
catch (Throwable currentAttemptException) {
166156
this.retryListener.onRetryFailure(retryExecution, currentAttemptException);
167157
try {
168158
long duration = backOffExecution.nextBackOff();
169-
if (logger.isDebugEnabled()) {
170-
logger.debug("Retry callback '" + callbackName + "' failed for " + currentAttemptException.getMessage() + ", backing off for " + duration + "ms");
171-
}
159+
logger.debug("Retry callback '" + callbackName + "' failed for " + currentAttemptException.getMessage() + ", backing off for " + duration + "ms");
172160
Thread.sleep(duration);
173161
}
174162
catch (InterruptedException interruptedException) {
163+
Thread.currentThread().interrupt();
175164
throw new RetryException("Unable to backoff for retry callback '" + callbackName + "'", interruptedException);
176165
}
177166
suppressedExceptions.add(currentAttemptException);
@@ -181,6 +170,7 @@ public void setRetryListener(RetryListener retryListener) {
181170
// retry policy exhausted at this point, throwing a RetryException with the initial exception as cause and remaining attempts exceptions as suppressed
182171
RetryException finalException = new RetryException("Retry policy for callback '" + callbackName + "' exhausted, aborting execution", initialException);
183172
suppressedExceptions.forEach(finalException::addSuppressed);
173+
this.retryListener.onRetryPolicyExhaustion(retryExecution, finalException);
184174
throw finalException;
185175
}
186176
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.core.retry.support.listener;
17+
package org.springframework.core.retry.support;
1818

1919
import java.util.LinkedList;
2020
import java.util.List;

spring-core/src/main/java/org/springframework/core/retry/support/MaxRetryAttemptsPolicy.java

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,33 @@
2828
*/
2929
public class MaxRetryAttemptsPolicy implements RetryPolicy {
3030

31-
private int maxRetryAttempts = 3;
31+
/**
32+
* The default maximum number of retry attempts.
33+
*/
34+
public static final int DEFAULT_MAX_RETRY_ATTEMPTS = 3;
35+
36+
private int maxRetryAttempts = DEFAULT_MAX_RETRY_ATTEMPTS;
37+
38+
/**
39+
* Create a new {@link MaxRetryAttemptsPolicy} with the default maximum number of retry attempts.
40+
*/
41+
public MaxRetryAttemptsPolicy() {
42+
}
43+
44+
/**
45+
* Create a new {@link MaxRetryAttemptsPolicy} with the specified maximum number of retry attempts.
46+
* @param maxRetryAttempts the maximum number of retry attempts. Must be greater than zero.
47+
*/
48+
public MaxRetryAttemptsPolicy(int maxRetryAttempts) {
49+
setMaxRetryAttempts(maxRetryAttempts);
50+
}
3251

3352
/**
3453
* Start a new retry execution.
3554
* @return a fresh {@link MaxRetryAttemptsPolicyExecution} ready to be used
3655
*/
3756
@Override
38-
public MaxRetryAttemptsPolicyExecution start() {
57+
public RetryExecution start() {
3958
return new MaxRetryAttemptsPolicyExecution();
4059
}
4160

@@ -51,17 +70,17 @@ public void setMaxRetryAttempts(int maxRetryAttempts) {
5170
/**
5271
* A {@link RetryExecution} based on a maximum number of retry attempts.
5372
*/
54-
public class MaxRetryAttemptsPolicyExecution implements RetryExecution {
73+
private class MaxRetryAttemptsPolicyExecution implements RetryExecution {
5574

56-
protected int retryAttempts;
75+
private int retryAttempts;
5776

58-
public MaxRetryAttemptsPolicyExecution() {
77+
MaxRetryAttemptsPolicyExecution() {
5978
this.retryAttempts = 0;
6079
}
6180

6281
@Override
6382
public boolean shouldRetry(Throwable throwable) {
64-
return this.retryAttempts++ < maxRetryAttempts;
83+
return this.retryAttempts++ < MaxRetryAttemptsPolicy.this.maxRetryAttempts;
6584
}
6685

6786
}

spring-core/src/main/java/org/springframework/core/retry/support/MaxRetryDurationPolicy.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,33 @@
3131
*/
3232
public class MaxRetryDurationPolicy implements RetryPolicy {
3333

34-
private Duration maxRetryDuration = Duration.ofSeconds(3); // what would be a reasonable here?
34+
/**
35+
* The default maximum retry duration.
36+
*/
37+
public static final Duration DEFAULT_MAX_RETRY_DURATION = Duration.ofSeconds(3);
38+
39+
private Duration maxRetryDuration = DEFAULT_MAX_RETRY_DURATION;
40+
41+
/**
42+
* Create a new {@link MaxRetryDurationPolicy} with the default maximum retry duration.
43+
*/
44+
public MaxRetryDurationPolicy() {
45+
}
46+
47+
/**
48+
* Create a new {@link MaxRetryDurationPolicy} with the specified maximum retry duration.
49+
* @param maxRetryDuration the maximum retry duration. Must be positive.
50+
*/
51+
public MaxRetryDurationPolicy(Duration maxRetryDuration) {
52+
setMaxRetryDuration(maxRetryDuration);
53+
}
3554

3655
/**
3756
* Start a new retry execution.
3857
* @return a fresh {@link MaxRetryDurationPolicyExecution} ready to be used
3958
*/
4059
@Override
41-
public MaxRetryDurationPolicyExecution start() {
60+
public RetryExecution start() {
4261
return new MaxRetryDurationPolicyExecution();
4362
}
4463

@@ -55,14 +74,14 @@ public void setMaxRetryDuration(Duration maxRetryDuration) {
5574
/**
5675
* A {@link RetryExecution} based on a maximum retry duration.
5776
*/
58-
public class MaxRetryDurationPolicyExecution implements RetryExecution {
77+
private class MaxRetryDurationPolicyExecution implements RetryExecution {
5978

60-
protected LocalDateTime retryStartTime = LocalDateTime.now();
79+
private final LocalDateTime retryStartTime = LocalDateTime.now();
6180

6281
@Override
6382
public boolean shouldRetry(Throwable throwable) {
6483
Duration currentRetryDuration = Duration.between(this.retryStartTime, LocalDateTime.now());
65-
return currentRetryDuration.compareTo(maxRetryDuration) <= 0;
84+
return currentRetryDuration.compareTo(MaxRetryDurationPolicy.this.maxRetryDuration) <= 0;
6685
}
6786

6887
}

spring-core/src/main/java/org/springframework/core/retry/support/PredicateRetryPolicy.java

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,10 @@ public PredicateRetryPolicy(Predicate<Throwable> predicate) {
4141

4242
/**
4343
* Start a new retry execution.
44-
* @return a fresh {@link PredicateRetryPolicyExecution} ready to be used
44+
* @return a fresh ready to be used
4545
*/
46-
public PredicateRetryPolicyExecution start() {
47-
return new PredicateRetryPolicyExecution(this.predicate);
48-
}
49-
50-
/**
51-
* A {@link RetryExecution} based on a predicate.
52-
*/
53-
public static class PredicateRetryPolicyExecution implements RetryExecution {
54-
55-
protected final Predicate<Throwable> predicate;
56-
57-
public PredicateRetryPolicyExecution(Predicate<Throwable> predicate) {
58-
this.predicate = predicate;
59-
}
60-
61-
@Override
62-
public boolean shouldRetry(Throwable throwable) {
63-
return this.predicate.test(throwable);
64-
}
65-
46+
public RetryExecution start() {
47+
return this.predicate::test;
6648
}
6749

6850
}

spring-core/src/main/java/org/springframework/core/retry/support/listener/package-info.java

Lines changed: 0 additions & 7 deletions
This file was deleted.

spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOffPolicy.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
*
5656
* @author Stephane Nicoll
5757
* @author Gary Russell
58+
* @author Mahmoud Ben Hassine
5859
* @since 7.0
5960
*/
6061
public class ExponentialBackOffPolicy implements BackOffPolicy {

spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,12 @@ public String getName() {
114114
};
115115
MaxRetryAttemptsPolicy retryPolicy = new MaxRetryAttemptsPolicy() {
116116
@Override
117-
public MaxRetryAttemptsPolicyExecution start() {
118-
return new MaxRetryAttemptsPolicyExecution() {
117+
public RetryExecution start() {
118+
return new RetryExecution() {
119+
int retryAttempts;
119120
@Override
120121
public boolean shouldRetry(Throwable throwable) {
121-
return super.shouldRetry(throwable) && throwable instanceof TechnicalException;
122+
return this.retryAttempts++ < 3 && throwable instanceof TechnicalException;
122123
}
123124
};
124125
}
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.core.retry.support.listener;
17+
package org.springframework.core.retry.support;
1818

1919
import java.util.Arrays;
2020

2121
import org.junit.jupiter.api.Test;
22-
import org.mockito.Mockito;
2322

2423
import org.springframework.core.retry.RetryExecution;
2524
import org.springframework.core.retry.RetryListener;
2625

26+
import static org.mockito.Mockito.mock;
2727
import static org.mockito.Mockito.verify;
2828

2929
/**
@@ -33,14 +33,14 @@
3333
*/
3434
class ComposedRetryListenerTests {
3535

36-
private final RetryListener listener1 = Mockito.mock(RetryListener.class);
37-
private final RetryListener listener2 = Mockito.mock(RetryListener.class);
36+
private final RetryListener listener1 = mock();
37+
private final RetryListener listener2 = mock();
3838

3939
private final CompositeRetryListener composedRetryListener = new CompositeRetryListener(Arrays.asList(listener1, listener2));
4040

4141
@Test
4242
void beforeRetry() {
43-
RetryExecution retryExecution = Mockito.mock(RetryExecution.class);
43+
RetryExecution retryExecution = mock();
4444
this.composedRetryListener.beforeRetry(retryExecution);
4545

4646
verify(this.listener1).beforeRetry(retryExecution);
@@ -50,7 +50,7 @@ void beforeRetry() {
5050
@Test
5151
void onSuccess() {
5252
Object result = new Object();
53-
RetryExecution retryExecution = Mockito.mock(RetryExecution.class);
53+
RetryExecution retryExecution = mock();
5454
this.composedRetryListener.onRetrySuccess(retryExecution, result);
5555

5656
verify(this.listener1).onRetrySuccess(retryExecution, result);
@@ -60,7 +60,7 @@ void onSuccess() {
6060
@Test
6161
void onFailure() {
6262
Exception exception = new Exception();
63-
RetryExecution retryExecution = Mockito.mock(RetryExecution.class);
63+
RetryExecution retryExecution = mock();
6464
this.composedRetryListener.onRetryFailure(retryExecution, exception);
6565

6666
verify(this.listener1).onRetryFailure(retryExecution, exception);
@@ -70,7 +70,7 @@ void onFailure() {
7070
@Test
7171
void onMaxAttempts() {
7272
Exception exception = new Exception();
73-
RetryExecution retryExecution = Mockito.mock(RetryExecution.class);
73+
RetryExecution retryExecution = mock();
7474
this.composedRetryListener.onRetryPolicyExhaustion(retryExecution, exception);
7575

7676
verify(this.listener1).onRetryPolicyExhaustion(retryExecution, exception);

0 commit comments

Comments
 (0)