Java tutorial
/* * Copyright 2015 Carsten Rambow. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.elomagic.maven.http; import java.io.BufferedInputStream; import java.io.File; import java.io.InputStream; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Enumeration; import java.util.Map; import java.util.Map.Entry; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.HttpClient; import org.apache.http.client.fluent.Executor; import org.apache.http.client.fluent.Form; import org.apache.http.client.fluent.Request; import org.apache.http.client.fluent.Response; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.ContentType; import org.apache.http.entity.FileEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.util.EntityUtils; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.settings.Server; import org.apache.maven.settings.Settings; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.IOUtil; /** * * @author Carsten Rambow */ @Mojo(name = "http") public class HTTPMojo extends AbstractMojo { @Parameter(defaultValue = "${session}", readonly = true) private MavenSession session; /** * HTTP request URL. * <p> * Required parameter */ @Parameter(required = true) private URL url; /** * HTTP request method. * <p> * Required parameter */ @Parameter(required = true) private RequestMethod method; /** * Server id to lookup credential for BASIC authentication . */ @Parameter private String serverId; /** * If set then this file will be uploaded. */ @Parameter private File fromFile; @Parameter private File toFile; /** * Folder where the downloaded content will be persisted. * <p> * Must be set when parameter <code>unpack</code> is true */ @Parameter private File outputDirectory; /** * This is MIME type of the body content which will be send with the HTTP request. * <p> * If unset then it will be tried to get from file extension of property <code>fromFile</code> */ @Parameter private String contentType; /** * HTTP header properties. * <p> * Optional parameters * <p> * Parameter "Accept" must be set with a different option parameter. */ @Parameter private Map<String, String> httpHeaders; /** * HTTP form properties. * <p> * Optional parameters */ @Parameter private Map<String, String> formParams; /** * If true then the response body will be unpacked into the folder of property <code>outputDirectory</code>. */ @Parameter private boolean unpack; /** * If true then response body will be printed on level "info" to the log output. */ @Parameter private boolean printResponse; /** * If true then plug-in will ignore any errors occurred during execution. */ @Parameter private boolean failOnError = true; /** * If true then relaxed SSL check for user generated certificates. */ @Parameter private boolean httpsInsecure; /** * Define accepted media types in the response. * <p> * Default: *//*; */ @Parameter private String accept = "*/*"; /** * Digest value. * <p> * If set then parameter <code>digestType<code> must also be set. * <p> * The response content will be validated against this value. */ @Parameter private String digest; /** * Type of digest (Default "sha1"). * <p> * Supported types: "md5", "sha1", "sha256", "sha384", "sha512" */ @Parameter private String digestType; @Override public void execute() throws MojoExecutionException, MojoFailureException { try { Executor executor; if (httpsInsecure) { getLog().info("Accepting unsecure HTTPS connections."); try { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustAllStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); final Registry<ConnectionSocketFactory> sfr = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslsf).build(); PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager( sfr); connectionManager.setDefaultMaxPerRoute(100); connectionManager.setMaxTotal(200); connectionManager.setValidateAfterInactivity(1000); HttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager) .build(); executor = Executor.newInstance(httpClient); } catch (Exception ex) { throw new Exception("Unable to setup HTTP client for unstrusted connections.", ex); } } else { executor = Executor.newInstance(); } Settings settings = session.getSettings(); if (StringUtils.isNotBlank(serverId)) { Server server = settings.getServer(serverId); if (server == null) { throw new Exception("Server ID \"" + serverId + "\" not found in your Maven settings.xml"); } getLog().debug("ServerId: " + serverId); executor.auth(server.getUsername(), server.getPassword()); } Request request = createRequestMethod(); request.setHeader("Accept", accept); if (httpHeaders != null) { for (Entry<String, String> entry : httpHeaders.entrySet()) { request.addHeader(entry.getKey(), entry.getValue()); } } if (formParams != null) { Form form = Form.form(); for (Entry<String, String> entry : formParams.entrySet()) { form.add(entry.getKey(), entry.getValue()); } } if (fromFile != null) { if (!fromFile.exists()) { throw new MojoExecutionException("From file \"" + fromFile + "\" doesn't exist."); } if (StringUtils.isBlank(contentType)) { contentType = Files.probeContentType(fromFile.toPath()); } getLog().debug("From file: " + fromFile); getLog().debug("Upload file size: " + FileUtils.byteCountToDisplaySize(new Long(fromFile.length()).intValue())); getLog().debug("Content type: " + contentType); if (StringUtils.isBlank(contentType)) { request.body(new FileEntity(fromFile)); } else { request.body(new FileEntity(fromFile, ContentType.create(contentType))); } } getLog().info(method + " " + url); Response response = executor.execute(request); handleResponse(response); } catch (Exception ex) { getLog().error(ex); if (failOnError) { throw new MojoExecutionException(ex.getMessage(), ex); } else { getLog().info("Fail on error is disabled. Continue execution."); } } } private void handleResponse(final Response response) throws Exception { HttpResponse httpResponse = response.returnResponse(); StatusLine statusLine = httpResponse.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode < 200 || statusCode > 299) { if (httpResponse.getEntity() != null) { getLog().info(EntityUtils.toString(httpResponse.getEntity())); } else { getLog().info("Response body is empty."); } throw new Exception("Response status code " + statusCode + ": " + statusLine.getReasonPhrase()); } getLog().info("Response status code " + statusCode + ": " + statusLine.getReasonPhrase()); HttpEntity entity = httpResponse.getEntity(); if (entity == null) { getLog().info("Response body is empty."); } else if (unpack) { File downloadedFile = File.createTempFile("mvn_http_plugin_", ".tmp"); try (InputStream in = entity.getContent()) { Files.copy(in, downloadedFile.toPath(), StandardCopyOption.REPLACE_EXISTING); if (StringUtils.isNoneBlank(digest)) { checkFile(downloadedFile.toPath()); } getLog().info("Downloaded file size: " + FileUtils.byteCountToDisplaySize(new Long(downloadedFile.length()).intValue())); unzipToFile(downloadedFile, outputDirectory); } finally { downloadedFile.delete(); } } else if (toFile != null) { try (InputStream in = entity.getContent()) { Files.copy(in, toFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } if (StringUtils.isNoneBlank(digest)) { checkFile(toFile.toPath()); } getLog().debug("Downloaded file location: " + toFile); getLog().debug("Downloaded file size: " + FileUtils.byteCountToDisplaySize(new Long(toFile.length()).intValue())); } else if (printResponse) { getLog().info("Response content: " + IOUtil.toString(entity.getContent(), "utf-8")); } } private Request createRequestMethod() throws Exception { switch (method) { case DELETE: return Request.Delete(url.toURI()); case GET: return Request.Get(url.toURI()); case HEAD: return Request.Head(url.toURI()); case POST: return Request.Post(url.toURI()); case PUT: return Request.Put(url.toURI()); } throw new Exception("Unsupported HTTP method \"" + method + "\"."); } /** * @todo Must may be refactored. Only a prototype * @param file * @param destDirectory * @throws MojoExecutionException */ private void unzipToFile(final File file, final File destDirectory) throws Exception { getLog().info("Extracting downloaded file to folder \"" + destDirectory + "\"."); int filesCount = 0; // create the destination directory structure (if needed) destDirectory.mkdirs(); // open archive for reading try (ZipFile zipFile = new ZipFile(file, ZipFile.OPEN_READ)) { // For every zip archive entry do Enumeration<? extends ZipEntry> zipFileEntries = zipFile.entries(); while (zipFileEntries.hasMoreElements()) { ZipEntry entry = zipFileEntries.nextElement(); getLog().debug("Extracting entry: " + entry); //create destination file // TODO Check entry path on path injections File destFile = new File(destDirectory, entry.getName()); //create parent directories if needed File parentDestFile = destFile.getParentFile(); parentDestFile.mkdirs(); if (!entry.isDirectory()) { BufferedInputStream bufIS = new BufferedInputStream(zipFile.getInputStream(entry)); // write the current file to disk Files.copy(bufIS, destFile.toPath(), StandardCopyOption.REPLACE_EXISTING); filesCount++; } } } getLog().info(filesCount + " files extracted."); } private void checkFile(final Path path) throws Exception { String fileDigest; try (InputStream in = Files.newInputStream(path)) { switch (digestType.toLowerCase()) { case "md5": fileDigest = DigestUtils.md5Hex(in); break; case "sha1": fileDigest = DigestUtils.sha1Hex(in); break; case "sha256": fileDigest = DigestUtils.sha256Hex(in); break; case "sha384": fileDigest = DigestUtils.sha384Hex(in); break; case "sha512": fileDigest = DigestUtils.sha512Hex(in); break; default: throw new Exception("Digest type \"" + digestType + "\" not supported."); } } if (!fileDigest.equalsIgnoreCase(digest)) { throw new Exception( digestType + " digest check failed. File=\"" + fileDigest + "\", Digest=\"" + digest + "\""); } } }