Skip to content

Commit

Permalink
Failed attempt listener not always called on completion. Re: issue #36.
Browse files Browse the repository at this point in the history
  • Loading branch information
jhalterman committed Jul 22, 2016
1 parent 21d862a commit 424f2b3
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 20 deletions.
11 changes: 6 additions & 5 deletions src/main/java/net/jodah/failsafe/AbstractExecution.java
Expand Up @@ -106,16 +106,17 @@ boolean complete(Object result, Throwable failure, boolean checkArgs) {
boolean maxDurationExceeded = retryPolicy.getMaxDuration() != null
&& elapsedNanos > retryPolicy.getMaxDuration().toNanos();
retriesExceeded = maxRetriesExceeded || maxDurationExceeded;
boolean shouldAbort = retryPolicy.canAbortFor(result, failure);
boolean shouldRetry = !retriesExceeded && !shouldAbort && checkArgs && retryPolicy.canRetryFor(result, failure);
completed = shouldAbort || !shouldRetry;
success = completed && !shouldRetry && !shouldAbort && failure == null;
boolean isAbortable = retryPolicy.canAbortFor(result, failure);
boolean isRetryable = retryPolicy.canRetryFor(result, failure);
boolean shouldRetry = !retriesExceeded && checkArgs && !isAbortable && retryPolicy.allowsRetries() && isRetryable;
completed = isAbortable || !shouldRetry;
success = completed && !isAbortable && !isRetryable && failure == null;

// Call listeners
if (listeners != null) {
if (!success)
listeners.handleFailedAttempt(result, failure, this);
if (shouldAbort)
if (isAbortable)
listeners.handleAbort(result, failure, this);
else {
if (retriesExceeded)
Expand Down
28 changes: 13 additions & 15 deletions src/main/java/net/jodah/failsafe/RetryPolicy.java
Expand Up @@ -129,8 +129,18 @@ public RetryPolicy abortWhen(Object result) {
}

/**
* Returns whether an execution can be aborted for the {@code result} and {@code failure} according to the configured
* abort conditions.
* Returns whether the policy allows retries according to the configured {@link #withMaxRetries(int) maxRetries} and
* {@link #withMaxDuration(long, TimeUnit) maxDuration}.
*
* @see #withMaxRetries(int)
* @see #withMaxDuration(long, TimeUnit)
*/
public boolean allowsRetries() {
return (maxRetries == -1 || maxRetries > 0) && (maxDuration == null || maxDuration.toNanos() > 0);
}

/**
* Returns whether an execution result can be aborted given the configured abort conditions.
*
* @see #abortIf(BiPredicate)
* @see #abortIf(Predicate)
Expand All @@ -148,16 +158,7 @@ public boolean canAbortFor(Object result, Throwable failure) {
}

/**
* Returns whether an execution can be retried according to the configured maxRetries and maxDuration.
*/
public boolean canRetry() {
return (maxRetries == -1 || maxRetries > 0) && (maxDuration == null || maxDuration.toNanos() > 0);
}

/**
* Returns whether an execution can be retried for the {@code result} and {@code failure} according to configured
* retry conditions, or if the {@code failure} is not null and no retry condition has been configured to check
* failures.
* Returns whether an execution result can be retried given the configured abort conditions.
*
* @see #retryIf(BiPredicate)
* @see #retryIf(Predicate)
Expand All @@ -167,9 +168,6 @@ public boolean canRetry() {
* @see #retryWhen(Object)
*/
public boolean canRetryFor(Object result, Throwable failure) {
if (!canRetry())
return false;

for (BiPredicate<Object, Throwable> predicate : retryConditions) {
if (predicate.test(result, failure))
return true;
Expand Down
49 changes: 49 additions & 0 deletions src/test/java/net/jodah/failsafe/issues/Issue36.java
@@ -0,0 +1,49 @@
package net.jodah.failsafe.issues;

import static org.testng.Assert.assertEquals;

import java.util.concurrent.atomic.AtomicInteger;

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import net.jodah.failsafe.Failsafe;
import net.jodah.failsafe.RetryPolicy;

/**
* https://github.com/jhalterman/failsafe/issues/36
*/
@Test
@SuppressWarnings("unchecked")
public class Issue36 {
RetryPolicy retryPolicy = new RetryPolicy().retryWhen(false).retryOn(Exception.class).withMaxRetries(3);
AtomicInteger calls;
AtomicInteger failedAttempts;
AtomicInteger retries;

@BeforeMethod
protected void beforeMethod() {
calls = new AtomicInteger();
failedAttempts = new AtomicInteger();
retries = new AtomicInteger();
}

public void test() {
Failsafe.with(retryPolicy)
.onFailedAttempt(e -> failedAttempts.incrementAndGet())
.onRetry(e -> retries.incrementAndGet())
.get(() -> {
calls.incrementAndGet();
return false;
});

// Then
assertCounters();
}

void assertCounters() {
assertEquals(calls.get(), 4);
assertEquals(failedAttempts.get(), 4);
assertEquals(retries.get(), 3);
}
}

0 comments on commit 424f2b3

Please sign in to comment.