Skip to content
This repository has been archived by the owner on Mar 30, 2019. It is now read-only.

Commit

Permalink
[#3] Merging various retry policies to allow deterministic RetryPolic…
Browse files Browse the repository at this point in the history
…y irrespective to order of declaration
  • Loading branch information
nurkiewicz committed Jan 5, 2014
1 parent 9a47f69 commit 3c091c2
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 291 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,38 +1,100 @@
package com.blogspot.nurkiewicz.asyncretry.policy;

import com.blogspot.nurkiewicz.asyncretry.RetryContext;
import com.blogspot.nurkiewicz.asyncretry.policy.exception.AbortPredicateRetryPolicy;
import com.blogspot.nurkiewicz.asyncretry.policy.exception.ExceptionClassRetryPolicy;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;

/**
* @author Tomasz Nurkiewicz
* @since 7/16/13, 6:05 PM
*/
public interface RetryPolicy {
public class RetryPolicy {

public static final RetryPolicy DEFAULT = new RetryInfinitelyPolicy();
public static final RetryPolicy DEFAULT = new RetryPolicy();

boolean shouldContinue(RetryContext context);
private final int maxRetries;
private final Set<Class<? extends Throwable>> retryOn;
private final Set<Class<? extends Throwable>> abortOn;
private final Predicate<Throwable> retryPredicate;
private final Predicate<Throwable> abortPredicate;

default RetryPolicy retryOn(Class<? extends Throwable>... retryOnThrowables) {
return ExceptionClassRetryPolicy.retryOn(this, retryOnThrowables);
public RetryPolicy retryOn(Class<? extends Throwable>... retryOnThrowables) {
return new RetryPolicy(maxRetries, setPlusElems(retryOn, retryOnThrowables), abortOn, retryPredicate, abortPredicate);
}

default RetryPolicy abortOn(Class<? extends Throwable>... retryOnThrowables) {
return ExceptionClassRetryPolicy.abortOn(this, retryOnThrowables);
public RetryPolicy abortOn(Class<? extends Throwable>... abortOnThrowables) {
return new RetryPolicy(maxRetries, retryOn, setPlusElems(abortOn, abortOnThrowables), retryPredicate, abortPredicate);
}

default RetryPolicy abortIf(Predicate<Throwable> retryPredicate) {
return new AbortPredicateRetryPolicy(this, retryPredicate);
public RetryPolicy abortIf(Predicate<Throwable> abortPredicate) {
return new RetryPolicy(maxRetries, retryOn, abortOn, retryPredicate, this.abortPredicate.or(abortPredicate));
}

default RetryPolicy dontRetry() {
return new MaxRetriesPolicy(this, 0);
public RetryPolicy retryIf(Predicate<Throwable> retryPredicate) {
return new RetryPolicy(maxRetries, retryOn, abortOn, this.retryPredicate.or(retryPredicate), abortPredicate);
}

default RetryPolicy withMaxRetries(int times) {
return new MaxRetriesPolicy(this, times);
public RetryPolicy dontRetry() {
return new RetryPolicy(0, retryOn, abortOn, retryPredicate, abortPredicate);
}

public RetryPolicy withMaxRetries(int times) {
return new RetryPolicy(times, retryOn, abortOn, retryPredicate, abortPredicate);
}

public RetryPolicy(int maxRetries, Set<Class<? extends Throwable>> retryOn, Set<Class<? extends Throwable>> abortOn, Predicate<Throwable> retryPredicate, Predicate<Throwable> abortPredicate) {
this.maxRetries = maxRetries;
this.retryOn = retryOn;
this.abortOn = abortOn;
this.retryPredicate = retryPredicate;
this.abortPredicate = abortPredicate;
}

public RetryPolicy() {
this(Integer.MAX_VALUE, Collections.emptySet(), Collections.emptySet(), th -> false, th -> false);
}

public boolean shouldContinue(RetryContext context) {
if (tooManyRetries(context)) {
return false;
}
if (abortPredicate.test(context.getLastThrowable())) {
return false;
}
if (retryPredicate.test(context.getLastThrowable())) {
return true;
}
return exceptionClassRetryable(context);
}

private boolean tooManyRetries(RetryContext context) {
return context.getRetryCount() > maxRetries;
}

private boolean exceptionClassRetryable(RetryContext context) {
if (context.getLastThrowable() == null) {
return false;
}
final Class<? extends Throwable> e = context.getLastThrowable().getClass();
if (abortOn.isEmpty()) {
return matches(e, retryOn);
} else {
return !matches(e, abortOn) && matches(e, retryOn);
}
}

private static boolean matches(Class<? extends Throwable> throwable, Set<Class<? extends Throwable>> set) {
return set.isEmpty() || set.stream().anyMatch(c -> c.isAssignableFrom(throwable));
}

private static <T> Set<T> setPlusElems(Set<T> initial, T... newElement) {
final HashSet<T> copy = new HashSet<>(initial);
copy.addAll(Arrays.asList(newElement));
return Collections.unmodifiableSet(copy);
}

}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.blogspot.nurkiewicz.asyncretry.AbstractBaseTestCase;
import org.testng.annotations.Test;

import static com.blogspot.nurkiewicz.asyncretry.policy.MaxRetriesPolicy.DEFAULT_MAX_RETRIES;
import static org.fest.assertions.api.Assertions.assertThat;

/**
Expand All @@ -12,26 +11,10 @@
*/
public class MaxRetriesPolicyTest extends AbstractBaseTestCase {

@Test
public void shouldStopAfterDefaultNumberOfRetries() throws Exception {
//given
final RetryPolicy retryPolicy = new MaxRetriesPolicy(new RetryInfinitelyPolicy());

//when
final boolean firstRetry = retryPolicy.shouldContinue(retry(1));
final boolean lastRetry = retryPolicy.shouldContinue(retry(DEFAULT_MAX_RETRIES));
final boolean tooManyRetries = retryPolicy.shouldContinue(retry(DEFAULT_MAX_RETRIES + 1));

//then
assertThat(firstRetry).isTrue();
assertThat(lastRetry).isTrue();
assertThat(tooManyRetries).isFalse();
}

@Test
public void shouldStopAfterConfiguredNumberOfRetries() throws Exception {
//given
final RetryPolicy retryPolicy = new RetryInfinitelyPolicy().withMaxRetries(7);
final RetryPolicy retryPolicy = new RetryPolicy().withMaxRetries(7);

//when
final boolean firstRetry = retryPolicy.shouldContinue(retry(1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,10 @@ public class AbortPredicateRetryPolicyTest extends AbstractExceptionClassRetryPo
@Mock
private RetryContext retryContextMock;

@Mock
private RetryPolicy retryPolicyMock;

@Test
public void shouldAbortIfPredicateTrue() throws Exception {
//given
final RetryPolicy retryPolicy = always.abortIf(t -> true);
final RetryPolicy retryPolicy = new RetryPolicy().abortIf(t -> true);

//when
final boolean shouldRetry = retryPolicy.shouldContinue(retryContextMock);
Expand All @@ -36,9 +33,8 @@ public void shouldAbortIfPredicateTrue() throws Exception {
@Test
public void shouldProceedIfPredicateFalseAndChildAccepts() throws Exception {
//given
final RetryPolicy retryPolicy = new AbortPredicateRetryPolicy(retryPolicyMock, t -> false);
given(retryPolicyMock.shouldContinue(notNullRetryContext())).willReturn(true);

final RetryPolicy retryPolicy = new RetryPolicy().abortIf(t -> false);
given(retryContextMock.getLastThrowable()).willReturn(new RuntimeException());

//when
final boolean shouldRetry = retryPolicy.shouldContinue(retryContextMock);
Expand All @@ -48,11 +44,9 @@ public void shouldProceedIfPredicateFalseAndChildAccepts() throws Exception {
}

@Test
public void shouldAbortIfPredicateFalseAndChildAborts() throws Exception {
public void shouldAbortIfPredicateFalseButShouldNotRetry() throws Exception {
//given
final RetryPolicy retryPolicy = new AbortPredicateRetryPolicy(retryPolicyMock, t -> false);
given(retryPolicyMock.shouldContinue(notNullRetryContext())).willReturn(false);

final RetryPolicy retryPolicy = new RetryPolicy().abortIf(t -> false).dontRetry();

//when
final boolean shouldRetry = retryPolicy.shouldContinue(retryContextMock);
Expand All @@ -64,8 +58,7 @@ public void shouldAbortIfPredicateFalseAndChildAborts() throws Exception {
@Test
public void shouldExamineExceptionAndDecide() throws Exception {
//given
final RetryPolicy retryPolicy = new AbortPredicateRetryPolicy(retryPolicyMock, t -> t.getMessage().contains("abort"));
given(retryPolicyMock.shouldContinue(notNullRetryContext())).willReturn(true);
final RetryPolicy retryPolicy = new RetryPolicy().abortIf(t -> t.getMessage().contains("abort"));

//when
final boolean abort = retryPolicy.shouldContinue(new AsyncRetryContext(retryPolicy, 1, new RuntimeException("abort")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.blogspot.nurkiewicz.asyncretry.AbstractBaseTestCase;
import com.blogspot.nurkiewicz.asyncretry.AsyncRetryContext;
import com.blogspot.nurkiewicz.asyncretry.policy.RetryInfinitelyPolicy;
import com.blogspot.nurkiewicz.asyncretry.policy.RetryPolicy;

/**
Expand All @@ -13,8 +12,6 @@ public class AbstractExceptionClassRetryPolicyTest extends AbstractBaseTestCase

private static final int ANY_RETRY = 7;

protected final RetryPolicy always = new RetryInfinitelyPolicy();

protected boolean shouldRetryOn(RetryPolicy policy, Throwable lastThrowable) {
return policy.shouldContinue(new AsyncRetryContext(policy, ANY_RETRY, lastThrowable));
}
Expand Down
Loading

0 comments on commit 3c091c2

Please sign in to comment.