| /******************************************************************************* |
| * Copyright (c) 2006, 2018 Cloudsmith Inc. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Cloudsmith Inc - initial API and implementation |
| * IBM Corporation - ongoing development |
| * Genuitec - Bug 291926 |
| ******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.repository; |
| |
| import java.net.URI; |
| import java.text.NumberFormat; |
| import java.util.SortedMap; |
| import java.util.TreeMap; |
| import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus; |
| import org.eclipse.equinox.p2.core.IProvisioningAgent; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * Converts progress of file download to average download speed and keeps track of |
| * when it is suitable to update a progress monitor. A suitably scaled and formatted string for use |
| * in progress monitoring is provided. It will publishes {@link DownloadProgressEvent} if {@link IProvisioningAgent} |
| * is given and {@link IProvisioningEventBus} is available. |
| */ |
| public class ProgressStatistics { |
| private static final int DEFAULT_REPORT_INTERVAL = 1000; |
| |
| private static final int SPEED_INTERVAL = 5000; |
| |
| private static final int SPEED_RESOLUTION = 1000; |
| |
| private static String convert(long amount) { |
| NumberFormat fmt = NumberFormat.getInstance(); |
| if (amount < 1024) |
| return fmt.format(amount) + "B"; //$NON-NLS-1$ |
| |
| fmt.setMaximumFractionDigits(2); |
| if (amount < 1024 * 1024) |
| return fmt.format(((double) amount) / 1024) + "kB"; //$NON-NLS-1$ |
| |
| return fmt.format(((double) amount) / (1024 * 1024)) + "MB"; //$NON-NLS-1$ |
| } |
| |
| private void publishEvent(DownloadProgressEvent event) { |
| if (m_agent != null) { |
| IProvisioningEventBus eventBus = m_agent.getService(IProvisioningEventBus.class); |
| if (eventBus != null) { |
| eventBus.publishEvent(event); |
| } |
| } |
| } |
| |
| final String m_fileName; |
| |
| private final long m_total; |
| |
| private final long m_startTime; |
| |
| long m_current; |
| |
| private long m_lastReportTime; |
| |
| private int m_reportInterval; |
| |
| private SortedMap<Long, Long> m_recentSpeedMap; |
| |
| private long m_recentSpeedMapKey; |
| |
| URI m_uri; |
| |
| IProvisioningAgent m_agent; |
| |
| public ProgressStatistics(IProvisioningAgent agent, URI uri, String fileName, long total) { |
| m_startTime = System.currentTimeMillis(); |
| m_fileName = fileName; |
| |
| m_total = total; |
| |
| m_current = 0; |
| m_lastReportTime = 0; |
| m_reportInterval = DEFAULT_REPORT_INTERVAL; |
| m_recentSpeedMap = new TreeMap<>(); |
| m_recentSpeedMapKey = 0L; |
| m_uri = uri; |
| m_agent = agent; |
| } |
| |
| public long getAverageSpeed() { |
| long dur = getDuration(); |
| if (dur > 0) |
| return (int) (m_current / (dur / 1000.0)); |
| return 0L; |
| } |
| |
| public long getDuration() { |
| return System.currentTimeMillis() - m_startTime; |
| } |
| |
| public double getPercentage() { |
| if (m_total > 0) |
| return ((double) m_current) / ((double) m_total); |
| |
| return 0.0; |
| } |
| |
| synchronized public long getRecentSpeed() { |
| removeObsoleteRecentSpeedData(getDuration() / SPEED_RESOLUTION); |
| long dur = 0L; |
| long amount = 0L; |
| SortedMap<Long, Long> relevantData = m_recentSpeedMap.headMap(Long.valueOf(m_recentSpeedMapKey)); |
| |
| if (!relevantData.isEmpty()) { |
| for (Long rl : relevantData.values()) { |
| dur += SPEED_RESOLUTION; |
| amount += rl.longValue(); |
| } |
| } |
| |
| if (dur >= 1000) |
| return amount / (dur / 1000); |
| |
| return 0L; |
| } |
| |
| public int getReportInterval() { |
| return m_reportInterval; |
| } |
| |
| public long getTotal() { |
| return m_total; |
| } |
| |
| public void increase(long inc) { |
| registerRecentSpeed(getDuration() / SPEED_RESOLUTION, inc); |
| m_current += inc; |
| } |
| |
| public synchronized String report() { |
| publishEvent(new DownloadProgressEvent(this)); |
| return createReportString(); |
| } |
| |
| private String createReportString() { |
| String uriString = m_uri.toString(); |
| if (m_fileName != null && uriString.endsWith(m_fileName)) |
| uriString = uriString.substring(0, uriString.lastIndexOf(m_fileName)); |
| if (m_current == 0L || getRecentSpeed() == 0L) { |
| // no meaningful speed data available |
| if (m_total == -1) { |
| return NLS.bind(Messages.fetching_0_from_1, new String[] {m_fileName, uriString}); |
| } |
| return NLS.bind(Messages.fetching_0_from_1_2, new String[] {m_fileName, uriString, convert(m_total)}); |
| } |
| return m_total != -1 ? NLS.bind(Messages.fetching_0_from_1_2_of_3_at_4, new String[] {m_fileName, uriString, convert(m_current), convert(m_total), convert(getRecentSpeed())}) : NLS.bind(Messages.fetching_0_from_1_2_at_3, new String[] {m_fileName, uriString, convert(m_current), convert(getRecentSpeed())}); |
| } |
| |
| public void setReportInterval(int reportInterval) { |
| m_reportInterval = reportInterval; |
| } |
| |
| public boolean shouldReport() { |
| long currentTime = System.currentTimeMillis(); |
| if (m_lastReportTime == 0 || currentTime - m_lastReportTime >= m_reportInterval) { |
| m_lastReportTime = currentTime; |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| return createReportString(); |
| } |
| |
| synchronized private void registerRecentSpeed(long key, long inc) { |
| Long keyL = Long.valueOf(key); |
| Long currentValueL = m_recentSpeedMap.get(keyL); |
| long currentValue = 0L; |
| if (currentValueL != null) |
| currentValue = currentValueL.longValue(); |
| |
| m_recentSpeedMap.put(keyL, Long.valueOf(inc + currentValue)); |
| |
| if (m_recentSpeedMapKey != key) { |
| m_recentSpeedMapKey = key; |
| removeObsoleteRecentSpeedData(key); |
| } |
| } |
| |
| synchronized private void removeObsoleteRecentSpeedData(long lastKey) { |
| long threshold = lastKey - SPEED_INTERVAL / SPEED_RESOLUTION; |
| m_recentSpeedMap.headMap(Long.valueOf(threshold)).clear(); |
| } |
| } |