├── docs
├── package-list
├── script.js
├── allclasses-noframe.html
├── allclasses-frame.html
├── com
│ └── github
│ │ └── rholder
│ │ └── retry
│ │ ├── package-frame.html
│ │ ├── package-tree.html
│ │ ├── WaitStrategy.html
│ │ ├── StopStrategy.html
│ │ ├── BlockStrategy.html
│ │ ├── RetryListener.html
│ │ ├── AttemptTimeLimiter.html
│ │ ├── BlockStrategies.html
│ │ ├── Retryer.RetryerCallable.html
│ │ ├── package-summary.html
│ │ └── RetryException.html
├── index.html
├── constant-values.html
├── deprecated-list.html
├── serialized-form.html
├── overview-tree.html
└── help-doc.html
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── NOTICE
├── .travis.yml
├── src
├── main
│ └── java
│ │ └── com
│ │ └── github
│ │ └── rholder
│ │ └── retry
│ │ ├── WaitStrategy.java
│ │ ├── StopStrategy.java
│ │ ├── RetryListener.java
│ │ ├── AttemptTimeLimiter.java
│ │ ├── BlockStrategy.java
│ │ ├── BlockStrategies.java
│ │ ├── RetryException.java
│ │ ├── Attempt.java
│ │ ├── AttemptTimeLimiters.java
│ │ ├── StopStrategies.java
│ │ ├── Retryer.java
│ │ └── RetryerBuilder.java
└── test
│ └── java
│ └── com
│ └── github
│ └── rholder
│ └── retry
│ ├── AttemptTimeLimitersTest.java
│ ├── AttemptTimeLimiterTest.java
│ ├── StopStrategiesTest.java
│ └── WaitStrategiesTest.java
├── HISTORY.md
├── gradlew.bat
├── gradlew
└── README.md
/docs/package-list:
--------------------------------------------------------------------------------
1 | com.github.rholder.retry
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhuffman/re-retrying/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea
3 | build
4 | out
5 | *.iml
6 | *.ipr
7 | *.iws
8 | fileTemplates
9 | gradle.properties
10 | .DS_Store
11 | classes/
12 | gradle/.DS_STORE
13 | local.properties
14 | .attach_pid*
15 | .#*
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
6 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright 2012-2015 Ray Holder
2 | Modifications copyright 2017-2018 Robert Huffman
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 |
--------------------------------------------------------------------------------
/docs/script.js:
--------------------------------------------------------------------------------
1 | function show(type)
2 | {
3 | count = 0;
4 | for (var key in methods) {
5 | var row = document.getElementById(key);
6 | if ((methods[key] & type) != 0) {
7 | row.style.display = '';
8 | row.className = (count++ % 2) ? rowColor : altColor;
9 | }
10 | else
11 | row.style.display = 'none';
12 | }
13 | updateTabs(type);
14 | }
15 |
16 | function updateTabs(type)
17 | {
18 | for (var value in tabs) {
19 | var sNode = document.getElementById(tabs[value][0]);
20 | var spanNode = sNode.firstChild;
21 | if (value == type) {
22 | sNode.className = activeTableTab;
23 | spanNode.innerHTML = tabs[value][1];
24 | }
25 | else {
26 | sNode.className = tableTab;
27 | spanNode.innerHTML = "" + tabs[value][1] + "";
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2012-2015 Ray Holder
2 | # Modifications copyright 2017-2018 Robert Huffman
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | sudo: false
17 | language: java
18 | jdk:
19 | - oraclejdk8
20 | - openjdk8
21 |
22 | # Avoid uploading the Gradle cache after every build
23 | # See https://docs.travis-ci.com/user/languages/java/#Caching
24 | before_cache:
25 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
26 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
27 | cache:
28 | directories:
29 | - $HOME/.gradle/caches/
30 | - $HOME/.gradle/wrapper/
31 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/WaitStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.rholder.retry;
18 |
19 | /**
20 | * A strategy used to decide how long to sleep before retrying after a failed attempt.
21 | *
22 | * @author JB
23 | */
24 | public interface WaitStrategy {
25 |
26 | /**
27 | * Returns the time, in milliseconds, to sleep before retrying.
28 | *
29 | * @param failedAttempt the previous failed {@code Attempt}
30 | * @return the sleep time before next attempt
31 | */
32 | long computeSleepTime(Attempt failedAttempt);
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/StopStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.rholder.retry;
18 |
19 | /**
20 | * A strategy used to decide if a retryer must stop retrying after a failed attempt or not.
21 | *
22 | * @author JB
23 | */
24 | public interface StopStrategy {
25 |
26 | /**
27 | * Returns true if the retryer should stop retrying.
28 | *
29 | * @param failedAttempt the previous failed {@code Attempt}
30 | * @return true if the retryer must stop, false otherwise
31 | */
32 | boolean shouldStop(Attempt failedAttempt);
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/RetryListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import com.google.common.annotations.Beta;
21 |
22 | /**
23 | * This listener provides callbacks for several events that occur when running
24 | * code through a {@link Retryer} instance.
25 | */
26 | @Beta
27 | public interface RetryListener {
28 |
29 | /**
30 | * This method with fire no matter what the result is and before the
31 | * retry predicate and stop strategies are applied.
32 | *
33 | * @param attempt the current {@link Attempt}
34 | */
35 | void onRetry(Attempt> attempt);
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/AttemptTimeLimiter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import java.util.concurrent.Callable;
21 |
22 | /**
23 | * A rule to wrap any single attempt in a time limit, where it will possibly be interrupted if the limit is exceeded.
24 | *
25 | * @author Jason Dunkelberger (dirkraft)
26 | */
27 | public interface AttemptTimeLimiter {
28 | /**
29 | * @param callable to subject to the time limit
30 | * @return the return of the given callable
31 | * @param The return type of the Callable's call method
32 | * @throws Exception any exception from this invocation
33 | */
34 | T call(Callable callable) throws Exception;
35 | }
36 |
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | ##2.0.0 - 2015-06-30
2 | * Calculate sleep time from failed attempt #25 (yaroslavm)
3 | * Be consistent about "duration" method parameters #30 (Stephan202)
4 | * Use an open Maven dependency range for Guava dependency #32 (voiceinsideyou)
5 | * Add @Beta RetryListener support #36 (kevinconaway)
6 | * Update to Gradle 2.x #38
7 | * Minimal breaking 1.0.x to 2.0.x API changes for Attempt state, hence the major version update
8 |
9 | ##1.0.7 - 2015-01-20
10 | * New composite wait strategy #12 (shasts)
11 | * Adding block strategies to the Retryer to decide how to block (tchdp)
12 |
13 | ##1.0.6 - 2014-03-26
14 | * Javadoc updates for Java 8 (shasts)
15 | * Bug from System.nanoTime() (fror), fix in #15
16 | * Travis CI testing now working for Java 8
17 |
18 | ##1.0.5 - 2013-12-04
19 | * Added Javadoc for all versions
20 | * Added FibonacciWaitStrategy (joschi)
21 | * Updated tested Guava version range from 10.x.x - 15.0 (joschi)
22 | * Updated all dependencies (joschi)
23 | * Updated to Gradle 1.9 (joschi)
24 |
25 | ##1.0.4 - 2013-07-08
26 | * Added tested Guava version range from 10.x.x - 14.0.1
27 | * Added Exception cause propagation to RetryException to fix #3
28 |
29 | ##1.0.3 - 2013-01-16
30 | * Added time limit per attempt in a Retryer (dirkraft)
31 | * Added license text
32 |
33 | ##1.0.2 - 2012-11-22
34 | * Added Gradle wrapper support
35 | * Updated top-level package to com.github.rholder.retry
36 |
37 | ##1.0.1 - 2012-08-29
38 | * Added Javadoc links
39 | * Added exponential wait strategy and unit tests
40 |
41 | ##1.0.0 - 2012-08-26
42 | * Initial stable release, packaging for Maven central, no changes from original source
43 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/BlockStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | /**
21 | * This is a strategy used to decide how a retryer should block between retry
22 | * attempts. Normally this is just a Thread.sleep(), but implementations can be
23 | * something more elaborate if desired.
24 | */
25 | public interface BlockStrategy {
26 |
27 | /**
28 | * Attempt to block for the designated amount of time. Implementations
29 | * that don't block or otherwise delay the processing from within this
30 | * method for the given sleep duration can significantly modify the behavior
31 | * of any configured {@link com.github.rholder.retry.WaitStrategy}. Caution
32 | * is advised when generating your own implementations.
33 | *
34 | * @param sleepTime the computed sleep duration in milliseconds
35 | * @throws InterruptedException If the calling thread is interrupted
36 | */
37 | void block(long sleepTime) throws InterruptedException;
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/BlockStrategies.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import javax.annotation.concurrent.Immutable;
21 |
22 | /**
23 | * Factory class for {@link BlockStrategy} instances.
24 | */
25 | @SuppressWarnings("WeakerAccess")
26 | public final class BlockStrategies {
27 |
28 | private static final BlockStrategy THREAD_SLEEP_STRATEGY = new ThreadSleepStrategy();
29 |
30 | private BlockStrategies() {
31 | }
32 |
33 | /**
34 | * Returns a block strategy that puts the current thread to sleep between
35 | * retries.
36 | *
37 | * @return a block strategy that puts the current thread to sleep between retries
38 | */
39 | public static BlockStrategy threadSleepStrategy() {
40 | return THREAD_SLEEP_STRATEGY;
41 | }
42 |
43 | @Immutable
44 | private static class ThreadSleepStrategy implements BlockStrategy {
45 |
46 | @Override
47 | public void block(long sleepTime) throws InterruptedException {
48 | Thread.sleep(sleepTime);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/com/github/rholder/retry/AttemptTimeLimitersTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * copyright 2017-2018 Robert Huffman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.rholder.retry;
17 |
18 | import com.google.common.collect.Sets;
19 | import org.junit.Test;
20 |
21 | import java.util.Collections;
22 | import java.util.Set;
23 | import java.util.concurrent.Callable;
24 | import java.util.concurrent.TimeUnit;
25 |
26 | import static org.junit.Assert.assertTrue;
27 | import static org.junit.Assert.fail;
28 |
29 | public class AttemptTimeLimitersTest {
30 |
31 | @Test
32 | public void testFixedTimeLimitWithNoExecutorReusesThreads() throws Exception {
33 | Set threadsUsed = Collections.synchronizedSet(Sets.newHashSet());
34 | Callable callable = () -> {
35 | threadsUsed.add(Thread.currentThread().getId());
36 | return null;
37 | };
38 |
39 | int iterations = 20;
40 | for (int i = 0; i < iterations; i++) {
41 | AttemptTimeLimiter timeLimiter =
42 | AttemptTimeLimiters.fixedTimeLimit(1, TimeUnit.SECONDS);
43 | timeLimiter.call(callable);
44 | }
45 | assertTrue("Should have used less than " + iterations +
46 | " threads", threadsUsed.size() < iterations);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/test/java/com/github/rholder/retry/AttemptTimeLimiterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import org.junit.Assert;
21 | import org.junit.Test;
22 |
23 | import java.util.concurrent.Callable;
24 | import java.util.concurrent.TimeUnit;
25 |
26 | /**
27 | * @author Jason Dunkelberger (dirkraft)
28 | */
29 | public class AttemptTimeLimiterTest {
30 |
31 | private final Retryer r = RetryerBuilder.newBuilder()
32 | .withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(1, TimeUnit.SECONDS))
33 | .build();
34 |
35 | @Test
36 | public void testAttemptTimeLimit() throws Exception {
37 | try {
38 | r.call(new SleepyOut(0L));
39 | } catch (Exception e) {
40 | Assert.fail("Should not timeout");
41 | }
42 |
43 | try {
44 | r.call(new SleepyOut(10 * 1000L));
45 | Assert.fail("Expected timeout exception");
46 | } catch (RetryException ignored) {
47 | }
48 | }
49 |
50 | static class SleepyOut implements Callable {
51 |
52 | final long sleepMs;
53 |
54 | SleepyOut(long sleepMs) {
55 | this.sleepMs = sleepMs;
56 | }
57 |
58 | @Override
59 | public Void call() throws Exception {
60 | Thread.sleep(sleepMs);
61 | return null;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/docs/allclasses-noframe.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | All Classes (re-retrying 3.0.1-SNAPSHOT API)
7 |
8 |
9 |
10 |
11 |
12 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/test/java/com/github/rholder/retry/StopStrategiesTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import org.junit.Test;
21 |
22 | import java.util.concurrent.TimeUnit;
23 |
24 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
25 | import static org.junit.Assert.assertFalse;
26 | import static org.junit.Assert.assertTrue;
27 |
28 | public class StopStrategiesTest {
29 |
30 | @Test
31 | public void testNeverStop() {
32 | assertFalse(StopStrategies.neverStop().shouldStop(failedAttempt(3, 6546L)));
33 | }
34 |
35 | @Test
36 | public void testStopAfterAttempt() {
37 | assertFalse(StopStrategies.stopAfterAttempt(3).shouldStop(failedAttempt(2, 6546L)));
38 | assertTrue(StopStrategies.stopAfterAttempt(3).shouldStop(failedAttempt(3, 6546L)));
39 | assertTrue(StopStrategies.stopAfterAttempt(3).shouldStop(failedAttempt(4, 6546L)));
40 | }
41 |
42 | @Test
43 | public void testStopAfterDelayWithMilliseconds() {
44 | assertFalse(StopStrategies.stopAfterDelay(1000, MILLISECONDS)
45 | .shouldStop(failedAttempt(2, 999L)));
46 | assertTrue(StopStrategies.stopAfterDelay(1000, MILLISECONDS)
47 | .shouldStop(failedAttempt(2, 1000L)));
48 | assertTrue(StopStrategies.stopAfterDelay(1000, MILLISECONDS)
49 | .shouldStop(failedAttempt(2, 1001L)));
50 | }
51 |
52 | @Test
53 | public void testStopAfterDelayWithTimeUnit() {
54 | assertFalse(StopStrategies.stopAfterDelay(1, TimeUnit.SECONDS).shouldStop(failedAttempt(2, 999L)));
55 | assertTrue(StopStrategies.stopAfterDelay(1, TimeUnit.SECONDS).shouldStop(failedAttempt(2, 1000L)));
56 | assertTrue(StopStrategies.stopAfterDelay(1, TimeUnit.SECONDS).shouldStop(failedAttempt(2, 1001L)));
57 | }
58 |
59 | private Attempt failedAttempt(int attemptNumber, long delaySinceFirstAttempt) {
60 | return new Attempt<>(new RuntimeException(), attemptNumber, delaySinceFirstAttempt);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/RetryException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import javax.annotation.Nonnull;
21 | import javax.annotation.concurrent.Immutable;
22 |
23 | /**
24 | * An exception indicating that none of the attempts of the {@link Retryer}
25 | * succeeded. If the last {@link Attempt} resulted in an Exception, it is set as
26 | * the cause of the {@link RetryException}.
27 | *
28 | * @author JB
29 | */
30 | @SuppressWarnings("WeakerAccess")
31 | @Immutable
32 | public final class RetryException extends Exception {
33 |
34 | private final Attempt> lastFailedAttempt;
35 |
36 | /**
37 | * If the last {@link Attempt} had an Exception, ensure it is available in
38 | * the stack trace.
39 | *
40 | * @param attempt what happened the last time we failed
41 | */
42 | RetryException(@Nonnull Attempt> attempt) {
43 | this("Retrying failed to complete successfully after " +
44 | attempt.getAttemptNumber() + " attempts.",
45 | attempt);
46 | }
47 |
48 | /**
49 | * If the last {@link Attempt} had an Exception, ensure it is available in
50 | * the stack trace.
51 | *
52 | * @param message Exception description to be added to the stack trace
53 | * @param attempt what happened the last time we failed
54 | */
55 | private RetryException(String message, Attempt> attempt) {
56 | super(message, attempt.hasException() ? attempt.getException() : null);
57 | this.lastFailedAttempt = attempt;
58 | }
59 |
60 | /**
61 | * Returns the number of failed attempts
62 | *
63 | * @return the number of failed attempts
64 | */
65 | public int getNumberOfFailedAttempts() {
66 | return lastFailedAttempt.getAttemptNumber();
67 | }
68 |
69 | /**
70 | * Returns the last failed attempt
71 | *
72 | * @return the last failed attempt
73 | */
74 | public Attempt> getLastFailedAttempt() {
75 | return lastFailedAttempt;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/docs/allclasses-frame.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | All Classes (re-retrying 3.0.1-SNAPSHOT API)
7 |
8 |
9 |
10 |
11 |
12 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/Attempt.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import static com.google.common.base.Preconditions.checkState;
21 |
22 | /**
23 | * An attempt of a call, which resulted either in a result returned by the call,
24 | * or in a Throwable thrown by the call.
25 | *
26 | * @param The type returned by the wrapped callable.
27 | * @author JB
28 | */
29 | @SuppressWarnings("WeakerAccess")
30 | public class Attempt {
31 |
32 | private final T result;
33 |
34 | private final Throwable throwable;
35 |
36 | private final int attemptNumber;
37 |
38 | private final long delaySinceFirstAttempt;
39 |
40 | Attempt(T result, int attemptNumber, long delaySinceFirstAttempt) {
41 | this.result = result;
42 | this.throwable = null;
43 | this.attemptNumber = attemptNumber;
44 | this.delaySinceFirstAttempt = delaySinceFirstAttempt;
45 | }
46 |
47 | Attempt(Throwable throwable, int attemptNumber, long delaySinceFirstAttempt) {
48 | this.result = null;
49 | this.throwable = throwable;
50 | this.attemptNumber = attemptNumber;
51 | this.delaySinceFirstAttempt = delaySinceFirstAttempt;
52 | }
53 |
54 | /**
55 | * Returns the result of the attempt, if any.
56 | *
57 | * @return the result of the attempt
58 | * @throws IllegalStateException If the attempt resulted in an exception rather
59 | * than returning a result.
60 | */
61 | public T get() {
62 | checkState(hasResult(), "The attempt resulted in an exception, not in a result");
63 | return result;
64 | }
65 |
66 | /**
67 | * Tells if the call returned a result or not
68 | *
69 | * @return true if the call returned a result, false
70 | * if it threw an exception
71 | */
72 | public boolean hasResult() {
73 | // Check the exception field, because the Callable may have succeeded and returned null.
74 | // In that case both exception and result will be null.
75 | return throwable == null;
76 | }
77 |
78 | /**
79 | * Tells if the call threw an exception or not
80 | *
81 | * @return true if the call threw an exception, false
82 | * if it returned a result
83 | */
84 | public boolean hasException() {
85 | return throwable != null;
86 | }
87 |
88 | /**
89 | * Gets the result of the call
90 | *
91 | * @return the result of the call
92 | * @throws IllegalStateException if the call didn't return a result, but threw an exception,
93 | * as indicated by {@link #hasResult()}
94 | */
95 | public T getResult() throws IllegalStateException {
96 | return get();
97 | }
98 |
99 | /**
100 | * Gets the exception thrown by the call
101 | *
102 | * @return the exception thrown by the call
103 | * @throws IllegalStateException if the call didn't throw an exception,
104 | * as indicated by {@link #hasException()}
105 | */
106 | public Throwable getException() throws IllegalStateException {
107 | checkState(hasException(), "The attempt resulted in a result, not in an exception");
108 | return throwable;
109 | }
110 |
111 | /**
112 | * The number, starting from 1, of this attempt.
113 | *
114 | * @return the attempt number
115 | */
116 | public int getAttemptNumber() {
117 | return attemptNumber;
118 | }
119 |
120 | /**
121 | * The delay since the start of the first attempt, in milliseconds.
122 | *
123 | * @return the delay since the start of the first attempt, in milliseconds
124 | */
125 | public long getDelaySinceFirstAttempt() {
126 | return delaySinceFirstAttempt;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/AttemptTimeLimiters.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import com.google.common.base.Preconditions;
21 | import com.google.common.util.concurrent.SimpleTimeLimiter;
22 | import com.google.common.util.concurrent.TimeLimiter;
23 |
24 | import javax.annotation.Nonnull;
25 | import javax.annotation.concurrent.Immutable;
26 | import java.util.concurrent.Callable;
27 | import java.util.concurrent.ExecutorService;
28 | import java.util.concurrent.Executors;
29 | import java.util.concurrent.TimeUnit;
30 |
31 | /**
32 | * Factory class for instances of {@link AttemptTimeLimiter}
33 | *
34 | * @author Jason Dunkelberger (dirkraft)
35 | */
36 | @SuppressWarnings("WeakerAccess")
37 | public class AttemptTimeLimiters {
38 |
39 | private AttemptTimeLimiters() {
40 | }
41 |
42 | /**
43 | * @return an {@link AttemptTimeLimiter} impl which has no time limit
44 | */
45 | public static AttemptTimeLimiter noTimeLimit() {
46 | return new NoAttemptTimeLimit();
47 | }
48 |
49 | /**
50 | * For control over thread management, it is preferable to offer an {@link ExecutorService}
51 | * through the other factory method, {@link #fixedTimeLimit(long, TimeUnit, ExecutorService)}.
52 | * All calls to this method use the same cached thread pool created by
53 | * {@link Executors#newCachedThreadPool()}. It is unbounded, meaning there is no limit to
54 | * the number of threads it will create. It will reuse idle threads if they are available,
55 | * and idle threads remain alive for 60 seconds.
56 | *
57 | * @param duration that an attempt may persist before being circumvented
58 | * @param timeUnit of the 'duration' arg
59 | * @return an {@link AttemptTimeLimiter} with a fixed time limit for each attempt
60 | */
61 | public static AttemptTimeLimiter fixedTimeLimit(long duration, @Nonnull TimeUnit timeUnit) {
62 | Preconditions.checkNotNull(timeUnit);
63 | return new FixedAttemptTimeLimit(duration, timeUnit);
64 | }
65 |
66 | /**
67 | * @param duration that an attempt may persist before being circumvented
68 | * @param timeUnit of the 'duration' arg
69 | * @param executorService used to enforce time limit
70 | * @return an {@link AttemptTimeLimiter} with a fixed time limit for each attempt
71 | */
72 | public static AttemptTimeLimiter fixedTimeLimit(
73 | long duration, @Nonnull TimeUnit timeUnit, @Nonnull ExecutorService executorService) {
74 | Preconditions.checkNotNull(timeUnit);
75 | return new FixedAttemptTimeLimit(duration, timeUnit, executorService);
76 | }
77 |
78 | @Immutable
79 | private static final class NoAttemptTimeLimit implements AttemptTimeLimiter {
80 | @Override
81 | public T call(Callable callable) throws Exception {
82 | return callable.call();
83 | }
84 | }
85 |
86 | @Immutable
87 | private static final class FixedAttemptTimeLimit implements AttemptTimeLimiter {
88 |
89 | /**
90 | * ExecutorService used when no ExecutorService is specified in the constructor
91 | */
92 | private static final ExecutorService defaultExecutorService = Executors.newCachedThreadPool();
93 |
94 | private final TimeLimiter timeLimiter;
95 | private final long duration;
96 | private final TimeUnit timeUnit;
97 |
98 | FixedAttemptTimeLimit(long duration, @Nonnull TimeUnit timeUnit) {
99 | this(duration, timeUnit, defaultExecutorService);
100 | }
101 |
102 | FixedAttemptTimeLimit(long duration, @Nonnull TimeUnit timeUnit, @Nonnull ExecutorService executorService) {
103 | this(SimpleTimeLimiter.create(executorService), duration, timeUnit);
104 | }
105 |
106 | private FixedAttemptTimeLimit(@Nonnull TimeLimiter timeLimiter, long duration, @Nonnull TimeUnit timeUnit) {
107 | Preconditions.checkNotNull(timeLimiter);
108 | Preconditions.checkNotNull(timeUnit);
109 | this.timeLimiter = timeLimiter;
110 | this.duration = duration;
111 | this.timeUnit = timeUnit;
112 | }
113 |
114 | @Override
115 | public T call(Callable callable) throws Exception {
116 | return timeLimiter.callWithTimeout(callable, duration, timeUnit);
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/docs/deprecated-list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Deprecated List (re-retrying 3.0.1-SNAPSHOT API)
7 |
8 |
9 |
10 |
11 |
12 |
22 |
25 |
26 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/StopStrategies.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import com.google.common.base.Preconditions;
21 |
22 | import javax.annotation.Nonnull;
23 | import javax.annotation.concurrent.Immutable;
24 | import java.util.concurrent.TimeUnit;
25 |
26 | /**
27 | * Factory class for {@link StopStrategy} instances.
28 | *
29 | * @author JB
30 | */
31 | public final class StopStrategies {
32 | private static final StopStrategy NEVER_STOP = new NeverStopStrategy();
33 |
34 | private StopStrategies() {
35 | }
36 |
37 | /**
38 | * Returns a stop strategy which never stops retrying. It might be best to
39 | * try not to abuse services with this kind of behavior when small wait
40 | * intervals between retry attempts are being used.
41 | *
42 | * @return a stop strategy which never stops
43 | */
44 | @SuppressWarnings("WeakerAccess")
45 | public static StopStrategy neverStop() {
46 | return NEVER_STOP;
47 | }
48 |
49 | /**
50 | * Returns a stop strategy which stops after N failed attempts.
51 | *
52 | * @param attemptNumber the number of failed attempts before stopping
53 | * @return a stop strategy which stops after {@code attemptNumber} attempts
54 | */
55 | @SuppressWarnings("WeakerAccess")
56 | public static StopStrategy stopAfterAttempt(int attemptNumber) {
57 | return new StopAfterAttemptStrategy(attemptNumber);
58 | }
59 |
60 | /**
61 | * Returns a stop strategy which stops after a given delay. If an
62 | * unsuccessful attempt is made, this {@link StopStrategy} will check if the
63 | * amount of time that's passed from the first attempt has exceeded the
64 | * given delay amount. If it has exceeded this delay, then using this
65 | * strategy causes the retrying to stop.
66 | *
67 | * @param delayInMillis the delay, in milliseconds, starting from first attempt
68 | * @return a stop strategy which stops after {@code delayInMillis} time in milliseconds
69 | * @deprecated Use {@link #stopAfterDelay(long, TimeUnit)} instead.
70 | */
71 | @Deprecated
72 | public static StopStrategy stopAfterDelay(long delayInMillis) {
73 | return stopAfterDelay(delayInMillis, TimeUnit.MILLISECONDS);
74 | }
75 |
76 | /**
77 | * Returns a stop strategy which stops after a given delay. If an
78 | * unsuccessful attempt is made, this {@link StopStrategy} will check if the
79 | * amount of time that's passed from the first attempt has exceeded the
80 | * given delay amount. If it has exceeded this delay, then using this
81 | * strategy causes the retrying to stop.
82 | *
83 | * @param duration the delay, starting from first attempt
84 | * @param timeUnit the unit of the duration
85 | * @return a stop strategy which stops after {@code delayInMillis} time in milliseconds
86 | */
87 | @SuppressWarnings("WeakerAccess")
88 | public static StopStrategy stopAfterDelay(long duration, @Nonnull TimeUnit timeUnit) {
89 | Preconditions.checkNotNull(timeUnit, "The time unit may not be null");
90 | return new StopAfterDelayStrategy(timeUnit.toMillis(duration));
91 | }
92 |
93 | @Immutable
94 | private static final class NeverStopStrategy implements StopStrategy {
95 | @Override
96 | public boolean shouldStop(Attempt failedAttempt) {
97 | return false;
98 | }
99 | }
100 |
101 | @Immutable
102 | private static final class StopAfterAttemptStrategy implements StopStrategy {
103 | private final int maxAttemptNumber;
104 |
105 | StopAfterAttemptStrategy(int maxAttemptNumber) {
106 | Preconditions.checkArgument(maxAttemptNumber >= 1, "maxAttemptNumber must be >= 1 but is %d", maxAttemptNumber);
107 | this.maxAttemptNumber = maxAttemptNumber;
108 | }
109 |
110 | @Override
111 | public boolean shouldStop(Attempt failedAttempt) {
112 | return failedAttempt.getAttemptNumber() >= maxAttemptNumber;
113 | }
114 | }
115 |
116 | @Immutable
117 | private static final class StopAfterDelayStrategy implements StopStrategy {
118 | private final long maxDelay;
119 |
120 | StopAfterDelayStrategy(long maxDelay) {
121 | Preconditions.checkArgument(maxDelay >= 0L, "maxDelay must be >= 0 but is %d", maxDelay);
122 | this.maxDelay = maxDelay;
123 | }
124 |
125 | @Override
126 | public boolean shouldStop(Attempt failedAttempt) {
127 | return failedAttempt.getDelaySinceFirstAttempt() >= maxDelay;
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
17 |
18 | [](https://travis-ci.org/rhuffman/re-retrying)
19 | [](https://github.com/rhuffman/re-retrying/releases/tag/v3.0.0-rc.1)
20 | [](https://github.com/rhuffman/re-retrying/blob/master/LICENSE)
21 |
22 | ## What is this?
23 | The re-retrying module provides a general purpose method for retrying arbitrary Java code with specific stop, retry, and exception handling capabilities that are enhanced by Guava's predicate matching.
24 |
25 | This is a fork of the [guava-retrying](https://github.com/rholder/guava-retrying) library by Ryan Holder (rholder), which is itself a fork of the [RetryerBuilder](http://code.google.com/p/guava-libraries/issues/detail?id=490) by Jean-Baptiste Nizet (JB). The guava-retrying project added a Gradle build for pushing it up to Maven Central, and exponential and Fibonacci backoff [WaitStrategies](http://rholder.github.io/guava-retrying/javadoc/2.0.0/com/github/rholder/retry/WaitStrategies.html) that might be useful for situations where more well-behaved service polling is preferred.
26 |
27 | Why was this fork necessary? The primary reason was to make it compatible with projects using later versions of Guava. See [this project's Wiki](https://github.com/rhuffman/re-retrying/wiki#why-fork) for more details.
28 |
29 | ## Maven
30 | ```xml
31 |
32 | tech.huffman.re-retrying
33 | re-retrying
34 | 3.0.0
35 |
36 | ```
37 |
38 | ## Gradle
39 | ```groovy
40 | compile "tech.huffman.re-retrying:re-retrying:3.0.0"
41 | ```
42 |
43 | ## Quickstart
44 |
45 | Given a function that reads an integer:
46 | ```java
47 |
48 | public int readAnInteger() throws IOException {
49 | ...
50 | }
51 | ```
52 |
53 | The following will retry if the result of the method is zero, if an `IOException` is thrown, or if any other `RuntimeException` is thrown from the `call()` method. It will stop after attempting to retry 3 times and throw a `RetryException` that contains information about the last failed attempt. If any other `Exception` pops out of the `call()` method it's wrapped and rethrown in an `ExecutionException`.
54 |
55 | ```java
56 | Retryer retryer = RetryerBuilder.newBuilder()
57 | .retryIfResult(Predicates.equalTo(0))
58 | .retryIfExceptionOfType(IOException.class)
59 | .retryIfRuntimeException()
60 | .withStopStrategy(StopStrategies.stopAfterAttempt(3))
61 | .build();
62 | try {
63 | retryer.call(this::readAnInteger);
64 | } catch (RetryException | ExecutionException e) {
65 | e.printStackTrace();
66 | }
67 | ```
68 |
69 | ## Exponential Backoff
70 |
71 | Create a `Retryer` that retries forever, waiting after every failed retry in increasing exponential backoff intervals until at most 5 minutes. After 5 minutes, retry from then on in 5 minute intervals.
72 |
73 | ```java
74 | Retryer retryer = RetryerBuilder.newBuilder()
75 | .retryIfExceptionOfType(IOException.class)
76 | .retryIfRuntimeException()
77 | .withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES))
78 | .withStopStrategy(StopStrategies.neverStop())
79 | .build();
80 | ```
81 | You can read more about [exponential backoff](http://en.wikipedia.org/wiki/Exponential_backoff) and the historic role it played in the development of TCP/IP in [Congestion Avoidance and Control](http://ee.lbl.gov/papers/congavoid.pdf).
82 |
83 | ## Fibonacci Backoff
84 |
85 | Create a `Retryer` that retries forever, waiting after every failed retry in increasing Fibonacci backoff intervals until at most 2 minutes. After 2 minutes, retry from then on in 2 minute intervals.
86 |
87 | ```java
88 | Retryer retryer = RetryerBuilder.newBuilder()
89 | .retryIfExceptionOfType(IOException.class)
90 | .retryIfRuntimeException()
91 | .withWaitStrategy(WaitStrategies.fibonacciWait(100, 2, TimeUnit.MINUTES))
92 | .withStopStrategy(StopStrategies.neverStop())
93 | .build();
94 | ```
95 |
96 | Similar to the `ExponentialWaitStrategy`, the `FibonacciWaitStrategy` follows a pattern of waiting an increasing amount of time after each failed attempt.
97 |
98 | Instead of an exponential function it's (obviously) using a [Fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_numbers) to calculate the wait time.
99 |
100 | Depending on the problem at hand, the `FibonacciWaitStrategy` might perform better and lead to better throughput than the `ExponentialWaitStrategy` - at least according to [A Performance Comparison of Different Backoff Algorithms under Different Rebroadcast Probabilities for MANETs](http://www.comp.leeds.ac.uk/ukpew09/papers/12.pdf).
101 |
102 | The implementation of `FibonacciWaitStrategy` is using an iterative version of the Fibonacci because a (naive) recursive version will lead to a [StackOverflowError](http://docs.oracle.com/javase/7/docs/api/java/lang/StackOverflowError.html) at a certain point (although very unlikely with useful parameters for retrying).
103 |
104 | Inspiration for this implementation came from [Efficient retry/backoff mechanisms](https://paperairoplane.net/?p=640).
105 |
106 | ## Documentation
107 | Javadoc can be found [here](http://rholder.github.io/guava-retrying/javadoc/2.0.0).
108 |
109 | ## Building from source
110 | The re-retrying module uses a [Gradle](http://gradle.org)-based build system. In the instructions below, [`./gradlew`](http://vimeo.com/34436402) is invoked from the root of the source tree and serves as a cross-platform, self-contained bootstrap mechanism for the build. The only prerequisites are [Git](https://help.github.com/articles/set-up-git) and JDK 1.8+.
111 |
112 | ### check out sources
113 | `git clone git://github.com/rhuffman/re-retrying.git`
114 |
115 | ### compile and test, build all jars
116 | `./gradlew build`
117 |
118 | ### install all jars into your local Maven cache
119 | `./gradlew install`
120 |
121 | ## License
122 | The re-retrying module is released under version 2.0 of the [Apache License](http://www.apache.org/licenses/LICENSE-2.0).
123 |
124 | ## Contributors
125 | * Jean-Baptiste Nizet (JB)
126 | * Jason Dunkelberger (dirkraft)
127 | * Diwaker Gupta (diwakergupta)
128 | * Jochen Schalanda (joschi)
129 | * Shajahan Palayil (shasts)
130 | * Olivier Grégoire (fror)
131 | * Andrei Savu (andreisavu)
132 | * (tchdp)
133 | * (squalloser)
134 | * Yaroslav Matveychuk (yaroslavm)
135 | * Stephan Schroevers (Stephan202)
136 | * Chad (voiceinsideyou)
137 | * Kevin Conaway (kevinconaway)
138 | * Alberto Scotto (alb-i986)
139 | * Ryan Holder(rholder)
140 | * Robert Huffman (rhuffman)
141 |
142 |
--------------------------------------------------------------------------------
/docs/overview-tree.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Class Hierarchy (re-retrying 3.0.1-SNAPSHOT API)
7 |
8 |
9 |
10 |
11 |
12 |
22 |
25 |
26 |
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
73 |
74 |
75 |
76 |
77 |
Package
78 |
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
79 |
80 |
Interfaces (italic)
81 |
Classes
82 |
Enums
83 |
Exceptions
84 |
Errors
85 |
Annotation Types
86 |
87 |
88 |
89 |
Class/Interface
90 |
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
91 |
92 |
Class inheritance diagram
93 |
Direct Subclasses
94 |
All Known Subinterfaces
95 |
All Known Implementing Classes
96 |
Class/interface declaration
97 |
Class/interface description
98 |
99 |
100 |
Nested Class Summary
101 |
Field Summary
102 |
Constructor Summary
103 |
Method Summary
104 |
105 |
106 |
Field Detail
107 |
Constructor Detail
108 |
Method Detail
109 |
110 |
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
111 |
112 |
113 |
Annotation Type
114 |
Each annotation type has its own separate page with the following sections:
115 |
116 |
Annotation Type declaration
117 |
Annotation Type description
118 |
Required Element Summary
119 |
Optional Element Summary
120 |
Element Detail
121 |
122 |
123 |
124 |
Enum
125 |
Each enum has its own separate page with the following sections:
126 |
127 |
Enum declaration
128 |
Enum description
129 |
Enum Constant Summary
130 |
Enum Constant Detail
131 |
132 |
133 |
134 |
Tree (Class Hierarchy)
135 |
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
136 |
137 |
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
138 |
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
139 |
140 |
141 |
142 |
Deprecated API
143 |
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
144 |
145 |
146 |
Index
147 |
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
148 |
149 |
150 |
Prev/Next
151 |
These links take you to the next or previous class, interface, package, or related page.
152 |
153 |
154 |
Frames/No Frames
155 |
These links show and hide the HTML frames. All pages are available with or without frames.
156 |
157 |
158 |
All Classes
159 |
The All Classes link shows all classes and interfaces except non-static nested types.
160 |
161 |
162 |
Serialized Form
163 |
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
This is a strategy used to decide how a retryer should block between retry
104 | attempts. Normally this is just a Thread.sleep(), but implementations can be
105 | something more elaborate if desired.
Attempt to block for the designated amount of time. Implementations
154 | that don't block or otherwise delay the processing from within this
155 | method for the given sleep duration can significantly modify the behavior
156 | of any configured WaitStrategy. Caution
157 | is advised when generating your own implementations.
158 |
159 |
Parameters:
160 |
sleepTime - the computed sleep duration in milliseconds
161 |
Throws:
162 |
java.lang.InterruptedException - If the calling thread is interrupted
241 |
242 |
243 |
244 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/Retryer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import com.google.common.base.Preconditions;
21 |
22 | import javax.annotation.Nonnull;
23 | import java.util.Collection;
24 | import java.util.List;
25 | import java.util.concurrent.Callable;
26 | import java.util.function.Predicate;
27 |
28 | /**
29 | * A retryer, which executes a call, and retries it until it succeeds, or
30 | * a stop strategy decides to stop retrying. A wait strategy is used to sleep
31 | * between attempts. The strategy to decide if the call succeeds or not is
32 | * also configurable.
33 | *
34 | * A retryer can also wrap the callable into a RetryerCallable, which can be submitted to an executor.
35 | *
36 | * Retryer instances are better constructed with a {@link RetryerBuilder}. A retryer
37 | * is thread-safe, provided the arguments passed to its constructor are thread-safe.
38 | *
39 | * @author JB
40 | * @author Jason Dunkelberger (dirkraft)
41 | */
42 | public final class Retryer {
43 | private final StopStrategy stopStrategy;
44 | private final WaitStrategy waitStrategy;
45 | private final BlockStrategy blockStrategy;
46 | private final AttemptTimeLimiter attemptTimeLimiter;
47 | private final List>> retryPredicates;
48 | private final Collection listeners;
49 |
50 | /**
51 | * @param attemptTimeLimiter to prevent from any single attempt from spinning infinitely
52 | * @param stopStrategy the strategy used to decide when the retryer must stop retrying
53 | * @param waitStrategy the strategy used to decide how much time to sleep between attempts
54 | * @param blockStrategy the strategy used to decide how to block between retry attempts;
55 | * eg, Thread#sleep(), latches, etc.
56 | * @param retryPredicates the predicates used to decide if the attempt must be retried (without
57 | * regard to the StopStrategy).
58 | * @param listeners collection of retry listeners
59 | */
60 | Retryer(@Nonnull AttemptTimeLimiter attemptTimeLimiter,
61 | @Nonnull StopStrategy stopStrategy,
62 | @Nonnull WaitStrategy waitStrategy,
63 | @Nonnull BlockStrategy blockStrategy,
64 | @Nonnull List>> retryPredicates,
65 | @Nonnull Collection listeners) {
66 | Preconditions.checkNotNull(attemptTimeLimiter, "timeLimiter may not be null");
67 | Preconditions.checkNotNull(stopStrategy, "stopStrategy may not be null");
68 | Preconditions.checkNotNull(waitStrategy, "waitStrategy may not be null");
69 | Preconditions.checkNotNull(blockStrategy, "blockStrategy may not be null");
70 | Preconditions.checkNotNull(retryPredicates, "retryPredicates may not be null");
71 | Preconditions.checkNotNull(listeners, "listeners may not null");
72 |
73 | this.attemptTimeLimiter = attemptTimeLimiter;
74 | this.stopStrategy = stopStrategy;
75 | this.waitStrategy = waitStrategy;
76 | this.blockStrategy = blockStrategy;
77 | this.retryPredicates = retryPredicates;
78 | this.listeners = listeners;
79 | }
80 |
81 | /**
82 | * Executes the given callable, retrying if necessary. If the retry predicate
83 | * accepts the attempt, the stop strategy is used to decide if a new attempt
84 | * must be made. Then the wait strategy is used to decide how much time to sleep
85 | * and a new attempt is made.
86 | *
87 | * @param callable the callable task to be executed
88 | * @param the return type of the Callable
89 | * @return the computed result of the given callable
90 | * @throws RetryException if all the attempts failed before the stop strategy decided to abort
91 | * @throws InterruptedException If this thread is interrupted. This can happen because
92 | * {@link Thread#sleep} is invoked between attempts
93 | */
94 | public T call(Callable callable) throws RetryException, InterruptedException {
95 | long startTimeMillis = System.currentTimeMillis();
96 | for (int attemptNumber = 1; ; attemptNumber++) {
97 | Attempt attempt;
98 | try {
99 | T result = attemptTimeLimiter.call(callable);
100 | attempt = new Attempt<>(result, attemptNumber, System.currentTimeMillis() - startTimeMillis);
101 | } catch(InterruptedException e) {
102 | throw e;
103 | } catch (Throwable t) {
104 | attempt = new Attempt<>(t, attemptNumber, System.currentTimeMillis() - startTimeMillis);
105 | }
106 |
107 | for (RetryListener listener : listeners) {
108 | listener.onRetry(attempt);
109 | }
110 |
111 | if (!shouldRetry(attempt)) {
112 | return getOrThrow(attempt);
113 | }
114 |
115 | if (stopStrategy.shouldStop(attempt)) {
116 | throw new RetryException(attempt);
117 | } else {
118 | long sleepTime = waitStrategy.computeSleepTime(attempt);
119 | blockStrategy.block(sleepTime);
120 | }
121 | }
122 | }
123 |
124 | /**
125 | * Executes the given runnable, retrying if necessary. If the retry predicate
126 | * accepts the attempt, the stop strategy is used to decide if a new attempt
127 | * must be made. Then the wait strategy is used to decide how much time to sleep
128 | * and a new attempt is made.
129 | *
130 | * @param runnable the runnable task to be executed
131 | * @throws RetryException if all the attempts failed before the stop strategy decided
132 | * to abort
133 | * @throws InterruptedException If this thread is interrupted. This can happen because
134 | * {@link Thread#sleep} is invoked between attempts
135 | */
136 | @SuppressWarnings("WeakerAccess")
137 | public void run(Runnable runnable) throws RetryException, InterruptedException {
138 | call(() -> {
139 | runnable.run();
140 | return null;
141 | });
142 | }
143 |
144 | /**
145 | * Throw the Attempt's exception, if it has one, wrapped in a RetryException. Otherwise,
146 | * return the attempt's result.
147 | *
148 | * @param attempt An attempt that was made by invoking the call
149 | * @param The type of the attempt
150 | * @return The result of the attempt
151 | * @throws RetryException If the attempt has an exception
152 | */
153 | private T getOrThrow(Attempt attempt) throws RetryException {
154 | if (attempt.hasException()) {
155 | throw new RetryException(attempt);
156 | }
157 | return attempt.get();
158 | }
159 |
160 | /**
161 | * Applies the retry predicates to the attempt, in order, until either one
162 | * predicate returns true or all predicates return false.
163 | *
164 | * @param attempt The attempt made by invoking the call
165 | */
166 | private boolean shouldRetry(Attempt> attempt) {
167 | for (Predicate> predicate : retryPredicates) {
168 | if (predicate.test(attempt)) {
169 | return true;
170 | }
171 | }
172 | return false;
173 | }
174 |
175 | /**
176 | * Wraps the given {@link Callable} in a {@link RetryerCallable}, which can
177 | * be submitted to an executor. The returned {@link RetryerCallable} uses
178 | * this {@link Retryer} instance to call the given {@link Callable}.
179 | *
180 | * @param callable the callable to wrap
181 | * @param the return type of the Callable
182 | * @return a {@link RetryerCallable} that behaves like the given {@link Callable} with retry behavior defined by this {@link Retryer}
183 | */
184 | @SuppressWarnings("WeakerAccess")
185 | public RetryerCallable wrap(Callable callable) {
186 | return new RetryerCallable<>(this, callable);
187 | }
188 |
189 | /**
190 | * A {@link Callable} which wraps another {@link Callable} in order to add
191 | * retrying behavior from a given {@link Retryer} instance.
192 | *
193 | * @author JB
194 | */
195 | public static class RetryerCallable implements Callable {
196 | private Retryer retryer;
197 | private Callable callable;
198 |
199 | private RetryerCallable(Retryer retryer,
200 | Callable callable) {
201 | this.retryer = retryer;
202 | this.callable = callable;
203 | }
204 |
205 | /**
206 | * Makes the enclosing retryer call the wrapped callable.
207 | *
208 | * @see Retryer#call(Callable)
209 | */
210 | @Override
211 | public T call() throws Exception {
212 | return retryer.call(callable);
213 | }
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/src/test/java/com/github/rholder/retry/WaitStrategiesTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import com.google.common.collect.Sets;
21 | import org.junit.Test;
22 |
23 | import java.util.Set;
24 | import java.util.concurrent.TimeUnit;
25 | import java.util.function.Function;
26 |
27 | import static org.junit.Assert.assertEquals;
28 | import static org.junit.Assert.assertTrue;
29 |
30 | public class WaitStrategiesTest {
31 |
32 |
33 | @Test
34 | public void testNoWait() {
35 | WaitStrategy noWait = WaitStrategies.noWait();
36 | assertEquals(0L, noWait.computeSleepTime(failedAttempt(18, 9879L)));
37 | }
38 |
39 | @Test
40 | public void testFixedWait() {
41 | WaitStrategy fixedWait = WaitStrategies.fixedWait(1000L, TimeUnit.MILLISECONDS);
42 | assertEquals(1000L, fixedWait.computeSleepTime(failedAttempt(12, 6546L)));
43 | }
44 |
45 | @Test
46 | public void testIncrementingWait() {
47 | WaitStrategy incrementingWait = WaitStrategies.incrementingWait(500L, TimeUnit.MILLISECONDS, 100L, TimeUnit.MILLISECONDS);
48 | assertEquals(500L, incrementingWait.computeSleepTime(failedAttempt(1, 6546L)));
49 | assertEquals(600L, incrementingWait.computeSleepTime(failedAttempt(2, 6546L)));
50 | assertEquals(700L, incrementingWait.computeSleepTime(failedAttempt(3, 6546L)));
51 | }
52 |
53 | @Test
54 | public void testRandomWait() {
55 | WaitStrategy randomWait = WaitStrategies.randomWait(1000L, TimeUnit.MILLISECONDS, 2000L, TimeUnit.MILLISECONDS);
56 | Set times = Sets.newHashSet();
57 | times.add(randomWait.computeSleepTime(failedAttempt(1, 6546L)));
58 | times.add(randomWait.computeSleepTime(failedAttempt(1, 6546L)));
59 | times.add(randomWait.computeSleepTime(failedAttempt(1, 6546L)));
60 | times.add(randomWait.computeSleepTime(failedAttempt(1, 6546L)));
61 | assertTrue(times.size() > 1); // if not, the random is not random
62 | for (long time : times) {
63 | assertTrue(time >= 1000L);
64 | assertTrue(time <= 2000L);
65 | }
66 | }
67 |
68 | @Test
69 | public void testRandomWaitWithoutMinimum() {
70 | WaitStrategy randomWait = WaitStrategies.randomWait(2000L, TimeUnit.MILLISECONDS);
71 | Set times = Sets.newHashSet();
72 | times.add(randomWait.computeSleepTime(failedAttempt(1, 6546L)));
73 | times.add(randomWait.computeSleepTime(failedAttempt(1, 6546L)));
74 | times.add(randomWait.computeSleepTime(failedAttempt(1, 6546L)));
75 | times.add(randomWait.computeSleepTime(failedAttempt(1, 6546L)));
76 | assertTrue(times.size() > 1); // if not, the random is not random
77 | for (long time : times) {
78 | assertTrue(time >= 0L);
79 | assertTrue(time <= 2000L);
80 | }
81 | }
82 |
83 | @Test
84 | public void testExponential() {
85 | WaitStrategy exponentialWait = WaitStrategies.exponentialWait();
86 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(1, 0)) == 2);
87 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(2, 0)) == 4);
88 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(3, 0)) == 8);
89 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(4, 0)) == 16);
90 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(5, 0)) == 32);
91 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(6, 0)) == 64);
92 | }
93 |
94 | @Test
95 | public void testExponentialWithMaximumWait() {
96 | WaitStrategy exponentialWait = WaitStrategies.exponentialWait(40, TimeUnit.MILLISECONDS);
97 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(1, 0)) == 2);
98 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(2, 0)) == 4);
99 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(3, 0)) == 8);
100 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(4, 0)) == 16);
101 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(5, 0)) == 32);
102 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(6, 0)) == 40);
103 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(7, 0)) == 40);
104 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(8, 0)) == 40);
105 | }
106 |
107 | @Test
108 | public void testExponentialWithMultiplierAndMaximumWait() {
109 | WaitStrategy exponentialWait = WaitStrategies.exponentialWait(1000, 50000, TimeUnit.MILLISECONDS);
110 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(1, 0)) == 2000);
111 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(2, 0)) == 4000);
112 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(3, 0)) == 8000);
113 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(4, 0)) == 16000);
114 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(5, 0)) == 32000);
115 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(6, 0)) == 50000);
116 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(7, 0)) == 50000);
117 | assertTrue(exponentialWait.computeSleepTime(failedAttempt(8, 0)) == 50000);
118 | }
119 |
120 | @Test
121 | public void testFibonacci() {
122 | WaitStrategy fibonacciWait = WaitStrategies.fibonacciWait();
123 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(1, 0L)) == 1L);
124 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(2, 0L)) == 1L);
125 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(3, 0L)) == 2L);
126 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(4, 0L)) == 3L);
127 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(5, 0L)) == 5L);
128 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(6, 0L)) == 8L);
129 | }
130 |
131 | @Test
132 | public void testFibonacciWithMaximumWait() {
133 | WaitStrategy fibonacciWait = WaitStrategies.fibonacciWait(10L, TimeUnit.MILLISECONDS);
134 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(1, 0L)) == 1L);
135 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(2, 0L)) == 1L);
136 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(3, 0L)) == 2L);
137 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(4, 0L)) == 3L);
138 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(5, 0L)) == 5L);
139 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(6, 0L)) == 8L);
140 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(7, 0L)) == 10L);
141 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(8, 0L)) == 10L);
142 | }
143 |
144 | @Test
145 | public void testFibonacciWithMultiplierAndMaximumWait() {
146 | WaitStrategy fibonacciWait = WaitStrategies.fibonacciWait(1000L, 50000L, TimeUnit.MILLISECONDS);
147 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(1, 0L)) == 1000L);
148 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(2, 0L)) == 1000L);
149 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(3, 0L)) == 2000L);
150 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(4, 0L)) == 3000L);
151 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(5, 0L)) == 5000L);
152 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(6, 0L)) == 8000L);
153 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(7, 0L)) == 13000L);
154 | assertTrue(fibonacciWait.computeSleepTime(failedAttempt(10, 0L)) == 50000L);
155 | }
156 |
157 | @Test
158 | public void testExceptionWait() {
159 | WaitStrategy exceptionWait = WaitStrategies.exceptionWait(
160 | RuntimeException.class, zeroSleepFunction());
161 | assertEquals(0L, exceptionWait.computeSleepTime(failedAttempt(42, 7227)));
162 |
163 | WaitStrategy oneMinuteWait = WaitStrategies.exceptionWait(RuntimeException.class, oneMinuteSleepFunction());
164 | assertEquals(3600 * 1000L, oneMinuteWait.computeSleepTime(failedAttempt(42, 7227)));
165 |
166 | WaitStrategy noMatchRetryAfterWait = WaitStrategies.exceptionWait(RetryAfterException.class, customSleepFunction());
167 | assertEquals(0L, noMatchRetryAfterWait.computeSleepTime(failedAttempt(42, 7227)));
168 |
169 | WaitStrategy retryAfterWait = WaitStrategies.exceptionWait(RetryAfterException.class, customSleepFunction());
170 | Attempt failedAttempt = new Attempt<>(
171 | new RetryAfterException(), 42, 7227L);
172 | assertEquals(29L, retryAfterWait.computeSleepTime(failedAttempt));
173 | }
174 |
175 | private Attempt failedAttempt(int attemptNumber, long delaySinceFirstAttempt) {
176 | return new Attempt<>(new RuntimeException(), attemptNumber, delaySinceFirstAttempt);
177 | }
178 |
179 | private Function zeroSleepFunction() {
180 | return input -> 0L;
181 | }
182 |
183 | private Function oneMinuteSleepFunction() {
184 | return input -> 3600 * 1000L;
185 | }
186 |
187 | private Function customSleepFunction() {
188 | return RetryAfterException::getRetryAfter;
189 | }
190 |
191 | public class RetryAfterException extends RuntimeException {
192 | private final long retryAfter = 29L;
193 |
194 | long getRetryAfter() {
195 | return retryAfter;
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rholder/retry/RetryerBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2015 Ray Holder
3 | * Modifications copyright 2017-2018 Robert Huffman
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.github.rholder.retry;
19 |
20 | import com.google.common.base.Preconditions;
21 | import com.google.common.collect.Lists;
22 |
23 | import javax.annotation.Nonnull;
24 | import java.util.ArrayList;
25 | import java.util.List;
26 | import java.util.function.Predicate;
27 |
28 | /**
29 | * A builder used to configure and create a {@link Retryer}.
30 | *
31 | * @author JB
32 | * @author Jason Dunkelberger (dirkraft)
33 | */
34 | @SuppressWarnings("WeakerAccess")
35 | public class RetryerBuilder {
36 | private AttemptTimeLimiter attemptTimeLimiter;
37 | private StopStrategy stopStrategy;
38 | private WaitStrategy waitStrategy;
39 | private BlockStrategy blockStrategy;
40 | private List>> retryPredicates = Lists.newArrayList();
41 | private List listeners = new ArrayList<>();
42 |
43 | private RetryerBuilder() {
44 | }
45 |
46 | public static RetryerBuilder newBuilder() {
47 | return new RetryerBuilder();
48 | }
49 |
50 | /**
51 | * Adds a listener that will be notified of each attempt that is made
52 | *
53 | * @param listener Listener to add
54 | * @return this
55 | */
56 | public RetryerBuilder withRetryListener(@Nonnull RetryListener listener) {
57 | Preconditions.checkNotNull(listener, "listener may not be null");
58 | listeners.add(listener);
59 | return this;
60 | }
61 |
62 | /**
63 | * Sets the wait strategy used to decide how long to sleep between failed attempts.
64 | * The default strategy is to retry immediately after a failed attempt.
65 | *
66 | * @param waitStrategy the strategy used to sleep between failed attempts
67 | * @return this
68 | * @throws IllegalStateException if a wait strategy has already been set.
69 | */
70 | public RetryerBuilder withWaitStrategy(@Nonnull WaitStrategy waitStrategy)
71 | throws IllegalStateException {
72 | Preconditions.checkNotNull(waitStrategy, "waitStrategy may not be null");
73 | Preconditions.checkState(this.waitStrategy == null,
74 | "a wait strategy has already been set %s", this.waitStrategy);
75 | this.waitStrategy = waitStrategy;
76 | return this;
77 | }
78 |
79 | /**
80 | * Sets the stop strategy used to decide when to stop retrying. The default strategy
81 | * is to not stop at all .
82 | *
83 | * @param stopStrategy the strategy used to decide when to stop retrying
84 | * @return this
85 | * @throws IllegalStateException if a stop strategy has already been set.
86 | */
87 | public RetryerBuilder withStopStrategy(@Nonnull StopStrategy stopStrategy)
88 | throws IllegalStateException {
89 | Preconditions.checkNotNull(stopStrategy, "stopStrategy may not be null");
90 | Preconditions.checkState(this.stopStrategy == null, "a stop strategy has already been set %s", this.stopStrategy);
91 | this.stopStrategy = stopStrategy;
92 | return this;
93 | }
94 |
95 | /**
96 | * Sets the block strategy used to decide how to block between retry attempts. The default strategy is to use Thread#sleep().
97 | *
98 | * @param blockStrategy the strategy used to decide how to block between retry attempts
99 | * @return this
100 | * @throws IllegalStateException if a block strategy has already been set.
101 | */
102 | public RetryerBuilder withBlockStrategy(@Nonnull BlockStrategy blockStrategy)
103 | throws IllegalStateException {
104 | Preconditions.checkNotNull(blockStrategy, "blockStrategy may not be null");
105 | Preconditions.checkState(this.blockStrategy == null,
106 | "a block strategy has already been set %s", this.blockStrategy);
107 | this.blockStrategy = blockStrategy;
108 | return this;
109 | }
110 |
111 |
112 | /**
113 | * Configures the retryer to limit the duration of any particular attempt by the given duration.
114 | *
115 | * @param attemptTimeLimiter to apply to each attempt
116 | * @return this
117 | */
118 | public RetryerBuilder withAttemptTimeLimiter(@Nonnull AttemptTimeLimiter attemptTimeLimiter) {
119 | Preconditions.checkNotNull(attemptTimeLimiter);
120 | this.attemptTimeLimiter = attemptTimeLimiter;
121 | return this;
122 | }
123 |
124 | /**
125 | * Configures the retryer to retry if an exception (i.e. any Exception or subclass
126 | * of Exception) is thrown by the call.
127 | *
128 | * @return this
129 | */
130 | public RetryerBuilder retryIfException() {
131 | retryPredicates.add(new ExceptionClassPredicate(Exception.class));
132 | return this;
133 | }
134 |
135 | /**
136 | * Configures the retryer to retry if a runtime exception (i.e. any RuntimeException or subclass
137 | * of RuntimeException) is thrown by the call.
138 | *
139 | * @return this
140 | */
141 | public RetryerBuilder retryIfRuntimeException() {
142 | retryPredicates.add(new ExceptionClassPredicate(RuntimeException.class));
143 | return this;
144 | }
145 |
146 | /**
147 | * Configures the retryer to retry if an exception of the given class (or subclass of the given class) is
148 | * thrown by the call.
149 | *
150 | * @param exceptionClass the type of the exception which should cause the retryer to retry
151 | * @return this
152 | */
153 | public RetryerBuilder retryIfExceptionOfType(@Nonnull Class extends Throwable> exceptionClass) {
154 | Preconditions.checkNotNull(exceptionClass, "exceptionClass may not be null");
155 | retryPredicates.add(new ExceptionClassPredicate(exceptionClass));
156 | return this;
157 | }
158 |
159 | /**
160 | * Configures the retryer to retry if an exception satisfying the given predicate is
161 | * thrown by the call.
162 | *
163 | * @param exceptionPredicate the predicate which causes a retry if satisfied
164 | * @return this
165 | */
166 | public RetryerBuilder retryIfException(@Nonnull Predicate exceptionPredicate) {
167 | Preconditions.checkNotNull(exceptionPredicate, "exceptionPredicate may not be null");
168 | retryPredicates.add(new ExceptionPredicate(exceptionPredicate));
169 | return this;
170 | }
171 |
172 | /**
173 | * Configures the retryer to retry if the result satisfies the given predicate.
174 | *
175 | * @param The type of object tested by the predicate
176 | * @param resultPredicate a predicate applied to the result, and which causes the retryer
177 | * to retry if the predicate is satisfied
178 | * @return this
179 | */
180 | public RetryerBuilder retryIfResult(@Nonnull Predicate resultPredicate) {
181 | Preconditions.checkNotNull(resultPredicate, "resultPredicate may not be null");
182 | retryPredicates.add(new ResultPredicate<>(resultPredicate));
183 | return this;
184 | }
185 |
186 | /**
187 | * Builds the retryer.
188 | *
189 | * @return the built retryer.
190 | */
191 | public Retryer build() {
192 | AttemptTimeLimiter theAttemptTimeLimiter = attemptTimeLimiter == null ? AttemptTimeLimiters.noTimeLimit() : attemptTimeLimiter;
193 | StopStrategy theStopStrategy = stopStrategy == null ? StopStrategies.neverStop() : stopStrategy;
194 | WaitStrategy theWaitStrategy = waitStrategy == null ? WaitStrategies.noWait() : waitStrategy;
195 | BlockStrategy theBlockStrategy = blockStrategy == null ? BlockStrategies.threadSleepStrategy() : blockStrategy;
196 |
197 | return new Retryer(
198 | theAttemptTimeLimiter,
199 | theStopStrategy,
200 | theWaitStrategy,
201 | theBlockStrategy,
202 | retryPredicates,
203 | listeners);
204 | }
205 |
206 | private static final class ExceptionClassPredicate implements Predicate> {
207 |
208 | private Class extends Throwable> exceptionClass;
209 |
210 | ExceptionClassPredicate(Class extends Throwable> exceptionClass) {
211 | this.exceptionClass = exceptionClass;
212 | }
213 |
214 | @Override
215 | public boolean test(Attempt> attempt) {
216 | return attempt.hasException() &&
217 | exceptionClass.isAssignableFrom(attempt.getException().getClass());
218 | }
219 | }
220 |
221 | private static final class ResultPredicate implements Predicate> {
222 |
223 | private Predicate delegate;
224 |
225 | ResultPredicate(Predicate delegate) {
226 | this.delegate = delegate;
227 | }
228 |
229 | @Override
230 | public boolean test(Attempt> attempt) {
231 | if (!attempt.hasResult()) {
232 | return false;
233 | }
234 | try {
235 | @SuppressWarnings("unchecked")
236 | T result = (T) attempt.getResult();
237 | return delegate.test(result);
238 | } catch (ClassCastException e) {
239 | return false;
240 | }
241 | }
242 | }
243 |
244 | private static final class ExceptionPredicate implements Predicate> {
245 |
246 | private Predicate delegate;
247 |
248 | ExceptionPredicate(Predicate delegate) {
249 | this.delegate = delegate;
250 | }
251 |
252 | @Override
253 | public boolean test(Attempt> attempt) {
254 | return attempt.hasException() && delegate.test(attempt.getException());
255 | }
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/docs/com/github/rholder/retry/Retryer.RetryerCallable.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Retryer.RetryerCallable (re-retrying 3.0.1-SNAPSHOT API)
7 |
8 |
9 |
10 |
11 |
12 |
28 |
31 |
32 |
@Immutable
125 | public final class RetryException
126 | extends java.lang.Exception
127 |
An exception indicating that none of the attempts of the Retryer
128 | succeeded. If the last Attempt resulted in an Exception, it is set as
129 | the cause of the RetryException.