Skip to content

Commit 9ae6438

Browse files
committed
Added result type params and unified result types
1 parent 7c3c1da commit 9ae6438

36 files changed

+220
-185
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1+
# Next Minor Release
2+
3+
### API Changes
4+
5+
- Added a generic result type `R` to `ExecutionContext`, `Execution` and `AsyncExecution`. This ensures that result types are unified across the API. It does mean that there are a few minor breaking changes to the API:
6+
- `ContextualSupplier` and `AsyncSupplier` now have an additional result type parameter `R`. Normally these types are used as lambda parameters where the type is inferred, so most users should not be impacted. But any explicit generic declaration of these types will not compile until the new parameter is added.
7+
- `PolicyExecutor`, which is part of the SPI, now accepts an additional result type parameter `R`. This is only relevant for SPI users who are implementing their own Policies.
8+
19
# 2.4.3
210

311
### Bug Fixes
412

513
- Fixed #289 - Binary imcompatibility with code that was compiled against previous Failsafe versions.
6-
14+
715
# 2.4.2
816

917
### Improvements

src/main/java/net/jodah/failsafe/AbstractExecution.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
/**
2727
* Common execution information.
2828
*
29+
* @param <R> result type
2930
* @author Jonathan Halterman
3031
*/
31-
@SuppressWarnings("WeakerAccess")
32-
public abstract class AbstractExecution extends ExecutionContext {
32+
public abstract class AbstractExecution<R> extends ExecutionContext<R> {
3333
final Scheduler scheduler;
34-
final FailsafeExecutor<Object> executor;
35-
final List<PolicyExecutor<Policy<Object>>> policyExecutors;
34+
final FailsafeExecutor<R> executor;
35+
final List<PolicyExecutor<R, Policy<R>>> policyExecutors;
3636

3737
// Internally mutable state
3838
/* Whether the supplier is in progress */
@@ -53,13 +53,13 @@ public abstract class AbstractExecution extends ExecutionContext {
5353
/**
5454
* Creates a new AbstractExecution for the {@code executor}.
5555
*/
56-
AbstractExecution(Scheduler scheduler, FailsafeExecutor<Object> executor) {
56+
AbstractExecution(Scheduler scheduler, FailsafeExecutor<R> executor) {
5757
this.scheduler = scheduler;
5858
this.executor = executor;
5959
policyExecutors = new ArrayList<>(executor.policies.size());
60-
ListIterator<Policy<Object>> policyIterator = executor.policies.listIterator(executor.policies.size());
60+
ListIterator<Policy<R>> policyIterator = executor.policies.listIterator(executor.policies.size());
6161
for (int i = 1; policyIterator.hasPrevious(); i++) {
62-
PolicyExecutor<Policy<Object>> policyExecutor = policyIterator.previous().toExecutor(this);
62+
PolicyExecutor<R, Policy<R>> policyExecutor = policyIterator.previous().toExecutor(this);
6363
policyExecutor.policyIndex = i;
6464
policyExecutors.add(policyExecutor);
6565
}
@@ -76,12 +76,13 @@ void record(ExecutionResult result) {
7676
record(result, false);
7777
}
7878

79+
@SuppressWarnings("unchecked")
7980
void record(ExecutionResult result, boolean timeout) {
8081
Assert.state(!completed, "Execution has already been completed");
8182
if (!interrupted) {
8283
recordAttempt();
8384
if (inProgress) {
84-
lastResult = result.getResult();
85+
lastResult = (R) result.getResult();
8586
lastFailure = result.getFailure();
8687
executions.incrementAndGet();
8788
if (!timeout)
@@ -126,7 +127,7 @@ boolean isAsyncExecution() {
126127
synchronized ExecutionResult postExecute(ExecutionResult result) {
127128
record(result);
128129
boolean allComplete = true;
129-
for (PolicyExecutor<Policy<Object>> policyExecutor : policyExecutors) {
130+
for (PolicyExecutor<R, Policy<R>> policyExecutor : policyExecutors) {
130131
result = policyExecutor.postExecute(result);
131132
allComplete = allComplete && result.isComplete();
132133
}

src/main/java/net/jodah/failsafe/AsyncExecution.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,19 @@
2929
* Tracks asynchronous executions and allows retries to be scheduled according to a {@link RetryPolicy}. May be
3030
* explicitly completed or made to retry.
3131
*
32+
* @param <R> result type
3233
* @author Jonathan Halterman
3334
*/
34-
@SuppressWarnings("WeakerAccess")
35-
public final class AsyncExecution extends AbstractExecution {
35+
public final class AsyncExecution<R> extends AbstractExecution<R> {
3636
private SettableSupplier<CompletableFuture<ExecutionResult>> innerExecutionSupplier;
3737
private Supplier<CompletableFuture<ExecutionResult>> outerExecutionSupplier;
38-
final FailsafeFuture<Object> future;
38+
final FailsafeFuture<R> future;
3939
private volatile boolean completeCalled;
4040
private volatile boolean retryCalled;
4141

42-
@SuppressWarnings("unchecked")
43-
<T> AsyncExecution(Scheduler scheduler, FailsafeFuture<T> future, FailsafeExecutor<?> executor) {
44-
super(scheduler, (FailsafeExecutor<Object>) executor);
45-
this.future = (FailsafeFuture<Object>) future;
42+
AsyncExecution(Scheduler scheduler, FailsafeFuture<R> future, FailsafeExecutor<R> executor) {
43+
super(scheduler, executor);
44+
this.future = future;
4645
}
4746

4847
void inject(Supplier<CompletableFuture<ExecutionResult>> syncSupplier, boolean asyncExecution) {
@@ -52,7 +51,7 @@ void inject(Supplier<CompletableFuture<ExecutionResult>> syncSupplier, boolean a
5251
outerExecutionSupplier = innerExecutionSupplier = Functions.toSettableSupplier(syncSupplier);
5352
}
5453

55-
for (PolicyExecutor<Policy<Object>> policyExecutor : policyExecutors)
54+
for (PolicyExecutor<R, Policy<R>> policyExecutor : policyExecutors)
5655
outerExecutionSupplier = policyExecutor.supplyAsync(outerExecutionSupplier, scheduler, this.future);
5756
}
5857

@@ -71,7 +70,7 @@ public void complete() {
7170
*
7271
* @throws IllegalStateException if the execution is already complete
7372
*/
74-
public boolean complete(Object result) {
73+
public boolean complete(R result) {
7574
postExecute(new ExecutionResult(result, null));
7675
return completed;
7776
}
@@ -86,7 +85,7 @@ public boolean complete(Object result) {
8685
*
8786
* @throws IllegalStateException if the execution is already complete
8887
*/
89-
public boolean complete(Object result, Throwable failure) {
88+
public boolean complete(R result, Throwable failure) {
9089
postExecute(new ExecutionResult(result, failure));
9190
return completed;
9291
}
@@ -98,8 +97,9 @@ public boolean complete(Object result, Throwable failure) {
9897
*
9998
* @throws IllegalStateException if a retry method has already been called or the execution is already complete
10099
*/
100+
@SuppressWarnings("unchecked")
101101
public boolean retry() {
102-
return retryFor(lastResult, lastFailure);
102+
return retryFor((R) lastResult, lastFailure);
103103
}
104104

105105
/**
@@ -109,7 +109,7 @@ public boolean retry() {
109109
*
110110
* @throws IllegalStateException if a retry method has already been called or the execution is already complete
111111
*/
112-
public boolean retryFor(Object result) {
112+
public boolean retryFor(R result) {
113113
return retryFor(result, null);
114114
}
115115

@@ -120,7 +120,7 @@ public boolean retryFor(Object result) {
120120
*
121121
* @throws IllegalStateException if a retry method has already been called or the execution is already complete
122122
*/
123-
public boolean retryFor(Object result, Throwable failure) {
123+
public boolean retryFor(R result, Throwable failure) {
124124
Assert.state(!retryCalled, "Retry has already been called");
125125
retryCalled = true;
126126
return !completeOrHandle(result, failure);
@@ -198,7 +198,7 @@ void executeAsync(boolean asyncExecution) {
198198
*
199199
* @throws IllegalStateException if the execution is already complete
200200
*/
201-
boolean completeOrHandle(Object result, Throwable failure) {
201+
boolean completeOrHandle(R result, Throwable failure) {
202202
synchronized (future) {
203203
ExecutionResult er = new ExecutionResult(result, failure).withWaitNanos(waitNanos);
204204
if (!completeCalled)

src/main/java/net/jodah/failsafe/CircuitBreakerExecutor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
/**
1919
* A PolicyExecutor that handles failures according to a {@link CircuitBreaker}.
2020
*
21+
* @param <R> result type
2122
* @author Jonathan Halterman
2223
*/
23-
class CircuitBreakerExecutor extends PolicyExecutor<CircuitBreaker> {
24-
CircuitBreakerExecutor(CircuitBreaker circuitBreaker, AbstractExecution execution) {
24+
class CircuitBreakerExecutor<R> extends PolicyExecutor<R, CircuitBreaker<R>> {
25+
CircuitBreakerExecutor(CircuitBreaker<R> circuitBreaker, AbstractExecution<R> execution) {
2526
super(circuitBreaker, execution);
2627
}
2728

src/main/java/net/jodah/failsafe/Execution.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,26 @@
2424
/**
2525
* Tracks executions and determines when an execution can be performed for a {@link RetryPolicy}.
2626
*
27+
* @param <R> result type
2728
* @author Jonathan Halterman
2829
*/
2930
@SuppressWarnings("WeakerAccess")
30-
public class Execution extends AbstractExecution {
31+
public class Execution<R> extends AbstractExecution<R> {
3132
/**
3233
* Creates a new {@link Execution} that will use the {@code policies} to handle failures. Policies are applied in
3334
* reverse order, with the last policy being applied first.
3435
*
3536
* @throws NullPointerException if {@code policies} is null
3637
* @throws IllegalArgumentException if {@code policies} is empty
3738
*/
38-
@SuppressWarnings("unchecked")
39-
public Execution(Policy... policies) {
39+
@SafeVarargs
40+
public Execution(Policy<R>... policies) {
4041
super(DelegatingScheduler.INSTANCE, new FailsafeExecutor<>(Arrays.asList(Assert.notNull(policies, "policies"))));
4142
preExecute();
4243
}
4344

44-
@SuppressWarnings("unchecked")
45-
Execution(FailsafeExecutor<?> executor) {
46-
super(DelegatingScheduler.INSTANCE, (FailsafeExecutor<Object>) executor);
45+
Execution(FailsafeExecutor<R> executor) {
46+
super(DelegatingScheduler.INSTANCE, executor);
4747
}
4848

4949
/**
@@ -52,7 +52,7 @@ public Execution(Policy... policies) {
5252
*
5353
* @throws IllegalStateException if the execution is already complete
5454
*/
55-
public boolean canRetryFor(Object result) {
55+
public boolean canRetryFor(R result) {
5656
preExecute();
5757
postExecute(new ExecutionResult(result, null));
5858
return !completed;
@@ -64,7 +64,7 @@ public boolean canRetryFor(Object result) {
6464
*
6565
* @throws IllegalStateException if the execution is already complete
6666
*/
67-
public boolean canRetryFor(Object result, Throwable failure) {
67+
public boolean canRetryFor(R result, Throwable failure) {
6868
preExecute();
6969
postExecute(new ExecutionResult(result, failure));
7070
return !completed;
@@ -99,7 +99,7 @@ public void complete() {
9999
*
100100
* @throws IllegalStateException if the execution is already complete
101101
*/
102-
public boolean complete(Object result) {
102+
public boolean complete(R result) {
103103
preExecute();
104104
postExecute(new ExecutionResult(result, null));
105105
return completed;
@@ -122,7 +122,7 @@ public boolean recordFailure(Throwable failure) {
122122
* Performs a synchronous execution.
123123
*/
124124
ExecutionResult executeSync(Supplier<ExecutionResult> supplier) {
125-
for (PolicyExecutor<Policy<Object>> policyExecutor : policyExecutors)
125+
for (PolicyExecutor<R, Policy<R>> policyExecutor : policyExecutors)
126126
supplier = policyExecutor.supply(supplier, scheduler);
127127

128128
ExecutionResult result = supplier.get();

src/main/java/net/jodah/failsafe/ExecutionContext.java

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
/**
2222
* Contextual execution information.
2323
*
24+
* @param <R> result type
2425
* @author Jonathan Halterman
2526
*/
26-
public class ExecutionContext {
27+
public class ExecutionContext<R> {
2728
volatile Duration startTime = Duration.ZERO;
2829
volatile Duration attemptStartTime = Duration.ZERO;
2930
// Number of execution attempts
@@ -34,13 +35,13 @@ public class ExecutionContext {
3435
// Internally mutable state
3536
// The index of a PolicyExecutor that cancelled the execution. 0 represents non-cancelled.
3637
volatile int cancelledIndex;
37-
volatile Object lastResult;
38+
volatile R lastResult;
3839
volatile Throwable lastFailure;
3940

4041
ExecutionContext() {
4142
}
4243

43-
private ExecutionContext(ExecutionContext context) {
44+
private ExecutionContext(ExecutionContext<R> context) {
4445
this.startTime = context.startTime;
4546
this.attemptStartTime = context.attemptStartTime;
4647
this.attempts = context.attempts;
@@ -93,17 +94,15 @@ public <T extends Throwable> T getLastFailure() {
9394
/**
9495
* Returns the last result that was recorded else {@code null}.
9596
*/
96-
@SuppressWarnings("unchecked")
97-
public <T> T getLastResult() {
98-
return (T) lastResult;
97+
public R getLastResult() {
98+
return lastResult;
9999
}
100100

101101
/**
102102
* Returns the last result that was recorded else the {@code defaultValue}.
103103
*/
104-
@SuppressWarnings("unchecked")
105-
public <T> T getLastResult(T defaultValue) {
106-
return lastResult != null ? (T) lastResult : defaultValue;
104+
public R getLastResult(R defaultValue) {
105+
return lastResult != null ? lastResult : defaultValue;
107106
}
108107

109108
/**
@@ -114,7 +113,7 @@ public Duration getStartTime() {
114113
}
115114

116115
/**
117-
* Returns whether the execution has ben cancelled. In this case the implementor should attempt to stop execution.
116+
* Returns whether the execution has been cancelled. In this case the implementor should attempt to stop execution.
118117
*/
119118
public boolean isCancelled() {
120119
return cancelledIndex != 0;
@@ -134,18 +133,18 @@ public boolean isRetry() {
134133
return attempts.get() > 0;
135134
}
136135

137-
public ExecutionContext copy() {
138-
return new ExecutionContext(this);
136+
public ExecutionContext<R> copy() {
137+
return new ExecutionContext<>(this);
139138
}
140139

141-
static ExecutionContext ofResult(Object result) {
142-
ExecutionContext context = new ExecutionContext();
140+
static <R> ExecutionContext<R> ofResult(R result) {
141+
ExecutionContext<R> context = new ExecutionContext<>();
143142
context.lastResult = result;
144143
return context;
145144
}
146145

147-
static ExecutionContext ofFailure(Throwable failure) {
148-
ExecutionContext context = new ExecutionContext();
146+
static <R> ExecutionContext<R> ofFailure(Throwable failure) {
147+
ExecutionContext<R> context = new ExecutionContext<>();
149148
context.lastFailure = failure;
150149
return context;
151150
}

0 commit comments

Comments
 (0)