View Javadoc
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  //	@Parameter(property = "guixer.token", required = true)
68  //	private String guixerToken;
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  		// Ignore race issues
78  		//
79  		final MavenProject project = this.project;
80  
81  		// 1. Check our directory is valid
82  
83  		// final GuixerOut guixerOut =
84  		loadGuixerOut(project);
85  
86  		// 2. Compose the archive file name
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 		// 3. Zip the archive
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 		// 4. Send the archive
136 
137 		// final String username = "toto";
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 		// We use a temporary directory, to skip all problems coming from
274 		// non-ASCII original dir names.
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 }