package org.corfudb.util.retry;
import lombok.SneakyThrows;
import java.util.function.Consumer;
/**
* The IRetry interface is used to support retry loops by using different strategies
* for the pause between the retries. It is done by a builder, that needs the
* correct strategy and the lambda to be executed. The code in the run function is responsible
* to handle the exceptions and to decide whether a retry is needed or the exception should be
* thrown outside.
* <p>
* Created by mwei on 9/1/15
*/
public interface IRetry<E extends Exception, F extends Exception, G extends Exception, H extends Exception, O, A extends IRetry> {
static <R, Z extends IRetry> IRetry<RuntimeException, RuntimeException, RuntimeException, RuntimeException, R, Z>
build(Class<Z> retryType, IRetryable<RuntimeException, RuntimeException, RuntimeException, RuntimeException, R> runFunction) {
return build(retryType, RuntimeException.class, RuntimeException.class, RuntimeException.class, RuntimeException.class, runFunction);
}
static <T extends Exception, R, Z extends IRetry> IRetry<T, RuntimeException, RuntimeException, RuntimeException, R, Z>
build(Class<Z> retryType, Class<? extends T> firstExceptionType, IRetryable<T, RuntimeException, RuntimeException, RuntimeException, R> runFunction) {
return build(retryType, firstExceptionType, RuntimeException.class, RuntimeException.class, RuntimeException.class, runFunction);
}
static <T extends Exception, U extends Exception, R, Z extends IRetry> IRetry<T, U, RuntimeException, RuntimeException, R, Z>
build(Class<Z> retryType, Class<? extends T> firstExceptionType, Class<? extends U> secondExceptionType, IRetryable<T, U, RuntimeException, RuntimeException, R> runFunction) {
return build(retryType, firstExceptionType, secondExceptionType, RuntimeException.class, RuntimeException.class, runFunction);
}
static <T extends Exception, U extends Exception, V extends Exception, R, Z extends IRetry> IRetry<T, U, V, RuntimeException, R, Z>
build(Class<Z> retryType, Class<? extends T> firstExceptionType, Class<? extends U> secondExceptionType, Class<? extends V> thirdExceptionType, IRetryable<T, U, V, RuntimeException, R> runFunction) {
return build(retryType, firstExceptionType, secondExceptionType, thirdExceptionType, RuntimeException.class, runFunction);
}
@SneakyThrows
static <T extends Exception, U extends Exception, V extends Exception, W extends Exception, R, Z extends IRetry>
IRetry<T, U, V, W, R, Z> build(Class<Z> retryType, Class<? extends T> firstExceptionType, Class<? extends U> secondExceptionType,
Class<? extends V> thirdExceptionType, Class<? extends W> fourthExceptionType, IRetryable<T, U, V, W, R> runFunction) {
return (IRetry<T, U, V, W, R, Z>) retryType.getConstructor(IRetryable.class).newInstance(runFunction);
}
/**
* Run the retry. This function may never return.
*/
default O run()
throws E, F, G, H, InterruptedException {
while (true) {
try {
return getRunFunction().retryFunction();
} catch (RetryNeededException ex) {
// retry requested
nextWait();
}
}
}
/**
* Get the function that needs to be retried.
*/
IRetryable<E, F, G, H, O> getRunFunction();
/**
* Configure settings for the underlying class.
* The consumer will be given access to the underlying retry type to configure type-specific options.
*
* @param settingsFunction A consumer with access to the underlying retry type.
* @return An IRetry, to support a fluent API interface.
*/
@SuppressWarnings("unchecked")
default IRetry<E, F, G, H, O, A> setOptions(Consumer<A> settingsFunction) {
settingsFunction.accept((A) this);
return this;
}
/**
* Apply the retry logic.
*
* @return True, if we should continue retrying, false otherwise.
*/
void nextWait() throws InterruptedException;
}