1 package io.guixer.maven;
2
3 import static com.google.common.base.Preconditions.checkNotNull;
4 import static com.google.common.collect.Lists.newArrayList;
5 import static java.nio.charset.StandardCharsets.UTF_8;
6 import static org.apache.commons.lang3.StringUtils.isBlank;
7 import static org.apache.commons.lang3.StringUtils.substringBeforeLast;
8
9 import java.io.ByteArrayOutputStream;
10 import java.io.File;
11 import java.io.FileInputStream;
12 import java.io.FileNotFoundException;
13 import java.io.FileOutputStream;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.io.OutputStream;
17 import java.nio.file.Files;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.zip.ZipEntry;
21 import java.util.zip.ZipOutputStream;
22
23 import javax.annotation.Nullable;
24
25 import org.apache.commons.io.IOUtils;
26 import org.apache.http.HttpResponse;
27 import org.apache.http.NameValuePair;
28 import org.apache.http.StatusLine;
29 import org.apache.http.client.HttpClient;
30 import org.apache.http.client.entity.UrlEncodedFormEntity;
31 import org.apache.http.client.methods.HttpPost;
32 import org.apache.http.entity.mime.MultipartEntity;
33 import org.apache.http.entity.mime.content.FileBody;
34 import org.apache.http.entity.mime.content.StringBody;
35 import org.apache.http.impl.client.DefaultHttpClient;
36 import org.apache.http.message.BasicNameValuePair;
37 import org.apache.maven.plugin.MojoExecutionException;
38 import org.apache.maven.plugin.MojoFailureException;
39 import org.apache.maven.plugins.annotations.LifecyclePhase;
40 import org.apache.maven.plugins.annotations.Mojo;
41 import org.apache.maven.plugins.annotations.Parameter;
42 import org.apache.maven.project.MavenProject;
43 import org.codehaus.plexus.util.FileUtils;
44 import org.joda.time.DateTime;
45 import org.json.simple.parser.ParseException;
46
47 import io.guixer.maven.ResponseParser.MyUpload;
48
49 @Mojo(name = "send", defaultPhase = LifecyclePhase.PACKAGE)
50 public class SendToGuixerMojo extends AbstractGuixerOutMojo {
51
52 @Parameter(defaultValue = "${project}", required = true, readonly = true)
53 private MavenProject project;
54
55 @Parameter(property = "guixer.apiBaseHref", required = true)
56 private String guixerApiBaseHref;
57
58 @Parameter(property = "guixer.username", required = true)
59 private String guixerUsername;
60
61 @Parameter(property = "guixer.password", required = true)
62 private String guixerPassword;
63
64 @Parameter(property = "guixer.projectName", required = false)
65 private String guixerProjectName;
66
67
68
69
70 @Parameter(alias = "guixer.attributes")
71 @Nullable
72 private Map<String, String> attributes;
73
74 @Override
75 public void execute() throws MojoExecutionException, MojoFailureException {
76
77
78
79 final MavenProject project = this.project;
80
81
82
83
84 loadGuixerOut(project);
85
86
87
88 final String mavenProjectName = isBlank(project.getName())
89 ? project.getArtifactId()
90 : project.getName();
91
92 final DateTime now = new DateTime();
93
94 @Nullable
95 final String ciJobId = System.getProperty("CI_JOB_ID");
96
97 @Nullable
98 final String ciJobIdEnvVariable = System.getenv("CI_JOB_ID");
99
100 getLog().info("CI_JOB_ID (system property): " + ciJobId);
101 getLog().info("CI_JOB_ID (env): " + ciJobId);
102
103 final StringBuilder sb = new StringBuilder(mavenProjectName);
104
105 sb.append("-").append(now.toString("yyyy-MM-dd-HH'h'mm"));
106
107 if (ciJobId != null) {
108
109 sb.append("-").append(ciJobId);
110
111 } else if (ciJobIdEnvVariable != null) {
112
113 sb.append("-").append(ciJobIdEnvVariable);
114 }
115
116 final String archiveFileName = sb.append(".zip").toString();
117
118 getLog().info("Archive file name: " + archiveFileName);
119
120
121
122 final File guixerOutDir = getGuixerOutDirectory(project);
123
124 final File archiveFile = new File(getTargetDir(project), archiveFileName);
125
126 try {
127
128 zipDirectory(guixerOutDir, archiveFile);
129
130 } catch (final IOException e) {
131
132 throw new MojoExecutionException("Error while zipping the directory: " + guixerOutDir.getAbsolutePath(), e);
133 }
134
135
136
137
138
139 final String projectName = guixerProjectName;
140
141 final String guixerBaseHrefWithTrailingSlash = guixerApiBaseHref.endsWith("/")
142 ? (guixerApiBaseHref)
143 : (guixerApiBaseHref + "/");
144
145 final String endpoint = guixerBaseHrefWithTrailingSlash + "api/uploads/new";
146
147 final MyUpload upload;
148
149 try {
150
151 upload = sendArchive(projectName, archiveFile, attributes, endpoint);
152
153 } catch (final IOException e) {
154
155 throw new MojoExecutionException("Error while sending the archive: " + archiveFile.getAbsolutePath(), e);
156
157 } catch (final ParseException e) {
158
159 throw new MojoExecutionException("Error while parsing the JSON response", e);
160 }
161
162 final String endpoint2 = substringBeforeLast(endpoint, "/new") + "/" + upload.getId();
163
164 try {
165
166 processUpload(endpoint2);
167
168 } catch (final IOException e) {
169
170 throw new MojoExecutionException("Error while processing the upload: " + upload.getId(), e);
171 }
172
173 final String revueUrl = guixerBaseHrefWithTrailingSlash + "uploads/" + upload.getId() + "/revue";
174
175 final String HR = "------------------------------------------------------------------------";
176
177 getLog().info(HR);
178
179 getLog().info("You can browse your upload at:");
180
181 getLog().info("");
182
183 getLog().info(" " + revueUrl);
184
185 getLog().info("");
186
187 getLog().info(HR);
188 }
189
190 private void zipDirectory(
191 final File dir,
192 final File zipFile
193 ) throws IOException {
194
195 getLog().debug("Zipping into: " + zipFile.getCanonicalPath() + "...");
196
197 try (OutputStream os = new FileOutputStream(zipFile)) {
198
199 try (ZipOutputStream zipOs = new ZipOutputStream(os)) {
200
201 zip(zipOs, "", dir);
202 }
203 }
204 }
205
206 private void zip(
207 final ZipOutputStream zipOs,
208 final String prefix,
209 final File file
210 ) throws IOException {
211
212 if (file.isHidden()) {
213
214 getLog().warn("Skipping hidden file: " + file.getCanonicalPath());
215
216 return;
217
218 } else if (!file.canRead()) {
219
220 getLog().warn("Skipping non-readable file: " + file.getCanonicalPath());
221
222 return;
223 }
224
225 getLog().debug("Adding to archive: " + file.getCanonicalPath());
226
227 final String fileName = file.getName();
228
229 if (file.isDirectory()) {
230
231 zipOs.putNextEntry(new ZipEntry(prefix + fileName + "/"));
232 zipOs.closeEntry();
233
234 for (final File childFile : file.listFiles()) {
235
236 zip(zipOs, prefix + fileName + "/", childFile);
237 }
238
239 } else {
240
241 zipOs.putNextEntry(new ZipEntry(prefix + fileName));
242
243 try (InputStream is = new FileInputStream(file)) {
244
245 IOUtils.copy(is, zipOs);
246 }
247
248 zipOs.closeEntry();
249 }
250 }
251
252 private MyUpload sendArchive(
253 @Nullable final String projectName,
254 final File file,
255 @Nullable final Map<String, String> attributes,
256 final String endpoint
257 ) throws IOException, ParseException {
258
259 checkNotNull(file, "file");
260 checkNotNull(endpoint, "endpoint");
261
262 if (!file.isFile()) {
263 throw new FileNotFoundException(file.getCanonicalPath());
264 }
265
266 getLog().info("Sending to: " + endpoint);
267
268 getLog().info("Using username: " + guixerUsername);
269 getLog().info("Using projectName: " + projectName);
270 getLog().info("Using file: " + file.getName());
271 getLog().debug("File full path: " + file.getCanonicalPath());
272
273
274
275
276 final File tmpDir = Files.createTempDirectory("og5").toFile();
277
278 final File tmpFile = new File(tmpDir, file.getName());
279
280 FileUtils.copyFile(file, tmpFile);
281
282 final HttpClient httpClient = new DefaultHttpClient();
283
284 try {
285
286 final HttpPost httpPost = new HttpPost(endpoint);
287
288 final FileBody archiveFileBody = new FileBody(tmpFile);
289
290 final MultipartEntity entity = new MultipartEntity();
291
292 entity.addPart("username", new StringBody(guixerUsername));
293 entity.addPart("password", new StringBody(guixerPassword));
294
295 if (projectName != null) {
296 entity.addPart("projectName", new StringBody(projectName));
297 }
298
299 entity.addPart("file", archiveFileBody);
300
301 if (attributes != null) {
302
303 for (final Map.Entry<String, String> entry : attributes.entrySet()) {
304
305 final String key = entry.getKey();
306
307 @Nullable
308 final String value = entry.getValue();
309
310 if (value == null) {
311
312 getLog().warn("Skipping attribute." + key + ": " + value);
313
314 } else {
315
316 getLog().info("Using attribute." + key + ": " + value);
317
318 entity.addPart("attribute." + key, new StringBody(value));
319 }
320 }
321 }
322
323 httpPost.setEntity(entity);
324
325 getLog().info("Sending...");
326
327 final HttpResponse response = httpClient.execute(httpPost);
328
329 final StatusLine statusLine = response.getStatusLine();
330
331 final int statusCode = statusLine.getStatusCode();
332
333 if (statusCode != 200) {
334
335 final String reasonPhrase = statusLine.getReasonPhrase();
336
337 throw new RuntimeException("Expected HTTP status: 200, but was: " + statusCode
338 + (isBlank(reasonPhrase) ? "" : (", " + reasonPhrase)));
339 }
340
341 getLog().info(byteCountSentSuccessfully(file.length()));
342
343 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
344
345 try (InputStream is = response.getEntity().getContent()) {
346
347 IOUtils.copy(is, bos);
348 }
349
350 final String json = bos.toString(UTF_8);
351
352 return ResponseParser.toUpload(json);
353
354 } finally {
355
356 httpClient.getConnectionManager().shutdown();
357
358 FileUtils.forceDelete(tmpDir);
359 }
360 }
361
362 private void processUpload(
363 final String endpoint
364 ) throws IOException {
365
366 getLog().info("Calling to process the upload: " + endpoint + "...");
367
368 final HttpClient httpClient = new DefaultHttpClient();
369
370 try {
371
372 final HttpPost httpPost = new HttpPost(endpoint);
373
374 final List<NameValuePair> params = newArrayList();
375
376 params.add(new BasicNameValuePair("username", guixerUsername));
377 params.add(new BasicNameValuePair("password", guixerPassword));
378
379 httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
380
381 final HttpResponse response = httpClient.execute(httpPost);
382
383 final int statusCode = response.getStatusLine().getStatusCode();
384
385 if (statusCode != 200) {
386
387 throw new RuntimeException("Expected HTTP status: 200, but was: " + statusCode);
388 }
389
390 } finally {
391
392 httpClient.getConnectionManager().shutdown();
393
394 }
395 }
396
397 private static String byteCountSentSuccessfully(
398 final long fileLength
399 ) {
400
401 final StringBuilder sb = new StringBuilder();
402
403 if (fileLength == 0L) {
404
405 sb.append("0 byte");
406
407 } else if (fileLength == 1L) {
408
409 sb.append("One byte");
410
411 } else {
412
413 sb.append(fileLength + " bytes");
414 }
415
416 return sb.append(" sent successfully.").toString();
417 }
418 }