) failurePredicate).test(u);
- }
-
- /**
- * Returns a predicate that evaluates the {@code resultPredicate} against a result, when present.
- *
- * Short-circuits to false without invoking {@code resultPredicate}, when result is not present (i.e.
- * BiPredicate.test(null, Throwable)).
- */
- static CheckedBiPredicate resultPredicateFor(CheckedPredicate resultPredicate) {
- return (t, u) -> {
- if (u == null) {
- return resultPredicate.test(t);
- } else {
- // resultPredicate is only defined over the success type.
- // It doesn't know how to handle a failure of type Throwable,
- // so we return false here.
- return false;
- }
- };
- }
-
- /**
- * Returns a predicate that returns whether any of the {@code failures} are assignable from an execution failure.
- */
- static CheckedBiPredicate failurePredicateFor(List> failures) {
- return (t, u) -> {
- if (u == null)
- return false;
- for (Class extends Throwable> failureType : failures)
- if (failureType.isAssignableFrom(u.getClass()))
- return true;
- return false;
- };
- }
}
diff --git a/core/src/main/java/dev/failsafe/FailurePredicates.java b/core/src/main/java/dev/failsafe/FailurePredicates.java
new file mode 100644
index 00000000..5331f021
--- /dev/null
+++ b/core/src/main/java/dev/failsafe/FailurePredicates.java
@@ -0,0 +1,42 @@
+package dev.failsafe;
+
+import dev.failsafe.function.CheckedBiPredicate;
+import dev.failsafe.function.CheckedPredicate;
+
+import java.util.List;
+import java.util.Objects;
+
+public final class FailurePredicates {
+ private FailurePredicates() {}
+
+ public static CheckedBiPredicate resultPredicateFor(R result) {
+ return (t, u) -> result == null ? t == null && u == null : Objects.equals(result, t);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static CheckedBiPredicate failurePredicateFor(
+ CheckedPredicate extends Throwable> failurePredicate) {
+ return (t, u) -> u != null && ((CheckedPredicate) failurePredicate).test(u);
+ }
+
+ public static CheckedBiPredicate resultPredicateFor(CheckedPredicate resultPredicate) {
+ return (t, u) -> {
+ if (u == null) {
+ return resultPredicate.test(t);
+ } else {
+ return false;
+ }
+ };
+ }
+
+ public static CheckedBiPredicate failurePredicateFor(List> failures) {
+ return (t, u) -> {
+ if (u == null)
+ return false;
+ for (Class extends Throwable> failureType : failures)
+ if (failureType.isAssignableFrom(u.getClass()))
+ return true;
+ return false;
+ };
+ }
+}
diff --git a/core/src/main/java/dev/failsafe/PolicyConfig.java b/core/src/main/java/dev/failsafe/PolicyConfig.java
index 914130fc..a76f93ca 100644
--- a/core/src/main/java/dev/failsafe/PolicyConfig.java
+++ b/core/src/main/java/dev/failsafe/PolicyConfig.java
@@ -28,6 +28,9 @@ public abstract class PolicyConfig {
volatile EventListener> successListener;
volatile EventListener> failureListener;
+ /** Indicates whether exceptions are checked by a configured failure condition */
+ protected boolean exceptionsChecked;
+
protected PolicyConfig() {
}
diff --git a/core/src/main/java/dev/failsafe/RetryPolicyBuilder.java b/core/src/main/java/dev/failsafe/RetryPolicyBuilder.java
index a6beec56..a6840053 100644
--- a/core/src/main/java/dev/failsafe/RetryPolicyBuilder.java
+++ b/core/src/main/java/dev/failsafe/RetryPolicyBuilder.java
@@ -31,6 +31,9 @@
import java.util.Arrays;
import java.util.List;
+import static dev.failsafe.FailurePredicates.failurePredicateFor;
+import static dev.failsafe.FailurePredicates.resultPredicateFor;
+
/**
* Builds {@link RetryPolicy} instances.
*
diff --git a/core/src/main/java/dev/failsafe/internal/CircuitBreakerImpl.java b/core/src/main/java/dev/failsafe/internal/CircuitBreakerImpl.java
index 1169adca..cd18b934 100644
--- a/core/src/main/java/dev/failsafe/internal/CircuitBreakerImpl.java
+++ b/core/src/main/java/dev/failsafe/internal/CircuitBreakerImpl.java
@@ -155,25 +155,14 @@ protected void recordResult(R result, Throwable exception) {
* Transitions to the {@code newState} if not already in that state and calls any associated event listener.
*/
protected void transitionTo(State newState, EventListener listener,
- ExecutionContext context) {
+ ExecutionContext context) {
boolean transitioned = false;
State currentState;
synchronized (this) {
currentState = getState();
- if (!getState().equals(newState)) {
- switch (newState) {
- case CLOSED:
- state.set(new ClosedState<>(this));
- break;
- case OPEN:
- Duration computedDelay = computeDelay(context);
- state.set(new OpenState<>(this, state.get(), computedDelay != null ? computedDelay : config.getDelay()));
- break;
- case HALF_OPEN:
- state.set(new HalfOpenState<>(this));
- break;
- }
+ if (!currentState.equals(newState)) {
+ updateStateBasedOn(newState, context);
transitioned = true;
}
}
@@ -186,6 +175,23 @@ protected void transitionTo(State newState, EventListener context) {
+ switch (newState) {
+ case CLOSED:
+ state.set(new ClosedState<>(this));
+ break;
+ case OPEN:
+ Duration computedDelay = computeDelay(context);
+ state.set(new OpenState<>(this, state.get(), computedDelay != null ? computedDelay : config.getDelay()));
+ break;
+ case HALF_OPEN:
+ state.set(new HalfOpenState<>(this));
+ break;
+ }
+ }
+
+
/**
* Records an execution failure.
*/
@@ -205,4 +211,12 @@ protected void open(ExecutionContext context) {
public PolicyExecutor toExecutor(int policyIndex) {
return new CircuitBreakerExecutor<>(this, policyIndex);
}
+
+ //Moved acquirePermit() from interface to implementation
+ @Override
+ public void acquirePermit() {
+ if (!tryAcquirePermit())
+ throw new CircuitBreakerOpenException(this);
+ }
+
}
diff --git a/core/src/main/java/dev/failsafe/internal/util/FutureLinkedList.java b/core/src/main/java/dev/failsafe/internal/util/FutureLinkedList.java
index 2794ed14..18726a19 100644
--- a/core/src/main/java/dev/failsafe/internal/util/FutureLinkedList.java
+++ b/core/src/main/java/dev/failsafe/internal/util/FutureLinkedList.java
@@ -26,12 +26,12 @@
* @author Jonathan Halterman
*/
public final class FutureLinkedList {
- Node head;
- Node tail;
+ Node headNode;
+ Node tailNode;
static class Node {
- Node previous;
- Node next;
+ Node previousNode;
+ Node nextNode;
CompletableFuture future;
}
@@ -44,12 +44,12 @@ public synchronized CompletableFuture add() {
node.future = new CompletableFuture<>();
node.future.whenComplete((result, error) -> remove(node));
- if (head == null)
- head = tail = node;
+ if (headNode == null)
+ headNode = tailNode = node;
else {
- tail.next = node;
- node.previous = tail;
- tail = node;
+ tailNode.nextNode = node;
+ node.previousNode = tailNode;
+ tailNode = node;
}
return node.future;
}
@@ -58,23 +58,23 @@ public synchronized CompletableFuture add() {
* Returns and removes the first future in the list, else returns {@code null} if the list is empty.
*/
public synchronized CompletableFuture pollFirst() {
- Node previousHead = head;
- if (head != null) {
- head = head.next;
- if (head != null)
- head.previous = null;
+ Node previousNodeHead = headNode;
+ if (headNode != null) {
+ headNode = headNode.nextNode;
+ if (headNode != null)
+ headNode.previousNode = null;
}
- return previousHead == null ? null : previousHead.future;
+ return previousNodeHead == null ? null : previousNodeHead.future;
}
private synchronized void remove(Node node) {
- if (node.previous != null)
- node.previous.next = node.next;
- if (node.next != null)
- node.next.previous = node.previous;
- if (head == node)
- head = node.next;
- if (tail == node)
- tail = node.previous;
+ if (node.previousNode != null)
+ node.previousNode.nextNode = node.nextNode;
+ if (node.nextNode != null)
+ node.nextNode.previousNode = node.previousNode;
+ if (headNode == node)
+ headNode = node.nextNode;
+ if (tailNode == node)
+ tailNode = node.previousNode;
}
}
diff --git a/core/src/main/java/dev/failsafe/internal/util/RandomDelay.java b/core/src/main/java/dev/failsafe/internal/util/RandomDelay.java
index 59902576..03eade8d 100644
--- a/core/src/main/java/dev/failsafe/internal/util/RandomDelay.java
+++ b/core/src/main/java/dev/failsafe/internal/util/RandomDelay.java
@@ -24,8 +24,11 @@ public final class RandomDelay {
private RandomDelay() {
}
- public static long randomDelayInRange(long delayMin, long delayMax, double random) {
- return (long) (random * (delayMax - delayMin)) + delayMin;
+ // Refactored: Introduce explaining variable
+ public static long randomDelayInRange(long minDelay, long maxDelay, double random) {
+ long delayRange = maxDelay - minDelay;
+ long randomOffset = (long) (delayRange * random);
+ return minDelay + randomOffset;
}
public static long randomDelay(long delay, long jitter, double random) {
diff --git a/core/src/test/java/dev/failsafe/internal/util/FutureLinkedListTest.java b/core/src/test/java/dev/failsafe/internal/util/FutureLinkedListTest.java
index 94b2c1e3..ec8172b0 100644
--- a/core/src/test/java/dev/failsafe/internal/util/FutureLinkedListTest.java
+++ b/core/src/test/java/dev/failsafe/internal/util/FutureLinkedListTest.java
@@ -34,11 +34,11 @@ public void testAdd() {
CompletableFuture f3 = list.add();
// Then
- assertNull(list.head.previous);
- assertEquals(list.head.future, f1);
- assertEquals(list.tail.future, f3);
- assertEquals(list.head.next.future, f2);
- assertEquals(list.tail.previous.future, f2);
+ assertNull(list.headNode.previousNode);
+ assertEquals(list.headNode.future, f1);
+ assertEquals(list.tailNode.future, f3);
+ assertEquals(list.headNode.nextNode.future, f2);
+ assertEquals(list.tailNode.previousNode.future, f2);
}
public void testPollFirst() {
@@ -55,13 +55,13 @@ public void testPollFirst() {
// When / Then
assertEquals(list.pollFirst(), f1);
- assertEquals(list.head.future, f2);
- assertNull(list.head.previous);
+ assertEquals(list.headNode.future, f2);
+ assertNull(list.headNode.previousNode);
assertEquals(list.pollFirst(), f2);
- assertEquals(list.head.future, f3);
- assertNull(list.head.previous);
+ assertEquals(list.headNode.future, f3);
+ assertNull(list.headNode.previousNode);
assertEquals(list.pollFirst(), f3);
- assertNull(list.head);
+ assertNull(list.headNode);
assertNull(list.pollFirst());
}
@@ -74,18 +74,18 @@ public void testRemove() {
// When / Then
f1.complete(null);
- assertEquals(list.head.future, f2);
- assertNull(list.head.previous);
- assertEquals(list.tail.previous.future, f2);
+ assertEquals(list.headNode.future, f2);
+ assertNull(list.headNode.previousNode);
+ assertEquals(list.tailNode.previousNode.future, f2);
f2.complete(null);
- assertEquals(list.head.future, f3);
- assertEquals(list.tail.future, f3);
- assertNull(list.head.previous);
- assertNull(list.head.next);
+ assertEquals(list.headNode.future, f3);
+ assertEquals(list.tailNode.future, f3);
+ assertNull(list.headNode.previousNode);
+ assertNull(list.headNode.nextNode);
f3.complete(null);
- assertNull(list.head);
- assertNull(list.tail);
+ assertNull(list.headNode);
+ assertNull(list.tailNode);
}
}
diff --git a/modules/okhttp/pom.xml b/modules/okhttp/pom.xml
index 131852c7..c8d86919 100644
--- a/modules/okhttp/pom.xml
+++ b/modules/okhttp/pom.xml
@@ -42,6 +42,12 @@
2.32.0
test
+
+ dev.failsafe
+ failsafe
+ 3.3.3-SNAPSHOT
+ compile
+
diff --git a/modules/retrofit/pom.xml b/modules/retrofit/pom.xml
index 7f503ce3..7d34e1b1 100644
--- a/modules/retrofit/pom.xml
+++ b/modules/retrofit/pom.xml
@@ -54,6 +54,12 @@
2.32.0
test
+
+ dev.failsafe
+ failsafe
+ 3.3.3-SNAPSHOT
+ compile
+