1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.eluder.coveralls.maven.plugin.httpclient;
25
26 import com.fasterxml.jackson.core.JsonProcessingException;
27 import com.fasterxml.jackson.databind.ObjectMapper;
28
29 import java.io.BufferedReader;
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.InputStreamReader;
33 import java.security.Provider;
34 import java.security.Security;
35
36 import org.apache.commons.lang3.StringUtils;
37 import org.apache.http.HttpEntity;
38 import org.apache.http.HttpResponse;
39 import org.apache.http.HttpStatus;
40 import org.apache.http.client.HttpClient;
41 import org.apache.http.client.methods.HttpPost;
42 import org.apache.http.entity.ContentType;
43 import org.apache.http.entity.mime.HttpMultipartMode;
44 import org.apache.http.entity.mime.MultipartEntityBuilder;
45 import org.eluder.coveralls.maven.plugin.ProcessingException;
46 import org.eluder.coveralls.maven.plugin.domain.CoverallsResponse;
47
48 public class CoverallsClient {
49
50 static {
51 for (Provider provider : Security.getProviders()) {
52 if (provider.getName().startsWith("SunPKCS11")) {
53 Security.removeProvider(provider.getName());
54 }
55 }
56 }
57
58 private static final String FILE_NAME = "coveralls.json";
59 private static final ContentType MIME_TYPE = ContentType.create("application/octet-stream", "utf-8");
60
61 private final String coverallsUrl;
62 private final HttpClient httpClient;
63 private final ObjectMapper objectMapper;
64
65 public CoverallsClient(final String coverallsUrl) {
66 this(coverallsUrl, new HttpClientFactory(coverallsUrl).create(), new ObjectMapper());
67 }
68
69 public CoverallsClient(final String coverallsUrl, final HttpClient httpClient, final ObjectMapper objectMapper) {
70 this.coverallsUrl = coverallsUrl;
71 this.httpClient = httpClient;
72 this.objectMapper = objectMapper;
73 }
74
75 public CoverallsResponse submit(final File file) throws ProcessingException, IOException {
76 HttpEntity entity = MultipartEntityBuilder.create()
77 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
78 .addBinaryBody("json_file", file, MIME_TYPE, FILE_NAME)
79 .build();
80 HttpPost post = new HttpPost(coverallsUrl);
81 post.setEntity(entity);
82 HttpResponse response = httpClient.execute(post);
83 return parseResponse(response);
84 }
85
86 private CoverallsResponse parseResponse(final HttpResponse response) throws ProcessingException, IOException {
87 HttpEntity entity = response.getEntity();
88 ContentType contentType = ContentType.getOrDefault(entity);
89 if (contentType.getCharset() == null) {
90 throw new ProcessingException(getResponseErrorMessage(response, "Response doesn't contain Content-Type header"));
91 }
92
93 if (response.getStatusLine().getStatusCode() >= HttpStatus.SC_INTERNAL_SERVER_ERROR) {
94 throw new IOException(getResponseErrorMessage(response, "Coveralls API internal error"));
95 }
96
97 try (BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), contentType.getCharset()))) {
98 CoverallsResponse cr = objectMapper.readValue(reader, CoverallsResponse.class);
99 if (cr.isError()) {
100 throw new ProcessingException(getResponseErrorMessage(response, cr.getMessage()));
101 }
102 return cr;
103 } catch (JsonProcessingException ex) {
104 throw new ProcessingException(getResponseErrorMessage(response, ex.getMessage()), ex);
105 }
106 }
107
108 private String getResponseErrorMessage(final HttpResponse response, final String message) {
109 int status = response.getStatusLine().getStatusCode();
110 String reason = response.getStatusLine().getReasonPhrase();
111 String errorMessage = "Report submission to Coveralls API failed with HTTP status " + status + ":";
112 if (StringUtils.isNotBlank(reason)) {
113 errorMessage += " " + reason;
114 }
115 if (StringUtils.isNotBlank(message)) {
116 errorMessage += " (" + message + ")";
117 }
118 return errorMessage;
119 }
120 }