blob: cfca3d83205fcf4d0d349d64ec5788d8f12e9642 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2017 IBM Corporation, and others.
* 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.
* Contributors:
* IBM Corporation - initial implementation
* Cloudsmith Inc - modified API, and implementation
* Red Hat Inc. - Bug 460967
******************************************************************************/
package org.eclipse.equinox.internal.p2.transport.ecf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ecf.core.*;
import org.eclipse.ecf.core.security.IConnectContext;
import org.eclipse.ecf.filetransfer.*;
import org.eclipse.ecf.filetransfer.events.IRemoteFileSystemBrowseEvent;
import org.eclipse.ecf.filetransfer.events.IRemoteFileSystemEvent;
import org.eclipse.ecf.filetransfer.identity.*;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.repository.*;
import org.eclipse.equinox.internal.p2.repository.Activator;
import org.eclipse.equinox.internal.p2.repository.Messages;
import org.eclipse.osgi.util.NLS;
/**
* The FileInfoReader is a {@link Job} similar to {@link FileReader}, but without the support
* from ECF (there is currently no way to wait on a BrowseRequest job, as this is internal to
* ECF). If such support is added, this class is easily modified.
*
*/
public class FileInfoReader extends Job implements IRemoteFileSystemListener {
private Exception exception;
private IProgressMonitor theMonitor;
private final int connectionRetryCount;
private final long connectionRetryDelay;
private final IConnectContext connectContext;
final Boolean[] barrier = new Boolean[1];
private IRemoteFile[] remoteFiles;
private IRemoteFileSystemRequest browseRequest;
@Override
protected IStatus run(IProgressMonitor monitor) {
synchronized (barrier) {
while (barrier[0] == null) {
try {
barrier.wait(1000);
if (theMonitor.isCanceled() && browseRequest != null)
browseRequest.cancel();
} catch (InterruptedException e) {
//ignore
}
}
}
return Status.OK_STATUS;
}
/**
* Waits until request is processed (barrier[0] is non null).
* This is a bit of a hack, as it would be better if the ECFBrowser worked in similar fashion to
* file transfer were a custom job can be supplied.
* TODO: log an issue for ECF.
*/
private void waitOnSelf() {
schedule();
while (barrier[0] == null) {
boolean logged = false;
try {
join();
} catch (InterruptedException e) {
if (!logged)
LogHelper.log(new Status(IStatus.WARNING, Activator.ID, "Unexpected interrupt while waiting on ECF browse request", e)); //$NON-NLS-1$
}
}
}
/**
* Create a new FileInfoReader that will retry failed connection attempts and sleep some amount of time between each
* attempt.
*/
public FileInfoReader(IConnectContext aConnectContext) {
super(Messages.repo_loading); // job label - TODO: this is a bad label
barrier[0] = null;
// Hide this job.
setSystem(true);
setUser(false);
connectionRetryCount = RepositoryPreferences.getConnectionRetryCount();
connectionRetryDelay = RepositoryPreferences.getConnectionMsRetryDelay();
connectContext = aConnectContext;
}
/**
* Get the requested information.
* @return IRemoteFile[] or null if there was an error.
* @throws CoreException
* @throws FileNotFoundException
* @throws AuthenticationFailedException
* @throws JREHttpClientRequiredException
*/
public IRemoteFile[] getRemoteFiles(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException, JREHttpClientRequiredException {
if (monitor != null)
monitor.beginTask(location.toString(), 1);
try {
sendBrowseRequest(location, monitor);
waitOnSelf();
// throw any exception received in a callback
checkException(location, connectionRetryCount);
return remoteFiles;
} finally {
if (monitor != null) {
monitor.done();
}
}
}
public IRemoteFile getRemoteFile(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException, JREHttpClientRequiredException {
getRemoteFiles(location, monitor);
return remoteFiles != null && remoteFiles.length > 0 ? remoteFiles[0] : null;
}
public long getLastModified(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException, JREHttpClientRequiredException {
IRemoteFile file = getRemoteFile(location, monitor);
if (file == null)
throw new FileNotFoundException(location.toString());
return file.getInfo().getLastModified();
}
@Override
public void handleRemoteFileEvent(IRemoteFileSystemEvent event) {
exception = event.getException();
if (exception != null) {
synchronized (barrier) {
barrier[0] = Boolean.TRUE;
barrier.notify();
}
} else if (event instanceof IRemoteFileSystemBrowseEvent) {
IRemoteFileSystemBrowseEvent fsbe = (IRemoteFileSystemBrowseEvent) event;
remoteFiles = fsbe.getRemoteFiles();
if (theMonitor != null)
theMonitor.worked(1);
synchronized (barrier) {
barrier[0] = Boolean.TRUE;
barrier.notify();
}
} else {
synchronized (barrier) {
barrier[0] = Boolean.FALSE; // ended by unknown reason
barrier.notify();
}
}
}
protected void sendBrowseRequest(URI uri, IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
IContainer container;
try {
container = ContainerFactory.getDefault().createContainer();
} catch (ContainerCreateException e) {
throw RepositoryStatusHelper.fromExceptionMessage(e ,Messages.ecf_configuration_error);
}
IRemoteFileSystemBrowserContainerAdapter adapter = container.getAdapter(IRemoteFileSystemBrowserContainerAdapter.class);
if (adapter == null) {
throw RepositoryStatusHelper.fromMessage(Messages.ecf_configuration_error);
}
adapter.setConnectContextForAuthentication(connectContext);
this.exception = null;
this.theMonitor = monitor;
for (int retryCount = 0;; retryCount++) {
if (monitor != null && monitor.isCanceled())
throw new OperationCanceledException();
try {
IFileID fileID = FileIDFactory.getDefault().createFileID(adapter.getBrowseNamespace(), uri.toString());
browseRequest = adapter.sendBrowseRequest(fileID, this);
} catch (RemoteFileSystemException e) {
exception = e;
} catch (FileCreateException e) {
exception = e;
}
if (checkException(uri, retryCount))
break;
}
}
protected Exception getException() {
return exception;
}
/**
* Utility method to check exception condition and determine if retry should be done.
* If there was an exception it is translated into one of the specified exceptions and thrown.
*
* @param uri the URI being read - used for logging purposes
* @param attemptCounter - the current attempt number (start with 0)
* @return true if the exception is an IOException and attemptCounter < connectionRetryCount, false otherwise
* @throws CoreException
* @throws FileNotFoundException
* @throws AuthenticationFailedException
* @throws JREHttpClientRequiredException
*/
private boolean checkException(URI uri, int attemptCounter) throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
// note that 'exception' could have been captured in a callback
if (exception != null) {
// check if HTTP client needs to be changed
RepositoryStatusHelper.checkJREHttpClientRequired(exception);
// if this is a authentication failure - it is not meaningful to continue
RepositoryStatusHelper.checkPermissionDenied(exception);
// if this is a file not found - it is not meaningful to continue
RepositoryStatusHelper.checkFileNotFound(exception, uri);
Throwable t = RepositoryStatusHelper.unwind(exception);
if (t instanceof CoreException)
throw RepositoryStatusHelper.unwindCoreException((CoreException) t);
if (t instanceof IOException && attemptCounter < connectionRetryCount) {
// TODO: Retry only certain exceptions or filter out
// some exceptions not worth retrying
//
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(attemptCounter)}), t));
Thread.sleep(connectionRetryDelay);
return false;
} catch (InterruptedException e) {
/* ignore */
}
}
throw RepositoryStatusHelper.wrap(exception);
}
return true;
}
}