blob: f9bf43efeffe90ae984c27718805457bc42405ad [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006-2009, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text of
* such license is available at www.eclipse.org.
******************************************************************************/
package org.eclipse.equinox.internal.p2.repository;
import java.io.*;
import java.net.URI;
import java.util.Date;
import org.eclipse.core.runtime.*;
import org.eclipse.ecf.core.security.IConnectContext;
import org.eclipse.ecf.filetransfer.*;
import org.eclipse.ecf.filetransfer.events.*;
import org.eclipse.ecf.filetransfer.identity.*;
import org.eclipse.ecf.filetransfer.service.IRetrieveFileTransferFactory;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.osgi.util.NLS;
/**
* @author Thomas Hallgren
* @author henrik.lindberg@cloudsmith.com - adaption to 1.4 and to this p2 package
*/
public class FileReader extends FileTransferJob implements IFileTransferListener {
private boolean closeStreamWhenFinished = false;
private Exception exception;
private FileInfo fileInfo;
private long lastProgressCount;
private long lastStatsCount;
private IProgressMonitor theMonitor;
private OutputStream theOutputStream;
private ProgressStatistics statistics;
private final int connectionRetryCount;
private final long connectionRetryDelay;
private final IConnectContext connectContext;
/**
* Create a new FileReader that will retry failed connection attempts and sleep some amount of time between each
* attempt.
*/
public FileReader(IConnectContext aConnectContext) {
super(Messages.FileTransport_reader); // job label
// Hide this job.
setSystem(true);
setUser(false);
connectionRetryCount = RepositoryPreferences.getConnectionRetryCount();
connectionRetryDelay = RepositoryPreferences.getConnectionMsRetryDelay();
connectContext = aConnectContext;
}
public FileInfo getLastFileInfo() {
return fileInfo;
}
public synchronized void handleTransferEvent(IFileTransferEvent event) {
if (event instanceof IIncomingFileTransferReceiveStartEvent) {
IIncomingFileTransfer source = ((IIncomingFileTransferEvent) event).getSource();
try {
FileInfo fi = new FileInfo();
Date lastModified = source.getRemoteLastModified();
if (lastModified != null)
fi.setLastModified(lastModified.getTime());
fi.setName(source.getRemoteFileName());
fi.setSize(source.getFileLength());
fileInfo = fi;
((IIncomingFileTransferReceiveStartEvent) event).receive(theOutputStream, this);
} catch (IOException e) {
exception = e;
return;
}
if (theMonitor != null) {
long fileLength = source.getFileLength();
statistics = new ProgressStatistics(source.getRemoteFileName(), fileLength);
theMonitor.beginTask(null, 1000);
theMonitor.subTask(statistics.report());
lastStatsCount = 0;
lastProgressCount = 0;
}
} else if (event instanceof IIncomingFileTransferReceiveDataEvent) {
IIncomingFileTransfer source = ((IIncomingFileTransferEvent) event).getSource();
if (theMonitor != null) {
if (theMonitor.isCanceled()) {
source.cancel();
return;
}
long br = source.getBytesReceived();
long count = br - lastStatsCount;
lastStatsCount = br;
statistics.increase(count);
fileInfo.setAverageSpeed(statistics.getAverageSpeed());
if (statistics.shouldReport()) {
count = br - lastProgressCount;
lastProgressCount = br;
theMonitor.subTask(statistics.report());
theMonitor.worked((int) (1000 * count / statistics.getTotal()));
}
}
} else if (event instanceof IIncomingFileTransferReceiveDoneEvent) {
if (closeStreamWhenFinished)
hardClose(theOutputStream);
if (exception == null)
exception = ((IIncomingFileTransferReceiveDoneEvent) event).getException();
}
}
public InputStream read(URI url) throws CoreException, FileNotFoundException, AuthenticationFailedException {
final PipedInputStream input = new PipedInputStream();
PipedOutputStream output;
try {
output = new PipedOutputStream(input);
} catch (IOException e) {
throw RepositoryStatusHelper.wrap(e);
}
RepositoryTracing.debug("Downloading {0}", url); //$NON-NLS-1$
final IProgressMonitor cancellationMonitor = new NullProgressMonitor();
sendRetrieveRequest(url, output, true, cancellationMonitor);
return new InputStream() {
public int available() throws IOException {
checkException();
return input.available();
}
public void close() throws IOException {
cancellationMonitor.setCanceled(true);
hardClose(input);
checkException();
}
public void mark(int readlimit) {
input.mark(readlimit);
}
public boolean markSupported() {
return input.markSupported();
}
public int read() throws IOException {
checkException();
return input.read();
}
public int read(byte b[]) throws IOException {
checkException();
return input.read(b);
}
public int read(byte b[], int off, int len) throws IOException {
checkException();
return input.read(b, off, len);
}
public void reset() throws IOException {
checkException();
input.reset();
}
public long skip(long n) throws IOException {
checkException();
return input.skip(n);
}
private void checkException() throws IOException {
if (getException() == null)
return;
IOException e;
Throwable t = RepositoryStatusHelper.unwind(getException());
if (t instanceof IOException)
e = (IOException) t;
else {
e = new IOException(t.getMessage());
e.initCause(t);
}
throw e;
}
};
}
/** 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 {
try {
sendRetrieveRequest(uri, anOutputStream, false, monitor);
join();
} catch (InterruptedException e) {
monitor.setCanceled(true);
throw new OperationCanceledException();
} finally {
if (monitor != null) {
if (statistics == null)
//
// Monitor was never started. See to that it's balanced
//
monitor.beginTask(null, 1);
else
statistics = null;
monitor.done();
}
}
}
protected void sendRetrieveRequest(URI uri, OutputStream outputStream, boolean closeStreamOnFinish, //
IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException {
IRetrieveFileTransferFactory factory = Activator.getDefault().getRetrieveFileTransferFactory();
if (factory == null) {
throw RepositoryStatusHelper.fromMessage(Messages.ecf_configuration_error);
}
IRetrieveFileTransferContainerAdapter adapter = factory.newInstance();
adapter.setConnectContextForAuthentication(connectContext);
this.exception = null;
this.closeStreamWhenFinished = closeStreamOnFinish;
this.fileInfo = null;
this.statistics = null;
this.lastProgressCount = 0L;
this.lastStatsCount = 0L;
this.theMonitor = monitor;
this.theOutputStream = outputStream;
for (int retryCount = 0;;) {
if (monitor != null && monitor.isCanceled())
throw new OperationCanceledException();
try {
IFileID fileID = FileIDFactory.getDefault().createFileID(adapter.getRetrieveNamespace(), uri.toString());
adapter.sendRetrieveRequest(fileID, this, null);
} catch (IncomingFileTransferException e) {
exception = e;
} catch (FileCreateException e) {
exception = e;
}
// note that 'exception' could have been captured in a callback
if (exception != null) {
// if this is a authentication failure - it is not meaningful to continue
RepositoryStatusHelper.checkPermissionDenied(exception);
Throwable t = RepositoryStatusHelper.unwind(exception);
if (t instanceof CoreException)
throw RepositoryStatusHelper.unwindCoreException((CoreException) t);
if (t instanceof FileNotFoundException)
//
// Connection succeeded but the target doesn't exist
//
throw (FileNotFoundException) t;
if (t instanceof IOException && retryCount < connectionRetryCount) {
// TODO: Retry only certain exceptions or filter out
// some exceptions not worth retrying
//
++retryCount;
exception = null;
try {
LogHelper.log(new Status(IStatus.WARNING, Activator.ID, NLS.bind(Messages.connection_to_0_failed_on_1_retry_attempt_2, new String[] {uri.toString(), t.getMessage(), String.valueOf(retryCount)}), t));
Thread.sleep(connectionRetryDelay);
continue;
} catch (InterruptedException e) {
/* ignore */
}
}
throw RepositoryStatusHelper.wrap(exception);
}
break;
}
}
protected Exception getException() {
return exception;
}
/**
* Closes input and output streams
* @param aStream
*/
public static void hardClose(Object aStream) {
if (aStream != null) {
try {
if (aStream instanceof OutputStream)
((OutputStream) aStream).close();
else if (aStream instanceof InputStream)
((InputStream) aStream).close();
} catch (IOException e) { /* ignore */
}
}
}
}