fr.free.jnizet.retry.RetryerBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of guava-retrying Show documentation
Show all versions of guava-retrying Show documentation
This is a small extension to Google's Guava library to allow for the creation of configurable retrying strategies for an arbitrary function call, such as something that talks to a remote service with flaky uptime.
package fr.free.jnizet.retry;
import javax.annotation.Nonnull;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
/**
* A builder used to configure and create a {@link Retryer}.
* @author JB
*
* @param the type of the retryer return value
*/
public class RetryerBuilder {
private StopStrategy stopStrategy;
private WaitStrategy waitStrategy;
private Predicate> rejectionPredicate = Predicates.>alwaysFalse();
private RetryerBuilder() {
}
/**
* Constructs a new builder
* @return the new builder
*/
public static RetryerBuilder newBuilder() {
return new RetryerBuilder();
}
/**
* Sets the wait strategy used to decide how long to sleep between failed attempts.
* The default strategy is to retry immediately after a failed attempt.
* @param waitStrategy the strategy used to sleep between failed attempts
* @return this
* @throws IllegalStateException if a wait strategy has already been set.
*/
public RetryerBuilder withWaitStrategy(@Nonnull WaitStrategy waitStrategy) throws IllegalStateException {
Preconditions.checkNotNull(waitStrategy, "waitStrategy may not be null");
Preconditions.checkState(this.waitStrategy == null, "a wait strategy has already been set %s", this.waitStrategy);
this.waitStrategy = waitStrategy;
return this;
}
/**
* Sets the wait strategy used to decide . The default strategy
* is to not sleep at all between attempts.
* @param stopStrategy the strategy used to sleep between failed attempts
* @return this
* @throws IllegalStateException if a stop strategy has already been set.
*/
public RetryerBuilder withStopStrategy(StopStrategy stopStrategy) {
Preconditions.checkNotNull(stopStrategy, "stopStrategy may not be null");
Preconditions.checkState(this.stopStrategy == null, "a stop strategy has already been set %s", this.stopStrategy);
this.stopStrategy = stopStrategy;
return this;
}
/**
* Configures the retryer to retry if an exception (i.e. any Exception
or subclass
* of Exception
) is thrown by the call.
* @return this
*/
public RetryerBuilder retryIfException() {
rejectionPredicate = Predicates.or(rejectionPredicate, new ExceptionClassPredicate(Exception.class));
return this;
}
/**
* Configures the retryer to retry if a runtime exception (i.e. any RuntimeException
or subclass
* of RuntimeException
) is thrown by the call.
* @return this
*/
public RetryerBuilder retryIfRuntimeException() {
rejectionPredicate = Predicates.or(rejectionPredicate, new ExceptionClassPredicate(RuntimeException.class));
return this;
}
/**
* Configures the retryer to retry if an exception of the given class (or subclass of the given class) is
* thrown by the call.
* @param exceptionClass the type of the exception which should cause the retryer to retry
* @return this
*/
public RetryerBuilder retryIfExceptionOfType(@Nonnull Class extends Throwable> exceptionClass) {
Preconditions.checkNotNull(exceptionClass, "exceptionClass may not be null");
rejectionPredicate = Predicates.or(rejectionPredicate, new ExceptionClassPredicate(exceptionClass));
return this;
}
/**
* Configures the retryer to retry if an exception satisfying the given predicate is
* thrown by the call.
* @param exceptionPredicate the predicate which causes a retry if satisfied
* @return this
*/
public RetryerBuilder retryIfException(@Nonnull Predicate exceptionPredicate) {
Preconditions.checkNotNull(exceptionPredicate, "exceptionPredicate may not be null");
rejectionPredicate = Predicates.or(rejectionPredicate, new ExceptionPredicate(exceptionPredicate));
return this;
}
/**
* Configures the retryer to retry if the result satisfies the given predicate.
* @param resultPredicate a predicate applied to the result, and which causes the retryer
* to retry if the predicate is satisfied
* @return this
*/
public RetryerBuilder retryIfResult(@Nonnull Predicate resultPredicate) {
Preconditions.checkNotNull(resultPredicate, "resultPredicate may not be null");
rejectionPredicate = Predicates.or(rejectionPredicate, new ResultPredicate(resultPredicate));
return this;
}
/**
* Builds the retryer.
* @return the built retryer.
*/
public Retryer build() {
StopStrategy theStopStrategy = stopStrategy == null ? StopStrategies.neverStop() : stopStrategy;
WaitStrategy theWaitStrategy = waitStrategy == null ? WaitStrategies.noWait() : waitStrategy;
return new Retryer(theStopStrategy, theWaitStrategy, rejectionPredicate);
}
private static final class ExceptionClassPredicate implements Predicate> {
private Class extends Throwable> exceptionClass;
public ExceptionClassPredicate(Class extends Throwable> exceptionClass) {
this.exceptionClass = exceptionClass;
}
@Override
public boolean apply(Attempt attempt) {
if (!attempt.hasException()) {
return false;
}
return exceptionClass.isAssignableFrom(attempt.getExceptionCause().getClass());
}
}
private static final class ResultPredicate implements Predicate> {
private Predicate delegate;
public ResultPredicate(Predicate delegate) {
this.delegate = delegate;
}
@Override
public boolean apply(Attempt attempt) {
if (!attempt.hasResult()) {
return false;
}
V result = attempt.getResult();
return delegate.apply(result);
}
}
private static final class ExceptionPredicate implements Predicate> {
private Predicate delegate;
public ExceptionPredicate(Predicate delegate) {
this.delegate = delegate;
}
@Override
public boolean apply(Attempt attempt) {
if (!attempt.hasException()) {
return false;
}
return delegate.apply(attempt.getExceptionCause());
}
}
}