Skip to content

Commit

Permalink
Add typed policies and events
Browse files Browse the repository at this point in the history
  • Loading branch information
jhalterman committed Jan 5, 2019
1 parent 82650f6 commit 72fd446
Show file tree
Hide file tree
Showing 47 changed files with 833 additions and 883 deletions.
26 changes: 12 additions & 14 deletions src/main/java/net/jodah/failsafe/AbstractExecution.java
Expand Up @@ -15,7 +15,6 @@
*/
package net.jodah.failsafe;

import net.jodah.failsafe.event.EventHandler;
import net.jodah.failsafe.internal.util.Assert;

import java.time.Duration;
Expand All @@ -24,7 +23,7 @@

@SuppressWarnings("WeakerAccess")
public abstract class AbstractExecution extends ExecutionContext {
final EventHandler eventHandler;
final FailsafeExecutor<Object> executor;
Callable<Object> callable;

// Internally mutable state
Expand All @@ -39,22 +38,22 @@ public abstract class AbstractExecution extends ExecutionContext {
/**
* Creates a new AbstractExecution for the {@code callable} and {@code config}.
*/
AbstractExecution(FailsafeConfig<Object, ?> config) {
AbstractExecution(FailsafeExecutor<Object> executor) {
super(Duration.ofNanos(System.nanoTime()));
eventHandler = config.listeners;
this.executor = executor;

PolicyExecutor next = null;
if (config.policies == null || config.policies.isEmpty()) {
if (executor.policies == null || executor.policies.isEmpty()) {
// Add policies in logical order
if (config.circuitBreaker != null)
next = buildPolicyExecutor(config.circuitBreaker, next);
if (config.retryPolicy != RetryPolicy.NEVER)
next = buildPolicyExecutor(config.retryPolicy, next);
if (config.fallback != null)
next = buildPolicyExecutor(config.fallback, next);
if (executor.circuitBreaker != null)
next = buildPolicyExecutor(executor.circuitBreaker, next);
if (executor.retryPolicy != RetryPolicy.NEVER)
next = buildPolicyExecutor(executor.retryPolicy, next);
if (executor.fallback != null)
next = buildPolicyExecutor(executor.fallback, next);
} else {
// Add policies in user-defined order
ListIterator<Policy> policyIterator = config.policies.listIterator(config.policies.size());
ListIterator<Policy> policyIterator = executor.policies.listIterator(executor.policies.size());
while (policyIterator.hasPrevious())
next = buildPolicyExecutor(policyIterator.previous(), next);
}
Expand All @@ -70,7 +69,6 @@ void inject(Callable<?> callable) {
private PolicyExecutor buildPolicyExecutor(Policy policy, PolicyExecutor next) {
PolicyExecutor policyExecutor = policy.toExecutor();
policyExecutor.execution = this;
policyExecutor.eventHandler = eventHandler;
policyExecutor.next = next;
return policyExecutor;
}
Expand Down Expand Up @@ -117,7 +115,7 @@ private ExecutionResult postExecute(ExecutionResult result, PolicyExecutor polic
ExecutionResult executeSync() {
ExecutionResult result = head.executeSync(null);
completed = result.completed;
eventHandler.handleComplete(result, this);
executor.handleComplete(result, this);
return result;
}

Expand Down
54 changes: 26 additions & 28 deletions src/main/java/net/jodah/failsafe/AbstractPolicy.java
Expand Up @@ -18,14 +18,15 @@
* handling of the execution failed.</li>
* </ul>
*
* @param <S> self type
* @param <R> result type
*/
@SuppressWarnings("unchecked")
public abstract class AbstractPolicy<R> implements Policy {
public abstract class AbstractPolicy<S, R> extends PolicyListeners<S, R> implements Policy<R> {
/** Indicates whether failures are checked by a configured failure condition */
protected boolean failuresChecked;
/** Conditions that determine whether an execution is a failure */
protected List<BiPredicate<Object, Throwable>> failureConditions;
protected List<BiPredicate<R, Throwable>> failureConditions;

AbstractPolicy() {
failureConditions = new ArrayList<>();
Expand All @@ -36,8 +37,7 @@ public abstract class AbstractPolicy<R> implements Policy {
*
* @throws NullPointerException if {@code failure} is null
*/
@SuppressWarnings({ "rawtypes" })
public R handle(Class<? extends Throwable> failure) {
public S handle(Class<? extends Throwable> failure) {
Assert.notNull(failure, "failure");
return handle(Arrays.asList(failure));
}
Expand All @@ -48,8 +48,7 @@ public R handle(Class<? extends Throwable> failure) {
* @throws NullPointerException if {@code failures} is null
* @throws IllegalArgumentException if failures is empty
*/
@SuppressWarnings("unchecked")
public R handle(Class<? extends Throwable>... failures) {
public S handle(Class<? extends Throwable>... failures) {
Assert.notNull(failures, "failures");
Assert.isTrue(failures.length > 0, "Failures cannot be empty");
return handle(Arrays.asList(failures));
Expand All @@ -61,24 +60,24 @@ public R handle(Class<? extends Throwable>... failures) {
* @throws NullPointerException if {@code failures} is null
* @throws IllegalArgumentException if failures is null or empty
*/
public R handle(List<Class<? extends Throwable>> failures) {
public S handle(List<Class<? extends Throwable>> failures) {
Assert.notNull(failures, "failures");
Assert.isTrue(!failures.isEmpty(), "failures cannot be empty");
failuresChecked = true;
failureConditions.add(failurePredicateFor(failures));
return (R) this;
return (S) this;
}

/**
* Specifies that a failure has occurred if the {@code failurePredicate} matches the failure.
*
* @throws NullPointerException if {@code failurePredicate} is null
*/
public R handleIf(Predicate<? extends Throwable> failurePredicate) {
public S handleIf(Predicate<? extends Throwable> failurePredicate) {
Assert.notNull(failurePredicate, "failurePredicate");
failuresChecked = true;
failureConditions.add(failurePredicateFor(failurePredicate));
return (R) this;
return (S) this;
}

/**
Expand All @@ -87,30 +86,30 @@ public R handleIf(Predicate<? extends Throwable> failurePredicate) {
* @throws NullPointerException if {@code resultPredicate} is null
*/
@SuppressWarnings("unchecked")
public <T> R handleIf(BiPredicate<T, ? extends Throwable> resultPredicate) {
public S handleIf(BiPredicate<R, ? extends Throwable> resultPredicate) {
Assert.notNull(resultPredicate, "resultPredicate");
failuresChecked = true;
failureConditions.add((BiPredicate<Object, Throwable>) resultPredicate);
return (R) this;
failureConditions.add((BiPredicate<R, Throwable>) resultPredicate);
return (S) this;
}

/**
* Specifies that a failure has occurred if the {@code result} matches the execution result.
*/
public R handleResult(Object result) {
public S handleResult(R result) {
failureConditions.add(resultPredicateFor(result));
return (R) this;
return (S) this;
}

/**
* Specifies that a failure has occurred if the {@code resultPredicate} matches the execution result.
*
* @throws NullPointerException if {@code resultPredicate} is null
*/
public <T> R handleResultIf(Predicate<T> resultPredicate) {
public S handleResultIf(Predicate<R> resultPredicate) {
Assert.notNull(resultPredicate, "resultPredicate");
failureConditions.add(resultPredicateFor(resultPredicate));
return (R) this;
return (S) this;
}

/**
Expand All @@ -120,11 +119,11 @@ public <T> R handleResultIf(Predicate<T> resultPredicate) {
* @see #handle(List)
* @see #handleIf(BiPredicate)
* @see #handleIf(Predicate)
* @see #handleResult(Object)
* @see #handleResult(R)
* @see #handleResultIf(Predicate)
*/
boolean isFailure(ExecutionResult result) {
return failureConditions.isEmpty() ? !result.success : isFailure(result.result, result.failure);
return failureConditions.isEmpty() ? !result.success : isFailure((R) result.result, result.failure);
}

/**
Expand All @@ -134,11 +133,11 @@ boolean isFailure(ExecutionResult result) {
* @see #handle(List)
* @see #handleIf(BiPredicate)
* @see #handleIf(Predicate)
* @see #handleResult(Object)
* @see #handleResult(R)
* @see #handleResultIf(Predicate)
*/
public boolean isFailure(Object result, Throwable failure) {
for (BiPredicate<Object, Throwable> predicate : failureConditions) {
public boolean isFailure(R result, Throwable failure) {
for (BiPredicate<R, Throwable> predicate : failureConditions) {
try {
if (predicate.test(result, failure))
return true;
Expand All @@ -155,15 +154,15 @@ public boolean isFailure(Object result, Throwable failure) {
/**
* Returns a predicate that evaluates whether the {@code result} equals an execution result.
*/
static BiPredicate<Object, Throwable> resultPredicateFor(final Object result) {
static <R> BiPredicate<R, Throwable> resultPredicateFor(R result) {
return (t, u) -> Objects.equals(result, t);
}

/**
* Returns a predicate that evaluates the {@code failurePredicate} against a failure.
*/
@SuppressWarnings("unchecked")
static BiPredicate<Object, Throwable> failurePredicateFor(final Predicate<? extends Throwable> failurePredicate) {
static <R> BiPredicate<R, Throwable> failurePredicateFor(Predicate<? extends Throwable> failurePredicate) {
return (t, u) -> u != null && ((Predicate<Throwable>) failurePredicate).test(u);
}

Expand All @@ -173,11 +172,10 @@ static BiPredicate<Object, Throwable> failurePredicateFor(final Predicate<? exte
* Short-circuts to false without invoking {@code resultPredicate},
* when result is not present (i.e. BiPredicate.test(null, Throwable)).
*/
@SuppressWarnings("unchecked")
static <T> BiPredicate<Object, Throwable> resultPredicateFor(final Predicate<T> resultPredicate) {
static <R> BiPredicate<R, Throwable> resultPredicateFor(Predicate<R> resultPredicate) {
return (t, u) -> {
if (u == null) {
return ((Predicate<Object>) resultPredicate).test(t);
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,
Expand All @@ -190,7 +188,7 @@ static <T> BiPredicate<Object, Throwable> resultPredicateFor(final Predicate<T>
/**
* Returns a predicate that returns whether any of the {@code failures} are assignable from an execution failure.
*/
static BiPredicate<Object, Throwable> failurePredicateFor(final List<Class<? extends Throwable>> failures) {
static <R> BiPredicate<R, Throwable> failurePredicateFor(List<Class<? extends Throwable>> failures) {
return (t, u) -> {
if (u == null)
return false;
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/net/jodah/failsafe/AsyncExecution.java
Expand Up @@ -34,13 +34,13 @@ public final class AsyncExecution extends AbstractExecution {
private volatile boolean retryCalled;

@SuppressWarnings("unchecked")
<T> AsyncExecution(Scheduler scheduler, FailsafeFuture<T> future, FailsafeConfig<?, ?> config) {
super((FailsafeConfig<Object, ?>) config);
<T> AsyncExecution(Scheduler scheduler, FailsafeFuture<T> future, FailsafeExecutor<?> executor) {
super((FailsafeExecutor<Object>) executor);
this.scheduler = scheduler;
this.future = (FailsafeFuture<Object>) future;
}

<T> AsyncExecution(Callable<T> callable, Scheduler scheduler, FailsafeFuture<T> future, FailsafeConfig<?, ?> config) {
<T> AsyncExecution(Callable<T> callable, Scheduler scheduler, FailsafeFuture<T> future, FailsafeExecutor<?> config) {
this(scheduler, future, config);
inject(callable);
}
Expand Down Expand Up @@ -141,7 +141,7 @@ boolean postExecute(ExecutionResult result) {
if (!completeCalled) {
if (super.postExecute(result)) {
future.complete(result.result, result.failure);
eventHandler.handleComplete(result, this);
executor.handleComplete(result, this);
}
completeCalled = true;
}
Expand Down Expand Up @@ -179,7 +179,7 @@ ExecutionResult executeAsync(ExecutionResult result, Scheduler scheduler, Failsa

if (result != null) {
completed = true;
eventHandler.handleComplete(result, this);
executor.handleComplete(result, this);
future.complete(result.result, result.failure);
}

Expand Down

0 comments on commit 72fd446

Please sign in to comment.