diff --git a/README.md b/README.md index 9c89eec..440e4c9 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,10 @@ Retrier retrier = new Retrier.Builder().withWaitStrategy(Retrier.Strategies.wait Alternatively, it is possible to use a provided exponential wait strategy which increases the wait between retries: ```java Retrier retrier = new Retrier.Builder().withWaitStrategy(Retrier.Strategies.waitExponential()).build(); +//same as +Retrier retrier = new Retrier.Builder().withWaitStrategy(Retrier.Strategies.waitExponential(1, 2)).build(); +//wait 1s, 2s, 4s, ... between retries +Retrier retrier = new Retrier.Builder().withWaitStrategy(Retrier.Strategies.waitExponential(1000, 2)).build(); ``` #### resultRetryStrategy diff --git a/src/main/java/io/github/alexo/retrier/Retrier.java b/src/main/java/io/github/alexo/retrier/Retrier.java index 6b59c6c..a4c44b8 100644 --- a/src/main/java/io/github/alexo/retrier/Retrier.java +++ b/src/main/java/io/github/alexo/retrier/Retrier.java @@ -12,21 +12,21 @@ * * @author Alex Objelean */ -public class Retrier { +public class Retrier { @FunctionalInterface - public interface GiveUpStrategy { - T whenNoMoreAttempts(T lastResult, Exception lastException) throws Exception; + public interface GiveUpStrategy { + T whenNoMoreAttempts(T lastResult, Exception lastException) throws Exception; } - private static final Retrier SINGLE_ATTEMPT = new Retrier.Builder().withStopStrategy(Strategies.stopAfter(1)) + private static final Retrier SINGLE_ATTEMPT = new Retrier.Builder().withStopStrategy(Strategies.stopAfter(1)) .build(); /** * Retrier that executes every operation exactly once. The operation is never retried. All exceptions are propagated * to the caller. */ - public static Retrier singleAttempt() { + public static Retrier singleAttempt() { return SINGLE_ATTEMPT; } @@ -37,7 +37,7 @@ public static Retrier singleAttempt() { /** * Strategies used to check if the retry is required given a successful execution with a result. */ - private final Predicate resultRetryStrategy; + private final Predicate resultRetryStrategy; /** * Strategies used to check if the retry should be stopped given the provided number of attempts already performed. * Useful to limit the total number of attempts to a bounded value. By default this value is unbounded. @@ -51,7 +51,7 @@ public static Retrier singleAttempt() { /** * What to do when all attempts have been exhausted and the retrier still wasn't able to perform the operation. */ - private final GiveUpStrategy giveUpStrategy; + private final GiveUpStrategy giveUpStrategy; /** * Utility class responsible for creating several useful types of wait strategy used by Retrier. @@ -59,13 +59,13 @@ public static Retrier singleAttempt() { * @author Alex Objelean */ public static class Strategies { - public static Function waitExponential(final double backoffBase) { + public static Function waitExponential(final long startWaitMillis, final double backoffBase) { return attempts -> { if (attempts > 0) { - final double backoffMillis = Math.pow(backoffBase, attempts); + final double backoffMillis = startWaitMillis * Math.pow(backoffBase, attempts); return Math.min(1000L, Math.round(backoffMillis)); } - return 0l; + return 0L; }; } @@ -78,7 +78,11 @@ public static Function waitConstantly(final long delay) { } public static Function waitExponential() { - return waitExponential(2); + return waitExponential(2.0D); + } + + public static Function waitExponential(final double backoffBase) { + return waitExponential(1L, 2.0D); } /** @@ -99,51 +103,48 @@ public static Predicate retryOn(Class... excepti /** * Default builder will retry on any exception for unlimited number of times without waiting between executions. */ - public static class Builder { + public static class Builder { private Predicate failedRetryStrategy = e -> true; - private GiveUpStrategy giveUpStrategy = new GiveUpStrategy() { - @Override - public T whenNoMoreAttempts(final T lastResult, final Exception lastException) throws Exception { - if (lastException != null) { - throw lastException; - } else { - return lastResult; - } + private final GiveUpStrategy giveUpStrategy = (lastResult, lastException) -> { + if (lastException != null) { + throw lastException; + } else { + return lastResult; } }; - private Predicate resultRetryStrategy = e -> false; + private Predicate resultRetryStrategy = e -> false; private Predicate stopStrategy = attempt -> false; - private Function waitStrategy = attempt -> 0l; + private Function waitStrategy = attempt -> 0L; - public Retrier build() { - return new Retrier(failedRetryStrategy, resultRetryStrategy, stopStrategy, waitStrategy, giveUpStrategy); + public Retrier build() { + return new Retrier(failedRetryStrategy, resultRetryStrategy, stopStrategy, waitStrategy, giveUpStrategy); } - public Builder withFailedRetryStrategy(final Predicate failedRetryStrategy) { + public Builder withFailedRetryStrategy(final Predicate failedRetryStrategy) { this.failedRetryStrategy = requireNonNull(failedRetryStrategy); return this; } - public Builder withResultRetryStrategy(final Predicate resultRetryStrategy) { + public Builder withResultRetryStrategy(final Predicate resultRetryStrategy) { this.resultRetryStrategy = requireNonNull(resultRetryStrategy); return this; } - public Builder withStopStrategy(final Predicate stopStrategy) { + public Builder withStopStrategy(final Predicate stopStrategy) { this.stopStrategy = requireNonNull(stopStrategy); return this; } - public Builder withWaitStrategy(final Function waitStrategy) { + public Builder withWaitStrategy(final Function waitStrategy) { this.waitStrategy = requireNonNull(waitStrategy); return this; } } - private Retrier(final Predicate exceptionRetryStrategy, final Predicate resultRetryStrategy, + private Retrier(final Predicate exceptionRetryStrategy, final Predicate resultRetryStrategy, final Predicate stopStrategy, final Function waitStrategy, - final GiveUpStrategy giveUpStrategy) { + final GiveUpStrategy giveUpStrategy) { this.exceptionRetryStrategy = exceptionRetryStrategy; this.resultRetryStrategy = resultRetryStrategy; this.stopStrategy = stopStrategy; @@ -160,7 +161,7 @@ private Retrier(final Predicate exceptionRetryStrategy, final Predica * @return the result of callable invocation. * @throws Exception if the original callback execution failed and {@link Retrier} has decided to stop retrying. */ - public T execute(final Callable callable) throws Exception { + public T execute(final Callable callable) throws Exception { int attempts = 0; boolean shouldRetry; boolean attemptFailed = false; diff --git a/src/test/java/io/github/alexo/retrier/RetrierTest.java b/src/test/java/io/github/alexo/retrier/RetrierTest.java index 6bb51ed..0aa0842 100644 --- a/src/test/java/io/github/alexo/retrier/RetrierTest.java +++ b/src/test/java/io/github/alexo/retrier/RetrierTest.java @@ -23,7 +23,7 @@ public class RetrierTest { private static final int MAX_ATTEPTS = 10; @Mock private Callable callable; - private Retrier victim; + private Retrier victim; @BeforeMethod public void setUp() { @@ -33,11 +33,11 @@ public void setUp() { victim = createDefaultBuilder().build(); } - private Retrier.Builder createDefaultBuilder() { - return new Retrier.Builder().withStopStrategy(stopAfter(MAX_ATTEPTS)); + private Retrier.Builder createDefaultBuilder() { + return new Retrier.Builder().withStopStrategy(stopAfter(MAX_ATTEPTS)); } - private Retrier withCustomExceptionFailedStrategy() { + private Retrier withCustomExceptionFailedStrategy() { return createDefaultBuilder().withFailedRetryStrategy(CustomException.class::isInstance).build(); } @@ -46,22 +46,22 @@ private static class CustomException extends Exception { @Test(expectedExceptions = NullPointerException.class) public void cannotBuildWithNullFailedRetryStrategy() { - new Retrier.Builder().withFailedRetryStrategy(null); + new Retrier.Builder<>().withFailedRetryStrategy(null); } @Test(expectedExceptions = NullPointerException.class) public void cannotBuildWithNullResultRetryStrategy() { - new Retrier.Builder().withResultRetryStrategy(null); + new Retrier.Builder<>().withResultRetryStrategy(null); } @Test(expectedExceptions = NullPointerException.class) public void cannotBuildWithNullStopStrategy() { - new Retrier.Builder().withStopStrategy(null); + new Retrier.Builder<>().withStopStrategy(null); } @Test(expectedExceptions = NullPointerException.class) public void cannotBuildWithNullWaitStrategy() { - new Retrier.Builder().withWaitStrategy(null); + new Retrier.Builder<>().withWaitStrategy(null); } @Test @@ -126,7 +126,7 @@ public void shouldInvokeOnlyOnceWhenFirstCallDoesNotFail() throws Exception { public void shouldRetryWhenUsingCustomRetryStrategy() throws Exception { final int retryResult = 0; // retry as long as result is 0 - victim = new Retrier.Builder().withResultRetryStrategy(i -> i.equals(retryResult)) + victim = new Retrier.Builder<>().withResultRetryStrategy(i -> i.equals(retryResult)) .withStopStrategy(stopAfter(MAX_ATTEPTS)).build(); doAnswer(i -> retryResult).doAnswer(i -> retryResult).doAnswer(i -> retryResult + 1) .when(callable).call();