Skip to content

Commit 0015407

Browse files
committed
Create function composition DSL for okhttp, retrofit, that mirrors Failsafe class
1 parent fd26fe0 commit 0015407

File tree

6 files changed

+81
-48
lines changed

6 files changed

+81
-48
lines changed

modules/okhttp/src/main/java/dev/failsafe/okhttp/FailsafeCall.java

+28-11
Original file line numberDiff line numberDiff line change
@@ -38,30 +38,47 @@ public final class FailsafeCall {
3838
private AtomicBoolean cancelled = new AtomicBoolean();
3939
private AtomicBoolean executed = new AtomicBoolean();
4040

41-
private FailsafeCall(okhttp3.Call call, FailsafeExecutor<Response> failsafe) {
42-
this.initialCall = call;
41+
private FailsafeCall(FailsafeExecutor<Response> failsafe, okhttp3.Call call) {
4342
this.failsafe = failsafe;
43+
this.initialCall = call;
44+
}
45+
46+
public static final class FailsafeCallBuilder {
47+
private FailsafeExecutor<Response> failsafe;
48+
49+
private FailsafeCallBuilder(FailsafeExecutor<Response> failsafe) {
50+
this.failsafe = failsafe;
51+
}
52+
53+
public <P extends Policy<Response>> FailsafeCallBuilder compose(P innerPolicy) {
54+
failsafe = failsafe.compose(innerPolicy);
55+
return this;
56+
}
57+
58+
public FailsafeCall compose(okhttp3.Call call) {
59+
return new FailsafeCall(failsafe, call);
60+
}
4461
}
4562

4663
/**
47-
* Returns a FailsafeCall for the {@code call}, {@code outerPolicy} and {@code policies}. See {@link
48-
* Failsafe#with(Policy, Policy[])} for docs on how policy composition works.
64+
* Returns a FailsafeCallBuilder for the {@code outerPolicy} and {@code policies}. See {@link Failsafe#with(Policy,
65+
* Policy[])} for docs on how policy composition works.
4966
*
5067
* @param <P> policy type
5168
* @throws NullPointerException if {@code call} or {@code outerPolicy} are null
5269
*/
5370
@SafeVarargs
54-
public static <P extends Policy<Response>> FailsafeCall of(okhttp3.Call call, P outerPolicy, P... policies) {
55-
return of(call, Failsafe.with(outerPolicy, policies));
71+
public static <P extends Policy<Response>> FailsafeCallBuilder with(P outerPolicy, P... policies) {
72+
return new FailsafeCallBuilder(Failsafe.with(outerPolicy, policies));
5673
}
5774

5875
/**
59-
* Returns a FailsafeCall for the {@code call} and {@code failsafeExecutor}.
76+
* Returns a FailsafeCallBuilder for the {@code failsafeExecutor}.
6077
*
61-
* @throws NullPointerException if {@code call} or {@code failsafeExecutor} are null
78+
* @throws NullPointerException if {@code failsafeExecutor} is null
6279
*/
63-
public static FailsafeCall of(okhttp3.Call call, FailsafeExecutor<Response> failsafeExecutor) {
64-
return new FailsafeCall(Assert.notNull(call, "call"), Assert.notNull(failsafeExecutor, "failsafeExecutor"));
80+
public static FailsafeCallBuilder with(FailsafeExecutor<Response> failsafeExecutor) {
81+
return new FailsafeCallBuilder(Assert.notNull(failsafeExecutor, "failsafeExecutor"));
6582
}
6683

6784
/**
@@ -80,7 +97,7 @@ public void cancel() {
8097
* Returns a clone of the FailsafeCall.
8198
*/
8299
public FailsafeCall clone() {
83-
return FailsafeCall.of(initialCall.clone(), failsafe);
100+
return new FailsafeCall(failsafe, initialCall.clone());
84101
}
85102

86103
/**

modules/okhttp/src/test/java/dev/failsafe/okhttp/FailsafeCallTest.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public void testCancel() {
168168
Call call = callFor("/test");
169169

170170
// When / Then Sync
171-
FailsafeCall failsafeCall = FailsafeCall.of(call, failsafe);
171+
FailsafeCall failsafeCall = FailsafeCall.with(failsafe).compose(call);
172172
runInThread(() -> {
173173
sleep(150);
174174
failsafeCall.cancel();
@@ -179,7 +179,7 @@ public void testCancel() {
179179

180180
// When / Then Async
181181
Call call2 = call.clone();
182-
FailsafeCall failsafeCall2 = FailsafeCall.of(call2, failsafe);
182+
FailsafeCall failsafeCall2 = FailsafeCall.with(failsafe).compose(call2);
183183
runInThread(() -> {
184184
sleep(150);
185185
failsafeCall2.cancel();
@@ -197,7 +197,7 @@ public void testCancelViaFuture() {
197197
Call call = callFor("/test");
198198

199199
// When / Then Async
200-
FailsafeCall failsafeCall = FailsafeCall.of(call, failsafe);
200+
FailsafeCall failsafeCall = FailsafeCall.with(failsafe).compose(call);
201201
Future<Response> future = failsafeCall.executeAsync();
202202
sleep(150);
203203
future.cancel(false);

modules/okhttp/src/test/java/dev/failsafe/okhttp/testing/OkHttpTesting.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private <T> void test(FailsafeExecutor<Response> failsafe, Call when, Then<Respo
7272

7373
// Run sync test and assert result
7474
System.out.println("\nRunning sync test");
75-
FailsafeCall failsafeCall = FailsafeCall.of(when, failsafe);
75+
FailsafeCall failsafeCall = FailsafeCall.with(failsafe).compose(when);
7676
if (expectedExceptions == null) {
7777
assertResult.accept(unwrapExceptions(failsafeCall::execute));
7878
} else {

modules/retrofit/src/main/java/dev/failsafe/retrofit/FailsafeCall.java

+45-29
Original file line numberDiff line numberDiff line change
@@ -27,45 +27,61 @@
2727
/**
2828
* A Failsafe wrapped Retrofit {@link Call}. Supports synchronous and asynchronous executions, and cancellation.
2929
*
30-
* @param <T> response type
30+
* @param <R> response type
3131
* @author Jonathan Halterman
3232
*/
33-
public final class FailsafeCall<T> {
34-
private final FailsafeExecutor<Response<T>> failsafe;
35-
private final retrofit2.Call<T> initialCall;
33+
public final class FailsafeCall<R> {
34+
private final FailsafeExecutor<Response<R>> failsafe;
35+
private final retrofit2.Call<R> initialCall;
3636

37-
private volatile Call<Response<T>> failsafeCall;
38-
private volatile CompletableFuture<Response<T>> failsafeFuture;
37+
private volatile Call<Response<R>> failsafeCall;
38+
private volatile CompletableFuture<Response<R>> failsafeFuture;
3939
private AtomicBoolean cancelled = new AtomicBoolean();
4040
private AtomicBoolean executed = new AtomicBoolean();
4141

42-
private FailsafeCall(retrofit2.Call<T> call, FailsafeExecutor<Response<T>> failsafe) {
43-
this.initialCall = call;
42+
private FailsafeCall(FailsafeExecutor<Response<R>> failsafe, retrofit2.Call<R> call) {
4443
this.failsafe = failsafe;
44+
this.initialCall = call;
45+
}
46+
47+
public static final class FailsafeCallBuilder<R> {
48+
private FailsafeExecutor<Response<R>> failsafe;
49+
50+
private FailsafeCallBuilder(FailsafeExecutor<Response<R>> failsafe) {
51+
this.failsafe = failsafe;
52+
}
53+
54+
public <P extends Policy<Response<R>>> FailsafeCallBuilder<R> compose(P innerPolicy) {
55+
failsafe = failsafe.compose(innerPolicy);
56+
return this;
57+
}
58+
59+
public FailsafeCall<R> compose(retrofit2.Call<R> call) {
60+
return new FailsafeCall<>(failsafe, call);
61+
}
4562
}
4663

4764
/**
48-
* Returns a FailsafeCall for the {@code call}, {@code outerPolicy} and {@code policies}. See {@link
49-
* Failsafe#with(Policy, Policy[])} for docs on how policy composition works.
65+
* Returns a FailsafeCallBuilder for the {@code outerPolicy} and {@code policies}. See {@link Failsafe#with(Policy,
66+
* Policy[])} for docs on how policy composition works.
5067
*
51-
* @param <T> response type
68+
* @param <R> result type
5269
* @param <P> policy type
5370
* @throws NullPointerException if {@code call} or {@code outerPolicy} are null
5471
*/
5572
@SafeVarargs
56-
public static <T, P extends Policy<Response<T>>> FailsafeCall<T> of(retrofit2.Call<T> call, P outerPolicy,
57-
P... policies) {
58-
return of(call, Failsafe.with(outerPolicy, policies));
73+
public static <R, P extends Policy<Response<R>>> FailsafeCallBuilder<R> with(P outerPolicy, P... policies) {
74+
return new FailsafeCallBuilder<>(Failsafe.with(outerPolicy, policies));
5975
}
6076

6177
/**
62-
* Returns a FailsafeCall for the {@code call} and {@code failsafeExecutor}.
78+
* Returns a FailsafeCallBuilder for the {@code failsafeExecutor}.
6379
*
64-
* @param <T> response type
65-
* @throws NullPointerException if {@code call} or {@code failsafeExecutor} are null
80+
* @param <R> result type
81+
* @throws NullPointerException if {@code failsafeExecutor} is null
6682
*/
67-
public static <T> FailsafeCall<T> of(retrofit2.Call<T> call, FailsafeExecutor<Response<T>> failsafeExecutor) {
68-
return new FailsafeCall<>(Assert.notNull(call, "call"), Assert.notNull(failsafeExecutor, "failsafeExecutor"));
83+
public static <R> FailsafeCallBuilder<R> with(FailsafeExecutor<Response<R>> failsafeExecutor) {
84+
return new FailsafeCallBuilder<>(Assert.notNull(failsafeExecutor, "failsafeExecutor"));
6985
}
7086

7187
/**
@@ -83,8 +99,8 @@ public void cancel() {
8399
/**
84100
* Returns a clone of the FailsafeCall.
85101
*/
86-
public FailsafeCall<T> clone() {
87-
return FailsafeCall.of(initialCall.clone(), failsafe);
102+
public FailsafeCall<R> clone() {
103+
return new FailsafeCall<>(failsafe, initialCall.clone());
88104
}
89105

90106
/**
@@ -95,7 +111,7 @@ public FailsafeCall<T> clone() {
95111
* @throws FailsafeException if the execution fails with a checked Exception. {@link FailsafeException#getCause()} can
96112
* be used to learn the underlying checked exception.
97113
*/
98-
public Response<T> execute() throws IOException {
114+
public Response<R> execute() throws IOException {
99115
Assert.isTrue(executed.compareAndSet(false, true), "already executed");
100116

101117
failsafeCall = failsafe.newCall(ctx -> {
@@ -114,22 +130,22 @@ public Response<T> execute() throws IOException {
114130
/**
115131
* Executes the call asynchronously until a successful result is returned or the configured policies are exceeded.
116132
*/
117-
public CompletableFuture<Response<T>> executeAsync() {
133+
public CompletableFuture<Response<R>> executeAsync() {
118134
if (!executed.compareAndSet(false, true)) {
119-
CompletableFuture<Response<T>> result = new CompletableFuture<>();
135+
CompletableFuture<Response<R>> result = new CompletableFuture<>();
120136
result.completeExceptionally(new IllegalStateException("already executed"));
121137
return result;
122138
}
123139

124140
failsafeFuture = failsafe.getAsyncExecution(exec -> {
125-
prepareCall(exec).enqueue(new Callback<T>() {
141+
prepareCall(exec).enqueue(new Callback<R>() {
126142
@Override
127-
public void onResponse(retrofit2.Call<T> call, Response<T> response) {
143+
public void onResponse(retrofit2.Call<R> call, Response<R> response) {
128144
exec.recordResult(response);
129145
}
130146

131147
@Override
132-
public void onFailure(retrofit2.Call<T> call, Throwable throwable) {
148+
public void onFailure(retrofit2.Call<R> call, Throwable throwable) {
133149
exec.recordException(throwable);
134150
}
135151
});
@@ -152,8 +168,8 @@ public boolean isExecuted() {
152168
return executed.get();
153169
}
154170

155-
private retrofit2.Call<T> prepareCall(ExecutionContext<Response<T>> ctx) {
156-
retrofit2.Call<T> call = ctx.isFirstAttempt() ? initialCall : initialCall.clone();
171+
private retrofit2.Call<R> prepareCall(ExecutionContext<Response<R>> ctx) {
172+
retrofit2.Call<R> call = ctx.isFirstAttempt() ? initialCall : initialCall.clone();
157173

158174
// Propagate cancellation to the call
159175
ctx.onCancel(() -> {

modules/retrofit/src/test/java/dev/retrofit/FailsafeCallTest.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public void testCancel() {
183183
Call<User> call = service.testUser();
184184

185185
// When / Then Sync
186-
FailsafeCall<User> failsafeCall = FailsafeCall.of(call, failsafe);
186+
FailsafeCall<User> failsafeCall = FailsafeCall.with(failsafe).compose(call);
187187
runInThread(() -> {
188188
sleep(150);
189189
failsafeCall.cancel();
@@ -194,7 +194,7 @@ public void testCancel() {
194194

195195
// When / Then Async
196196
Call<User> call2 = call.clone();
197-
FailsafeCall<User> failsafeCall2 = FailsafeCall.of(call2, failsafe);
197+
FailsafeCall<User> failsafeCall2 = FailsafeCall.with(failsafe).compose(call2);
198198
runInThread(() -> {
199199
sleep(150);
200200
failsafeCall2.cancel();
@@ -210,7 +210,7 @@ public void testCancelViaFuture() {
210210
mockDelayedResponse(200, "foo", 1000);
211211
FailsafeExecutor<Response<User>> failsafe = Failsafe.none();
212212
Call<User> call = service.testUser();
213-
FailsafeCall<User> failsafeCall = FailsafeCall.of(call, failsafe);
213+
FailsafeCall<User> failsafeCall = FailsafeCall.with(failsafe).compose(call);
214214

215215
// When / Then Async
216216
Future<Response<User>> future = failsafeCall.executeAsync();

modules/retrofit/src/test/java/dev/retrofit/testing/RetrofitTesting.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ private <T> void test(FailsafeExecutor<Response<T>> failsafe, Call<T> when, Then
6868

6969
// Run sync test and assert result
7070
System.out.println("\nRunning sync test");
71-
FailsafeCall<T> failsafeCall = FailsafeCall.of(when, failsafe);
71+
FailsafeCall<T> failsafeCall = FailsafeCall.with(failsafe).compose(when);
7272
if (expectedExceptions == null) {
7373
assertResult.accept(unwrapExceptions(failsafeCall::execute));
7474
} else {

0 commit comments

Comments
 (0)