blob: 3ca4f8b8ac159f720b56e775dd0bbfdd970f3db8 [file] [log] [blame]
/*******************************************************************************
* 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();
}
}