1 package fr.ifremer.quadrige2.core.dao.technical.http;
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 import com.google.common.base.Joiner;
27 import com.google.common.base.Preconditions;
28 import com.google.gson.Gson;
29 import com.google.gson.GsonBuilder;
30 import fr.ifremer.quadrige2.core.dao.technical.gson.Gsons;
31 import fr.ifremer.quadrige2.core.exception.BadUpdateDtException;
32 import fr.ifremer.quadrige2.core.exception.DataLockedException;
33 import fr.ifremer.quadrige2.core.exception.DeleteForbiddenException;
34 import fr.ifremer.quadrige2.core.exception.Quadrige2TechnicalException;
35 import org.apache.commons.io.FileUtils;
36 import org.apache.commons.io.IOUtils;
37 import org.apache.commons.lang3.builder.ToStringBuilder;
38 import org.apache.commons.lang3.builder.ToStringStyle;
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.apache.http.Header;
42 import org.apache.http.HttpHeaders;
43 import org.apache.http.HttpResponse;
44 import org.apache.http.client.HttpClient;
45 import org.apache.http.client.methods.HttpUriRequest;
46 import org.apache.http.client.utils.URIBuilder;
47 import org.apache.http.util.EntityUtils;
48 import org.nuiton.i18n.I18n;
49 import org.nuiton.jaxx.application.type.ApplicationProgressionModel;
50
51 import java.io.*;
52 import java.lang.reflect.Type;
53 import java.net.URI;
54 import java.net.URISyntaxException;
55 import java.nio.charset.StandardCharsets;
56 import java.util.List;
57 import java.util.regex.Matcher;
58 import java.util.regex.Pattern;
59
60
61
62
63 public class HttpHelper {
64
65 private static final Log log = LogFactory.getLog(HttpHelper.class);
66
67 private final static Pattern PATTERN_INTERNAL_SERVER_ERROR_MESSAGE = Pattern.compile("\\\"\\[([0-9]+)\\] .*");
68
69
70
71
72
73
74
75 @SuppressWarnings("unchecked")
76 public static void executeRequest(HttpClient httpClient, HttpUriRequest request) {
77 executeRequest(httpClient, request, null, null);
78 }
79
80
81
82
83
84
85
86
87
88
89
90 @SuppressWarnings("unchecked")
91 public static <T> T executeRequest(HttpClient httpClient, HttpUriRequest request, Gson gson, Class<? extends T> resultClass) {
92 T result = null;
93
94 if (log.isDebugEnabled()) {
95 log.debug("Executing request : " + request.getRequestLine());
96 }
97
98
99 if (I18n.getDefaultLocale() != null) {
100 request.setHeader(HttpHeaders.ACCEPT_LANGUAGE, I18n.getDefaultLocale().getLanguage());
101 }
102 else if (log.isWarnEnabled()) {
103 log.warn(String.format("I18n not initialized properly: no default locale found ! Could not set HTTP header [%s] with user language", HttpHeaders.ACCEPT_LANGUAGE));
104 }
105
106 try {
107 HttpResponse response = httpClient.execute(request);
108
109 if (log.isDebugEnabled()) {
110 log.debug("Received response : " + response.getStatusLine());
111 }
112
113 switch (response.getStatusLine().getStatusCode()) {
114 case HttpStatus.SC_OK: {
115 if (gson != null && resultClass != null) {
116 result = (T) parseResponse(response, gson, resultClass);
117 }
118 EntityUtils.consume(response.getEntity());
119 break;
120 }
121 case HttpStatus.SC_UNAUTHORIZED:
122 case HttpStatus.SC_FORBIDDEN:
123 throw new HttpAuthenticationException(I18n.t("quadrige2.error.authenticate.unauthorized"));
124 case HttpStatus.SC_NOT_FOUND:
125 throw new HttpNotFoundException(I18n.t("quadrige2.error.notFound"));
126 default:
127 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.authenticate.failed", response.getStatusLine().toString()));
128 }
129
130 }
131 catch (InterruptedIOException e) {
132 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.timeout"), e);
133 }
134 catch (IOException e) {
135 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.connect"), e);
136 }
137
138 return result;
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152 @SuppressWarnings("unchecked")
153 public static <T> T executeRequest(HttpClient httpClient, HttpUriRequest request, Gson gson, Type type) {
154 T result = null;
155
156 if (log.isDebugEnabled()) {
157 log.debug("Executing request : " + request.getRequestLine());
158 }
159
160 try {
161 HttpResponse response = httpClient.execute(request);
162
163 if (log.isDebugEnabled()) {
164 log.debug("Received response : " + response.getStatusLine());
165 }
166
167 switch (response.getStatusLine().getStatusCode()) {
168 case HttpStatus.SC_OK: {
169 if (gson != null && type != null) {
170 result = parseResponse(response, gson, type);
171 }
172 EntityUtils.consume(response.getEntity());
173 break;
174 }
175
176 case HttpStatus.SC_INTERNAL_SERVER_ERROR: {
177 String errorMessage = (String)parseResponse(response, gson, String.class);
178 switch (getInternalServerErrorCode(errorMessage)) {
179 case HttpStatus.SC_BAD_UPDATE_DT:
180 throw new BadUpdateDtException(errorMessage);
181 case HttpStatus.SC_DATA_LOCKED:
182 throw new DataLockedException(errorMessage);
183 case HttpStatus.SC_DELETE_FORBIDDEN:
184 List<String> objectIds = null;
185
186 Header header = response.getFirstHeader(fr.ifremer.quadrige2.core.dao.technical.http.HttpHeaders.HH_DELETE_FORBIDDEN_OBJECT_IDS);
187 if (gson != null && header != null) {
188 objectIds = gson.fromJson(header.getValue(), List.class);
189 }
190 throw new DeleteForbiddenException(errorMessage, objectIds);
191 default:
192 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.server.internal", errorMessage));
193 }
194 }
195
196 case HttpStatus.SC_UNAUTHORIZED:
197 case HttpStatus.SC_FORBIDDEN:
198 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.authenticate.unauthorized"));
199 default:
200 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.authenticate.failed", response.getStatusLine().toString()));
201 }
202
203 }
204 catch (InterruptedIOException e) {
205 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.timeout"), e);
206 }
207 catch (IOException e) {
208 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.connect"), e);
209 }
210
211 return result;
212 }
213
214
215
216
217
218
219
220
221
222 public static URI getAppendedPath(URI baseURI, String... path) throws URISyntaxException {
223
224 String pathToAppend = Joiner.on('/').skipNulls().join(path);
225
226 URIBuilder builder = new URIBuilder(baseURI);
227 builder.setPath(baseURI.getPath() + pathToAppend);
228 return builder.build();
229
230 }
231
232
233
234
235
236
237
238
239
240
241 public static void executeDownloadFileRequest(HttpClient httpClient, HttpUriRequest request, ApplicationProgressionModel progressionModel, File outputFile) throws IOException {
242
243 Preconditions.checkNotNull(httpClient);
244 Preconditions.checkNotNull(request);
245 Preconditions.checkNotNull(progressionModel);
246 Preconditions.checkNotNull(outputFile);
247
248 if (!outputFile.createNewFile()) {
249 throw new Quadrige2TechnicalException(String.format("File %s already exists", outputFile.getAbsolutePath()));
250 }
251
252
253 OutputStream os = null;
254 InputStream is = null;
255
256 try {
257
258 HttpResponse response = httpClient.execute(request);
259
260 if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
261
262 FileUtils.deleteQuietly(outputFile);
263
264 if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) {
265 throw new HttpNotFoundException(
266 String.format("error while downloading file from [%s]; server responds: %s", request.getURI(), response.getStatusLine().getReasonPhrase()));
267 }
268 throw new Quadrige2TechnicalException(
269 String.format("error while downloading file from [%s]; server responds: %s", request.getURI(), response.getStatusLine().getReasonPhrase()));
270 }
271
272 os = new BufferedOutputStream(new FileOutputStream(outputFile));
273 is = new BufferedInputStream(response.getEntity().getContent());
274
275
276 long contentLength = response.getEntity().getContentLength();
277 progressionModel.setTotal((int)contentLength);
278
279 long count = 0L;
280 int bytesReaded;
281 byte[] bytes = new byte[4096];
282
283 while ((bytesReaded = is.read(bytes)) != -1) {
284
285
286 os.write(bytes, 0, bytesReaded);
287
288
289 count += bytesReaded;
290 progressionModel.setCurrent((int)count);
291
292 }
293
294 }
295 finally {
296 IOUtils.closeQuietly(is);
297 IOUtils.closeQuietly(os);
298 }
299 }
300
301
302
303
304
305
306
307 public static int getInternalServerErrorCode(String message) {
308 if (message != null) {
309 Matcher matcher = PATTERN_INTERNAL_SERVER_ERROR_MESSAGE.matcher(message);
310 if (matcher.matches()) {
311 String errorCodeAsStr = matcher.group(1);
312 return Integer.parseInt(errorCodeAsStr);
313 }
314 }
315
316 return HttpStatus.SC_INTERNAL_SERVER_ERROR;
317 }
318
319
320
321
322
323
324
325
326
327 public static String getInternalServerErrorMessage(int errorCode, String message) {
328 return String.format("[%s] %s", errorCode, message);
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342 protected static Object parseResponse(HttpResponse response, Gson gson, Class<?> ResultClass) throws IOException {
343 Object result;
344
345 boolean stringOutput = ResultClass.equals(String.class);
346
347
348 if (stringOutput) {
349 InputStream content = null;
350 try {
351 content = response.getEntity().getContent();
352 String stringContent = getContentAsString(content);
353 if (log.isDebugEnabled()) {
354 log.debug("Parsing response:\n" + stringContent);
355 }
356
357 return stringContent;
358 }
359 finally {
360 if (content!= null) {
361 content.close();
362 }
363 }
364 }
365
366
367 else {
368 InputStream content = null;
369 try {
370 content = response.getEntity().getContent();
371 Reader reader = new InputStreamReader(content, StandardCharsets.UTF_8);
372 result = gson.fromJson(reader, ResultClass);
373 }
374 finally {
375 if (content!= null) {
376 content.close();
377 }
378 }
379 }
380
381
382 if (result == null) {
383 throw new Quadrige2TechnicalException("emptyResponse");
384 }
385
386 if (log.isTraceEnabled()) {
387 log.trace("response: " + ToStringBuilder.reflectionToString(result, ToStringStyle.SHORT_PREFIX_STYLE));
388 }
389
390 return result;
391 }
392
393
394
395
396
397
398
399
400
401
402
403 protected static <T> T parseResponse(HttpResponse response, Gson gson, Type typeOfT) throws IOException {
404 T result;
405
406 InputStream content = null;
407 try {
408 content = response.getEntity().getContent();
409 Reader reader = new InputStreamReader(content, StandardCharsets.UTF_8);
410 result = gson.fromJson(reader, typeOfT);
411 }
412 finally {
413 if (content!= null) {
414 content.close();
415 }
416 }
417
418 if (result == null) {
419 throw new Quadrige2TechnicalException("emptyResponse");
420 }
421
422 if (log.isTraceEnabled()) {
423 log.trace("response: " + ToStringBuilder.reflectionToString(result, ToStringStyle.SHORT_PREFIX_STYLE));
424 }
425
426 return result;
427 }
428
429
430
431
432
433
434
435
436 protected static String getContentAsString(InputStream content) throws IOException {
437 Reader reader = new InputStreamReader(content, StandardCharsets.UTF_8);
438 StringBuilder result = new StringBuilder();
439 char[] buf = new char[64];
440 int len = 0;
441 while((len = reader.read(buf)) != -1) {
442 result.append(buf, 0, len);
443 }
444 return result.toString();
445 }
446
447 }