Skip to content

Commit 1dc0c8d

Browse files
committed
Add non-wrapped exceptions to get()
1 parent 7f411c6 commit 1dc0c8d

File tree

8 files changed

+289
-11
lines changed

8 files changed

+289
-11
lines changed

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
*/
1616
package net.jodah.failsafe;
1717

18-
import net.jodah.failsafe.internal.util.Assert;
19-
import net.jodah.failsafe.internal.util.DelegatingScheduler;
20-
2118
import java.util.Arrays;
2219
import java.util.function.Supplier;
2320

21+
import net.jodah.failsafe.internal.util.Assert;
22+
import net.jodah.failsafe.internal.util.DelegatingScheduler;
23+
2424
/**
2525
* Tracks executions and determines when an execution can be performed for a {@link RetryPolicy}.
2626
*
@@ -131,4 +131,14 @@ ExecutionResult executeSync(Supplier<ExecutionResult> supplier) {
131131
executor.handleComplete(result, this);
132132
return result;
133133
}
134+
135+
<E extends Throwable> ExecutionResultWithException<E> executeSyncWithException(Supplier<ExecutionResultWithException<E>> supplier) {
136+
for (PolicyExecutor<Policy<Object>> policyExecutor : policyExecutors)
137+
supplier = policyExecutor.supplyWithException(supplier, scheduler);
138+
139+
ExecutionResultWithException<E> result = supplier.get();
140+
completed = result.isComplete();
141+
executor.handleComplete(result, this);
142+
return result;
143+
}
134144
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public ExecutionResult(Object result, Throwable failure) {
5050
this(result, failure, false, 0, false, failure == null, failure == null);
5151
}
5252

53-
private ExecutionResult(Object result, Throwable failure, boolean nonResult, long waitNanos, boolean complete,
53+
protected ExecutionResult(Object result, Throwable failure, boolean nonResult, long waitNanos, boolean complete,
5454
boolean success, Boolean successAll) {
5555
this.nonResult = nonResult;
5656
this.result = result;
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License
15+
*/
16+
package net.jodah.failsafe;
17+
18+
import java.util.Objects;
19+
20+
/**
21+
* The result of an execution. Immutable.
22+
* <p>
23+
* Part of the Failsafe SPI.
24+
*
25+
* @author Jonathan Halterman
26+
*/
27+
public class ExecutionResultWithException<E extends Throwable> extends ExecutionResult {
28+
public ExecutionResultWithException(Object result, E failure) {
29+
this(result, failure, false, 0, false, failure == null, failure == null);
30+
}
31+
32+
private ExecutionResultWithException(Object result, E failure, boolean nonResult, long waitNanos, boolean complete,
33+
boolean success, Boolean successAll) {
34+
super(result, failure, nonResult, waitNanos, complete, success, successAll);
35+
}
36+
37+
/**
38+
* Returns a an ExecutionResult with the {@code result} set, {@code completed} true and {@code success} true.
39+
*/
40+
public static <E extends Throwable> ExecutionResultWithException<E> successWithException(Object result) {
41+
return new ExecutionResultWithException<>(result, null);
42+
}
43+
44+
/**
45+
* Returns a an ExecutionResult with the {@code failure} set, {@code completed} true and {@code success} false.
46+
*/
47+
public static <E extends Throwable> ExecutionResultWithException<E> failureWithException(E failure) {
48+
return new ExecutionResultWithException<>(null, failure, false, 0, true, false, false);
49+
}
50+
51+
@SuppressWarnings("unchecked")
52+
public E getFailure() {
53+
return (E) super.getFailure();
54+
}
55+
56+
/**
57+
* Returns a copy of the ExecutionResult with a non-result, and completed and success set to true. Returns
58+
* {@code this} if {@link #success} and {@link #result} are unchanged.
59+
*/
60+
ExecutionResultWithException<E> withNonResult() {
61+
return super.isSuccess() && super.getResult() == null && super.isNonResult() ?
62+
this :
63+
new ExecutionResultWithException<E>(null, null, true, super.getWaitNanos(), true, true, super.getSuccessAll());
64+
}
65+
66+
/**
67+
* Returns a copy of the ExecutionResult with the {@code result} value, and completed and success set to true. Returns
68+
* {@code this} if {@link #success} and {@link #result} are unchanged.
69+
*/
70+
public ExecutionResultWithException<E> withResult(Object result) {
71+
return super.isSuccess() && ((super.getResult() == null && result == null) || (super.getResult() != null && super.getResult().equals(result))) ?
72+
this :
73+
new ExecutionResultWithException<>(result, null, super.isNonResult(), super.getWaitNanos(), true, true, super.getSuccessAll());
74+
}
75+
76+
/**
77+
* Returns a copy of the ExecutionResult with the value set to true, else this if nothing has changed.
78+
*/
79+
@SuppressWarnings("unchecked")
80+
public ExecutionResultWithException<E> withComplete() {
81+
return super.isComplete() ? this : new ExecutionResultWithException<E>(super.getResult(), (E) super.getFailure(), super.isNonResult(), super.getWaitNanos(), true, super.isSuccess(), super.getSuccessAll());
82+
}
83+
84+
/**
85+
* Returns a copy of the ExecutionResult with the {@code completed} and {@code success} values.
86+
*/
87+
@SuppressWarnings("unchecked")
88+
ExecutionResultWithException<E> with(boolean completed, boolean success) {
89+
return super.isComplete() == completed && super.isSuccess() == success ?
90+
this :
91+
new ExecutionResultWithException<E>(super.getResult(), (E) super.getFailure(), super.isNonResult(), super.getWaitNanos(), completed, success,
92+
super.isSuccess() && super.getSuccessAll());
93+
}
94+
95+
/**
96+
* Returns a copy of the ExecutionResult with the {@code waitNanos}, {@code completed} and {@code success} values.
97+
*/
98+
@SuppressWarnings("unchecked")
99+
public ExecutionResultWithException<E> with(long waitNanos, boolean completed, boolean success) {
100+
return super.getWaitNanos() == waitNanos && super.isComplete() == completed && super.isSuccess() == success ?
101+
this :
102+
new ExecutionResultWithException<E>(super.getResult(), (E) super.getFailure(), super.isNonResult(), waitNanos, completed, success,
103+
super.isSuccess() && super.getSuccessAll());
104+
}
105+
106+
@Override
107+
public String toString() {
108+
return "ExecutionResult[" + "result=" + super.getResult() + ", failure=" + super.getFailure() + ", nonResult=" + super.isNonResult()
109+
+ ", waitNanos=" + super.getWaitNanos() + ", complete=" + super.isComplete() + ", success=" + super.isSuccess() + ", successAll=" + super.getSuccessAll()
110+
+ ']';
111+
}
112+
113+
@Override
114+
public boolean equals(Object o) {
115+
if (this == o)
116+
return true;
117+
if (o == null || getClass() != o.getClass())
118+
return false;
119+
ExecutionResultWithException<E> that = (ExecutionResultWithException<E>) o;
120+
return Objects.equals(super.getResult(), that.getResult()) && Objects.equals(super.getFailure(), that.getFailure());
121+
}
122+
123+
@Override
124+
public int hashCode() {
125+
return Objects.hash(super.getResult(), super.getFailure());
126+
}
127+
}

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

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,36 @@
1515
*/
1616
package net.jodah.failsafe;
1717

18-
import net.jodah.failsafe.event.ExecutionCompletedEvent;
19-
import net.jodah.failsafe.function.*;
20-
import net.jodah.failsafe.internal.EventListener;
21-
import net.jodah.failsafe.internal.util.Assert;
22-
import net.jodah.failsafe.util.concurrent.Scheduler;
18+
import static net.jodah.failsafe.Functions.getPromise;
19+
import static net.jodah.failsafe.Functions.getPromiseExecution;
20+
import static net.jodah.failsafe.Functions.getPromiseOfStage;
21+
import static net.jodah.failsafe.Functions.getPromiseOfStageExecution;
22+
import static net.jodah.failsafe.Functions.toAsyncSupplier;
23+
import static net.jodah.failsafe.Functions.toCtxSupplier;
24+
import static net.jodah.failsafe.Functions.toSupplier;
2325

2426
import java.util.List;
25-
import java.util.concurrent.*;
27+
import java.util.concurrent.CompletableFuture;
28+
import java.util.concurrent.CompletionStage;
29+
import java.util.concurrent.ExecutorService;
30+
import java.util.concurrent.ForkJoinPool;
31+
import java.util.concurrent.RejectedExecutionException;
32+
import java.util.concurrent.ScheduledExecutorService;
2633
import java.util.function.Function;
2734
import java.util.function.Supplier;
2835

29-
import static net.jodah.failsafe.Functions.*;
36+
import net.jodah.failsafe.event.ExecutionCompletedEvent;
37+
import net.jodah.failsafe.function.AsyncRunnable;
38+
import net.jodah.failsafe.function.AsyncSupplier;
39+
import net.jodah.failsafe.function.CheckedConsumer;
40+
import net.jodah.failsafe.function.CheckedRunnable;
41+
import net.jodah.failsafe.function.CheckedSupplier;
42+
import net.jodah.failsafe.function.CheckedSupplierWithException;
43+
import net.jodah.failsafe.function.ContextualRunnable;
44+
import net.jodah.failsafe.function.ContextualSupplier;
45+
import net.jodah.failsafe.internal.EventListener;
46+
import net.jodah.failsafe.internal.util.Assert;
47+
import net.jodah.failsafe.util.concurrent.Scheduler;
3048

3149
/**
3250
* <p>
@@ -67,6 +85,10 @@ public <T extends R> T get(CheckedSupplier<T> supplier) {
6785
return call(execution -> Assert.notNull(supplier, "supplier"));
6886
}
6987

88+
public <T extends R, E extends Throwable> T getWithException(CheckedSupplierWithException<T, E> supplier) throws E {
89+
return callWithException(execution -> Assert.notNull(supplier, "supplier"));
90+
}
91+
7092
/**
7193
* Executes the {@code supplier} until a successful result is returned or the configured policies are exceeded.
7294
*
@@ -385,6 +407,23 @@ private <T> T call(Function<Execution, CheckedSupplier<?>> supplierFn) {
385407
return (T) result.getResult();
386408
}
387409

410+
@SuppressWarnings("unchecked")
411+
private <T, E extends Throwable> T callWithException(Function<Execution, CheckedSupplierWithException<?, E>> supplierFn) throws E {
412+
Execution execution = new Execution(this);
413+
Supplier<ExecutionResultWithException<E>> supplier = Functions.getWithException(supplierFn.apply(execution), execution);
414+
415+
ExecutionResultWithException<E> result = execution.executeSyncWithException(supplier);
416+
Throwable failure = result.getFailure();
417+
if (failure != null) {
418+
if (failure instanceof RuntimeException)
419+
throw (RuntimeException) failure;
420+
if (failure instanceof Error)
421+
throw (Error) failure;
422+
throw (E) failure;
423+
}
424+
return (T) result.getResult();
425+
}
426+
388427
/**
389428
* Calls the asynchronous {@code supplier} via the configured Scheduler, handling results according to the configured
390429
* policies.

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,35 @@ else if (throwable instanceof InterruptedException)
6666
};
6767
}
6868

69+
static <T, E extends Throwable> Supplier<ExecutionResultWithException<E>> getWithException(CheckedSupplierWithException<T, E> supplier, AbstractExecution execution) throws E {
70+
return () -> {
71+
ExecutionResultWithException<E> result;
72+
Throwable throwable = null;
73+
try {
74+
execution.preExecute();
75+
result = ExecutionResultWithException.successWithException(supplier.get());
76+
} catch (Throwable t) {
77+
throwable = t;
78+
// this probably needs some extra code in case an unexpected Exception is thrown
79+
result = ExecutionResultWithException.failureWithException((E) t);
80+
} finally {
81+
// Guard against race with Timeout interruption
82+
synchronized (execution) {
83+
execution.canInterrupt = false;
84+
if (execution.interrupted)
85+
// Clear interrupt flag if interruption was intended
86+
Thread.interrupted();
87+
else if (throwable instanceof InterruptedException)
88+
// Set interrupt flag if interrupt occurred but was not intentional
89+
Thread.currentThread().interrupt();
90+
}
91+
}
92+
93+
execution.record(result);
94+
return result;
95+
};
96+
}
97+
6998
/**
7099
* Returns a Supplier that pre-executes the {@code execution}, applies the {@code supplier}, records the result and
71100
* returns a promise containing the result.

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ protected ExecutionResult preExecute() {
4545
return null;
4646
}
4747

48+
protected <E extends Throwable>ExecutionResultWithException<E> preExecuteWithException() {
49+
return null;
50+
}
51+
4852
/**
4953
* Performs an execution by calling pre-execute else calling the supplier and doing a post-execute.
5054
*/
@@ -58,6 +62,16 @@ protected Supplier<ExecutionResult> supply(Supplier<ExecutionResult> supplier, S
5862
};
5963
}
6064

65+
protected <E extends Throwable> Supplier<ExecutionResultWithException<E>> supplyWithException(Supplier<ExecutionResultWithException<E>> supplier, Scheduler scheduler) {
66+
return () -> {
67+
ExecutionResultWithException<E> result = preExecuteWithException();
68+
if (result != null)
69+
return result;
70+
71+
return postExecuteWithException(supplier.get());
72+
};
73+
}
74+
6175
/**
6276
* Performs synchronous post-execution handling for a {@code result}.
6377
*/
@@ -74,6 +88,19 @@ protected ExecutionResult postExecute(ExecutionResult result) {
7488
return result;
7589
}
7690

91+
protected <E extends Throwable> ExecutionResultWithException<E> postExecuteWithException(ExecutionResultWithException<E> result) {
92+
if (isFailure(result)) {
93+
result = onFailureWithException(result.with(false, false));
94+
callFailureListener(result);
95+
} else {
96+
result = result.with(true, true);
97+
onSuccess(result);
98+
callSuccessListener(result);
99+
}
100+
101+
return result;
102+
}
103+
77104
/**
78105
* Performs an async execution by calling pre-execute else calling the supplier and doing a post-execute.
79106
*/
@@ -134,6 +161,11 @@ protected ExecutionResult onFailure(ExecutionResult result) {
134161
return result;
135162
}
136163

164+
protected <E extends Throwable> ExecutionResultWithException<E> onFailureWithException(ExecutionResultWithException<E> result) {
165+
return result;
166+
}
167+
168+
137169
/**
138170
* Performs potentially asynchrononus post-execution handling for a failed {@code result}, possibly creating a new
139171
* result, else returning the original {@code result}.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package net.jodah.failsafe.function;
2+
3+
@FunctionalInterface
4+
public interface CheckedSupplierWithException<T, E extends Throwable> {
5+
T get() throws E;
6+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License
15+
*/
16+
package net.jodah.failsafe.functional;
17+
18+
import java.io.IOException;
19+
20+
import org.testng.annotations.Test;
21+
22+
import net.jodah.failsafe.Failsafe;
23+
import net.jodah.failsafe.RetryPolicy;
24+
25+
@Test
26+
public class SupplierWithExceptionTest {
27+
@Test(expectedExceptions = IOException.class)
28+
public void testCheckedException() throws IOException {
29+
RetryPolicy<Object> retryPolicy = new RetryPolicy<>();
30+
31+
Failsafe.with(retryPolicy).getWithException(() -> {
32+
throw new IOException();
33+
});
34+
}
35+
}

0 commit comments

Comments
 (0)