/* * 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. * * Contributions from 2013-2017 where performed either by US government * employees, or under US Veterans Health Administration contracts. * * US Veterans Health Administration contributions by government employees * are work of the U.S. Government and are not subject to copyright * protection in the United States. Portions contributed by government * employees are USGovWork (17USC ยง105). Not subject to copyright. * * Contribution by contractors to the US Veterans Health Administration * during this period are contractually contributed under the * Apache License, Version 2.0. * * See: https://www.usa.gov/government-works * * Contributions prior to 2013: * * Copyright (C) International Health Terminology Standards Development Organisation. * Licensed under the Apache License, Version 2.0. * */ package sh.isaac.api.util; //~--- JDK imports ------------------------------------------------------------ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.Base64; import java.util.concurrent.ExecutionException; //~--- non-JDK imports -------------------------------------------------------- import javafx.beans.value.ChangeListener; import javafx.concurrent.Task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; //~--- classes ---------------------------------------------------------------- /** * {@link MavenPublish}. * * @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a> */ public class MavenPublish extends Task<Integer> { /** The log. */ private static Logger log = LoggerFactory.getLogger(MavenPublish.class); //~--- fields -------------------------------------------------------------- /** The group id. */ String groupId; /** The artifact id. */ String artifactId; /** The version. */ String version; /** The pom file. */ File pomFile; /** The data files. */ File[] dataFiles; /** The url. */ String url; /** The username. */ String username; /** The psswrd. */ String psswrd; //~--- constructors -------------------------------------------------------- /** * Instantiates a new maven publish. * * @param groupId the group id * @param artifactId the artifact id * @param version the version * @param pomFile the pom file * @param dataFiles the data files * @param url the url * @param username the username * @param psswrd the psswrd * @throws Exception the exception */ public MavenPublish(String groupId, String artifactId, String version, File pomFile, File[] dataFiles, String url, String username, String psswrd) throws Exception { this.groupId = groupId; this.artifactId = artifactId; this.version = version; this.pomFile = pomFile; this.dataFiles = dataFiles; this.url = url; this.username = username; this.psswrd = psswrd; log.debug("Maven Publish task constructed for GAV: {}:{}:{}", groupId, artifactId, version); } //~--- methods ------------------------------------------------------------- /** * Call. * * @return the integer * @throws Exception the exception * @see javafx.concurrent.Task#call() */ @Override protected Integer call() throws Exception { log.debug("Maven publish task begins"); updateProgress(-1, 0); updateMessage("Creating Checksum Files"); writeChecksumFile(this.pomFile, "MD5"); writeChecksumFile(this.pomFile, "SHA1"); for (final File f: this.dataFiles) { writeChecksumFile(f, "MD5"); writeChecksumFile(f, "SHA1"); } updateMessage("Uploading data files"); for (final File f: this.dataFiles) { // TODO check maven upload order putFile(f, null); putFile(new File(f.getParentFile(), f.getName() + ".md5"), null); putFile(new File(f.getParentFile(), f.getName() + ".sha1"), null); } updateMessage("Uploading pom files"); putFile(this.pomFile, "pom"); putFile(new File(this.pomFile.getParentFile(), this.pomFile.getName() + ".md5"), "pom.md5"); putFile(new File(this.pomFile.getParentFile(), this.pomFile.getName() + ".sha1"), "pom.sha1"); updateMessage("Publish Complete"); updateProgress(10, 10); log.debug("Maven Publish Task Complete"); return 0; } /** * Put file. * * @param file the file * @param targetFileName the target file name * @throws Exception the exception */ private void putFile(File file, String targetFileName) throws Exception { final String groupIdTemp = this.groupId.replaceAll("\\.", "//"); final URL url = new URL(this.url + (this.url.endsWith("/") ? "" : "/") + groupIdTemp + "/" + this.artifactId + "/" + this.version + "/" + ((targetFileName == null) ? file.getName() : targetFileName)); log.info("Uploading " + file.getAbsolutePath() + " to " + url.toString()); updateMessage("Uploading " + file.getName()); updateProgress(0, file.length()); final HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); if ((this.username.length() > 0) || (this.psswrd.length() > 0)) { final String encoded = Base64.getEncoder() .encodeToString((this.username + ":" + this.psswrd).getBytes()); httpCon.setRequestProperty("Authorization", "Basic " + encoded); } httpCon.setDoOutput(true); httpCon.setRequestMethod("PUT"); httpCon.setConnectTimeout(30 * 1000); httpCon.setReadTimeout(60 * 60 * 1000); final long fileLength = file.length(); httpCon.setFixedLengthStreamingMode(fileLength); final byte[] buf = new byte[8192]; long loopCount = 0; int read = 0; try (OutputStream out = httpCon.getOutputStream(); FileInputStream fis = new FileInputStream(file);) { while ((read = fis.read(buf, 0, buf.length)) > 0) { // update every MB if (loopCount++ % 128 == 0) { updateProgress((loopCount * 8192l), fileLength); updateMessage("Uploading " + file.getName() + " - " + (loopCount * 8192l) + " / " + fileLength); } out.write(buf, 0, read); } out.flush(); } final StringBuilder sb = new StringBuilder(); try (InputStream is = httpCon.getInputStream();) { read = 0; final byte[] buffer = new byte[1024]; final CharBuffer cBuffer = ByteBuffer.wrap(buffer) .asCharBuffer(); while (read != -1) { read = is.read(buffer); if (read > 0) { sb.append(cBuffer, 0, read); } } } httpCon.disconnect(); if (sb.toString() .trim() .length() > 0) { throw new Exception("The server reported an error during the publish operation: " + sb.toString()); } log.info("Upload Successful"); updateMessage(""); updateProgress(-1, 0); } /** * Write checksum file. * * @param file the file * @param type the type * @throws IOException Signals that an I/O exception has occurred. * @throws InterruptedException the interrupted exception * @throws ExecutionException the execution exception */ private void writeChecksumFile(File file, String type) throws IOException, InterruptedException, ExecutionException { updateMessage("Calculating Checksum for " + file.getName()); final Task<String> gen = ChecksumGenerator.calculateChecksum(type, file); gen.messageProperty() .addListener((ChangeListener<String>) (observable, oldValue, newValue) -> updateMessage(newValue)); gen.progressProperty() .addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> updateProgress(gen.getWorkDone(), gen.getTotalWork())); WorkExecutors.get() .getExecutor() .execute(gen); final String checksum = gen.get(); updateMessage("Writing checksum file"); log.debug("Writing {} checksum file with {}", type, checksum); Files.write(new File(file.getParentFile(), file.getName() + "." + type.toLowerCase()).toPath(), (checksum + " " + file.getName()).getBytes(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); updateMessage(""); updateProgress(-1, 0); } }