Created
February 19, 2019 13:07
-
-
Save henrrich/f04d09d5df68e14762832f6a85d8251c to your computer and use it in GitHub Desktop.
a java retry util implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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