package com.nurkiewicz.asyncretry.policy; import com.nurkiewicz.asyncretry.RetryContext; 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 class RetryPolicy { public static final RetryPolicy DEFAULT = new RetryPolicy(); 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; public RetryPolicy retryOn(Class<? extends Throwable>... retryOnThrowables) { return new RetryPolicy(maxRetries, setPlusElems(retryOn, retryOnThrowables), abortOn, retryPredicate, abortPredicate); } public RetryPolicy abortOn(Class<? extends Throwable>... abortOnThrowables) { return new RetryPolicy(maxRetries, retryOn, setPlusElems(abortOn, abortOnThrowables), retryPredicate, abortPredicate); } public RetryPolicy abortIf(Predicate<Throwable> abortPredicate) { return new RetryPolicy(maxRetries, retryOn, abortOn, retryPredicate, this.abortPredicate.or(abortPredicate)); } public RetryPolicy retryIf(Predicate<Throwable> retryPredicate) { return new RetryPolicy(maxRetries, retryOn, abortOn, this.retryPredicate.or(retryPredicate), abortPredicate); } 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); } }