Skip to content

Commit b6039c5

Browse files
committed
feat: Add new prop for network exception retry strategy in Python and Java (box/box-codegen#763)
1 parent e6c1f9a commit b6039c5

File tree

4 files changed

+121
-75
lines changed

4 files changed

+121
-75
lines changed

.codegen.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "engineHash": "43a5daf", "specHash": "eaa9cf0", "version": "0.7.0" }
1+
{ "engineHash": "ac37b50", "specHash": "eaa9cf0", "version": "0.7.0" }

src/main/java/com/box/sdkgen/networking/boxnetworkclient/BoxNetworkClient.java

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ public FetchResponse fetch(FetchOptions options) {
8080
FetchResponse fetchResponse = null;
8181
Exception exceptionThrown = null;
8282

83-
int attemptNumber = 0;
83+
int attemptNumber = 1;
84+
int numberOfRetriesOnException = 0;
85+
int attemptForRetry = 0;
86+
boolean shouldRetry = false;
8487

8588
while (true) {
8689
request = prepareRequest(fetchOptions, authenticationNeeded, networkSession);
@@ -105,6 +108,8 @@ public FetchResponse fetch(FetchOptions options) {
105108
? response.networkResponse().request().url().toString()
106109
: response.request().url().toString();
107110

111+
attemptForRetry = attemptNumber;
112+
108113
if (Objects.equals(
109114
fetchOptions.getResponseFormat().getEnumValue(), ResponseFormat.BINARY)) {
110115
fetchResponse =
@@ -128,51 +133,60 @@ public FetchResponse fetch(FetchOptions options) {
128133
(modifiedResponse, interceptor) -> interceptor.afterRequest(modifiedResponse),
129134
(o1, o2) -> o2);
130135

131-
boolean shouldRetry =
132-
networkSession
133-
.getRetryStrategy()
134-
.shouldRetry(fetchOptions, fetchResponse, attemptNumber);
135-
136-
if (shouldRetry) {
137-
TimeUnit.SECONDS.sleep(
138-
(long)
139-
networkSession
140-
.getRetryStrategy()
141-
.retryAfter(fetchOptions, fetchResponse, attemptNumber));
142-
attemptNumber++;
143-
continue;
136+
} catch (Exception e) {
137+
exceptionThrown = e;
138+
numberOfRetriesOnException++;
139+
attemptForRetry = numberOfRetriesOnException;
140+
if (response != null) {
141+
response.close();
144142
}
145143

146-
if (fetchResponse.getStatus() >= 300
147-
&& fetchResponse.getStatus() < 400
148-
&& fetchOptions.followRedirects) {
149-
if (!fetchResponse.getHeaders().containsKey("Location")) {
150-
throw new BoxSDKError(
151-
"Redirect response missing Location header for " + fetchOptions.getUrl());
144+
fetchResponse =
145+
new FetchResponse.Builder(0, new TreeMap<>(String.CASE_INSENSITIVE_ORDER)).build();
146+
}
147+
148+
shouldRetry =
149+
networkSession
150+
.getRetryStrategy()
151+
.shouldRetry(fetchOptions, fetchResponse, attemptForRetry);
152+
153+
if (shouldRetry) {
154+
double retryDelay =
155+
networkSession
156+
.getRetryStrategy()
157+
.retryAfter(fetchOptions, fetchResponse, attemptForRetry);
158+
if (retryDelay > 0) {
159+
try {
160+
TimeUnit.SECONDS.sleep((long) retryDelay);
161+
} catch (InterruptedException ie) {
162+
Thread.currentThread().interrupt();
163+
throw new BoxSDKError("Retry interrupted", ie);
152164
}
153-
URI originalUri = URI.create(fetchOptions.getUrl());
154-
URI redirectUri = URI.create(fetchResponse.getHeaders().get("Location"));
155-
boolean sameOrigin =
156-
originalUri.getHost().equals(redirectUri.getHost())
157-
&& originalUri.getPort() == redirectUri.getPort()
158-
&& originalUri.getScheme().equals(redirectUri.getScheme());
159-
return fetch(
160-
new FetchOptions.Builder(fetchResponse.getHeaders().get("Location"), "GET")
161-
.responseFormat(fetchOptions.getResponseFormat())
162-
.auth(sameOrigin ? fetchOptions.getAuth() : null)
163-
.networkSession(networkSession)
164-
.build());
165165
}
166+
attemptNumber++;
167+
continue;
168+
}
166169

167-
} catch (Exception e) {
168-
exceptionThrown = e;
169-
// Retry network exception only once
170-
if (attemptNumber > 1) {
171-
if (response != null) {
172-
response.close();
173-
}
174-
throw new BoxSDKError(e.getMessage(), e);
170+
if (fetchResponse != null
171+
&& fetchResponse.getStatus() >= 300
172+
&& fetchResponse.getStatus() < 400
173+
&& fetchOptions.followRedirects) {
174+
if (!fetchResponse.getHeaders().containsKey("Location")) {
175+
throw new BoxSDKError(
176+
"Redirect response missing Location header for " + fetchOptions.getUrl());
175177
}
178+
URI originalUri = URI.create(fetchOptions.getUrl());
179+
URI redirectUri = URI.create(fetchResponse.getHeaders().get("Location"));
180+
boolean sameOrigin =
181+
originalUri.getHost().equals(redirectUri.getHost())
182+
&& originalUri.getPort() == redirectUri.getPort()
183+
&& originalUri.getScheme().equals(redirectUri.getScheme());
184+
return fetch(
185+
new FetchOptions.Builder(fetchResponse.getHeaders().get("Location"), "GET")
186+
.responseFormat(fetchOptions.getResponseFormat())
187+
.auth(sameOrigin ? fetchOptions.getAuth() : null)
188+
.networkSession(networkSession)
189+
.build());
176190
}
177191

178192
if (fetchResponse != null

src/main/java/com/box/sdkgen/networking/defaultnetworkclient/DefaultNetworkClient.java

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ public FetchResponse fetch(FetchOptions options) {
6666
FetchResponse fetchResponse = null;
6767
Exception exceptionThrown = null;
6868

69-
int attemptNumber = 0;
69+
int attemptNumber = 1;
70+
int numberOfRetriesOnException = 0;
71+
int attemptForRetry = 0;
72+
boolean shouldRetry = false;
7073

7174
while (true) {
7275
request = prepareRequest(fetchOptions, authenticationNeeded, networkSession);
@@ -88,6 +91,8 @@ public FetchResponse fetch(FetchOptions options) {
8891
response.networkResponse() != null
8992
? response.networkResponse().request().url().toString()
9093
: response.request().url().toString();
94+
95+
attemptForRetry = attemptNumber;
9196
fetchResponse =
9297
Objects.equals(fetchOptions.getResponseFormat().getEnumValue(), ResponseFormat.BINARY)
9398
? new FetchResponse.Builder(response.code(), headersMap)
@@ -109,45 +114,53 @@ public FetchResponse fetch(FetchOptions options) {
109114
(modifiedResponse, interceptor) -> interceptor.afterRequest(modifiedResponse),
110115
(o1, o2) -> o2);
111116

112-
boolean shouldRetry =
113-
networkSession
114-
.getRetryStrategy()
115-
.shouldRetry(fetchOptions, fetchResponse, attemptNumber);
116-
117-
if (shouldRetry) {
118-
TimeUnit.SECONDS.sleep(
119-
(long)
120-
networkSession
121-
.getRetryStrategy()
122-
.retryAfter(fetchOptions, fetchResponse, attemptNumber));
123-
attemptNumber++;
124-
continue;
117+
} catch (Exception e) {
118+
exceptionThrown = e;
119+
numberOfRetriesOnException++;
120+
attemptForRetry = numberOfRetriesOnException;
121+
if (response != null) {
122+
response.close();
125123
}
126124

127-
if (fetchResponse.getStatus() >= 300
128-
&& fetchResponse.getStatus() < 400
129-
&& fetchOptions.followRedirects) {
130-
if (!fetchResponse.getHeaders().containsKey("Location")) {
131-
throw new BoxSDKError(
132-
"Redirect response missing Location header for " + fetchOptions.getUrl());
125+
fetchResponse =
126+
new FetchResponse.Builder(0, new TreeMap<>(String.CASE_INSENSITIVE_ORDER)).build();
127+
}
128+
129+
shouldRetry =
130+
networkSession
131+
.getRetryStrategy()
132+
.shouldRetry(fetchOptions, fetchResponse, attemptForRetry);
133+
134+
if (shouldRetry) {
135+
double retryDelay =
136+
networkSession
137+
.getRetryStrategy()
138+
.retryAfter(fetchOptions, fetchResponse, attemptForRetry);
139+
if (retryDelay > 0) {
140+
try {
141+
TimeUnit.SECONDS.sleep((long) retryDelay);
142+
} catch (InterruptedException ie) {
143+
Thread.currentThread().interrupt();
144+
throw new BoxSDKError("Retry interrupted", ie);
133145
}
134-
return fetch(
135-
new FetchOptions.Builder(fetchResponse.getHeaders().get("Location"), "GET")
136-
.responseFormat(fetchOptions.getResponseFormat())
137-
.auth(fetchOptions.getAuth())
138-
.networkSession(networkSession)
139-
.build());
140146
}
147+
attemptNumber++;
148+
continue;
149+
}
141150

142-
} catch (Exception e) {
143-
exceptionThrown = e;
144-
// Retry network exception only once
145-
if (attemptNumber > 1) {
146-
if (response != null) {
147-
response.close();
148-
}
149-
throw new BoxSDKError(e.getMessage(), e);
151+
if (fetchResponse.getStatus() >= 300
152+
&& fetchResponse.getStatus() < 400
153+
&& fetchOptions.followRedirects) {
154+
if (!fetchResponse.getHeaders().containsKey("Location")) {
155+
throw new BoxSDKError(
156+
"Redirect response missing Location header for " + fetchOptions.getUrl());
150157
}
158+
return fetch(
159+
new FetchOptions.Builder(fetchResponse.getHeaders().get("Location"), "GET")
160+
.responseFormat(fetchOptions.getResponseFormat())
161+
.auth(fetchOptions.getAuth())
162+
.networkSession(networkSession)
163+
.build());
151164
}
152165

153166
if (fetchResponse != null

src/main/java/com/box/sdkgen/networking/retries/BoxRetryStrategy.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,28 @@ public class BoxRetryStrategy implements RetryStrategy {
1313

1414
public double retryBaseInterval;
1515

16+
public int maxRetriesOnException;
17+
1618
public BoxRetryStrategy() {
1719
this.maxAttempts = 5;
1820
this.retryRandomizationFactor = 0.5;
1921
this.retryBaseInterval = 1;
22+
this.maxRetriesOnException = 2;
2023
}
2124

2225
protected BoxRetryStrategy(Builder builder) {
2326
this.maxAttempts = builder.maxAttempts;
2427
this.retryRandomizationFactor = builder.retryRandomizationFactor;
2528
this.retryBaseInterval = builder.retryBaseInterval;
29+
this.maxRetriesOnException = builder.maxRetriesOnException;
2630
}
2731

2832
@Override
2933
public boolean shouldRetry(
3034
FetchOptions fetchOptions, FetchResponse fetchResponse, int attemptNumber) {
35+
if (fetchResponse.getStatus() == 0) {
36+
return attemptNumber <= this.maxRetriesOnException;
37+
}
3138
boolean isSuccessful = fetchResponse.getStatus() >= 200 && fetchResponse.getStatus() < 400;
3239
String retryAfterHeader = fetchResponse.getHeaders().get("Retry-After");
3340
boolean isAcceptedWithRetryAfter =
@@ -79,6 +86,10 @@ public double getRetryBaseInterval() {
7986
return retryBaseInterval;
8087
}
8188

89+
public int getMaxRetriesOnException() {
90+
return maxRetriesOnException;
91+
}
92+
8293
public static class Builder {
8394

8495
protected int maxAttempts;
@@ -87,10 +98,13 @@ public static class Builder {
8798

8899
protected double retryBaseInterval;
89100

101+
protected int maxRetriesOnException;
102+
90103
public Builder() {
91104
this.maxAttempts = 5;
92105
this.retryRandomizationFactor = 0.5;
93106
this.retryBaseInterval = 1;
107+
this.maxRetriesOnException = 2;
94108
}
95109

96110
public Builder maxAttempts(int maxAttempts) {
@@ -108,6 +122,11 @@ public Builder retryBaseInterval(double retryBaseInterval) {
108122
return this;
109123
}
110124

125+
public Builder maxRetriesOnException(int maxRetriesOnException) {
126+
this.maxRetriesOnException = maxRetriesOnException;
127+
return this;
128+
}
129+
111130
public BoxRetryStrategy build() {
112131
return new BoxRetryStrategy(this);
113132
}

0 commit comments

Comments
 (0)