forked from gooddata/gooddata-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAbstractService.java
More file actions
168 lines (135 loc) · 5.58 KB
/
AbstractService.java
File metadata and controls
168 lines (135 loc) · 5.58 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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*
* Copyright (C) 2007-2014, GoodData(R) Corporation. All rights reserved.
*/
package com.gooddata;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.client.HttpMessageConverterExtractor;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import static java.lang.String.format;
import static org.springframework.http.HttpMethod.GET;
/**
*/
public abstract class AbstractService {
public static Integer WAIT_BEFORE_RETRY_IN_MILLIS = 5 * 1000;
public static Integer MAX_ATTEMPTS = 5;
protected final RestTemplate restTemplate;
private final RequestCallback noopRequestCallback = new RequestCallback() {
@Override
public void doWithRequest(final ClientHttpRequest request) throws IOException {
}
};
private final ResponseExtractor<ClientHttpResponse> reusableResponseExtractor = new ResponseExtractor<ClientHttpResponse>() {
@Override
public ClientHttpResponse extractData(final ClientHttpResponse response) throws IOException {
return new ReusableClientHttpResponse(response);
}
};
public AbstractService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public <T> T poll(URI pollingUri, Class<T> cls) {
return poll(pollingUri, new StatusOkConditionCallback(), cls);
}
public <T> T poll(String pollingUri, Class<T> cls) {
return poll(URI.create(pollingUri), new StatusOkConditionCallback(), cls);
}
public <T> T poll(String pollingUri, ConditionCallback condition, Class<T> returnClass) {
return poll(URI.create(pollingUri), condition, returnClass);
}
public <T> T poll(URI pollingUri, ConditionCallback condition, Class<T> returnClass) {
int attempt = 0;
while (true) {
final ClientHttpResponse response = restTemplate.execute(pollingUri, GET, noopRequestCallback,
reusableResponseExtractor);
try {
if (condition.finished(response)) {
return new HttpMessageConverterExtractor<>(returnClass, restTemplate.getMessageConverters())
.extractData(response);
} else if (HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series())) {
throw new GoodDataException(
format("Polling returned client error HTTP status %s", response.getStatusCode().value())
);
}
} catch (IOException e) {
throw new GoodDataException("I/O error occurred during HTTP response extraction", e);
}
if (attempt >= MAX_ATTEMPTS - 1) {
throw new GoodDataException(format("Max number of attempts (%s) exceeded", MAX_ATTEMPTS));
}
try {
Thread.sleep(WAIT_BEFORE_RETRY_IN_MILLIS);
} catch (InterruptedException e) {
// do nothing
}
attempt++;
}
}
protected <T> T extractData(ClientHttpResponse response, Class<T> cls) throws IOException {
return new HttpMessageConverterExtractor<>(cls, restTemplate.getMessageConverters()).extractData(response);
}
public static interface ConditionCallback {
boolean finished(ClientHttpResponse response) throws IOException;
}
public static class StatusOkConditionCallback implements ConditionCallback {
@Override
public boolean finished(ClientHttpResponse response) throws IOException {
return HttpStatus.OK.equals(response.getStatusCode());
}
}
private class ReusableClientHttpResponse implements ClientHttpResponse {
private final byte[] body;
private final HttpStatus statusCode;
private final int rawStatusCode;
private final String statusText;
private final HttpHeaders headers;
public ReusableClientHttpResponse(ClientHttpResponse response) {
try {
body = FileCopyUtils.copyToByteArray(response.getBody());
statusCode = response.getStatusCode();
rawStatusCode = response.getRawStatusCode();
statusText = response.getStatusText();
headers = response.getHeaders();
} catch (IOException e) {
throw new RuntimeException("Unable to read from HTTP response", e);
} finally {
if (response != null) {
response.close();
}
}
}
@Override
public HttpStatus getStatusCode() throws IOException {
return statusCode;
}
@Override
public int getRawStatusCode() throws IOException {
return rawStatusCode;
}
@Override
public String getStatusText() throws IOException {
return statusText;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream(body);
}
@Override
public void close() {
//already closed
}
}
}