/******************************************************************************* * Copyright (c) 2011, 2016 Eurotech and/or its affiliates * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eurotech *******************************************************************************/ package org.eclipse.kura.core.deployment.download.impl; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Date; import java.util.concurrent.CancellationException; import org.eclipse.kura.KuraErrorCode; import org.eclipse.kura.KuraException; import org.eclipse.kura.core.deployment.CloudDeploymentHandlerV2; import org.eclipse.kura.core.deployment.CloudDeploymentHandlerV2.DOWNLOAD_STATUS; import org.eclipse.kura.core.deployment.download.DeploymentPackageDownloadOptions; import org.eclipse.kura.core.deployment.download.DownloadCountingOutputStream; import org.eclipse.kura.core.deployment.download.DownloadFactory; import org.eclipse.kura.core.deployment.download.DownloadFileUtilities; import org.eclipse.kura.core.deployment.download.DownloadOptions; import org.eclipse.kura.core.deployment.install.DeploymentPackageInstallOptions; import org.eclipse.kura.core.deployment.progress.ProgressEvent; import org.eclipse.kura.core.deployment.progress.ProgressListener; import org.eclipse.kura.core.deployment.util.FileUtilities; import org.eclipse.kura.core.deployment.util.HashUtil; import org.eclipse.kura.message.KuraResponsePayload; import org.eclipse.kura.ssl.SslManagerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DownloadImpl implements ProgressListener { private static final Logger s_logger = LoggerFactory.getLogger(DownloadImpl.class); public static final String RESOURCE_DOWNLOAD = "download"; private final CloudDeploymentHandlerV2 callback; private final DeploymentPackageDownloadOptions options; private DownloadCountingOutputStream downloadHelper; private SslManagerService sslManagerService; private boolean alreadyDownloadedFlag; private String verificationDirectory; public DownloadImpl(DeploymentPackageDownloadOptions options, CloudDeploymentHandlerV2 callback) { this.options = options; this.callback = callback; } // ---------------------------------------------------------------- // // Public methods // // ---------------------------------------------------------------- public DownloadCountingOutputStream getDownloadHelper() { return this.downloadHelper; } public void setSslManager(SslManagerService sslManager) { this.sslManagerService = sslManager; } public void setAlreadyDownloadedFlag(boolean alreadyDownloaded) { this.alreadyDownloadedFlag = alreadyDownloaded; } public void setVerificationDirectory(String verificationDirectory) { this.verificationDirectory = verificationDirectory; } @Override public void progressChanged(ProgressEvent progress) { s_logger.info("{}% downloaded", progress.getTransferProgress()); KuraNotifyPayload notify = new KuraNotifyPayload(progress.getClientId()); notify.setTimestamp(new Date()); notify.setTransferSize(progress.getTransferSize()); notify.setTransferProgress(progress.getTransferProgress()); notify.setTransferStatus(progress.getTransferStatus()); notify.setJobId(progress.getJobId()); if (progress.getExceptionMessage() != null) { notify.setErrorMessage(progress.getExceptionMessage()); } notify.setTransferIndex(progress.getDownloadIndex()); this.callback.publishMessage(this.options, notify, RESOURCE_DOWNLOAD); } public void downloadDeploymentPackageInternal() throws KuraException { File dpFile = null; int downloadIndex = 0; boolean downloadSuccess = true; try { // Download the package to a temporary file. // Check for file existence has already been done dpFile = DownloadFileUtilities.getDpDownloadFile(this.options); boolean forceDownload = this.options.isDownloadForced(); if (!this.alreadyDownloadedFlag || forceDownload) { s_logger.info("To download"); incrementalDownloadFromURL(dpFile, this.options.getDeployUri(), downloadIndex); downloadIndex++; if (this.options.getVerifierURL() != null) { File dpVerifier = getDpVerifierFile(this.options); incrementalDownloadFromURL(dpVerifier, this.options.getVerifierURL(), downloadIndex); } } else { alreadyDownloadedAsync(); } } catch (CancellationException ce) { s_logger.error("Download exception", ce); downloadSuccess = false; } catch (Exception e) { s_logger.info("Download exception", e); downloadSuccess = false; downloadFailedAsync(downloadIndex); } if (downloadSuccess && dpFile != null && this.options.isInstall()) { s_logger.info("Ready to install"); this.callback.installDownloadedFile(dpFile, this.options); } } public boolean isAlreadyDownloaded() throws KuraException { try { File dp = DownloadFileUtilities.getDpDownloadFile(this.options); return dp.exists(); } catch (Exception e) { throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e); } } public boolean deleteDownloadedFile() throws KuraException { try { return DownloadFileUtilities.deleteDownloadedFile(this.options); } catch (Exception e) { throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e); } } // ---------------------------------------------------------------- // // Private methods // // ---------------------------------------------------------------- private void incrementalDownloadFromURL(File dpFile, String url, int downloadIndex) throws Exception { OutputStream os = null; try { os = new FileOutputStream(dpFile); DownloadOptions downloadOptions = new DownloadOptions(); downloadOptions.setOut(os); downloadOptions.setRequestOptions(this.options); downloadOptions.setCallback(this); downloadOptions.setSslManagerService(this.sslManagerService); downloadOptions.setDownloadURL(url); downloadOptions.setAlreadyDownloaded(downloadIndex); this.downloadHelper = DownloadFactory.getDownloadInstance(this.options.getDownloadProtocol(), downloadOptions); this.downloadHelper.startWork(); this.downloadHelper.close(); } finally { if (os != null) { try { os.close(); } catch (IOException e1) { s_logger.error("Exception while trying to close stream.", e1); } } } if (this.options.getHash() != null) { String[] hashAlgorithmValue = this.options.getHash().split(":"); String hashAlgorithm = null; String hashValue = null; if (hashAlgorithmValue.length == 2) { hashAlgorithm = hashAlgorithmValue[0].trim(); hashValue = hashAlgorithmValue[1].trim(); } s_logger.info("--> Going to verify hash signature!"); try { String checksum = HashUtil.hash(hashAlgorithm, dpFile); if (hashAlgorithm == null || "".equals(hashAlgorithm) || hashValue == null || "".equals(hashValue) || checksum == null || !checksum.equals(hashValue)) { throw new KuraException(KuraErrorCode.INTERNAL_ERROR, null, "Failed to verify checksum with algorithm: " + hashAlgorithm); } } catch (Exception e) { dpFile.delete(); throw e; } } } // Synchronous messages public static void downloadInProgressSyncMessage(KuraResponsePayload respPayload, DownloadCountingOutputStream downloadHelper, DeploymentPackageDownloadOptions downloadOptions) { respPayload.setTimestamp(new Date()); respPayload.addMetric(KuraNotifyPayload.METRIC_TRANSFER_SIZE, downloadHelper.getTotalBytes().intValue()); respPayload.addMetric(KuraNotifyPayload.METRIC_TRANSFER_PROGRESS, downloadHelper.getDownloadTransferProgressPercentage().intValue()); respPayload.addMetric(KuraNotifyPayload.METRIC_TRANSFER_STATUS, downloadHelper.getDownloadTransferStatus().getStatusString()); respPayload.addMetric(KuraNotifyPayload.METRIC_JOB_ID, downloadOptions.getJobId()); } public static void downloadAlreadyDoneSyncMessage(KuraResponsePayload respPayload) { respPayload.setTimestamp(new Date()); respPayload.addMetric(KuraNotifyPayload.METRIC_TRANSFER_SIZE, 0); respPayload.addMetric(KuraNotifyPayload.METRIC_TRANSFER_PROGRESS, 100); respPayload.addMetric(KuraNotifyPayload.METRIC_TRANSFER_STATUS, DOWNLOAD_STATUS.ALREADY_DONE); } private void alreadyDownloadedAsync() { KuraNotifyPayload notify = new KuraNotifyPayload(this.options.getClientId()); notify.setTimestamp(new Date()); notify.setTransferSize(0); notify.setTransferProgress(100); notify.setTransferStatus(DOWNLOAD_STATUS.COMPLETED.getStatusString()); notify.setJobId(this.options.getJobId()); this.callback.publishMessage(this.options, notify, RESOURCE_DOWNLOAD); } private void downloadFailedAsync(int downloadIndex) { KuraNotifyPayload notify = new KuraNotifyPayload(this.options.getClientId()); notify.setTimestamp(new Date()); notify.setTransferSize(0); notify.setTransferProgress(0); notify.setTransferStatus(DOWNLOAD_STATUS.FAILED.getStatusString()); notify.setJobId(this.options.getJobId()); notify.setErrorMessage("Error during download process and verification!"); // message to get cause notify.setTransferIndex(downloadIndex); this.callback.publishMessage(this.options, notify, RESOURCE_DOWNLOAD); } private File getDpVerifierFile(DeploymentPackageInstallOptions options) throws IOException { String shName = FileUtilities.getFileName(options.getDpName(), options.getDpVersion(), "_verifier.sh"); String packageFilename = new StringBuilder().append(this.verificationDirectory).append(File.separator) .append(shName).toString(); return new File(packageFilename); } }