package rocks.inspectit.ui.rcp.storage.http; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.ui.progress.UIJob; import rocks.inspectit.ui.rcp.formatter.NumberFormatter; /** * Transfer monitor class that collects {@link DataSample}s during the transfer and provides * informations about the average transfer rate, total bytes transfered, etc. * * @author Ivan Senic * */ public class TransferDataMonitor { /** * Millis to pass to display the message. */ private static final long DISPLAY_MESSAGE_RATE = 1000; /** * Percentage of file size that will be used if Gzip is used. */ private static final double GZIP_FILE_SIZE_RATIO = 0.15; /** * This value is provided in the CMR by default and represents minimum file size for which GZIp * compression will be used. */ private static final long MIN_GZIP_FILE_SIZE = 1048576; /** * Amount of bytes transfered. */ private long totalBytesTransfered; /** * Time when download started. */ private long downloadStartTime; /** * Sub monitor to report to. */ private SubMonitor subMonitor; /** * Files that have to be downloaded. */ private Map<String, Long> files; /** * If the GZip compression is active. */ private boolean gzipCompression; /** * Amount of finished transfers. */ private int filesCount = 0; /** * Total size that needs to be downloaded. */ private long totalSize; /** * Amount of size we believe in finished files. */ private long finishedFilesSize; /** * Size of the currently downloaded file. */ private long currentFileSize; /** * Amount of bytes that we reported for current file. */ private long currentFileReal; /** * Job for displying the message. */ private DisplayMessageJob displayMessageJob = new DisplayMessageJob(); /** * Default constructor. Same as calling * {@link TransferDataMonitor#TransferDataMonitor(SubMonitor, Map, false)}. * * @param subMonitor * Monitor to report to. * @param files * Files to be transfered. */ public TransferDataMonitor(SubMonitor subMonitor, Map<String, Long> files) { this(subMonitor, files, false); } /** * @param subMonitor * Monitor to report to. * @param files * Files to be transfered. * @param gzipCompression * If GZip compression will be active. */ public TransferDataMonitor(SubMonitor subMonitor, Map<String, Long> files, boolean gzipCompression) { this.subMonitor = subMonitor; this.files = files; this.gzipCompression = gzipCompression; totalSize = 0; // try to calculate the total size based on if the gzip is on for (Map.Entry<String, Long> entry : files.entrySet()) { totalSize += getFileSize(entry.getValue().longValue()); } subMonitor.setWorkRemaining((int) totalSize); displayMessageJob.schedule(); } /** * Marks the start of the file download. * * @param fileName * Name of the file that is downloaded. */ public void startTransfer(String fileName) { if (0 == filesCount) { downloadStartTime = System.currentTimeMillis(); } // reset values for the current file currentFileReal = 0; currentFileSize = getFileSize(files.get(fileName).longValue()); } /** * Informs that the download of the file has ended. * * @param fileName * Name of the file that has been downloaded. */ public void endTransfer(String fileName) { // if the file is smaller than expected (can happen with gzip) add remaining expected size // to the submonitor filesCount++; long leftNotReported = currentFileSize - currentFileReal; if (leftNotReported > 0) { subMonitor.worked((int) leftNotReported); } // update the total finised files size long originalSize = files.get(fileName).longValue(); finishedFilesSize += getFileSize(originalSize); if (files.size() == filesCount) { // when last file is finished we cancel the message job and inform submonitor displayMessageJob.cancel(); subMonitor.subTask(""); subMonitor.done(); } } /** * Adds the sample. * * @param byteCount * Bytes transfered. */ public void addSample(long byteCount) { // transfer rate totalBytesTransfered += byteCount; // reporting stuff long reportSize = currentFileSize - currentFileReal; // only report if there is some size left if (reportSize > 0) { reportSize = Math.min(reportSize, byteCount); subMonitor.worked((int) reportSize); } currentFileReal += byteCount; } /** * Displays the current state on monitor. */ private void displayMessageOnMonitor() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("File "); stringBuilder.append(filesCount + 1); stringBuilder.append('/'); stringBuilder.append(files.size()); stringBuilder.append(" ("); stringBuilder.append(NumberFormatter.humanReadableByteCount(totalBytesTransfered)); if (!gzipCompression) { stringBuilder.append(" out of "); stringBuilder.append(NumberFormatter.humanReadableByteCount(totalSize)); } stringBuilder.append(" @ "); stringBuilder.append(NumberFormatter.humanReadableByteCount((long) getAverageTransferRate())); stringBuilder.append("/s) Remaining time: "); if (gzipCompression) { stringBuilder.append("app. "); } long quasiBytesLeft = totalSize - finishedFilesSize; if (currentFileReal < currentFileSize) { quasiBytesLeft -= currentFileReal; } else { quasiBytesLeft -= currentFileSize; } // always report at least one sec long millisLeft = getMillisLeft(quasiBytesLeft); millisLeft += millisLeft % 1000; stringBuilder.append(NumberFormatter.humanReadableMillisCount(millisLeft, true)); subMonitor.subTask(stringBuilder.toString()); } /** * Returns the average transfer rate. * * @return Returns the average transfer rate. */ private double getAverageTransferRate() { return totalBytesTransfered / ((System.currentTimeMillis() - downloadStartTime) / 1000.0d); } /** * Returns time left for the given amounts of bytes to be downloaded. Note that this is not * related to the current transfer rate, but to the time since the download started. Thus this * can be used as the information when it is gonna be completely over. * * @param bytesMore * Bytes left to be downloaded. * @return Estimated time left in milliseconds. */ private long getMillisLeft(long bytesMore) { return (long) ((bytesMore * 1000.0d) / getAverageTransferRate()); } /** * Returns the size of the file based on if the current download is compressed or not. * * @param originalFileSize * Original file size. * @return Returns file size to use. */ private long getFileSize(long originalFileSize) { if (gzipCompression && (originalFileSize > MIN_GZIP_FILE_SIZE)) { return (long) (originalFileSize * GZIP_FILE_SIZE_RATIO); } else { return originalFileSize; } } /** * Job that will display the message. * * @author Ivan Senic * */ private class DisplayMessageJob extends UIJob { /** * Default constructor. */ public DisplayMessageJob() { super("Display Message"); setUser(false); } /** * {@inheritDoc} */ @Override public IStatus runInUIThread(IProgressMonitor monitor) { displayMessageOnMonitor(); if (!monitor.isCanceled()) { schedule(DISPLAY_MESSAGE_RATE); } return Status.OK_STATUS; } } }