-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathRetryInterceptor.java
More file actions
137 lines (111 loc) · 4.56 KB
/
RetryInterceptor.java
File metadata and controls
137 lines (111 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package com.contentstack.sdk;
import java.io.IOException;
import java.util.Arrays;
import java.util.logging.Logger;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class RetryInterceptor implements Interceptor {
private static final Logger logger = Logger.getLogger(RetryInterceptor.class.getName());
private final RetryOptions retryOptions;
public RetryInterceptor(RetryOptions retryOptions) {
if (retryOptions == null) {
throw new NullPointerException("RetryOptions cannot be null");
}
this.retryOptions = retryOptions;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = null;
IOException lastException = null;
// If retry is disabled, just proceed with the request once
if (!retryOptions.isRetryEnabled()) {
return chain.proceed(request);
}
int attempt = 0;
// retryLimit means number of retries, so total attempts = 1 initial + retryLimit retries
int maxAttempts = retryOptions.getRetryLimit() + 1;
while (attempt < maxAttempts) {
try {
if(response != null) {
response.close();
}
response = chain.proceed(request);
if (shouldRetry(response.code()) && (attempt + 1) < maxAttempts) {
logger.fine("Retry attempt " + (attempt + 1) + " for status " + response.code() + " on " + request.url());
long delay = calculateDelay(attempt, response.code(), null);
Thread.sleep(delay);
attempt++;
continue;
}
return response;
} catch (IOException e) {
// Network error occurred
lastException = e;
if ((attempt + 1) < maxAttempts) {
try {
long delay = calculateDelay(attempt, -1, e);
Thread.sleep(delay);
attempt++;
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new IOException("Retry interrupted", ie);
}
continue;
} else {
// No more retries, throw the exception
throw e;
}
} catch (InterruptedException e) {
// Thread was interrupted during sleep
Thread.currentThread().interrupt();
if (response != null) response.close();
throw new IOException("Retry interrupted", e);
}
}
// Should not reach here normally
if (lastException != null) {
throw lastException;
}
return response;
}
/**
* Determines if a status code should trigger a retry.
*
* @param statusCode HTTP status code
* @return true if this status code is retryable
*/
private boolean shouldRetry(int statusCode) {
return Arrays.stream(retryOptions.getRetryableStatusCodes()).anyMatch(code -> code == statusCode);
}
/**
* Calculates the delay before the next retry attempt.
*
* @param attempt current attempt number (0-based)
* @param statusCode HTTP status code (-1 for network errors)
* @param exception the IOException that occurred (may be null)
* @return delay in milliseconds
*/
private long calculateDelay(int attempt, int statusCode, IOException exception) {
if(retryOptions.hasCustomBackoff()) {
return retryOptions.getCustomBackoffStrategy().calculateDelay(attempt, statusCode, exception);
}
long baseDelay = retryOptions.getRetryDelay();
switch (retryOptions.getBackoffStrategy()) {
case FIXED:
// baseDelay already set above
break;
case LINEAR:
baseDelay = retryOptions.getRetryDelay() * (attempt + 1);
break;
case EXPONENTIAL:
baseDelay = (long) (retryOptions.getRetryDelay() * Math.pow(2,attempt));
break;
default:
// baseDelay already set above
break;
}
return baseDelay;
}
}