Skip to content

Commit

Permalink
Implemented separate event types
Browse files Browse the repository at this point in the history
- Renamed FailsafeEvent to ExecutionEvent
- Implemented ExecutionAttempted and ExecutedCompleted events
  • Loading branch information
jhalterman committed Jan 16, 2019
1 parent 795b91a commit 45692d5
Show file tree
Hide file tree
Showing 14 changed files with 177 additions and 68 deletions.
8 changes: 4 additions & 4 deletions src/main/java/net/jodah/failsafe/FailsafeExecutor.java
Expand Up @@ -15,7 +15,7 @@
*/
package net.jodah.failsafe;

import net.jodah.failsafe.event.FailsafeEvent;
import net.jodah.failsafe.event.ExecutionCompletedEvent;
import net.jodah.failsafe.function.*;
import net.jodah.failsafe.internal.util.Assert;
import net.jodah.failsafe.internal.util.CommonPoolScheduler;
Expand Down Expand Up @@ -140,7 +140,7 @@ else if (failureListener != null && !result.getSuccessAll())
* Registers the {@code listener} to be called when an execution is complete for all of the configured {@link Policy
* policies}.
*/
public FailsafeExecutor<R> onComplete(CheckedConsumer<? extends FailsafeEvent<R>> listener) {
public FailsafeExecutor<R> onComplete(CheckedConsumer<? extends ExecutionCompletedEvent<R>> listener) {
completeListener = EventListener.of(Assert.notNull(listener, "listener"));
return this;
}
Expand All @@ -150,7 +150,7 @@ public FailsafeExecutor<R> onComplete(CheckedConsumer<? extends FailsafeEvent<R>
* handler is called when execution is complete and <i>any</i> policy fails.
*/
@Override
public FailsafeExecutor<R> onFailure(CheckedConsumer<? extends FailsafeEvent<R>> listener) {
public FailsafeExecutor<R> onFailure(CheckedConsumer<? extends ExecutionCompletedEvent<R>> listener) {
return super.onFailure(listener);
}

Expand All @@ -160,7 +160,7 @@ public FailsafeExecutor<R> onFailure(CheckedConsumer<? extends FailsafeEvent<R>>
* succeed, then the {@link #onFailure(CheckedConsumer)} registered listener is called instead.
*/
@Override
public FailsafeExecutor<R> onSuccess(CheckedConsumer<? extends FailsafeEvent<R>> listener) {
public FailsafeExecutor<R> onSuccess(CheckedConsumer<? extends ExecutionCompletedEvent<R>> listener) {
return super.onSuccess(listener);
}

Expand Down
23 changes: 18 additions & 5 deletions src/main/java/net/jodah/failsafe/PolicyListeners.java
@@ -1,6 +1,7 @@
package net.jodah.failsafe;

import net.jodah.failsafe.event.FailsafeEvent;
import net.jodah.failsafe.event.ExecutionAttemptedEvent;
import net.jodah.failsafe.event.ExecutionCompletedEvent;
import net.jodah.failsafe.function.CheckedConsumer;
import net.jodah.failsafe.internal.util.Assert;

Expand All @@ -17,10 +18,22 @@ public interface EventListener {
void handle(Object result, Throwable failure, ExecutionContext context);

@SuppressWarnings("unchecked")
static <R> EventListener of(CheckedConsumer<? extends FailsafeEvent<R>> handler) {
static <R> EventListener of(CheckedConsumer<? extends ExecutionCompletedEvent<R>> handler) {
return (Object result, Throwable failure, ExecutionContext context) -> {
try {
((CheckedConsumer<FailsafeEvent<R>>) handler).accept(new FailsafeEvent<>((R) result, failure, context));
((CheckedConsumer<ExecutionCompletedEvent<R>>) handler).accept(
new ExecutionCompletedEvent<>((R) result, failure, context));
} catch (Exception ignore) {
}
};
}

@SuppressWarnings("unchecked")
static <R> EventListener ofAttempt(CheckedConsumer<? extends ExecutionAttemptedEvent<R>> handler) {
return (Object result, Throwable failure, ExecutionContext context) -> {
try {
((CheckedConsumer<ExecutionAttemptedEvent<R>>) handler).accept(
new ExecutionAttemptedEvent<>((R) result, failure, context));
} catch (Exception ignore) {
}
};
Expand All @@ -37,15 +50,15 @@ default void handle(ExecutionResult result, ExecutionContext context) {
/**
* Registers the {@code listener} to be called when an execution fails for a {@link Policy}.
*/
public S onFailure(CheckedConsumer<? extends FailsafeEvent<R>> listener) {
public S onFailure(CheckedConsumer<? extends ExecutionCompletedEvent<R>> listener) {
failureListener = EventListener.of(Assert.notNull(listener, "listener"));
return (S) this;
}

/**
* Registers the {@code listener} to be called when an execution is successful for a {@link Policy}.
*/
public S onSuccess(CheckedConsumer<? extends FailsafeEvent<R>> listener) {
public S onSuccess(CheckedConsumer<? extends ExecutionCompletedEvent<R>> listener) {
successListener = EventListener.of(Assert.notNull(listener, "listener"));
return (S) this;
}
Expand Down
15 changes: 8 additions & 7 deletions src/main/java/net/jodah/failsafe/RetryPolicy.java
Expand Up @@ -15,7 +15,8 @@
*/
package net.jodah.failsafe;

import net.jodah.failsafe.event.FailsafeEvent;
import net.jodah.failsafe.event.ExecutionAttemptedEvent;
import net.jodah.failsafe.event.ExecutionCompletedEvent;
import net.jodah.failsafe.function.CheckedConsumer;
import net.jodah.failsafe.internal.executor.RetryPolicyExecutor;
import net.jodah.failsafe.internal.util.Assert;
Expand Down Expand Up @@ -254,16 +255,16 @@ public boolean isAbortable(R result, Throwable failure) {
/**
* Registers the {@code listener} to be called when an execution is aborted.
*/
public RetryPolicy<R> onAbort(CheckedConsumer<? extends FailsafeEvent<R>> listener) {
public RetryPolicy<R> onAbort(CheckedConsumer<? extends ExecutionCompletedEvent<R>> listener) {
abortListener = EventListener.of(Assert.notNull(listener, "listener"));
return this;
}

/**
* Registers the {@code listener} to be called when an execution attempt fails.
*/
public RetryPolicy<R> onFailedAttempt(CheckedConsumer<? extends FailsafeEvent<R>> listener) {
failedAttemptListener = EventListener.of(Assert.notNull(listener, "listener"));
public RetryPolicy<R> onFailedAttempt(CheckedConsumer<? extends ExecutionAttemptedEvent<R>> listener) {
failedAttemptListener = EventListener.ofAttempt(Assert.notNull(listener, "listener"));
return this;
}

Expand All @@ -272,16 +273,16 @@ public RetryPolicy<R> onFailedAttempt(CheckedConsumer<? extends FailsafeEvent<R>
* max retry attempts} or {@link RetryPolicy#withMaxDuration(Duration) max duration} are
* exceeded.
*/
public RetryPolicy<R> onRetriesExceeded(CheckedConsumer<? extends FailsafeEvent<R>> listener) {
public RetryPolicy<R> onRetriesExceeded(CheckedConsumer<? extends ExecutionCompletedEvent<R>> listener) {
retriesExceededListener = EventListener.of(Assert.notNull(listener, "listener"));
return this;
}

/**
* Registers the {@code listener} to be called before an execution is retried.
*/
public RetryPolicy<R> onRetry(CheckedConsumer<? extends FailsafeEvent<R>> listener) {
retryListener = EventListener.of(Assert.notNull(listener, "listener"));
public RetryPolicy<R> onRetry(CheckedConsumer<? extends ExecutionAttemptedEvent<R>> listener) {
retryListener = EventListener.ofAttempt(Assert.notNull(listener, "listener"));
return this;
}

Expand Down
@@ -0,0 +1,39 @@
package net.jodah.failsafe.event;

import net.jodah.failsafe.ExecutionContext;

/**
* Indicates an execution was attempted.
*
* @param <R> result type
* @author Jonathan Halterman
*/
public class ExecutionAttemptedEvent<R> extends ExecutionEvent {
private final R result;
private final Throwable failure;

public ExecutionAttemptedEvent(R result, Throwable failure, ExecutionContext context) {
super(context);
this.result = result;
this.failure = failure;
}

/**
* Returns the failure that preceeded the event, else {@code null} if there was none.
*/
public Throwable getLastFailure() {
return failure;
}

/**
* Returns the result that preceeded the event, else {@code null} if there was none.
*/
public R getLastResult() {
return result;
}

@Override
public String toString() {
return "ExecutionAttemptedEvent[" + "result=" + result + ", failure=" + failure + ']';
}
}
@@ -0,0 +1,39 @@
package net.jodah.failsafe.event;

import net.jodah.failsafe.ExecutionContext;

/**
* Indicates an execution was completed.
*
* @param <R> result type
* @author Jonathan Halterman
*/
public class ExecutionCompletedEvent<R> extends ExecutionEvent {
private final R result;
private final Throwable failure;

public ExecutionCompletedEvent(R result, Throwable failure, ExecutionContext context) {
super(context);
this.result = result;
this.failure = failure;
}

/**
* Returns the failure that preceeded the event, else {@code null} if there was none.
*/
public Throwable getFailure() {
return failure;
}

/**
* Returns the result that preceeded the event, else {@code null} if there was none.
*/
public R getResult() {
return result;
}

@Override
public String toString() {
return "ExecutionCompletedEvent[" + "result=" + result + ", failure=" + failure + ']';
}
}
39 changes: 39 additions & 0 deletions src/main/java/net/jodah/failsafe/event/ExecutionEvent.java
@@ -0,0 +1,39 @@
package net.jodah.failsafe.event;

import net.jodah.failsafe.ExecutionContext;

import java.time.Duration;

/**
* Encapsulates information about a Failsafe execution.
*
* @author Jonathan Halterman
*/
public class ExecutionEvent {
private final ExecutionContext context;

public ExecutionEvent(ExecutionContext context) {
this.context = context;
}

/**
* Returns the elapsed time since initial execution began.
*/
public Duration getElapsedTime() {
return context.getElapsedTime();
}

/**
* Gets the number of executions so far.
*/
public int getExecutions() {
return context.getExecutions();
}

/**
* Returns the time that the initial execution started.
*/
public Duration getStartTime() {
return context.getStartTime();
}
}
24 changes: 0 additions & 24 deletions src/main/java/net/jodah/failsafe/event/FailsafeEvent.java

This file was deleted.

Expand Up @@ -179,9 +179,11 @@ else if (policy.getJitterFactor() > 0.0)
boolean completed = isAbortable || !shouldRetry;
boolean success = completed && result.success && !isAbortable;

// Call listeners
// Call attempt listeners
if (failedAttemptListener != null && !success)
failedAttemptListener.handle(result, execution);

// Call completion listeners
if (abortListener != null && isAbortable)
abortListener.handle(result, execution);
else if (retriesExceededListener != null && !success && retriesExceeded)
Expand Down
32 changes: 16 additions & 16 deletions src/test/java/net/jodah/failsafe/AsyncFailsafeTest.java
Expand Up @@ -60,9 +60,9 @@ private void assertRunAsync(Object runnable) throws Throwable {

// When / Then
Future<?> future = runAsync(Failsafe.with(retryAlways).with(executor).onComplete(e -> {
waiter.assertEquals(e.context.getExecutions(), expectedExecutions.get());
waiter.assertNull(e.result);
waiter.assertNull(e.failure);
waiter.assertEquals(e.getExecutions(), expectedExecutions.get());
waiter.assertNull(e.getResult());
waiter.assertNull(e.getFailure());
waiter.resume();
}), runnable);
assertNull(future.get());
Expand All @@ -76,9 +76,9 @@ private void assertRunAsync(Object runnable) throws Throwable {

// When
Future<?> future2 = runAsync(Failsafe.with(retryTwice).with(executor).onComplete(e -> {
waiter.assertEquals(e.context.getExecutions(), expectedExecutions.get());
waiter.assertNull(e.result);
waiter.assertTrue(e.failure instanceof ConnectException);
waiter.assertEquals(e.getExecutions(), expectedExecutions.get());
waiter.assertNull(e.getResult());
waiter.assertTrue(e.getFailure() instanceof ConnectException);
waiter.resume();
}), runnable);

Expand Down Expand Up @@ -122,9 +122,9 @@ private void assertGetAsync(Object supplier) throws Throwable {

// When / Then
Future<Boolean> future = getAsync(Failsafe.with(retryPolicy).with(executor).onComplete(e -> {
waiter.assertEquals(e.context.getExecutions(), expectedExecutions.get());
waiter.assertTrue(e.result);
waiter.assertNull(e.failure);
waiter.assertEquals(e.getExecutions(), expectedExecutions.get());
waiter.assertTrue(e.getResult());
waiter.assertNull(e.getFailure());
waiter.resume();
}), supplier);

Expand All @@ -140,9 +140,9 @@ private void assertGetAsync(Object supplier) throws Throwable {

// When / Then
Future<Boolean> future2 = getAsync(Failsafe.with(retryTwice).with(executor).onComplete(e -> {
waiter.assertEquals(e.context.getExecutions(), expectedExecutions.get());
waiter.assertNull(e.result);
waiter.assertTrue(e.failure instanceof ConnectException);
waiter.assertEquals(e.getExecutions(), expectedExecutions.get());
waiter.assertNull(e.getResult());
waiter.assertTrue(e.getFailure() instanceof ConnectException);
waiter.resume();
}), supplier);
assertThrows(future2::get, futureAsyncThrowables);
Expand Down Expand Up @@ -245,8 +245,8 @@ private void assertCancel(Function<FailsafeExecutor<?>, Future<?>> executorCalla
// Given
Waiter waiter = new Waiter();
Future<?> future = executorCallable.apply(Failsafe.with(retryAlways).with(executor).onComplete(e -> {
waiter.assertNull(e.result);
waiter.assertTrue(e.failure instanceof CancellationException);
waiter.assertNull(e.getResult());
waiter.assertTrue(e.getFailure() instanceof CancellationException);
waiter.resume();
}));

Expand Down Expand Up @@ -308,8 +308,8 @@ public void shouldCancelOnFutureAsyncExecutor() throws Throwable {

public void shouldManuallyRetryAndComplete() throws Throwable {
Failsafe.with(retryAlways).with(executor).onComplete(e -> {
waiter.assertTrue(e.result);
waiter.assertNull(e.failure);
waiter.assertTrue(e.getResult());
waiter.assertNull(e.getFailure());
waiter.resume();
}).getAsyncExecution(exec -> {
if (exec.getExecutions() < 2)
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/net/jodah/failsafe/FailsafeFutureTest.java
Expand Up @@ -32,8 +32,8 @@ public class FailsafeFutureTest {
public void shouldCallOnCompleteWhenCancelled() throws Throwable {
Waiter waiter = new Waiter();
CompletableFuture<String> future = Failsafe.with(new RetryPolicy<String>()).with(executor).onComplete(e -> {
waiter.assertNull(e.result);
waiter.assertTrue(e.failure instanceof CancellationException);
waiter.assertNull(e.getResult());
waiter.assertTrue(e.getFailure() instanceof CancellationException);
waiter.resume();
}).getAsync(() -> {
Thread.sleep(1000);
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/net/jodah/failsafe/SyncFailsafeTest.java
Expand Up @@ -232,8 +232,8 @@ public void shouldCompleteWhenMaxDurationExceeded() {
RetryPolicy<Object> retryPolicy = new RetryPolicy<>().handleResult(false).withMaxDuration(Duration.ofMillis(100));

assertEquals(Failsafe.with(retryPolicy).onFailure(e -> {
assertEquals(e.result, Boolean.FALSE);
assertNull(e.failure);
assertEquals(e.getResult(), Boolean.FALSE);
assertNull(e.getFailure());
}).get(() -> {
Testing.sleep(120);
return service.connect();
Expand Down

0 comments on commit 45692d5

Please sign in to comment.