package ftp; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Map; import javax.swing.JOptionPane; import javax.swing.SwingWorker; import models.Image; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.apache.thrift.TException; import config.Config; import util.ResourceLoader; /** * Execute file download in a background thread and update the progress. * * @author www.codejava.net * */ public class DownloadTask extends SwingWorker { /** * Logger instance for this class. */ private final static Logger LOGGER = Logger.getLogger(DownloadTask.class); private static final int BUFFER_SIZE = 8 * 1024 * 1024; private static final double UPDATE_INTERVAL_SECONDS = 0.6; private static final double UPDATE_INTERVAL_MS = UPDATE_INTERVAL_SECONDS * 1000; private static final double BYTES_PER_MIB = 1024 * 1024; private String host; private int port; private String username; private String password; private String downloadPath; private String saveDir; private int percentCompleted; public DownloadTask(String host, int port, String username, String password, String downloadPath, String saveDir) { this.host = host; this.port = port; this.username = username; this.password = password; this.downloadPath = downloadPath; this.saveDir = saveDir; } /** * Executed in background thread */ @Override protected Void doInBackground() throws Exception { FTPUtility util = new FTPUtility(host, port, username, password); try { util.connect(); // show filesize in the GUI long fileSize = util.getFileSize(downloadPath); firePropertyChange("filesize", 0, fileSize); util.downloadFile(downloadPath); // prepare the input/output streams String fileName = new File(downloadPath).getName(); File downloadFile = new File(saveDir + File.separator + fileName); FileOutputStream outputStream = new FileOutputStream(downloadFile); InputStream inputStream = util.getInputStream(); // initialize the counters needed for speed calculations percentCompleted = 0; byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = -1; long totalBytesRead = 0; long lastUpdate = 0; long lastBytes = 0; long currentBytes = 0; while ((bytesRead = inputStream.read(buffer)) != -1 && !isCancelled()) { outputStream.write(buffer, 0, bytesRead); currentBytes += bytesRead; totalBytesRead += bytesRead; long now = System.currentTimeMillis(); if (lastUpdate + UPDATE_INTERVAL_MS < now) { percentCompleted = (int) ((totalBytesRead * 100) / fileSize); setProgress(percentCompleted); lastBytes = (lastBytes * 2 + currentBytes) / 3; final double speed = lastBytes / UPDATE_INTERVAL_SECONDS; firePropertyChange("speed", 0, speed / BYTES_PER_MIB); firePropertyChange("bytesread", 0, totalBytesRead); lastUpdate = now; currentBytes = 0; } } // finalize the download by updating the progress bar one last time // (in case we didn't get to do it because of the time interval) percentCompleted = (int) ((totalBytesRead * 100) / fileSize); setProgress(percentCompleted); firePropertyChange("bytesread", 0, totalBytesRead); outputStream.close(); util.finish(); } catch (FTPException ex) { JOptionPane.showMessageDialog(null, "Error downloading file: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); ex.printStackTrace(); setProgress(0); cancel(true); } finally { util.disconnect(); } return null; } /** * Executed in Swing's event dispatching thread */ @Override protected void done() { if (!isCancelled() && percentCompleted==100) { LOGGER.info("Datei erfolgreich heruntergeladen."); String vmxResult = ""; vmxResult = generateVmx() ? "Passende VMX generiert." : "Keine passende VMX generiert!"; JOptionPane.showMessageDialog(null, "Datei erfolgreich heruntergeladen. " + vmxResult, "Message", JOptionPane.INFORMATION_MESSAGE); } else if(!isCancelled() && percentCompleted != 100){ LOGGER.error("Datei wurde unvollständig heruntergeladen."); JOptionPane.showMessageDialog(null, "Datei wurde unvollständig heruntergeladen. Bitte wiederholen.", "Message", JOptionPane.INFORMATION_MESSAGE); } } /** * Helper to generate the vmx for the downloaded image * @return true|false indicating the success of the file creation */ private boolean generateVmx() { String vmxTemplate = ResourceLoader.getTextFile("/txt/vmx_template"); // TODO: sanity checks on vmxTemplate would be good here... just to be safe // now we replace the placeholder variables with the real data // for this, we first need to get the image information from the server LOGGER.debug("Image's ID: " + Image.image.getImageId()); Map imageData = null; try { imageData = models.Client.clientcon.getClient().getImageData(Image.image.getImageId(), Image.image.getVersion()); } catch (TException e) { LOGGER.error("Thrift exception during transfer, see trace: ", e); return false; } // sanity check, shouldn't happen. if (imageData == null) { LOGGER.error("Could not query the image information from the server!"); LOGGER.error("Image's ID: " + Image.image.getImageId()); LOGGER.error("Image's version: " + Image.image.getVersion()); return false; } // TODO: sanity checks on the content of imageData would be good here... // use the information we received about the image vmxTemplate = vmxTemplate.replace("%VM_DISPLAY_NAME%", imageData.get("name")); vmxTemplate = vmxTemplate.replace("%VM_GUEST_OS%", imageData.get("os")); vmxTemplate = vmxTemplate.replace("%VM_CPU_COUNT%", imageData.get("cpu")); vmxTemplate = vmxTemplate.replace("%VM_RAM_SIZE%", String.valueOf(Integer.valueOf(imageData.get("ram")) * 1024)); vmxTemplate = vmxTemplate.replace("%VM_DISK_PATH%", imageData.get("path").replaceFirst("^prod/", "")); // build filename for the vmx, basicly the same as the path of the vmdk // just without the leading "prod/" and "vmx" instead of "vmdk" at the end. String targetFilename = Config.getLastDownloadPath() + File.separator + imageData.get("path").replaceFirst("^prod/", "").replaceFirst("\\.vmdk$", "") + ".vmx"; try { // try to write it to file FileUtils.writeStringToFile(new File(targetFilename), vmxTemplate, StandardCharsets.UTF_8); } catch (IOException e) { LOGGER.error("Could not write vmx-template to '" + targetFilename + "'. See trace: ", e); return false; } return true; } }