Skip to content

Instantly share code, notes, and snippets.

@henrrich
Created February 19, 2019 13:07
Show Gist options
  • Save henrrich/f04d09d5df68e14762832f6a85d8251c to your computer and use it in GitHub Desktop.
Save henrrich/f04d09d5df68e14762832f6a85d8251c to your computer and use it in GitHub Desktop.
a java retry util implementation
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
public class RetryUtil {
private static final int TRY_MULTIPLE_TIMES_DEFAULT_SLEEP = 100;
public static class Backoff {
public final static Backoff NONE = new Backoff(TRY_MULTIPLE_TIMES_DEFAULT_SLEEP, sleepDuration -> sleepDuration);
public final static Backoff EXPONENTIAL = new Backoff(TRY_MULTIPLE_TIMES_DEFAULT_SLEEP, sleepDuration -> sleepDuration * 2);
private int initialSleepDuration;
private Function<Integer, Integer> sleepDurationCalculator;
public Backoff(int initialSleepDuration, Function<Integer, Integer> sleepDurationCalculator) {
this.initialSleepDuration = initialSleepDuration;
this.sleepDurationCalculator = sleepDurationCalculator;
}
public int getInitialSleepDuration() {
return initialSleepDuration;
}
public Function<Integer, Integer> getSleepDurationCalculator() {
return sleepDurationCalculator;
}
}
/**
* {@code threadSleep} is wrapper around {@code thread.sleep} the catches the {@code InterruptedException} and
* re-throws it as a {@code RuntimeException}
* @param millis the length of time to sleep in milliseconds
*/
public static void threadSleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void tryMultipleTimes(int tries, Runnable test, boolean retryOnFailure) {
tryMultipleTimes(tries, test, retryOnFailure, Backoff.NONE);
}
public static void tryMultipleTimes(int tries, Runnable test, boolean retryOnFailure, Backoff backoff) {
Runnable nop = () -> {};
tryMultipleTimes(tries, test, nop, retryOnFailure, backoff);
}
public static void tryMultipleTimes(int tries, Runnable test, Runnable reRun, boolean retryOnFailure, Backoff backoff) {
if (retryOnFailure) {
tryMultipleTimesRetryOnFailure(tries, test, reRun, backoff.getInitialSleepDuration(), backoff.getSleepDurationCalculator());
} else {
tryMultipleTimesFailOnFailure(tries, test, reRun, backoff.getInitialSleepDuration(), backoff.getSleepDurationCalculator());
}
}
private static void tryMultipleTimesRetryOnFailure(int tries, Runnable test, Runnable reRunOnFailure, int sleepDuration, Function<Integer, Integer> sleepDurationCalculator) {
log.debug("Trying multiple times, retrying on failure. Try counter: " + tries);
try {
test.run();
} catch (Throwable e) {
if (tries > 1) {
log.debug("Failed, trying again. Try counter: " + tries);
threadSleep(sleepDuration);
reRunOnFailure.run();
tryMultipleTimesRetryOnFailure(--tries, test, reRunOnFailure, sleepDurationCalculator.apply(sleepDuration), sleepDurationCalculator);
} else {
log.debug("Failed max number of tries, giving up");
throw e;
}
}
}
private static void tryMultipleTimesFailOnFailure(int tries, Runnable test, Runnable reRunOnSuccess, int sleepDuration, Function<Integer, Integer> sleepDurationCalculator) {
log.debug("Trying multiple times, failing on failure. Try counter: " + tries);
try {
test.run();
} catch (Throwable e) {
log.debug("Failed, giving up");
throw e;
}
if (tries > 1) {
log.debug("Successful, trying again. Decreasing try counter: " + tries);
threadSleep(sleepDuration);
reRunOnSuccess.run();
tryMultipleTimesFailOnFailure(--tries, test, reRunOnSuccess, sleepDurationCalculator.apply(sleepDuration), sleepDurationCalculator);
}
}
/**
* This method runs the {@code supplier} until it returns a non null response or it has been run the
* maximum number of {@code tries}. Between each try it waits awhile before trying again.
*
* @param tries the number of times the supplier should be executed trying to get a value from it.
* @param supplier the code to run until it returns a non null value or the maximum number of {@code tries} has been reached.
* @param <T> the type on the result from the {@code supplier}
* @return an {@code Optional} of type {@code T} that contains the result from the {@code supplier}.
*/
public static <T> Optional<T> tryMultipleTimes(int tries, Supplier<T> supplier) {
return tryMultipleTimes(tries, supplier, TRY_MULTIPLE_TIMES_DEFAULT_SLEEP);
}
public static <T> Optional<T> tryMultipleTimes(int tries, Supplier<T> supplier, int delay) {
int initialValue = tries;
T result = supplier.get();
tries--;
while (tries > 0 && result == null) {
threadSleep(delay);
result = supplier.get();
tries--;
}
log.debug("Tried (%d times) to get a value. Returning: %s.", (initialValue - tries), result == null ? "null" : "non null");
return Optional.ofNullable(result);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment