Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenrik Lindberg2009-04-14 15:08:43 +0000
committerHenrik Lindberg2009-04-14 15:08:43 +0000
commit8f2c289ae949c30f5ceddef9679214d427ad859c (patch)
tree6e430b7d4c4fcc4de235c418e3b75a04bfdacf9d /bundles/org.eclipse.equinox.p2.repository
parentb61bea3b11db7c7caa16502748b54ec6eec7e339 (diff)
downloadrt.equinox.p2-8f2c289ae949c30f5ceddef9679214d427ad859c.tar.gz
rt.equinox.p2-8f2c289ae949c30f5ceddef9679214d427ad859c.tar.xz
rt.equinox.p2-8f2c289ae949c30f5ceddef9679214d427ad859c.zip
https://bugs.eclipse.org/bugs/show_bug.cgi?id=266243 - make download of meta data repository index files resumable (resume functionality controled via property).
Also includes: https://bugs.eclipse.org/bugs/show_bug.cgi?id=271473 - include URI in download progress statistics messages
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.repository')
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/CacheManager.java193
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/DownloadStatus.java23
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java97
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Messages.java2
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/ProgressStatistics.java8
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatus.java17
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java69
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/messages.properties2
8 files changed, 346 insertions, 65 deletions
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/CacheManager.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/CacheManager.java
index 7062d79f8..109879e06 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/CacheManager.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/CacheManager.java
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * henrik.lindberg@cloudsmith.com - resume of download
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata.repository;
@@ -17,14 +18,13 @@ import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.ecf.filetransfer.UserCancelledException;
import org.eclipse.equinox.internal.p2.core.helpers.*;
-import org.eclipse.equinox.internal.p2.repository.AuthenticationFailedException;
-import org.eclipse.equinox.internal.p2.repository.RepositoryTransport;
+import org.eclipse.equinox.internal.p2.repository.*;
+import org.eclipse.equinox.internal.p2.repository.Activator;
import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException;
import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus;
import org.eclipse.equinox.internal.provisional.p2.core.eventbus.SynchronousProvisioningListener;
import org.eclipse.equinox.internal.provisional.p2.core.location.AgentLocation;
-import org.eclipse.equinox.internal.provisional.p2.repository.IRepository;
-import org.eclipse.equinox.internal.provisional.p2.repository.RepositoryEvent;
+import org.eclipse.equinox.internal.provisional.p2.repository.*;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
@@ -38,6 +38,9 @@ import org.osgi.framework.ServiceReference;
* was created for the repository.
*/
public class CacheManager {
+ private static final String PROP_RESUMABLE = "org.eclipse.equinox.p2.metadata.repository.resumable"; //$NON-NLS-1$
+ private static final String RESUME_DEFAULT = "true"; //$NON-NLS-1$
+ private static final String DOWNLOADING = "downloading"; //$NON-NLS-1$
private static SynchronousProvisioningListener busListener;
private static final String JAR_EXTENSION = ".jar"; //$NON-NLS-1$
private static final String XML_EXTENSION = ".xml"; //$NON-NLS-1$
@@ -128,7 +131,7 @@ public class CacheManager {
LogHelper.log(new Status(IStatus.WARNING, Activator.ID, "Server returned lastModified <= 0 for " + xmlLocation)); //$NON-NLS-1$
} catch (UserCancelledException e) {
- throw new ProvisionException(Status.CANCEL_STATUS);
+ throw new OperationCanceledException();
} catch (FileNotFoundException e) {
throw new FileNotFoundException(NLS.bind(Messages.CacheManager_Neither_0_nor_1_found, jarLocation, xmlLocation));
} catch (AuthenticationFailedException e) {
@@ -149,23 +152,53 @@ public class CacheManager {
return cacheFile;
// Need to update cache
+
+ // check if download is resumable
cacheFile = new File(dataAreaFile, prefix + hashCode + useExtension);
cacheFile.getParentFile().mkdirs();
- OutputStream metadata = new BufferedOutputStream(new FileOutputStream(cacheFile));
- IStatus result;
+ File resumeFile = new File(new File(cacheFile.getParentFile(), DOWNLOADING), cacheFile.getName());
+ // if append should be performed or not
+ boolean append = false;
+ if (resumeFile.exists()) {
+ // the resume file can be too old
+ if (lastModifiedRemote != resumeFile.lastModified() || lastModifiedRemote <= 0)
+ safeDelete(resumeFile);
+ else {
+ if (resumeFile.renameTo(cacheFile))
+ append = true;
+ else
+ LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.CacheManager_CouldNotMove_0_ToCache, resumeFile)));
+ }
+ }
+
+ StatefulStream metadata = new StatefulStream(new FileOutputStream(cacheFile, append));
+ IStatus result = null;
try {
submonitor.setWorkRemaining(1000);
- result = getTransport().download(remoteFile, metadata, submonitor.newChild(1000));
+ // resume from cache file's length if in append mode
+ result = getTransport().download(remoteFile, metadata, append ? cacheFile.length() : -1, submonitor.newChild(1000));
+ } catch (OperationCanceledException e) {
+ // need to pick up the status - a new operation canceled exception is thrown at the end
+ // as status will be CANCEL.
+ result = metadata.getStatus();
} finally {
metadata.close();
+ // result is null if a runtime error (other than OperationCanceledException)
+ // occurred, just delete the cache file (or a later attempt could fail
+ // with "premature end of file").
+ if (result == null)
+ cacheFile.delete();
}
- if (!result.isOK()) {
- //don't leave a partial cache file lying around
- // TODO: HENRIK Handle resume
+ if (result.isOK())
+ return cacheFile;
+
+ // if possible, keep a partial download to be resumed.
+ if (!makeResumeable(cacheFile, remoteFile, result))
cacheFile.delete();
- throw new ProvisionException(result);
- }
- return cacheFile;
+
+ if (result.getSeverity() == IStatus.CANCEL || monitor.isCanceled())
+ throw new OperationCanceledException();
+ throw new ProvisionException(result);
} finally {
if (monitor != null)
@@ -174,15 +207,76 @@ public class CacheManager {
}
/**
- * Deletes the local cache file for the given repository
+ * Make cacheFile resumable and return true if it was possible.
+ * @param cacheFile - the partially downloaded file to make resumeable
+ * @param downloadStatus - the download status reported for the partial download
+ * @return true if the file was made resumable, false otherwise
+ */
+ private boolean makeResumeable(File cacheFile, URI remoteFile, IStatus status) {
+ if (status == null || status.isOK() || cacheFile == null || !(status instanceof DownloadStatus))
+ return false;
+ // check if resume feature is turned off
+ if (!isResumeEnabled())
+ return false;
+ DownloadStatus downloadStatus = (DownloadStatus) status;
+ long currentLength = cacheFile.length();
+ // if cache file does not exist, or nothing was written to it, there is nothing to resume
+ if (currentLength == 0L)
+ return false;
+
+ long reportedSize = downloadStatus.getFileSize();
+ long reportedModified = downloadStatus.getLastModified();
+
+ if (reportedSize == DownloadStatus.UNKNOWN_SIZE || reportedSize == 0L) {
+ LogHelper.log(new Status(IStatus.WARNING, Activator.ID, NLS.bind(Messages.CacheManager_DownloadOf_0_NotResumable_NoFileSize, remoteFile)));
+ return false;
+ }
+ if (reportedModified <= 0) {
+ LogHelper.log(new Status(IStatus.WARNING, Activator.ID, NLS.bind(Messages.CacheManager_DownloadOf_0_NotResumable_NoLastModified, remoteFile)));
+ return false;
+ }
+
+ // if more than what was reported has been written something odd is going on, and we can't
+ // trust the reported size.
+ // There is a small chance that user canceled in the time window after the full download is seen, and the result is returned. In this
+ // case the reported and current lengths will be equal.
+ if (reportedSize < currentLength) {
+ LogHelper.log(new Status(IStatus.WARNING, Activator.ID, NLS.bind(Messages.CacheManager_DownloadOf_0_NotResumable_MoreReadThanSpecified, remoteFile)));
+ return false;
+ }
+ File resumeDir = new File(cacheFile.getParentFile(), DOWNLOADING);
+ if (!resumeDir.mkdir()) {
+ LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.CacheManager_CanNotCreateDir_0_ForResumeOf_1, resumeDir, remoteFile)));
+ return false;
+ }
+ // move partial cache file to "downloading" directory
+ File resumeFile = new File(resumeDir, cacheFile.getName());
+ if (!cacheFile.renameTo(resumeFile)) {
+ LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.CacheManager_CouldNotMove_0_to_1_ForResumedDownload, cacheFile, resumeFile)));
+ return false;
+ }
+ // touch the file with remote modified time
+ if (!resumeFile.setLastModified(reportedModified)) {
+ LogHelper.log(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.CacheManager_CouldNotSetLastModifiedOn_0_ForResume, resumeFile)));
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deletes the local cache file(s) for the given repository
* @param repositoryLocation
*/
void deleteCache(URI repositoryLocation) {
for (Iterator it = knownPrefixes.iterator(); it.hasNext();) {
String prefix = (String) it.next();
- File cacheFile = getCache(repositoryLocation, prefix);
- if (cacheFile != null)
- safeDelete(cacheFile);
+ File[] cacheFiles = getCacheFiles(repositoryLocation, prefix);
+ for (int i = 0; i < cacheFiles.length; i++) {
+ // delete the cache file if it exists
+ safeDelete(cacheFiles[i]);
+ // delete a resumable download if it exists
+ safeDelete(new File(new File(cacheFiles[i].getParentFile(), DOWNLOADING), cacheFiles[i].getName()));
+ }
}
}
@@ -194,17 +288,39 @@ public class CacheManager {
* the cache file does not exist.
*/
private File getCache(URI repositoryLocation, String prefix) {
+ File[] files = getCacheFiles(repositoryLocation, prefix);
+ if (files[0].exists())
+ return files[0];
+ return files[1].exists() ? files[1] : null;
+
+ // AgentLocation agentLocation = (AgentLocation) ServiceHelper.getService(Activator.getContext(), AgentLocation.class.getName());
+ // URL dataArea = agentLocation.getDataArea(Activator.ID + "/cache/"); //$NON-NLS-1$
+ // File dataAreaFile = URLUtil.toFile(dataArea);
+ // int hashCode = computeHash(repositoryLocation);
+ // File cacheFile = new File(dataAreaFile, prefix + hashCode + JAR_EXTENSION);
+ // if (!cacheFile.exists()) {
+ // cacheFile = new File(dataAreaFile, prefix + hashCode + XML_EXTENSION);
+ // if (!cacheFile.exists())
+ // return null;
+ // }
+ // return cacheFile;
+ }
+
+ /**
+ * Determines the local file paths of the repository's potential cache files.
+ * @param repositoryLocation The location to compute the cache for
+ * @param prefix The prefix to use for this location
+ * @return A {@link File} array with the cache files for JAR and XML extensions.
+ */
+ private File[] getCacheFiles(URI repositoryLocation, String prefix) {
+ File[] files = new File[2];
AgentLocation agentLocation = (AgentLocation) ServiceHelper.getService(Activator.getContext(), AgentLocation.class.getName());
URL dataArea = agentLocation.getDataArea(Activator.ID + "/cache/"); //$NON-NLS-1$
File dataAreaFile = URLUtil.toFile(dataArea);
int hashCode = computeHash(repositoryLocation);
- File cacheFile = new File(dataAreaFile, prefix + hashCode + JAR_EXTENSION);
- if (!cacheFile.exists()) {
- cacheFile = new File(dataAreaFile, prefix + hashCode + XML_EXTENSION);
- if (!cacheFile.exists())
- return null;
- }
- return cacheFile;
+ files[0] = new File(dataAreaFile, prefix + hashCode + JAR_EXTENSION);
+ files[1] = new File(dataAreaFile, prefix + hashCode + XML_EXTENSION);
+ return files;
}
private Object getService(BundleContext ctx, String name) {
@@ -266,4 +382,31 @@ public class CacheManager {
busListener = null;
}
}
+
+ public boolean isResumeEnabled() {
+ String resumeProp = System.getProperty(PROP_RESUMABLE, RESUME_DEFAULT);
+ return Boolean.valueOf(resumeProp).booleanValue();
+ }
+
+ /**
+ * IStateful implementation of BufferedOutputStream. Class is used to get the status from
+ * a download operation.
+ */
+ private static class StatefulStream extends BufferedOutputStream implements IStateful {
+ private IStatus status;
+
+ public StatefulStream(OutputStream stream) {
+ super(stream);
+ }
+
+ public IStatus getStatus() {
+
+ return status;
+ }
+
+ public void setStatus(IStatus aStatus) {
+ status = aStatus;
+ }
+
+ }
}
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/DownloadStatus.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/DownloadStatus.java
index 024025e52..8b7811f41 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/DownloadStatus.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/DownloadStatus.java
@@ -18,8 +18,11 @@ import org.eclipse.core.runtime.Status;
*/
public class DownloadStatus extends Status {
public static final long UNKNOWN_RATE = -1;
+ public static final long UNKNOWN_SIZE = -1;
private long speed = UNKNOWN_RATE;
+ private long fileSize = UNKNOWN_SIZE;
+ private long lastModified = 0;
/**
* Constructs a new DownloadStatus with the given attributes.
@@ -35,6 +38,10 @@ public class DownloadStatus extends Status {
super(severity, pluginId, message, exception);
}
+ public DownloadStatus(int severity, String pluginId, int code, String message, Throwable exception) {
+ super(severity, pluginId, code, message, exception);
+ }
+
/**
* Returns the download rate in bytes per second. If the rate is unknown,
* @{link {@link #UNKNOWN_RATE}} is returned.
@@ -51,4 +58,20 @@ public class DownloadStatus extends Status {
public void setTransferRate(long rate) {
this.speed = rate;
}
+
+ public void setFileSize(long aFileSize) {
+ fileSize = aFileSize;
+ }
+
+ public long getFileSize() {
+ return fileSize;
+ }
+
+ public void setLastModified(long timestamp) {
+ lastModified = timestamp;
+ }
+
+ public long getLastModified() {
+ return lastModified;
+ }
}
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java
index f9bf43efe..b4fd3b1e3 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java
@@ -24,6 +24,7 @@ import org.eclipse.osgi.util.NLS;
* @author henrik.lindberg@cloudsmith.com - adaption to 1.4 and to this p2 package
*/
public class FileReader extends FileTransferJob implements IFileTransferListener {
+ private static IFileReaderProbe testProbe;
private boolean closeStreamWhenFinished = false;
private Exception exception;
private FileInfo fileInfo;
@@ -35,6 +36,7 @@ public class FileReader extends FileTransferJob implements IFileTransferListener
private final int connectionRetryCount;
private final long connectionRetryDelay;
private final IConnectContext connectContext;
+ private URI uri;
/**
* Create a new FileReader that will retry failed connection attempts and sleep some amount of time between each
@@ -75,12 +77,13 @@ public class FileReader extends FileTransferJob implements IFileTransferListener
if (theMonitor != null) {
long fileLength = source.getFileLength();
- statistics = new ProgressStatistics(source.getRemoteFileName(), fileLength);
+ statistics = new ProgressStatistics(uri, source.getRemoteFileName(), fileLength);
theMonitor.beginTask(null, 1000);
theMonitor.subTask(statistics.report());
lastStatsCount = 0;
lastProgressCount = 0;
}
+ onStart(source);
} else if (event instanceof IIncomingFileTransferReceiveDataEvent) {
IIncomingFileTransfer source = ((IIncomingFileTransferEvent) event).getSource();
if (theMonitor != null) {
@@ -101,12 +104,14 @@ public class FileReader extends FileTransferJob implements IFileTransferListener
theMonitor.worked((int) (1000 * count / statistics.getTotal()));
}
}
+ onData(source);
} else if (event instanceof IIncomingFileTransferReceiveDoneEvent) {
if (closeStreamWhenFinished)
hardClose(theOutputStream);
if (exception == null)
exception = ((IIncomingFileTransferReceiveDoneEvent) event).getException();
+ onDone(((IIncomingFileTransferReceiveDoneEvent) event).getSource());
}
}
@@ -121,7 +126,7 @@ public class FileReader extends FileTransferJob implements IFileTransferListener
RepositoryTracing.debug("Downloading {0}", url); //$NON-NLS-1$
final IProgressMonitor cancellationMonitor = new NullProgressMonitor();
- sendRetrieveRequest(url, output, true, cancellationMonitor);
+ sendRetrieveRequest(url, output, null, true, cancellationMonitor);
return new InputStream() {
public int available() throws IOException {
@@ -185,27 +190,34 @@ public class FileReader extends FileTransferJob implements IFileTransferListener
};
}
- /** Only request info
- * @deprecated REMOVE THIS METHOD - SHOULD USE BROWSE INSTEAD TO ONLY GET HEAD - ALSO REMOVE PARAMTER ONLYHEAD
- * @param uri
- * @return FileInfo
- * @throws CoreException
- * @throws FileNotFoundException
- * @throws AuthenticationFailedException
- */
- public FileInfo readInfo(URI uri) throws CoreException, FileNotFoundException, AuthenticationFailedException {
- sendRetrieveRequest(uri, null, false, null);
- return getLastFileInfo();
+ // /** Only request info
+ // * @deprecated REMOVE THIS METHOD - SHOULD USE BROWSE INSTEAD TO ONLY GET HEAD - ALSO REMOVE PARAMTER ONLYHEAD
+ // * @param uri
+ // * @return FileInfo
+ // * @throws CoreException
+ // * @throws FileNotFoundException
+ // * @throws AuthenticationFailedException
+ // */
+ // public FileInfo readInfo(URI uri) throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ // sendRetrieveRequest(uri, null, false, null);
+ // return getLastFileInfo();
+ // }
+ public void readInto(URI uri, OutputStream anOutputStream, IProgressMonitor monitor) //
+ throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ readInto(uri, anOutputStream, -1, monitor);
}
- public void readInto(URI uri, OutputStream anOutputStream, IProgressMonitor monitor) //
+ public void readInto(URI uri, OutputStream anOutputStream, long startPos, IProgressMonitor monitor) //
throws CoreException, FileNotFoundException, AuthenticationFailedException {
try {
- sendRetrieveRequest(uri, anOutputStream, false, monitor);
+ sendRetrieveRequest(uri, anOutputStream, (startPos != -1 ? new DownloadRange(startPos) : null), false, monitor);
+
join();
} catch (InterruptedException e) {
monitor.setCanceled(true);
throw new OperationCanceledException();
+ // } catch (Throwable t) {
+ // t.printStackTrace();
} finally {
if (monitor != null) {
if (statistics == null)
@@ -220,7 +232,7 @@ public class FileReader extends FileTransferJob implements IFileTransferListener
}
}
- protected void sendRetrieveRequest(URI uri, OutputStream outputStream, boolean closeStreamOnFinish, //
+ protected void sendRetrieveRequest(URI uri, OutputStream outputStream, DownloadRange range, boolean closeStreamOnFinish, //
IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException {
IRetrieveFileTransferFactory factory = Activator.getDefault().getRetrieveFileTransferFactory();
@@ -239,6 +251,7 @@ public class FileReader extends FileTransferJob implements IFileTransferListener
this.lastStatsCount = 0L;
this.theMonitor = monitor;
this.theOutputStream = outputStream;
+ this.uri = uri;
for (int retryCount = 0;;) {
if (monitor != null && monitor.isCanceled())
@@ -246,11 +259,17 @@ public class FileReader extends FileTransferJob implements IFileTransferListener
try {
IFileID fileID = FileIDFactory.getDefault().createFileID(adapter.getRetrieveNamespace(), uri.toString());
- adapter.sendRetrieveRequest(fileID, this, null);
+ if (range != null)
+ adapter.sendRetrieveRequest(fileID, range, this, null);
+ else
+ adapter.sendRetrieveRequest(fileID, this, null);
} catch (IncomingFileTransferException e) {
exception = e;
} catch (FileCreateException e) {
exception = e;
+ } catch (Throwable t) {
+ if (exception != null)
+ exception.printStackTrace();
}
// note that 'exception' could have been captured in a callback
@@ -309,4 +328,48 @@ public class FileReader extends FileTransferJob implements IFileTransferListener
}
}
+ private static class DownloadRange implements IFileRangeSpecification {
+
+ private long startPosition;
+
+ public DownloadRange(long startPos) {
+ startPosition = startPos;
+ }
+
+ public long getEndPosition() {
+ return -1;
+ }
+
+ public long getStartPosition() {
+ return startPosition;
+ }
+
+ }
+
+ private void onDone(IIncomingFileTransfer source) {
+ if (testProbe != null)
+ testProbe.onDone(this, source, theMonitor);
+ }
+
+ private void onStart(IIncomingFileTransfer source) {
+ if (testProbe != null)
+ testProbe.onStart(this, source, theMonitor);
+ }
+
+ private void onData(IIncomingFileTransfer source) {
+ if (testProbe != null)
+ testProbe.onData(this, source, theMonitor);
+ }
+
+ public static void setTestProbe(IFileReaderProbe probe) {
+ testProbe = probe;
+ }
+
+ public interface IFileReaderProbe {
+ public void onStart(FileReader reader, IIncomingFileTransfer source, IProgressMonitor monitor);
+
+ public void onData(FileReader reader, IIncomingFileTransfer source, IProgressMonitor monitor);
+
+ public void onDone(FileReader reader, IIncomingFileTransfer source, IProgressMonitor monitor);
+ }
}
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Messages.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Messages.java
index e15d1a93e..0fb7714db 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Messages.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Messages.java
@@ -66,7 +66,9 @@ public class Messages extends NLS {
public static String TransportErrorTranslator_UnknownHost;
public static String fetching_0_1_at_2;
+ public static String fetching_0_from_1_2_at_3;
public static String fetching_0_1_of_2_at_3;
+ public static String fetching_0_from_1_2_of_3_at_4;
public static String connection_to_0_failed_on_1_retry_attempt_2;
public static String FileTransport_reader;
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/ProgressStatistics.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/ProgressStatistics.java
index f34a6c17f..786209b96 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/ProgressStatistics.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/ProgressStatistics.java
@@ -7,6 +7,7 @@
******************************************************************************/
package org.eclipse.equinox.internal.p2.repository;
+import java.net.URI;
import java.text.NumberFormat;
import java.util.*;
import java.util.Map.Entry;
@@ -55,7 +56,9 @@ public class ProgressStatistics {
private long m_recentSpeedMapKey;
- public ProgressStatistics(String fileName, long total) {
+ private URI m_uri;
+
+ public ProgressStatistics(URI uri, String fileName, long total) {
m_startTime = System.currentTimeMillis();
m_fileName = fileName;
@@ -66,6 +69,7 @@ public class ProgressStatistics {
m_reportInterval = DEFAULT_REPORT_INTERVAL;
m_recentSpeedMap = new TreeMap();
m_recentSpeedMapKey = 0L;
+ m_uri = uri;
}
public long getAverageSpeed() {
@@ -121,7 +125,7 @@ public class ProgressStatistics {
}
public synchronized String report() {
- return m_total != -1 ? NLS.bind(Messages.fetching_0_1_of_2_at_3, new String[] {m_fileName, convert(m_current), convert(m_total), convert(getRecentSpeed())}) : NLS.bind(Messages.fetching_0_1_at_2, new String[] {m_fileName, convert(m_current), convert(getRecentSpeed())});
+ return m_total != -1 ? NLS.bind(Messages.fetching_0_from_1_2_of_3_at_4, new String[] {m_fileName, m_uri.toString(), convert(m_current), convert(m_total), convert(getRecentSpeed())}) : NLS.bind(Messages.fetching_0_from_1_2_at_3, new String[] {m_fileName, m_uri.toString(), convert(m_current), convert(getRecentSpeed())});
}
public void setReportInterval(int reportInterval) {
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatus.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatus.java
index 36a511ec5..434ac77e6 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatus.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatus.java
@@ -14,7 +14,6 @@ package org.eclipse.equinox.internal.p2.repository;
import java.io.FileNotFoundException;
import java.net.*;
import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
import org.eclipse.ecf.core.identity.IDCreateException;
import org.eclipse.ecf.filetransfer.IncomingFileTransferException;
import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException;
@@ -108,24 +107,24 @@ public class RepositoryStatus {
}
}
- public static IStatus forStatus(IStatus original, URI toDownload) {
+ public static DownloadStatus forStatus(IStatus original, URI toDownload) {
Throwable t = original.getException();
return forException(t, toDownload);
}
- public static IStatus forException(Throwable t, URI toDownload) {
+ public static DownloadStatus forException(Throwable t, URI toDownload) {
if (t instanceof FileNotFoundException || (t instanceof IncomingFileTransferException && ((IncomingFileTransferException) t).getErrorCode() == 404))
- return new Status(IStatus.ERROR, Activator.ID, ProvisionException.ARTIFACT_NOT_FOUND, NLS.bind(Messages.artifact_not_found, toDownload), t);
+ return new DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.ARTIFACT_NOT_FOUND, NLS.bind(Messages.artifact_not_found, toDownload), t);
if (t instanceof ConnectException)
- return new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, NLS.bind(Messages.TransportErrorTranslator_UnableToConnectToRepository_0, toDownload), t);
+ return new DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, NLS.bind(Messages.TransportErrorTranslator_UnableToConnectToRepository_0, toDownload), t);
if (t instanceof UnknownHostException)
- return new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.TransportErrorTranslator_UnknownHost, toDownload), t);
+ return new DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.TransportErrorTranslator_UnknownHost, toDownload), t);
if (t instanceof IDCreateException) {
IStatus status = ((IDCreateException) t).getStatus();
if (status != null && status.getException() != null)
t = status.getException();
- return new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.TransportErrorTranslator_MalformedRemoteFileReference, toDownload), t);
+ return new DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.TransportErrorTranslator_MalformedRemoteFileReference, toDownload), t);
}
if (t instanceof IncomingFileTransferException) {
int code = ((IncomingFileTransferException) t).getErrorCode();
@@ -136,12 +135,12 @@ public class RepositoryStatus {
provisionCode = ProvisionException.REPOSITORY_FAILED_AUTHENTICATION;
else if (code == 404)
provisionCode = ProvisionException.ARTIFACT_NOT_FOUND;
- return new Status(IStatus.ERROR, Activator.ID, provisionCode, //
+ return new DownloadStatus(IStatus.ERROR, Activator.ID, provisionCode, //
codeToMessage(code, toDownload.toString()), t);
}
}
// Add more specific translation here
- return new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.io_failedRead, toDownload), t);
+ return new DownloadStatus(IStatus.ERROR, Activator.ID, NLS.bind(Messages.io_failedRead, toDownload), t);
}
}
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java
index 94bfaf543..381a16e8f 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java
@@ -48,46 +48,91 @@ public class RepositoryTransport extends Transport {
/**
* Perform a download, writing into the target output stream. Progress is reported on the
* monitor. If the <code>target</code> is an instance of {@link IStateful} the resulting status
- * is also set on the target.
+ * is also set on the target. An IStateful target is updated with status even if this methods
+ * throws {@link OperationCanceledException}.
*
* @returns IStatus, that is a {@link DownloadStatus} on success.
* @param toDownload URI of file to download
* @param target OutputStream where result is written
+ * @param startPos the starting position of the download, or -1 for from start
* @param monitor where progress should be reported
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public IStatus download(URI toDownload, OutputStream target, IProgressMonitor monitor) {
+ public IStatus download(URI toDownload, OutputStream target, long startPos, IProgressMonitor monitor) {
+
boolean promptUser = false;
AuthenticationInfo loginDetails = null;
for (int i = RepositoryPreferences.getLoginRetryCount(); i > 0; i++) {
+ FileReader reader = null;
try {
loginDetails = Credentials.forLocation(toDownload, promptUser, loginDetails);
IConnectContext context = (loginDetails == null) ? null : ConnectContextFactory.createUsernamePasswordConnectContext(loginDetails.getUserName(), loginDetails.getPassword());
// perform the download
- FileReader reader = new FileReader(context);
- reader.readInto(toDownload, target, monitor);
+ reader = new FileReader(context);
+ reader.readInto(toDownload, target, startPos, monitor);
+
+ // check that job ended ok - throw exceptions otherwise
+ IStatus result = reader.getResult();
+ if (result.getSeverity() == IStatus.CANCEL)
+ throw new UserCancelledException();
+ if (!result.isOK())
+ throw new CoreException(result);
+
// Download status is expected on success
DownloadStatus status = new DownloadStatus(IStatus.OK, Activator.ID, Status.OK_STATUS.getMessage());
- status.setTransferRate(reader.getLastFileInfo().getAverageSpeed());
- return statusOn(target, status);
+ return statusOn(target, status, reader);
} catch (UserCancelledException e) {
- statusOn(target, Status.CANCEL_STATUS);
+ statusOn(target, new DownloadStatus(IStatus.CANCEL, Activator.ID, 1, "", null), reader); //$NON-NLS-1$
throw new OperationCanceledException();
+ } catch (OperationCanceledException e) {
+ statusOn(target, new DownloadStatus(IStatus.CANCEL, Activator.ID, 1, "", null), reader); //$NON-NLS-1$
+ throw e;
} catch (CoreException e) {
- return statusOn(target, RepositoryStatus.forStatus(e.getStatus(), toDownload));
+ return statusOn(target, RepositoryStatus.forStatus(e.getStatus(), toDownload), reader);
} catch (FileNotFoundException e) {
- return statusOn(target, RepositoryStatus.forException(e, toDownload));
+ return statusOn(target, RepositoryStatus.forException(e, toDownload), reader);
} catch (AuthenticationFailedException e) {
promptUser = true;
}
}
// reached maximum number of retries without success
- IStatus status = new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_AUTHENTICATION, //
+ DownloadStatus status = new DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_AUTHENTICATION, //
NLS.bind(Messages.UnableToRead_0_TooManyAttempts, toDownload), null);
- return statusOn(target, status);
+ return statusOn(target, status, null);
+ }
+
+ /**
+ * Perform a download, writing into the target output stream. Progress is reported on the
+ * monitor. If the <code>target</code> is an instance of {@link IStateful} the resulting status
+ * is also set on the target.
+ *
+ * @returns IStatus, that is a {@link DownloadStatus} on success.
+ * @param toDownload URI of file to download
+ * @param target OutputStream where result is written
+ * @param monitor where progress should be reported
+ */
+ public IStatus download(URI toDownload, OutputStream target, IProgressMonitor monitor) {
+ return download(toDownload, target, -1, monitor);
}
- private static IStatus statusOn(OutputStream target, IStatus status) {
+ /**
+ * Set the status on the output stream if it implements IStateful.
+ * Update the DownloadStatus with information from FileReader.
+ * @param target an OutputStream possibly implementing IStateful
+ * @param status a DownloadStatus configured with status message, code, etc
+ * @param reader a FileReade that was used to download (or null if not known).
+ * @return the configured DownloadStatus status.
+ */
+ private static DownloadStatus statusOn(OutputStream target, DownloadStatus status, FileReader reader) {
+ if (reader != null) {
+ FileInfo fi = reader.getLastFileInfo();
+ if (fi != null) {
+ status.setFileSize(fi.getSize());
+ status.setLastModified(fi.getLastModified());
+ status.setTransferRate(fi.getAverageSpeed());
+ }
+ }
if (target instanceof IStateful)
((IStateful) target).setStatus(status);
return status;
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/messages.properties b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/messages.properties
index 76e3f0934..97e433f1a 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/messages.properties
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/messages.properties
@@ -65,7 +65,9 @@ TransportErrorTranslator_UnableToConnectToRepository_0=Unable to connect to repo
TransportErrorTranslator_UnknownErrorCode=HTTP Server Unknown HTTP Response Code ({0}):{1}
TransportErrorTranslator_UnknownHost=Unknown Host: {0}
fetching_0_1_at_2=Fetching {0} ({1} at {2}/s)
+fetching_0_from_1_2_at_3=Fetching {0} ({2} at {3}/s) from {1}
fetching_0_1_of_2_at_3=Fetching {0} ({1} of {2} at {3}/s)
+fetching_0_from_1_2_of_3_at_4=Fetching {0} ({2} of {3} at {4}/s) from {1}
FileTransport_reader=File Transport Reader
connection_to_0_failed_on_1_retry_attempt_2=Connection to {0} failed on {1}. Retry attempt {2} started
UnableToRead_0_TooManyAttempts=Unable to read repository at: {0}. Too many failed attempts.

Back to the top