Skip to content

Commit 77e1af3

Browse files
committed
Use checked predicates for matching results and failures
Switches the policy builders to use checked predicates for matching results and failures. Exceptions were already being treated as false results. This additional lenience will make it easier to assert results that throw checked exceptions. The API really should have always done this.
1 parent c7f5aeb commit 77e1af3

14 files changed

+138
-85
lines changed

core/src/main/java/dev/failsafe/CircuitBreakerBuilder.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,22 @@
1717

1818
import dev.failsafe.event.CircuitBreakerStateChangedEvent;
1919
import dev.failsafe.event.EventListener;
20+
import dev.failsafe.function.CheckedBiPredicate;
21+
import dev.failsafe.function.CheckedPredicate;
2022
import dev.failsafe.internal.CircuitBreakerImpl;
2123
import dev.failsafe.internal.util.Assert;
2224
import dev.failsafe.internal.util.Durations;
2325

2426
import java.time.Duration;
25-
import java.util.function.BiPredicate;
26-
import java.util.function.Predicate;
2727

2828
/**
2929
* Builds {@link CircuitBreaker} instances.
3030
* <ul>
3131
* <li>By default, any exception is considered a failure and will be handled by the policy. You can override this by
3232
* specifying your own {@code handle} conditions. The default exception handling condition will only be overridden by
33-
* another condition that handles failure exceptions such as {@link #handle(Class)} or {@link #handleIf(BiPredicate)}.
33+
* another condition that handles failure exceptions such as {@link #handle(Class)} or {@link #handleIf(CheckedBiPredicate)}.
3434
* Specifying a condition that only handles results, such as {@link #handleResult(Object)} or
35-
* {@link #handleResultIf(Predicate)} will not replace the default exception handling condition.</li>
35+
* {@link #handleResultIf(CheckedPredicate)} will not replace the default exception handling condition.</li>
3636
* <li>If multiple {@code handle} conditions are specified, any condition that matches an execution result or failure
3737
* will trigger policy handling.</li>
3838
* </ul>

core/src/main/java/dev/failsafe/FailurePolicyBuilder.java

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,23 @@
1515
*/
1616
package dev.failsafe;
1717

18+
import dev.failsafe.function.CheckedBiPredicate;
19+
import dev.failsafe.function.CheckedPredicate;
1820
import dev.failsafe.internal.util.Assert;
1921

2022
import java.util.Arrays;
2123
import java.util.List;
2224
import java.util.Objects;
23-
import java.util.function.BiPredicate;
2425
import java.util.function.Predicate;
2526

2627
/**
2728
* A Policy that allows configurable conditions to determine whether an execution is a failure.
2829
* <ul>
2930
* <li>By default, any exception is considered a failure and will be handled by the policy. You can override this by
3031
* specifying your own {@code handle} conditions. The default exception handling condition will only be overridden by
31-
* another condition that handles failure exceptions such as {@link #handle(Class)} or {@link #handleIf(BiPredicate)}.
32+
* another condition that handles failure exceptions such as {@link #handle(Class)} or {@link #handleIf(CheckedBiPredicate)}.
3233
* Specifying a condition that only handles results, such as {@link #handleResult(Object)} or
33-
* {@link #handleResultIf(Predicate)} will not replace the default exception handling condition.</li>
34+
* {@link #handleResultIf(CheckedPredicate)} will not replace the default exception handling condition.</li>
3435
* <li>If multiple {@code handle} conditions are specified, any condition that matches an execution result or failure
3536
* will trigger policy handling.</li>
3637
* </ul>
@@ -84,27 +85,29 @@ public S handle(List<Class<? extends Throwable>> failures) {
8485
}
8586

8687
/**
87-
* Specifies that a failure has occurred if the {@code failurePredicate} matches the failure.
88+
* Specifies that a failure has occurred if the {@code failurePredicate} matches the failure. Any exception thrown
89+
* from the {@code failurePredicate} is treated as a {@code false} result.
8890
*
8991
* @throws NullPointerException if {@code failurePredicate} is null
9092
*/
91-
public S handleIf(Predicate<? extends Throwable> failurePredicate) {
93+
public S handleIf(CheckedPredicate<? extends Throwable> failurePredicate) {
9294
Assert.notNull(failurePredicate, "failurePredicate");
9395
config.failuresChecked = true;
9496
config.failureConditions.add(failurePredicateFor(failurePredicate));
9597
return (S) this;
9698
}
9799

98100
/**
99-
* Specifies that a failure has occurred if the {@code resultPredicate} matches the execution result.
101+
* Specifies that a failure has occurred if the {@code resultPredicate} matches the execution result. Any exception
102+
* thrown from the {@code resultPredicate} is treated as a {@code false} result.
100103
*
101104
* @throws NullPointerException if {@code resultPredicate} is null
102105
*/
103106
@SuppressWarnings("unchecked")
104-
public S handleIf(BiPredicate<R, ? extends Throwable> resultPredicate) {
107+
public S handleIf(CheckedBiPredicate<R, ? extends Throwable> resultPredicate) {
105108
Assert.notNull(resultPredicate, "resultPredicate");
106109
config.failuresChecked = true;
107-
config.failureConditions.add((BiPredicate<R, Throwable>) resultPredicate);
110+
config.failureConditions.add((CheckedBiPredicate<R, Throwable>) resultPredicate);
108111
return (S) this;
109112
}
110113

@@ -120,11 +123,12 @@ public S handleResult(R result) {
120123
/**
121124
* Specifies that a failure has occurred if the {@code resultPredicate} matches the execution result. This method is
122125
* only considered when a result is returned from an execution, not when an exception is thrown. To handle results or
123-
* exceptions with the same condition, use {@link #handleIf(BiPredicate)}.
126+
* exceptions with the same condition, use {@link #handleIf(CheckedBiPredicate)}. Any exception thrown from the {@code
127+
* resultPredicate} is treated as a {@code false} result.
124128
*
125129
* @throws NullPointerException if {@code resultPredicate} is null
126130
*/
127-
public S handleResultIf(Predicate<R> resultPredicate) {
131+
public S handleResultIf(CheckedPredicate<R> resultPredicate) {
128132
Assert.notNull(resultPredicate, "resultPredicate");
129133
config.failureConditions.add(resultPredicateFor(resultPredicate));
130134
return (S) this;
@@ -133,16 +137,17 @@ public S handleResultIf(Predicate<R> resultPredicate) {
133137
/**
134138
* Returns a predicate that evaluates whether the {@code result} equals an execution result.
135139
*/
136-
static <R> BiPredicate<R, Throwable> resultPredicateFor(R result) {
140+
static <R> CheckedBiPredicate<R, Throwable> resultPredicateFor(R result) {
137141
return (t, u) -> result == null ? t == null && u == null : Objects.equals(result, t);
138142
}
139143

140144
/**
141145
* Returns a predicate that evaluates the {@code failurePredicate} against a failure.
142146
*/
143147
@SuppressWarnings("unchecked")
144-
static <R> BiPredicate<R, Throwable> failurePredicateFor(Predicate<? extends Throwable> failurePredicate) {
145-
return (t, u) -> u != null && ((Predicate<Throwable>) failurePredicate).test(u);
148+
static <R> CheckedBiPredicate<R, Throwable> failurePredicateFor(
149+
CheckedPredicate<? extends Throwable> failurePredicate) {
150+
return (t, u) -> u != null && ((CheckedPredicate<Throwable>) failurePredicate).test(u);
146151
}
147152

148153
/**
@@ -151,7 +156,7 @@ static <R> BiPredicate<R, Throwable> failurePredicateFor(Predicate<? extends Thr
151156
* Short-circuits to false without invoking {@code resultPredicate}, when result is not present (i.e.
152157
* BiPredicate.test(null, Throwable)).
153158
*/
154-
static <R> BiPredicate<R, Throwable> resultPredicateFor(Predicate<R> resultPredicate) {
159+
static <R> CheckedBiPredicate<R, Throwable> resultPredicateFor(CheckedPredicate<R> resultPredicate) {
155160
return (t, u) -> {
156161
if (u == null) {
157162
return resultPredicate.test(t);
@@ -167,7 +172,7 @@ static <R> BiPredicate<R, Throwable> resultPredicateFor(Predicate<R> resultPredi
167172
/**
168173
* Returns a predicate that returns whether any of the {@code failures} are assignable from an execution failure.
169174
*/
170-
static <R> BiPredicate<R, Throwable> failurePredicateFor(List<Class<? extends Throwable>> failures) {
175+
static <R> CheckedBiPredicate<R, Throwable> failurePredicateFor(List<Class<? extends Throwable>> failures) {
171176
return (t, u) -> {
172177
if (u == null)
173178
return false;

core/src/main/java/dev/failsafe/FailurePolicyConfig.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
*/
1616
package dev.failsafe;
1717

18+
import dev.failsafe.function.CheckedBiPredicate;
19+
import dev.failsafe.function.CheckedPredicate;
20+
1821
import java.util.ArrayList;
1922
import java.util.List;
20-
import java.util.function.BiPredicate;
21-
import java.util.function.Predicate;
2223

2324
/**
2425
* Configuration for policies that handle specific failures and conditions.
@@ -30,7 +31,7 @@ public abstract class FailurePolicyConfig<R> extends PolicyConfig<R> {
3031
/** Indicates whether failures are checked by a configured failure condition */
3132
boolean failuresChecked;
3233
/** Conditions that determine whether an execution is a failure */
33-
List<BiPredicate<R, Throwable>> failureConditions;
34+
List<CheckedBiPredicate<R, Throwable>> failureConditions;
3435

3536
protected FailurePolicyConfig() {
3637
failureConditions = new ArrayList<>();
@@ -54,12 +55,12 @@ public boolean isFailuresChecked() {
5455
*
5556
* @see FailurePolicyBuilder#handle(Class...)
5657
* @see FailurePolicyBuilder#handle(List)
57-
* @see FailurePolicyBuilder#handleIf(BiPredicate)
58-
* @see FailurePolicyBuilder#handleIf(Predicate)
58+
* @see FailurePolicyBuilder#handleIf(CheckedBiPredicate)
59+
* @see FailurePolicyBuilder#handleIf(CheckedPredicate)
5960
* @see FailurePolicyBuilder#handleResult(Object)
60-
* @see FailurePolicyBuilder#handleResultIf(Predicate)
61+
* @see FailurePolicyBuilder#handleResultIf(CheckedPredicate)
6162
*/
62-
public List<BiPredicate<R, Throwable>> getFailureConditions() {
63+
public List<CheckedBiPredicate<R, Throwable>> getFailureConditions() {
6364
return failureConditions;
6465
}
6566
}

core/src/main/java/dev/failsafe/FallbackBuilder.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,22 @@
1717

1818
import dev.failsafe.event.EventListener;
1919
import dev.failsafe.event.ExecutionAttemptedEvent;
20+
import dev.failsafe.function.CheckedBiPredicate;
2021
import dev.failsafe.function.CheckedFunction;
22+
import dev.failsafe.function.CheckedPredicate;
2123
import dev.failsafe.internal.FallbackImpl;
2224
import dev.failsafe.internal.util.Assert;
2325

2426
import java.util.concurrent.CompletableFuture;
25-
import java.util.function.BiPredicate;
26-
import java.util.function.Predicate;
2727

2828
/**
2929
* Builds {@link Fallback} instances.
3030
* <ul>
3131
* <li>By default, any exception is considered a failure and will be handled by the policy. You can override this by
3232
* specifying your own {@code handle} conditions. The default exception handling condition will only be overridden by
33-
* another condition that handles failure exceptions such as {@link #handle(Class)} or {@link #handleIf(BiPredicate)}.
33+
* another condition that handles failure exceptions such as {@link #handle(Class)} or {@link #handleIf(CheckedBiPredicate)}.
3434
* Specifying a condition that only handles results, such as {@link #handleResult(Object)} or
35-
* {@link #handleResultIf(Predicate)} will not replace the default exception handling condition.</li>
35+
* {@link #handleResultIf(CheckedPredicate)} will not replace the default exception handling condition.</li>
3636
* <li>If multiple {@code handle} conditions are specified, any condition that matches an execution result or failure
3737
* will trigger policy handling.</li>
3838
* </ul>

core/src/main/java/dev/failsafe/RetryPolicyBuilder.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import dev.failsafe.event.ExecutionAttemptedEvent;
2020
import dev.failsafe.event.ExecutionCompletedEvent;
2121
import dev.failsafe.event.ExecutionScheduledEvent;
22+
import dev.failsafe.function.CheckedBiPredicate;
23+
import dev.failsafe.function.CheckedPredicate;
2224
import dev.failsafe.internal.RetryPolicyImpl;
2325
import dev.failsafe.internal.util.Assert;
2426
import dev.failsafe.internal.util.Durations;
@@ -28,8 +30,6 @@
2830
import java.util.ArrayList;
2931
import java.util.Arrays;
3032
import java.util.List;
31-
import java.util.function.BiPredicate;
32-
import java.util.function.Predicate;
3333

3434
/**
3535
* Builds {@link RetryPolicy instances}.
@@ -40,9 +40,9 @@
4040
* configuration methods.</li>
4141
* <li>By default, any exception is considered a failure and will be handled by the policy. You can override this by
4242
* specifying your own {@code handle} conditions. The default exception handling condition will only be overridden by
43-
* another condition that handles failure exceptions such as {@link #handle(Class)} or {@link #handleIf(BiPredicate)}.
43+
* another condition that handles failure exceptions such as {@link #handle(Class)} or {@link #handleIf(CheckedBiPredicate)}.
4444
* Specifying a condition that only handles results, such as {@link #handleResult(Object)} or
45-
* {@link #handleResultIf(Predicate)} will not replace the default exception handling condition.</li>
45+
* {@link #handleResultIf(CheckedPredicate)} will not replace the default exception handling condition.</li>
4646
* <li>If multiple {@code handle} conditions are specified, any condition that matches an execution result or failure
4747
* will trigger policy handling.</li>
4848
* <li>The {@code abortOn}, {@code abortWhen} and {@code abortIf} methods describe when retries should be aborted.</li>
@@ -83,24 +83,26 @@ public RetryPolicy<R> build() {
8383
}
8484

8585
/**
86-
* Specifies that retries should be aborted if the {@code completionPredicate} matches the completion result.
86+
* Specifies that retries should be aborted if the {@code completionPredicate} matches the completion result. Any
87+
* exception thrown from the {@code completionPredicate} is treated as a {@code false} result.
8788
*
8889
* @throws NullPointerException if {@code completionPredicate} is null
8990
*/
9091
@SuppressWarnings("unchecked")
91-
public RetryPolicyBuilder<R> abortIf(BiPredicate<R, ? extends Throwable> completionPredicate) {
92+
public RetryPolicyBuilder<R> abortIf(CheckedBiPredicate<R, ? extends Throwable> completionPredicate) {
9293
Assert.notNull(completionPredicate, "completionPredicate");
93-
config.abortConditions.add((BiPredicate<R, Throwable>) completionPredicate);
94+
config.abortConditions.add((CheckedBiPredicate<R, Throwable>) completionPredicate);
9495
return this;
9596
}
9697

9798
/**
9899
* Specifies that retries should be aborted if the {@code resultPredicate} matches the result. Predicate is not
99-
* invoked when the operation fails.
100+
* invoked when the operation fails. Any exception thrown from the {@code resultPredicate} is treated as a {@code
101+
* false} result.
100102
*
101103
* @throws NullPointerException if {@code resultPredicate} is null
102104
*/
103-
public RetryPolicyBuilder<R> abortIf(Predicate<R> resultPredicate) {
105+
public RetryPolicyBuilder<R> abortIf(CheckedPredicate<R> resultPredicate) {
104106
Assert.notNull(resultPredicate, "resultPredicate");
105107
config.abortConditions.add(resultPredicateFor(resultPredicate));
106108
return this;
@@ -146,11 +148,12 @@ public RetryPolicyBuilder<R> abortOn(List<Class<? extends Throwable>> failures)
146148
}
147149

148150
/**
149-
* Specifies that retries should be aborted if the {@code failurePredicate} matches the failure.
151+
* Specifies that retries should be aborted if the {@code failurePredicate} matches the failure. Any exception thrown
152+
* from the {@code failurePredicate} is treated as a {@code false} result.
150153
*
151154
* @throws NullPointerException if {@code failurePredicate} is null
152155
*/
153-
public RetryPolicyBuilder<R> abortOn(Predicate<? extends Throwable> failurePredicate) {
156+
public RetryPolicyBuilder<R> abortOn(CheckedPredicate<? extends Throwable> failurePredicate) {
154157
Assert.notNull(failurePredicate, "failurePredicate");
155158
config.abortConditions.add(failurePredicateFor(failurePredicate));
156159
return this;

core/src/main/java/dev/failsafe/RetryPolicyConfig.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
import dev.failsafe.event.ExecutionAttemptedEvent;
2020
import dev.failsafe.event.ExecutionCompletedEvent;
2121
import dev.failsafe.event.ExecutionScheduledEvent;
22+
import dev.failsafe.function.CheckedBiPredicate;
23+
import dev.failsafe.function.CheckedPredicate;
2224

2325
import java.time.Duration;
2426
import java.time.temporal.ChronoUnit;
2527
import java.util.ArrayList;
2628
import java.util.List;
27-
import java.util.function.BiPredicate;
28-
import java.util.function.Predicate;
2929

3030
/**
3131
* Configuration for a {@link RetryPolicy}.
@@ -46,7 +46,7 @@ public class RetryPolicyConfig<R> extends DelayablePolicyConfig<R> {
4646
double jitterFactor;
4747
Duration maxDuration;
4848
int maxRetries;
49-
List<BiPredicate<R, Throwable>> abortConditions;
49+
List<CheckedBiPredicate<R, Throwable>> abortConditions;
5050

5151
// Listeners
5252
EventListener<ExecutionCompletedEvent<R>> abortListener;
@@ -94,12 +94,12 @@ public boolean allowsRetries() {
9494
*
9595
* @see RetryPolicyBuilder#abortOn(Class...)
9696
* @see RetryPolicyBuilder#abortOn(List)
97-
* @see RetryPolicyBuilder#abortOn(Predicate)
98-
* @see RetryPolicyBuilder#abortIf(BiPredicate)
99-
* @see RetryPolicyBuilder#abortIf(Predicate)
97+
* @see RetryPolicyBuilder#abortOn(CheckedPredicate)
98+
* @see RetryPolicyBuilder#abortIf(CheckedBiPredicate)
99+
* @see RetryPolicyBuilder#abortIf(CheckedPredicate)
100100
* @see RetryPolicyBuilder#abortWhen(Object)
101101
*/
102-
public List<BiPredicate<R, Throwable>> getAbortConditions() {
102+
public List<CheckedBiPredicate<R, Throwable>> getAbortConditions() {
103103
return abortConditions;
104104
}
105105

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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 dev.failsafe.function;
17+
18+
/**
19+
* A BiPredicate that throws checked exceptions.
20+
*
21+
* @param <T> first input type
22+
* @param <U> second input type
23+
* @author Jonathan Halterman
24+
*/
25+
@FunctionalInterface
26+
public interface CheckedBiPredicate<T, U> {
27+
boolean test(T t, U u) throws Throwable;
28+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 dev.failsafe.function;
17+
18+
/**
19+
* A Predicate that throws checked exceptions.
20+
*
21+
* @param <T> input type
22+
* @author Jonathan Halterman
23+
*/
24+
@FunctionalInterface
25+
public interface CheckedPredicate<T> {
26+
boolean test(T t) throws Throwable;
27+
}

0 commit comments

Comments
 (0)