Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 8823822

Browse files
committed
made changes to support the async call executor without an executorservice
1 parent ca160a9 commit 8823822

File tree

2 files changed

+109
-17
lines changed

2 files changed

+109
-17
lines changed

src/main/java/com/evanlennick/retry4j/AsyncCallExecutor.java

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
import java.util.concurrent.Callable;
77
import java.util.concurrent.CompletableFuture;
88
import java.util.concurrent.ExecutorService;
9-
import java.util.concurrent.Executors;
109

1110
/**
1211
* Implementation that kicks off each retry request in its own separate thread that does not block the thread the
13-
* execution is called from.
12+
* execution is called from. If you provide an ExecutorService, it will be used when creating threads.
1413
*
1514
* @param <T> The type that is returned by the Callable (eg: Boolean, Void, Object, etc)
1615
*/
@@ -30,10 +29,8 @@ public class AsyncCallExecutor<T> implements RetryExecutor<T, CompletableFuture<
3029

3130
private RetryListener onCompletionListener;
3231

33-
private static final int DEFAULT_NUMBER_OF_THREADS_IN_POOL = 5;
34-
3532
public AsyncCallExecutor(RetryConfig config) {
36-
this(config, Executors.newFixedThreadPool(DEFAULT_NUMBER_OF_THREADS_IN_POOL));
33+
this(config, null);
3734
}
3835

3936
public AsyncCallExecutor(RetryConfig config, ExecutorService executorService) {
@@ -58,18 +55,26 @@ public CompletableFuture<Status<T>> execute(Callable<T> callable, String callNam
5855

5956
CompletableFuture<Status<T>> completableFuture = new CompletableFuture<>();
6057

61-
executorService.submit(() -> {
62-
try {
63-
Status<T> status = synchronousCallExecutor.execute(callable, callName);
64-
completableFuture.complete(status);
65-
} catch (Throwable t) {
66-
completableFuture.completeExceptionally(t);
67-
}
68-
});
58+
if (executorService != null) {
59+
executorService.submit(()
60+
-> executeFuture(callable, callName, synchronousCallExecutor, completableFuture));
61+
} else {
62+
(new Thread(()
63+
-> executeFuture(callable, callName, synchronousCallExecutor, completableFuture))).start();
64+
}
6965

7066
return completableFuture;
7167
}
7268

69+
private void executeFuture(Callable<T> callable, String callName, CallExecutor<T> synchronousCallExecutor, CompletableFuture<Status<T>> completableFuture) {
70+
try {
71+
Status<T> status = synchronousCallExecutor.execute(callable, callName);
72+
completableFuture.complete(status);
73+
} catch (Throwable t) {
74+
completableFuture.completeExceptionally(t);
75+
}
76+
}
77+
7378
public AsyncCallExecutor<T> afterFailedTry(RetryListener listener) {
7479
this.afterFailedTryListener = listener;
7580
return this;
@@ -100,6 +105,15 @@ public void setConfig(RetryConfig config) {
100105
this.config = config;
101106
}
102107

108+
public void setExecutorService(ExecutorService executorService) {
109+
this.executorService = executorService;
110+
}
111+
112+
public ExecutorService getExecutorService() {
113+
return executorService;
114+
}
115+
116+
@Deprecated
103117
public ExecutorService getThreadExecutorService() {
104118
return executorService;
105119
}

src/test/java/com/evanlennick/retry4j/AsyncCallExecutorTest.java

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
import com.evanlennick.retry4j.config.RetryConfigBuilder;
55
import com.evanlennick.retry4j.exception.RetriesExhaustedException;
66
import com.evanlennick.retry4j.exception.UnexpectedException;
7+
import org.testng.annotations.AfterClass;
78
import org.testng.annotations.BeforeClass;
89
import org.testng.annotations.Test;
910

1011
import java.time.Duration;
1112
import java.util.concurrent.Callable;
1213
import java.util.concurrent.CompletableFuture;
1314
import java.util.concurrent.ExecutionException;
15+
import java.util.concurrent.ExecutorService;
16+
import java.util.concurrent.Executors;
1417

1518
import static org.assertj.core.api.Assertions.assertThat;
1619
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -21,6 +24,8 @@ public class AsyncCallExecutorTest {
2124

2225
private RetryConfig failOnAnyExceptionConfig;
2326

27+
private ExecutorService executorService;
28+
2429
@BeforeClass
2530
public void setup() {
2631
retryOnAnyExceptionConfig = new RetryConfigBuilder()
@@ -36,10 +41,17 @@ public void setup() {
3641
.withMaxNumberOfTries(1)
3742
.withDelayBetweenTries(Duration.ofMillis(0))
3843
.build();
44+
45+
executorService = Executors.newFixedThreadPool(5);
46+
}
47+
48+
@AfterClass
49+
public void teardown() {
50+
executorService.shutdown();
3951
}
4052

4153
@Test
42-
public void verifyMultipleCalls() throws Exception {
54+
public void verifyMultipleCalls_noExecutorService() throws Exception {
4355
Callable<Boolean> callable = () -> true;
4456

4557
AsyncCallExecutor<Boolean> executor = new AsyncCallExecutor<>(retryOnAnyExceptionConfig);
@@ -58,7 +70,7 @@ public void verifyMultipleCalls() throws Exception {
5870
}
5971

6072
@Test
61-
public void verifyOneCall_success() throws Exception {
73+
public void verifyOneCall_success_noExecutorService() throws Exception {
6274
Callable<Boolean> callable = () -> true;
6375

6476
AsyncCallExecutor<Boolean> executor = new AsyncCallExecutor<>(retryOnAnyExceptionConfig);
@@ -71,7 +83,7 @@ public void verifyOneCall_success() throws Exception {
7183
}
7284

7385
@Test
74-
public void verifyOneCall_failDueToTooManyRetries() throws Exception {
86+
public void verifyOneCall_failDueToTooManyRetries_noExecutorService() throws Exception {
7587
Callable<Boolean> callable = () -> { throw new RuntimeException(); };
7688

7789
AsyncCallExecutor<Boolean> executor = new AsyncCallExecutor<>(retryOnAnyExceptionConfig);
@@ -84,7 +96,7 @@ public void verifyOneCall_failDueToTooManyRetries() throws Exception {
8496
}
8597

8698
@Test
87-
public void verifyOneCall_failDueToUnexpectedException() throws Exception {
99+
public void verifyOneCall_failDueToUnexpectedException_noExecutorService() throws Exception {
88100
Callable<Boolean> callable = () -> { throw new RuntimeException(); };
89101

90102
AsyncCallExecutor<Boolean> executor = new AsyncCallExecutor<>(failOnAnyExceptionConfig);
@@ -95,4 +107,70 @@ public void verifyOneCall_failDueToUnexpectedException() throws Exception {
95107
.isExactlyInstanceOf(ExecutionException.class)
96108
.hasCauseExactlyInstanceOf(UnexpectedException.class);
97109
}
110+
111+
@Test
112+
public void verifyMultipleCalls_withExecutorService() throws Exception {
113+
Callable<Boolean> callable = () -> true;
114+
115+
AsyncCallExecutor<Boolean> executor = new AsyncCallExecutor<>(
116+
retryOnAnyExceptionConfig,
117+
executorService);
118+
119+
CompletableFuture<Status<Boolean>> future1 = executor.execute(callable);
120+
CompletableFuture<Status<Boolean>> future2 = executor.execute(callable);
121+
CompletableFuture<Status<Boolean>> future3 = executor.execute(callable);
122+
123+
CompletableFuture combinedFuture
124+
= CompletableFuture.allOf(future1, future2, future3);
125+
combinedFuture.get();
126+
127+
assertThat(future1).isDone();
128+
assertThat(future2).isDone();
129+
assertThat(future3).isDone();
130+
}
131+
132+
@Test
133+
public void verifyOneCall_success_withExecutorService() throws Exception {
134+
Callable<Boolean> callable = () -> true;
135+
136+
AsyncCallExecutor<Boolean> executor = new AsyncCallExecutor<>(
137+
retryOnAnyExceptionConfig,
138+
executorService);
139+
140+
CompletableFuture<Status<Boolean>> future = executor.execute(callable);
141+
142+
Status<Boolean> status = future.get();
143+
assertThat(future).isDone();
144+
assertThat(status.wasSuccessful()).isTrue();
145+
}
146+
147+
@Test
148+
public void verifyOneCall_failDueToTooManyRetries_withExecutorService() throws Exception {
149+
Callable<Boolean> callable = () -> { throw new RuntimeException(); };
150+
151+
AsyncCallExecutor<Boolean> executor = new AsyncCallExecutor<>(
152+
retryOnAnyExceptionConfig,
153+
executorService);
154+
155+
CompletableFuture<Status<Boolean>> future = executor.execute(callable);
156+
157+
assertThatThrownBy(future::get)
158+
.isExactlyInstanceOf(ExecutionException.class)
159+
.hasCauseExactlyInstanceOf(RetriesExhaustedException.class);
160+
}
161+
162+
@Test
163+
public void verifyOneCall_failDueToUnexpectedException_withExecutorService() throws Exception {
164+
Callable<Boolean> callable = () -> { throw new RuntimeException(); };
165+
166+
AsyncCallExecutor<Boolean> executor = new AsyncCallExecutor<>(
167+
failOnAnyExceptionConfig,
168+
executorService);
169+
170+
CompletableFuture<Status<Boolean>> future = executor.execute(callable);
171+
172+
assertThatThrownBy(future::get)
173+
.isExactlyInstanceOf(ExecutionException.class)
174+
.hasCauseExactlyInstanceOf(UnexpectedException.class);
175+
}
98176
}

0 commit comments

Comments
 (0)