All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fitbur.failsafe.RetryPolicy Maven / Gradle / Ivy

The newest version!
package com.fitbur.failsafe;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import com.fitbur.failsafe.function.BiPredicate;
import com.fitbur.failsafe.function.Predicate;
import com.fitbur.failsafe.internal.util.Assert;
import com.fitbur.failsafe.util.Duration;

/**
 * A policy that defines when retries should be performed.
 * 
 * 

* The {@code retryOn} methods describe when a retry should be performed for a particular failure. The {@code retryWhen} * method describes when a retry should be performed for a particular result. If multiple {@code retryOn} or * {@code retryWhen} conditions are specified, any matching condition can allow a retry. The {@code abortOn}, * {@code abortWhen} and {@code abortIf} methods describe when retries should be aborted. * * @author Jonathan Halterman */ public final class RetryPolicy { static final RetryPolicy NEVER = new RetryPolicy().withMaxRetries(0); private Duration delay; private double delayMultiplier; private Duration maxDelay; private Duration maxDuration; private int maxRetries; private boolean failureConditionChecked; private List> retryPredicates; private List> abortPredicates; /** * Creates a retry policy that always retries with no delay. */ public RetryPolicy() { delay = Duration.NONE; maxRetries = -1; abortPredicates = new ArrayList>(); retryPredicates = new ArrayList>(); } /** * Copy constructor. */ public RetryPolicy(RetryPolicy rp) { this.delay = rp.delay; this.delayMultiplier = rp.delayMultiplier; this.maxDelay = rp.maxDelay; this.maxDuration = rp.maxDuration; this.maxRetries = rp.maxRetries; this.retryPredicates = new ArrayList>(rp.retryPredicates); this.abortPredicates = new ArrayList>(rp.abortPredicates); this.failureConditionChecked = rp.failureConditionChecked; } /** * Specifies that retries should be aborted if the {@code completionPredicate} matches the completion result. * * @throws NullPointerException if {@code completionPredicate} is null */ @SuppressWarnings("unchecked") public RetryPolicy abortIf(BiPredicate completionPredicate) { Assert.notNull(completionPredicate, "completionPredicate"); abortPredicates.add((BiPredicate) completionPredicate); return this; } /** * Specifies that retries should be aborted if the {@code resultPredicate} matches the result. * * @throws NullPointerException if {@code resultPredicate} is null */ public RetryPolicy abortIf(Predicate resultPredicate) { Assert.notNull(resultPredicate, "resultPredicate"); abortPredicates.add(Predicates.resultPredicateFor(resultPredicate)); return this; } /** * Specifies when retries should be aborted. Any failure that is assignable from the {@code failures} will be result * in retries being aborted. * * @throws NullPointerException if {@code failures} is null * @throws IllegalArgumentException if failures is empty */ public RetryPolicy abortOn(Class... failures) { Assert.notNull(failures, "failures"); Assert.isTrue(failures.length > 0, "Failures cannot be empty"); return abortOn(Arrays.asList(failures)); } /** * Specifies when retries should be aborted. Any failure that is assignable from the {@code failures} will be result * in retries being aborted. * * @throws NullPointerException if {@code failures} is null * @throws IllegalArgumentException if failures is null or empty */ public RetryPolicy abortOn(List> failures) { Assert.notNull(failures, "failures"); Assert.isTrue(!failures.isEmpty(), "failures cannot be empty"); abortPredicates.add(Predicates.failurePredicateFor(failures)); return this; } /** * Specifies that retries should be aborted if the {@code failurePredicate} matches the failure. * * @throws NullPointerException if {@code failurePredicate} is null */ public RetryPolicy abortOn(Predicate failurePredicate) { Assert.notNull(failurePredicate, "failurePredicate"); abortPredicates.add(Predicates.failurePredicateFor(failurePredicate)); return this; } /** * Specifies that retries should be aborted if the execution result matches the {@code result}. */ public RetryPolicy abortWhen(Object result) { abortPredicates.add(Predicates.resultPredicateFor(result)); return this; } /** * Returns whether an execution can be aborted for the {@code result} and {@code failure} according to the policy. */ public boolean canAbortFor(Object result, Throwable failure) { for (BiPredicate predicate : abortPredicates) { if (predicate.test(result, failure)) return true; } return false; } /** * 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 the policy. */ public boolean canRetryFor(Object result, Throwable failure) { if (!canRetry()) return false; for (BiPredicate predicate : retryPredicates) { if (predicate.test(result, failure)) return true; } return failure != null && !failureConditionChecked; } /** * Returns a copy of this RetryPolicy. */ public RetryPolicy copy() { return new RetryPolicy(this); } /** * Returns the delay between retries. Defaults to {@link Duration#NONE}. * * @see #withDelay(long, TimeUnit) * @see #withBackoff(long, long, TimeUnit) * @see #withBackoff(long, long, TimeUnit, double) */ public Duration getDelay() { return delay; } /** * Returns the delay multiplier for backoff retries. * * @see #withBackoff(long, long, TimeUnit, double) */ public double getDelayMultiplier() { return delayMultiplier; } /** * Returns the max delay between backoff retries. * * @see #withBackoff(long, long, TimeUnit) */ public Duration getMaxDelay() { return maxDelay; } /** * Returns the max duration to perform retries for. * * @see #withMaxDuration(long, TimeUnit) */ public Duration getMaxDuration() { return maxDuration; } /** * Returns the max retries. Defaults to -1, which retries forever. * * @see #withMaxRetries(int) */ public int getMaxRetries() { return maxRetries; } /** * Specifies that a retry should occur if the {@code completionPredicate} matches the completion result and the retry * policy is not exceeded. * * @throws NullPointerException if {@code completionPredicate} is null */ @SuppressWarnings("unchecked") public RetryPolicy retryIf(BiPredicate completionPredicate) { Assert.notNull(completionPredicate, "completionPredicate"); failureConditionChecked = true; retryPredicates.add((BiPredicate) completionPredicate); return this; } /** * Specifies that a retry should occur if the {@code resultPredicate} matches the result and the retry policy is not * exceeded. * * @throws NullPointerException if {@code resultPredicate} is null */ public RetryPolicy retryIf(Predicate resultPredicate) { Assert.notNull(resultPredicate, "resultPredicate"); retryPredicates.add(Predicates.resultPredicateFor(resultPredicate)); return this; } /** * Specifies the failures to retry on. Any failure that is assignable from the {@code failures} will be retried. * * @throws NullPointerException if {@code failures} is null * @throws IllegalArgumentException if failures is empty */ @SuppressWarnings("unchecked") public RetryPolicy retryOn(Class... failures) { Assert.notNull(failures, "failures"); Assert.isTrue(failures.length > 0, "Failures cannot be empty"); return retryOn(Arrays.asList(failures)); } /** * Specifies the failures to retry on. Any failure that is assignable from the {@code failures} will be retried. * * @throws NullPointerException if {@code failures} is null * @throws IllegalArgumentException if failures is null or empty */ public RetryPolicy retryOn(List> failures) { Assert.notNull(failures, "failures"); Assert.isTrue(!failures.isEmpty(), "failures cannot be empty"); failureConditionChecked = true; retryPredicates.add(Predicates.failurePredicateFor(failures)); return this; } /** * Specifies that a retry should occur if the {@code failurePredicate} matches the failure and the retry policy is not * exceeded. * * @throws NullPointerException if {@code failurePredicate} is null */ public RetryPolicy retryOn(Predicate failurePredicate) { Assert.notNull(failurePredicate, "failurePredicate"); failureConditionChecked = true; retryPredicates.add(Predicates.failurePredicateFor(failurePredicate)); return this; } /** * Specifies that a retry should occur if the execution result matches the {@code result} and the retry policy is not * exceeded. */ public RetryPolicy retryWhen(Object result) { retryPredicates.add(Predicates.resultPredicateFor(result)); return this; } /** * Sets the {@code delay} between retries, exponentially backing of to the {@code maxDelay} and multiplying successive * delays by a factor of 2. * * @throws NullPointerException if {@code timeUnit} is null * @throws IllegalArgumentException if {@code delay} is <= 0 or {@code delay} is >= {@code maxDelay} */ public RetryPolicy withBackoff(long delay, long maxDelay, TimeUnit timeUnit) { return withBackoff(delay, maxDelay, timeUnit, 2); } /** * Sets the {@code delay} between retries, exponentially backing of to the {@code maxDelay} and multiplying successive * delays by the {@code delayMultiplier}. * * @throws NullPointerException if {@code timeUnit} is null * @throws IllegalStateException if {@code delay} is >= the maxDuration * @throws IllegalArgumentException if {@code delay} <= 0, {@code delay} is >= {@code maxDelay}, or the * {@code delayMultiplier} is <= 1 */ public RetryPolicy withBackoff(long delay, long maxDelay, TimeUnit timeUnit, double delayMultiplier) { Assert.notNull(timeUnit, "timeUnit"); this.delay = new Duration(delay, timeUnit); this.maxDelay = new Duration(maxDelay, timeUnit); this.delayMultiplier = delayMultiplier; Assert.isTrue(this.delay.toNanos() > 0, "The delay must be greater than 0"); if (maxDuration != null) Assert.state(this.delay.toNanos() < this.maxDuration.toNanos(), "delay must be less than the maxDuration"); Assert.isTrue(this.delay.toNanos() < this.maxDelay.toNanos(), "delay must be less than the maxDelay"); Assert.isTrue(delayMultiplier > 1, "delayMultiplier must be greater than 1"); return this; } /** * Sets the {@code delay} between retries. * * @throws NullPointerException if {@code timeUnit} is null * @throws IllegalArgumentException if {@code delay} <= 0 * @throws IllegalStateException if {@code delay} is >= the maxDuration */ public RetryPolicy withDelay(long delay, TimeUnit timeUnit) { Assert.notNull(timeUnit, "timeUnit"); this.delay = new Duration(delay, timeUnit); Assert.isTrue(this.delay.toNanos() > 0, "delay must be greater than 0"); if (maxDuration != null) Assert.state(this.delay.toNanos() < maxDuration.toNanos(), "delay must be less than the maxDuration"); Assert.state(maxDelay == null, "Backoff delays have already been set"); return this; } /** * Sets the max duration to perform retries for. * * @throws NullPointerException if {@code timeUnit} is null * @throws IllegalStateException if {@code maxDuration} is <= the delay */ public RetryPolicy withMaxDuration(long maxDuration, TimeUnit timeUnit) { Assert.notNull(timeUnit, "timeUnit"); this.maxDuration = new Duration(maxDuration, timeUnit); Assert.state(this.maxDuration.toNanos() > delay.toNanos(), "maxDuration must be greater than the delay"); return this; } /** * Sets the max number of retries to perform. -1 indicates to retry forever. * * @throws IllegalArgumentException if {@code maxRetries} < -1 */ public RetryPolicy withMaxRetries(int maxRetries) { Assert.isTrue(maxRetries >= -1, "maxRetries must be greater than or equal to -1"); this.maxRetries = maxRetries; return this; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy