Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.transport.ecf/src/org')
-rw-r--r--bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java169
-rw-r--r--bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/ECFTransportComponent.java13
-rw-r--r--bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java247
-rw-r--r--bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java509
-rw-r--r--bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Messages.java95
-rw-r--r--bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryStatus.java155
-rw-r--r--bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryStatusHelper.java320
-rw-r--r--bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryTransport.java279
-rw-r--r--bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/messages.properties76
9 files changed, 1863 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java
new file mode 100644
index 000000000..b21c52c03
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc - initial API and implementation
+ * IBM Corporation - ongoing development
+ * Genuitec - Bug 291926
+ ******************************************************************************/
+package org.eclipse.equinox.internal.p2.transport.ecf;
+
+import org.eclipse.ecf.filetransfer.service.IRetrieveFileTransferFactory;
+import org.eclipse.ecf.provider.filetransfer.IFileTransferProtocolToFactoryMapper;
+import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
+import org.osgi.framework.*;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * The activator class controls the plug-in life cycle.
+ * This activator has helper methods to get file transfer service tracker, and
+ * for making sure required ECF bundles are started.
+ */
+public class Activator implements BundleActivator {
+
+ public static final String ID = "org.eclipse.equinox.p2.transport.ecf"; //$NON-NLS-1$
+ private static final String HTTP = "http"; //$NON-NLS-1$
+ private static final String HTTPS = "https"; //$NON-NLS-1$
+
+ private static BundleContext context;
+ // tracker for ECF service
+ private ServiceTracker<IRetrieveFileTransferFactory, IRetrieveFileTransferFactory> retrievalFactoryTracker;
+
+ // tracker for protocolToFactoryMapperTracker
+ private ServiceTracker<IFileTransferProtocolToFactoryMapper, IFileTransferProtocolToFactoryMapper> protocolToFactoryMapperTracker = null;
+
+ // The shared instance
+ private static Activator plugin;
+
+ public void start(BundleContext aContext) throws Exception {
+ Activator.context = aContext;
+ Activator.plugin = this;
+ }
+
+ public void stop(BundleContext aContext) throws Exception {
+ Activator.context = null;
+ Activator.plugin = null;
+ if (retrievalFactoryTracker != null) {
+ retrievalFactoryTracker.close();
+ retrievalFactoryTracker = null;
+ }
+ if (protocolToFactoryMapperTracker != null) {
+ protocolToFactoryMapperTracker.close();
+ protocolToFactoryMapperTracker = null;
+ }
+
+ }
+
+ public static BundleContext getContext() {
+ return Activator.context;
+ }
+
+ /**
+ * Get singleton instance.
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+ /**
+ * Returns a {@link IRetrieveFileTransferFactory} using a {@link ServiceTracker} after having attempted
+ * to start the bundle "org.eclipse.ecf.provider.filetransfer". If something is wrong with the configuration
+ * this method returns null.
+ * @return a factory, or null, if configuration is incorrect
+ */
+ public IRetrieveFileTransferFactory getRetrieveFileTransferFactory() {
+ return getFileTransferServiceTracker().getService();
+ }
+
+ public synchronized void useJREHttpClient() {
+ IFileTransferProtocolToFactoryMapper mapper = getProtocolToFactoryMapper();
+ if (mapper != null) {
+ // remove http
+ // Remove browse provider
+ String providerId = mapper.getBrowseFileTransferFactoryId(HTTP);
+ if (providerId != null) {
+ mapper.removeBrowseFileTransferFactory(providerId);
+ }
+ // Remove retrieve provider
+ providerId = mapper.getRetrieveFileTransferFactoryId(HTTP);
+ if (providerId != null) {
+ mapper.removeRetrieveFileTransferFactory(providerId);
+ }
+ // Remove send provider
+ providerId = mapper.getSendFileTransferFactoryId(HTTP);
+ if (providerId != null) {
+ mapper.removeSendFileTransferFactory(providerId);
+ }
+ // remove https
+ // Remove browse provider
+ providerId = mapper.getBrowseFileTransferFactoryId(HTTPS);
+ if (providerId != null) {
+ mapper.removeBrowseFileTransferFactory(providerId);
+ }
+ // Remove retrieve provider
+ providerId = mapper.getRetrieveFileTransferFactoryId(HTTPS);
+ if (providerId != null) {
+ mapper.removeRetrieveFileTransferFactory(providerId);
+ }
+ // Remove send provider
+ providerId = mapper.getSendFileTransferFactoryId(HTTPS);
+ if (providerId != null) {
+ mapper.removeSendFileTransferFactory(providerId);
+ }
+ }
+ }
+
+ /**
+ * Gets the singleton ServiceTracker for the IRetrieveFileTransferFactory and starts the bundles
+ * "org.eclipse.ecf" and
+ * "org.eclipse.ecf.provider.filetransfer" on first call.
+ * @return ServiceTracker
+ */
+ private synchronized ServiceTracker<IRetrieveFileTransferFactory, IRetrieveFileTransferFactory> getFileTransferServiceTracker() {
+ if (retrievalFactoryTracker == null) {
+ retrievalFactoryTracker = new ServiceTracker<IRetrieveFileTransferFactory, IRetrieveFileTransferFactory>(Activator.getContext(), IRetrieveFileTransferFactory.class, null);
+ retrievalFactoryTracker.open();
+ startBundle("org.eclipse.ecf"); //$NON-NLS-1$
+ startBundle("org.eclipse.ecf.provider.filetransfer"); //$NON-NLS-1$
+ }
+ return retrievalFactoryTracker;
+ }
+
+ private IFileTransferProtocolToFactoryMapper getProtocolToFactoryMapper() {
+ if (protocolToFactoryMapperTracker == null) {
+ protocolToFactoryMapperTracker = new ServiceTracker<IFileTransferProtocolToFactoryMapper, IFileTransferProtocolToFactoryMapper>(context, IFileTransferProtocolToFactoryMapper.class, null);
+ protocolToFactoryMapperTracker.open();
+ }
+ return protocolToFactoryMapperTracker.getService();
+ }
+
+ private boolean startBundle(String bundleId) {
+ PackageAdmin packageAdmin = (PackageAdmin) ServiceHelper.getService(Activator.getContext(), PackageAdmin.class.getName());
+ if (packageAdmin == null)
+ return false;
+
+ Bundle[] bundles = packageAdmin.getBundles(bundleId, null);
+ if (bundles != null && bundles.length > 0) {
+ for (int i = 0; i < bundles.length; i++) {
+ try {
+ if ((bundles[i].getState() & Bundle.INSTALLED) == 0) {
+ bundles[i].start(Bundle.START_ACTIVATION_POLICY);
+ bundles[i].start(Bundle.START_TRANSIENT);
+ return true;
+ }
+ } catch (BundleException e) {
+ // failed, try next bundle
+ }
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/ECFTransportComponent.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/ECFTransportComponent.java
new file mode 100644
index 000000000..18a391407
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/ECFTransportComponent.java
@@ -0,0 +1,13 @@
+package org.eclipse.equinox.internal.p2.transport.ecf;
+
+import org.eclipse.equinox.p2.core.IProvisioningAgent;
+import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory;
+
+public class ECFTransportComponent implements IAgentServiceFactory {
+
+ @Override
+ public Object createService(IProvisioningAgent agent) {
+ return new RepositoryTransport();
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java
new file mode 100644
index 000000000..2fc0afb28
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 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
+ ******************************************************************************/
+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.Activator;
+import org.eclipse.equinox.internal.p2.repository.AuthenticationFailedException;
+import org.eclipse.equinox.internal.p2.repository.JREHttpClientRequiredException;
+import org.eclipse.equinox.internal.p2.repository.Messages;
+import org.eclipse.equinox.internal.p2.repository.RepositoryPreferences;
+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;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ 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();
+ }
+
+ 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.fromMessage(Messages.ecf_configuration_error);
+ }
+
+ IRemoteFileSystemBrowserContainerAdapter adapter = (IRemoteFileSystemBrowserContainerAdapter) 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;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java
new file mode 100644
index 000000000..d1760c6a8
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java
@@ -0,0 +1,509 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 Cloudsmith Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc - initial API and implementation
+ * IBM Corporation - ongoing development
+ * Sonatype Inc - ongoing development
+ ******************************************************************************/
+package org.eclipse.equinox.internal.p2.transport.ecf;
+
+import java.io.*;
+import java.net.SocketTimeoutException;
+import java.net.URI;
+import java.util.Date;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+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.equinox.internal.p2.repository.AuthenticationFailedException;
+import org.eclipse.equinox.internal.p2.repository.FileInfo;
+import org.eclipse.equinox.internal.p2.repository.JREHttpClientRequiredException;
+import org.eclipse.equinox.internal.p2.repository.Messages;
+import org.eclipse.equinox.internal.p2.repository.ProgressStatistics;
+import org.eclipse.equinox.internal.p2.repository.RepositoryPreferences;
+import org.eclipse.equinox.internal.p2.repository.RepositoryTracing;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * FileReader is an ECF FileTransferJob implementation.
+ */
+public final class FileReader extends FileTransferJob implements IFileTransferListener {
+ /**
+ * Class used to suppress warnings about a job being blocked by another job.
+ * Since we are running a job that will always be blocked by another job that
+ * is actually performing the transfer, these messages are unnecessary and ugly.
+ */
+ static class SuppressBlockedMonitor extends SubProgressMonitor {
+ public SuppressBlockedMonitor(IProgressMonitor monitor, int ticks) {
+ super(monitor, ticks);
+ }
+
+ public void setBlocked(IStatus reason) {
+ //do nothing
+ }
+
+ public void clearBlocked() {
+ //do nothing
+ }
+ }
+
+ private static IFileReaderProbe testProbe;
+ private boolean closeStreamWhenFinished = false;
+ private Exception exception;
+ private FileInfo fileInfo;
+ private long lastProgressCount;
+ private long lastStatsCount;
+ protected IProgressMonitor theMonitor;
+ private OutputStream theOutputStream;
+ private ProgressStatistics statistics;
+ private final int connectionRetryCount;
+ private final long connectionRetryDelay;
+ private final IConnectContext connectContext;
+ private URI requestUri;
+ protected IFileTransferConnectStartEvent connectEvent;
+ private Job cancelJob;
+ private boolean monitorStarted;
+
+ /**
+ * 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;
+ }
+
+ /**
+ * A job to handle cancelation when trying to establish a socket connection.
+ * At this point we don't have a transfer job running yet, so we need a separate
+ * job to monitor for cancelation.
+ */
+ protected class CancelHandler extends Job {
+ private boolean done = false;
+
+ protected CancelHandler() {
+ super(Messages.FileTransport_cancelCheck);
+ setSystem(true);
+ }
+
+ public IStatus run(IProgressMonitor jobMonitor) {
+ while (!done && !jobMonitor.isCanceled()) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ return Status.CANCEL_STATUS;
+ }
+ if (theMonitor != null && theMonitor.isCanceled())
+ if (connectEvent != null)
+ connectEvent.cancel();
+ }
+ return Status.OK_STATUS;
+ }
+
+ protected void canceling() {
+ //wake up from sleep in run method
+ Thread t = getThread();
+ if (t != null)
+ t.interrupt();
+ }
+
+ }
+
+ public synchronized void handleTransferEvent(IFileTransferEvent event) {
+ if (event instanceof IFileTransferConnectStartEvent) {
+ // keep the connect event to be able to cancel the transfer
+ connectEvent = (IFileTransferConnectStartEvent) event;
+ cancelJob = new CancelHandler();
+ //schedule with a delay to avoid the overhead of an extra job on a fast connection
+ cancelJob.schedule(500);
+ } else if (event instanceof IIncomingFileTransferReceiveStartEvent) {
+ //we no longer need the cancel handler because we are about to fork the transfer job
+ if (cancelJob != null)
+ cancelJob.cancel();
+ 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;
+ }
+ long fileLength = source.getFileLength();
+ ProgressStatistics stats = new ProgressStatistics(requestUri, source.getRemoteFileName(), fileLength);
+ setStatistics(stats);
+
+ if (theMonitor != null) {
+ theMonitor.beginTask(null, 1000);
+ monitorStarted = true;
+ theMonitor.subTask(stats.report());
+ lastStatsCount = 0;
+ lastProgressCount = 0;
+ }
+ onStart(source);
+ } 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;
+ ProgressStatistics stats = getStatistics();
+ if (stats != null) {
+ stats.increase(count);
+ fileInfo.setAverageSpeed(stats.getAverageSpeed());
+ if (stats.shouldReport()) {
+ count = br - lastProgressCount;
+ lastProgressCount = br;
+ theMonitor.subTask(stats.report());
+ theMonitor.worked((int) (1000 * count / stats.getTotal()));
+ }
+ }
+ }
+ onData(source);
+ } else if (event instanceof IIncomingFileTransferReceiveDoneEvent) {
+ if (closeStreamWhenFinished)
+ hardClose(theOutputStream);
+
+ if (exception == null)
+ exception = ((IIncomingFileTransferReceiveDoneEvent) event).getException();
+ onDone(((IIncomingFileTransferReceiveDoneEvent) event).getSource());
+ }
+ }
+
+ public InputStream read(URI url, final IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
+ 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$
+
+ sendRetrieveRequest(url, output, null, true, monitor);
+
+ return new InputStream() {
+ public int available() throws IOException {
+ checkException();
+ return input.available();
+ }
+
+ public void close() throws IOException {
+ 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 {
+ if (t instanceof UserCancelledException) {
+ Throwable cause = t;
+ t = new OperationCanceledException(t.getMessage());
+ t.initCause(cause);
+ }
+ e = new IOException(t.getMessage());
+ e.initCause(t);
+ }
+ throw e;
+ }
+ };
+ }
+
+ public void readInto(URI uri, OutputStream anOutputStream, IProgressMonitor monitor) //
+ throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
+ readInto(uri, anOutputStream, -1, monitor);
+ }
+
+ public boolean belongsTo(Object family) {
+ return family == this;
+ }
+
+ public void readInto(URI uri, OutputStream anOutputStream, long startPos, IProgressMonitor monitor) //
+ throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
+ if (monitor == null)
+ monitor = new NullProgressMonitor();
+ try {
+ sendRetrieveRequest(uri, anOutputStream, (startPos != -1 ? new DownloadRange(startPos) : null), false, monitor);
+ Job.getJobManager().join(this, new SuppressBlockedMonitor(monitor, 0));
+ if (monitor.isCanceled() && connectEvent != null)
+ connectEvent.cancel();
+ // check and throw exception if received in callback
+ checkException(uri, connectionRetryCount);
+ } catch (InterruptedException e) {
+ monitor.setCanceled(true);
+ throw new OperationCanceledException();
+ } finally {
+ // kill the cancelJob, if there is one
+ if (cancelJob != null) {
+ cancelJob.cancel();
+ cancelJob = null;
+ }
+ // If monitor was never started, make sure it is balanced
+ if (!monitorStarted)
+ monitor.beginTask(null, 1);
+ monitorStarted = false;
+ monitor.done();
+ }
+ }
+
+ protected void sendRetrieveRequest(URI uri, OutputStream outputStream, DownloadRange range, boolean closeStreamOnFinish, //
+ IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
+
+ 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.monitorStarted = false;
+ this.theOutputStream = outputStream;
+ this.requestUri = uri;
+
+ for (int retryCount = 0;; retryCount++) {
+ if (monitor != null && monitor.isCanceled())
+ throw new OperationCanceledException();
+
+ try {
+ IFileID fileID = FileIDFactory.getDefault().createFileID(adapter.getRetrieveNamespace(), uri.toString());
+ 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();
+ }
+ if (checkException(uri, retryCount))
+ break;
+ }
+ }
+
+ /**
+ * 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
+ */
+ 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 an '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);
+
+ // not meaningful to try 'timeout again' - if a server is that busy, we
+ // need to wait for quite some time before retrying- it is not likely it is
+ // just a temporary network thing.
+ if (t instanceof SocketTimeoutException)
+ throw RepositoryStatusHelper.wrap(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;
+ }
+
+ 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 */
+ }
+ }
+ }
+
+ 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);
+ }
+
+ /**
+ * Sets a testing probe that can intercept events on the file reader for testing purposes.
+ * This method should only ever be called from automated test suites.
+ */
+ public static void setTestProbe(IFileReaderProbe probe) {
+ testProbe = probe;
+ }
+
+ /**
+ * Sets the progress statistics. This method is synchronized because the field
+ * is accessed from both the transfer thread and the thread initiating the transfer
+ * and we need to ensure field values are consistent across threads.
+ *
+ * @param statistics the statistics to set, or <code>null</code>
+ */
+ private synchronized void setStatistics(ProgressStatistics statistics) {
+ this.statistics = statistics;
+ }
+
+ /**
+ * Returns the progress statistics. This method is synchronized because the field
+ * is accessed from both the transfer thread and the thread initiating the transfer
+ * and we need to ensure field values are consistent across threads.
+ *
+ * @return the statistics, or <code>null</code>
+ */
+ private synchronized ProgressStatistics getStatistics() {
+ return statistics;
+ }
+
+ /**
+ * An interface to allow automated tests to hook into file reader events
+ * @see #setTestProbe
+ */
+ 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.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Messages.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Messages.java
new file mode 100644
index 000000000..8d51aaac3
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Messages.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Cloudsmith Inc - additional messages
+ * Sonatype Inc - ongoing development
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.transport.ecf;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.equinox.internal.p2.repository.messages"; //$NON-NLS-1$
+
+ public static String CacheManager_AuthenticationFaileFor_0;
+ public static String CacheManager_FailedCommunicationWithRepo_0;
+ public static String CacheManager_Neither_0_nor_1_found;
+ public static String CacheManage_ErrorRenamingCache;
+
+ public static String artifact_not_found;
+ public static String io_failedRead;
+ public static String ecf_configuration_error;
+ public static String repoMan_internalError;
+ public static String repo_loading;
+
+ public static String exception_malformedRepoURI;
+ public static String TransportErrorTranslator_400;
+ public static String TransportErrorTranslator_401;
+ public static String TransportErrorTranslator_402;
+ public static String TransportErrorTranslator_403;
+ public static String TransportErrorTranslator_404;
+ public static String TransportErrorTranslator_405;
+ public static String TransportErrorTranslator_406;
+ public static String TransportErrorTranslator_407;
+ public static String TransportErrorTranslator_408;
+ public static String TransportErrorTranslator_409;
+ public static String TransportErrorTranslator_410;
+ public static String TransportErrorTranslator_411;
+ public static String TransportErrorTranslator_412;
+ public static String TransportErrorTranslator_413;
+ public static String TransportErrorTranslator_414;
+ public static String TransportErrorTranslator_415;
+ public static String TransportErrorTranslator_416;
+ public static String TransportErrorTranslator_417;
+ public static String TransportErrorTranslator_418;
+ public static String TransportErrorTranslator_422;
+ public static String TransportErrorTranslator_423;
+ public static String TransportErrorTranslator_424;
+ public static String TransportErrorTranslator_425;
+ public static String TransportErrorTranslator_426;
+ public static String TransportErrorTranslator_449;
+ public static String TransportErrorTranslator_450;
+ public static String TransportErrorTranslator_500;
+ public static String TransportErrorTranslator_501;
+ public static String TransportErrorTranslator_502;
+ public static String TransportErrorTranslator_503;
+ public static String TransportErrorTranslator_504;
+ public static String TransportErrorTranslator_505;
+ public static String TransportErrorTranslator_506;
+ public static String TransportErrorTranslator_507;
+ public static String TransportErrorTranslator_508;
+ public static String TransportErrorTranslator_510;
+ public static String TransportErrorTranslator_MalformedRemoteFileReference;
+ public static String TransportErrorTranslator_UnableToConnectToRepository_0;
+
+ public static String TransportErrorTranslator_UnknownErrorCode;
+ public static String TransportErrorTranslator_UnknownHost;
+
+ public static String fetching_0_from_1_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;
+ public static String FileTransport_cancelCheck;
+
+ public static String UnableToRead_0_TooManyAttempts;
+ public static String UnableToRead_0_UserCanceled;
+
+ public static String RepositoryTransport_failedReadRepo;
+
+ static {
+ // initialize resource bundles
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ // Do not instantiate
+ }
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryStatus.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryStatus.java
new file mode 100644
index 000000000..ccc465939
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryStatus.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.p2.transport.ecf;
+
+import org.eclipse.equinox.internal.p2.repository.DownloadStatus;
+import org.eclipse.equinox.p2.core.ProvisionException;
+
+import java.io.FileNotFoundException;
+import java.net.*;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.ecf.core.identity.IDCreateException;
+import org.eclipse.ecf.filetransfer.BrowseFileTransferException;
+import org.eclipse.ecf.filetransfer.IncomingFileTransferException;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Utility class to transform transport errors into error messages.
+ *
+ */
+public class RepositoryStatus {
+
+ public static String codeToMessage(int code, String toDownload) {
+ switch (code) {
+ case 400 :
+ return NLS.bind(Messages.TransportErrorTranslator_400, toDownload);
+ case 401 :
+ return NLS.bind(Messages.TransportErrorTranslator_401, toDownload);
+ case 402 :
+ return NLS.bind(Messages.TransportErrorTranslator_402, toDownload);
+ case 403 :
+ return NLS.bind(Messages.TransportErrorTranslator_403, toDownload);
+ case 404 :
+ return NLS.bind(Messages.TransportErrorTranslator_404, toDownload);
+ case 405 :
+ return NLS.bind(Messages.TransportErrorTranslator_405, toDownload);
+ case 406 :
+ return NLS.bind(Messages.TransportErrorTranslator_406, toDownload);
+ case 407 :
+ return NLS.bind(Messages.TransportErrorTranslator_407, toDownload);
+ case 408 :
+ return NLS.bind(Messages.TransportErrorTranslator_408, toDownload);
+ case 409 :
+ return NLS.bind(Messages.TransportErrorTranslator_409, toDownload);
+ case 410 :
+ return NLS.bind(Messages.TransportErrorTranslator_410, toDownload);
+ case 411 :
+ return NLS.bind(Messages.TransportErrorTranslator_411, toDownload);
+ case 412 :
+ return NLS.bind(Messages.TransportErrorTranslator_412, toDownload);
+ case 413 :
+ return NLS.bind(Messages.TransportErrorTranslator_413, toDownload);
+ case 414 :
+ return NLS.bind(Messages.TransportErrorTranslator_414, toDownload);
+ case 415 :
+ return NLS.bind(Messages.TransportErrorTranslator_415, toDownload);
+ case 416 :
+ return NLS.bind(Messages.TransportErrorTranslator_416, toDownload);
+ case 417 :
+ return NLS.bind(Messages.TransportErrorTranslator_417, toDownload);
+ case 418 :
+ return NLS.bind(Messages.TransportErrorTranslator_418, toDownload);
+ case 422 :
+ return NLS.bind(Messages.TransportErrorTranslator_422, toDownload);
+ case 423 :
+ return NLS.bind(Messages.TransportErrorTranslator_423, toDownload);
+ case 424 :
+ return NLS.bind(Messages.TransportErrorTranslator_424, toDownload);
+ case 425 :
+ return NLS.bind(Messages.TransportErrorTranslator_425, toDownload);
+ case 426 :
+ return NLS.bind(Messages.TransportErrorTranslator_426, toDownload);
+ case 449 :
+ return NLS.bind(Messages.TransportErrorTranslator_449, toDownload);
+ case 450 :
+ return NLS.bind(Messages.TransportErrorTranslator_450, toDownload);
+
+ case 500 :
+ return NLS.bind(Messages.TransportErrorTranslator_500, toDownload);
+ case 501 :
+ return NLS.bind(Messages.TransportErrorTranslator_501, toDownload);
+ case 502 :
+ return NLS.bind(Messages.TransportErrorTranslator_502, toDownload);
+ case 503 :
+ return NLS.bind(Messages.TransportErrorTranslator_503, toDownload);
+ case 504 :
+ return NLS.bind(Messages.TransportErrorTranslator_504, toDownload);
+ case 505 :
+ return NLS.bind(Messages.TransportErrorTranslator_505, toDownload);
+ case 506 :
+ return NLS.bind(Messages.TransportErrorTranslator_506, toDownload);
+ case 507 :
+ return NLS.bind(Messages.TransportErrorTranslator_507, toDownload);
+ case 508 :
+ return NLS.bind(Messages.TransportErrorTranslator_508, toDownload);
+ case 510 :
+ return NLS.bind(Messages.TransportErrorTranslator_510, toDownload);
+
+ default :
+ return NLS.bind(Messages.TransportErrorTranslator_UnknownErrorCode, Integer.toString(code), toDownload);
+ }
+ }
+
+ public static DownloadStatus forStatus(IStatus original, URI toDownload) {
+ Throwable t = original.getException();
+ return forException(t, toDownload);
+ }
+
+ public static DownloadStatus forException(Throwable t, URI toDownload) {
+ if (t instanceof FileNotFoundException || (t instanceof IncomingFileTransferException && ((IncomingFileTransferException) t).getErrorCode() == 404))
+ 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 DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, NLS.bind(Messages.TransportErrorTranslator_UnableToConnectToRepository_0, toDownload), t);
+ if (t instanceof UnknownHostException)
+ 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 DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.TransportErrorTranslator_MalformedRemoteFileReference, toDownload), t);
+ }
+ int code = 0;
+
+ // default to report as read repository error
+ int provisionCode = ProvisionException.REPOSITORY_FAILED_READ;
+
+ if (t instanceof IncomingFileTransferException)
+ code = ((IncomingFileTransferException) t).getErrorCode();
+ else if (t instanceof BrowseFileTransferException)
+ code = ((BrowseFileTransferException) t).getErrorCode();
+
+ // Switch on error codes in the HTTP error code range.
+ // Note that 404 uses ARTIFACT_NOT_FOUND (as opposed to REPOSITORY_NOT_FOUND, which
+ // is determined higher up in the calling chain).
+ if (code == 401)
+ provisionCode = ProvisionException.REPOSITORY_FAILED_AUTHENTICATION;
+ else if (code == 404)
+ provisionCode = ProvisionException.ARTIFACT_NOT_FOUND;
+
+ // Add more specific translation here
+
+ return new DownloadStatus(IStatus.ERROR, Activator.ID, provisionCode, //
+ code == 0 ? NLS.bind(Messages.io_failedRead, toDownload) //
+ : codeToMessage(code, toDownload.toString()), t);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryStatusHelper.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryStatusHelper.java
new file mode 100644
index 000000000..f2fb848f7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryStatusHelper.java
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc, and other.
+ * The code, documentation and other materials contained herein have been
+ * licensed under the Eclipse Public License - v 1.0 by the individual
+ * copyright holders listed above, as Initial Contributors under such license.
+ * The text of such license is available at www.eclipse.org.
+ * Contributors:
+ * Cloudsmith Inc. - Initial API and implementation
+ * IBM Corporation - Original Implementation of checkPermissionDenied
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.transport.ecf;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ecf.filetransfer.BrowseFileTransferException;
+import org.eclipse.ecf.filetransfer.IncomingFileTransferException;
+import org.eclipse.equinox.internal.p2.repository.AuthenticationFailedException;
+import org.eclipse.equinox.internal.p2.repository.JREHttpClientRequiredException;
+import org.eclipse.equinox.p2.core.ProvisionException;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * RepositoryStatusHelper is a utility class for processing of exceptions and status.
+ */
+public abstract class RepositoryStatusHelper {
+
+ private static final long serialVersionUID = 1L;
+ protected static final String SERVER_REDIRECT = "Server redirected too many times"; //$NON-NLS-1$
+
+ public static IStatus createStatus(String nlsMessage, Object arg) {
+ return createExceptionStatus(null, nlsMessage, new Object[] {arg});
+ }
+
+ public static IStatus createStatus(String nlsMessage, Object arg1, Object arg2) {
+ return createExceptionStatus(null, nlsMessage, new Object[] {arg1, arg2});
+ }
+
+ public static IStatus createStatus(String nlsMessage, Object arg1, Object arg2, Object arg3) {
+ return createExceptionStatus(null, nlsMessage, new Object[] {arg1, arg2, arg3});
+ }
+
+ public static IStatus createStatus(String nlsMessage, Object[] args) {
+ return createExceptionStatus(null, nlsMessage, args);
+ }
+
+ public static IStatus createStatus(String nlsMessage) {
+ return createExceptionStatus(null, nlsMessage, new Object[] {});
+ }
+
+ public static IStatus createExceptionStatus(Throwable cause) {
+ return (cause instanceof CoreException) ? ((CoreException) cause).getStatus() : new Status(IStatus.ERROR, Activator.ID, IStatus.OK, cause.getMessage(), cause);
+ }
+
+ public static IStatus createExceptionStatus(Throwable cause, String nlsMessage, Object[] args) {
+ if (args != null && args.length > 0)
+ nlsMessage = NLS.bind(nlsMessage, args);
+ return new Status(IStatus.ERROR, Activator.ID, IStatus.OK, nlsMessage, cause);
+ }
+
+ public static IStatus createExceptionStatus(Throwable cause, String nlsMessage, Object arg1, Object arg2, Object arg3) {
+ return createExceptionStatus(cause, nlsMessage, new Object[] {arg1, arg2, arg3});
+ }
+
+ public static IStatus createExceptionStatus(Throwable cause, String nlsMessage, Object arg1, Object arg2) {
+ return createExceptionStatus(cause, nlsMessage, new Object[] {arg1, arg2});
+ }
+
+ public static IStatus createExceptionStatus(Throwable cause, String nlsMessage, Object arg1) {
+ return createExceptionStatus(cause, nlsMessage, new Object[] {arg1});
+ }
+
+ public static IStatus createExceptionStatus(Throwable cause, String nlsMessage) {
+ return createExceptionStatus(cause, nlsMessage, new Object[] {});
+ }
+
+ public static void deeplyPrint(Throwable e, PrintStream strm, boolean stackTrace) {
+ deeplyPrint(e, strm, stackTrace, 0);
+ }
+
+ public static CoreException fromMessage(String nlsMessage, Object[] args) {
+ return fromExceptionMessage(null, nlsMessage, args);
+ }
+
+ public static CoreException fromMessage(String nlsMessage, Object arg1) {
+ return fromExceptionMessage(null, nlsMessage, new Object[] {arg1});
+ }
+
+ public static CoreException fromMessage(String nlsMessage, Object arg1, Object arg2) {
+ return fromExceptionMessage(null, nlsMessage, new Object[] {arg1, arg2});
+ }
+
+ public static CoreException fromMessage(String nlsMessage, Object arg1, Object arg2, Object arg3) {
+ return fromExceptionMessage(null, nlsMessage, new Object[] {arg1, arg2, arg3});
+ }
+
+ public static CoreException fromMessage(String nlsMessage) {
+ return fromExceptionMessage(null, nlsMessage, new Object[] {});
+ }
+
+ public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage, Object[] args) {
+ CoreException ce = new CoreException(createExceptionStatus(cause, nlsMessage, args));
+ if (cause != null)
+ ce.initCause(cause);
+ return ce;
+ }
+
+ public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage, Object arg1, Object arg2, Object arg3) {
+ return fromExceptionMessage(cause, nlsMessage, new Object[] {arg1, arg2, arg3});
+ }
+
+ public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage, Object arg1, Object arg2) {
+ return fromExceptionMessage(cause, nlsMessage, new Object[] {arg1, arg2});
+ }
+
+ public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage, Object arg1) {
+ return fromExceptionMessage(cause, nlsMessage, new Object[] {arg1});
+ }
+
+ public static CoreException fromExceptionMessage(Throwable cause, String nlsMessage) {
+ return fromExceptionMessage(cause, nlsMessage, new Object[] {});
+ }
+
+ public static Throwable unwind(Throwable t) {
+ for (;;) {
+ Class<? extends Throwable> tc = t.getClass();
+
+ // We don't use instanceof operator since we want
+ // the explicit class, not subclasses.
+ //
+ if (tc != RuntimeException.class && tc != InvocationTargetException.class && tc != IOException.class)
+ break;
+
+ Throwable cause = t.getCause();
+ if (cause == null)
+ break;
+
+ String msg = t.getMessage();
+ if (msg != null && !msg.equals(cause.toString()))
+ break;
+
+ t = cause;
+ }
+ return t;
+ }
+
+ public static CoreException unwindCoreException(CoreException exception) {
+ IStatus status = exception.getStatus();
+ while (status != null && status.getException() instanceof CoreException) {
+ exception = (CoreException) status.getException();
+ status = exception.getStatus();
+ }
+ return exception;
+ }
+
+ public static CoreException wrap(IStatus status) {
+ CoreException e = new CoreException(status);
+ Throwable t = status.getException();
+ if (t != null)
+ e.initCause(t);
+ return e;
+ }
+
+ public static CoreException wrap(Throwable t) {
+ t = unwind(t);
+ if (t instanceof CoreException)
+ return unwindCoreException((CoreException) t);
+
+ if (t instanceof OperationCanceledException || t instanceof InterruptedException)
+ return new CoreException(Status.CANCEL_STATUS);
+
+ String msg = t.toString();
+ return fromExceptionMessage(t, msg);
+ }
+
+ private static void appendLevelString(PrintStream strm, int level) {
+ if (level > 0) {
+ strm.print("[0"); //$NON-NLS-1$
+ for (int idx = 1; idx < level; ++idx) {
+ strm.print('.');
+ strm.print(level);
+ }
+ strm.print(']');
+ }
+ }
+
+ private static void deeplyPrint(CoreException ce, PrintStream strm, boolean stackTrace, int level) {
+ appendLevelString(strm, level);
+ if (stackTrace)
+ ce.printStackTrace(strm);
+ deeplyPrint(ce.getStatus(), strm, stackTrace, level);
+ }
+
+ private static void deeplyPrint(IStatus status, PrintStream strm, boolean stackTrace, int level) {
+ appendLevelString(strm, level);
+ String msg = status.getMessage();
+ strm.println(msg);
+ Throwable cause = status.getException();
+ if (cause != null) {
+ strm.print("Caused by: "); //$NON-NLS-1$
+ if (stackTrace || !(msg.equals(cause.getMessage()) || msg.equals(cause.toString())))
+ deeplyPrint(cause, strm, stackTrace, level);
+ }
+
+ if (status.isMultiStatus()) {
+ IStatus[] children = status.getChildren();
+ for (int i = 0; i < children.length; i++)
+ deeplyPrint(children[i], strm, stackTrace, level + 1);
+ }
+ }
+
+ private static void deeplyPrint(Throwable t, PrintStream strm, boolean stackTrace, int level) {
+ if (t instanceof CoreException)
+ deeplyPrint((CoreException) t, strm, stackTrace, level);
+ else {
+ appendLevelString(strm, level);
+ if (stackTrace)
+ t.printStackTrace(strm);
+ else {
+ strm.println(t.toString());
+ Throwable cause = t.getCause();
+ if (cause != null) {
+ strm.print("Caused by: "); //$NON-NLS-1$
+ deeplyPrint(cause, strm, stackTrace, level);
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if the given exception represents that a switch to the JRE HTTP Client
+ * is required. ECF sets the HTTP status code 477 to indicate this.
+ * If the JRE HTTP client is required a JREHttpClientRequiredException is thrown.
+ */
+ public static void checkJREHttpClientRequired(Throwable t) throws JREHttpClientRequiredException {
+ if (t instanceof IncomingFileTransferException) {
+ if (((IncomingFileTransferException) t).getErrorCode() == 477)
+ throw new JREHttpClientRequiredException();
+ } else if (t instanceof BrowseFileTransferException) {
+ if (((BrowseFileTransferException) t).getErrorCode() == 477)
+ throw new JREHttpClientRequiredException();
+ }
+
+ }
+
+ /**
+ * Check if the given exception represents a permission failure (401 for HTTP),
+ * and throw a AuthenticationFailedException if a permission failure was encountered.
+ */
+ public static void checkPermissionDenied(Throwable t) throws AuthenticationFailedException {
+ // From Use of File Transfer
+ if (t instanceof IncomingFileTransferException) {
+ if (((IncomingFileTransferException) t).getErrorCode() == 401)
+ throw new AuthenticationFailedException();
+ IStatus status = ((IncomingFileTransferException) t).getStatus();
+ t = status == null ? t : status.getException();
+ // From Use of Browse
+ } else if (t instanceof BrowseFileTransferException) {
+ if (((BrowseFileTransferException) t).getErrorCode() == 401)
+ throw new AuthenticationFailedException();
+ IStatus status = ((BrowseFileTransferException) t).getStatus();
+ t = status == null ? t : status.getException();
+ }
+
+ if (t == null || !(t instanceof IOException))
+ return;
+
+ // TODO: is this needed (for 401) now that ECF throws exceptions with codes?
+ // try to figure out if we have a 401 by parsing the exception message
+ // There is unfortunately no specific (general) exception for "redirected too many times" - which is commonly
+ // caused by a failed login. The message and exception are different in different implementations
+ // of http client.
+ String m = t.getMessage();
+ if (m != null && (m.indexOf(" 401 ") != -1 || m.indexOf(SERVER_REDIRECT) != -1)) //$NON-NLS-1$
+ throw new AuthenticationFailedException();
+ if ("org.apache.commons.httpclient.RedirectException".equals(t.getClass().getName())) //$NON-NLS-1$
+ throw new AuthenticationFailedException();
+ }
+
+ /**
+ * Translates exceptions representing "FileNotFound" into FileNotFoundException.
+ * @param t the throwable to check
+ * @param toDownload the URI the exception was thrown for
+ * @throws FileNotFoundException if 't' represents a file not found
+ */
+ public static void checkFileNotFound(Throwable t, URI toDownload) throws FileNotFoundException {
+ if (t instanceof IncomingFileTransferException) {
+ IncomingFileTransferException e = (IncomingFileTransferException) t;
+ if (e.getErrorCode() == 404 || e.getErrorCode() == 403 || e.getErrorCode() == 300)
+ throw new FileNotFoundException(toDownload.toString());
+ }
+ if (t instanceof BrowseFileTransferException) {
+ BrowseFileTransferException e = (BrowseFileTransferException) t;
+ if (e.getErrorCode() == 404 || e.getErrorCode() == 403 || e.getErrorCode() == 300)
+ throw new FileNotFoundException(toDownload.toString());
+ }
+
+ if (t instanceof FileNotFoundException)
+ throw (FileNotFoundException) t;
+ if (t instanceof CoreException) {
+ IStatus status = ((CoreException) t).getStatus();
+ Throwable e = status == null ? null : status.getException();
+ if (e instanceof FileNotFoundException)
+ throw (FileNotFoundException) e;
+ }
+ }
+
+ public static IStatus malformedAddressStatus(String address, Throwable t) {
+ return new Status(IStatus.ERROR, Activator.ID, //
+ ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.exception_malformedRepoURI, address), t);
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryTransport.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryTransport.java
new file mode 100644
index 000000000..b7d0b268c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/RepositoryTransport.java
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010, IBM Corporation and other.
+ * 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 API and implementation.
+ * Cloudsmith Inc - Implementation
+ ******************************************************************************/
+
+package org.eclipse.equinox.internal.p2.transport.ecf;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ConnectException;
+import java.net.URI;
+import java.net.UnknownHostException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ecf.core.identity.IDCreateException;
+import org.eclipse.ecf.core.security.ConnectContextFactory;
+import org.eclipse.ecf.core.security.IConnectContext;
+import org.eclipse.ecf.filetransfer.BrowseFileTransferException;
+import org.eclipse.ecf.filetransfer.IncomingFileTransferException;
+import org.eclipse.ecf.filetransfer.UserCancelledException;
+import org.eclipse.equinox.internal.p2.repository.AuthenticationFailedException;
+import org.eclipse.equinox.internal.p2.repository.Credentials;
+import org.eclipse.equinox.internal.p2.repository.Credentials.LoginCanceledException;
+import org.eclipse.equinox.internal.p2.repository.DownloadStatus;
+import org.eclipse.equinox.internal.p2.repository.FileInfo;
+import org.eclipse.equinox.internal.p2.repository.JREHttpClientRequiredException;
+import org.eclipse.equinox.internal.p2.repository.Messages;
+import org.eclipse.equinox.internal.p2.repository.RepositoryPreferences;
+import org.eclipse.equinox.internal.p2.repository.Transport;
+import org.eclipse.equinox.internal.provisional.p2.repository.IStateful;
+import org.eclipse.equinox.p2.core.IProvisioningAgent;
+import org.eclipse.equinox.p2.core.ProvisionException;
+import org.eclipse.equinox.p2.core.UIServices.AuthenticationInfo;
+import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * RepositoryTransport adapts p2 to ECF file download and file browsing.
+ * Download is performed by {@link FileReader}, and file browsing is performed by
+ * {@link FileInfoReader}.
+ */
+public class RepositoryTransport extends Transport implements IAgentServiceFactory {
+ private static RepositoryTransport instance;
+
+ /**
+ * Returns an shared instance of Generic Transport
+ */
+ // public static synchronized RepositoryTransport getInstance() {
+ // if (instance == null) {
+ // instance = new RepositoryTransport();
+ // }
+ // return instance;
+ // }
+
+ public IStatus download(URI toDownload, OutputStream target, long startPos, IProgressMonitor monitor) {
+
+ boolean promptUser = false;
+ boolean useJREHttp = 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
+ reader = new FileReader(context);
+ reader.readInto(toDownload, target, startPos, monitor);
+
+ // check that job ended ok - throw exceptions otherwise
+ IStatus result = reader.getResult();
+ if (result == null) {
+ String msg = NLS.bind(Messages.RepositoryTransport_failedReadRepo, toDownload);
+ DownloadStatus ds = new DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, msg, null);
+ return statusOn(target, ds, reader);
+ }
+ if (result.getSeverity() == IStatus.CANCEL)
+ throw new OperationCanceledException();
+ 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());
+ return statusOn(target, status, reader);
+ } catch (UserCancelledException e) {
+ 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) {
+ if (e.getStatus().getException() == null)
+ return statusOn(target, forException(e, toDownload), reader);
+ return statusOn(target, forStatus(e.getStatus(), toDownload), reader);
+ } catch (FileNotFoundException e) {
+ return statusOn(target, forException(e, toDownload), reader);
+ } catch (AuthenticationFailedException e) {
+ promptUser = true;
+ } catch (Credentials.LoginCanceledException e) {
+ DownloadStatus status = new DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_AUTHENTICATION, //
+ NLS.bind(Messages.UnableToRead_0_UserCanceled, toDownload), null);
+ return statusOn(target, status, null);
+ } catch (JREHttpClientRequiredException e) {
+ if (!useJREHttp) {
+ useJREHttp = true; // only do this once
+ i++; // need an extra retry
+ Activator.getDefault().useJREHttpClient();
+ }
+ }
+ }
+ // reached maximum number of retries without success
+ DownloadStatus status = new DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_AUTHENTICATION, //
+ NLS.bind(Messages.UnableToRead_0_TooManyAttempts, toDownload), null);
+ return statusOn(target, status, null);
+ }
+
+ public IStatus download(URI toDownload, OutputStream target, IProgressMonitor monitor) {
+ return download(toDownload, target, -1, monitor);
+ }
+
+ public InputStream stream(URI toDownload, IProgressMonitor monitor) throws FileNotFoundException, CoreException, AuthenticationFailedException {
+
+ boolean promptUser = false;
+ boolean useJREHttp = 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 streamed download
+ reader = new FileReader(context);
+ return reader.read(toDownload, monitor);
+ } catch (UserCancelledException e) {
+ throw new OperationCanceledException();
+ } catch (AuthenticationFailedException e) {
+ promptUser = true;
+ } catch (CoreException e) {
+ // must translate this core exception as it is most likely not informative to a user
+ if (e.getStatus().getException() == null)
+ throw new CoreException(RepositoryStatus.forException(e, toDownload));
+ throw new CoreException(RepositoryStatus.forStatus(e.getStatus(), toDownload));
+ } catch (LoginCanceledException e) {
+ // i.e. same behavior when user cancels as when failing n attempts.
+ throw new AuthenticationFailedException();
+ } catch (JREHttpClientRequiredException e) {
+ if (!useJREHttp) {
+ useJREHttp = true; // only do this once
+ i++; // need an extra retry
+ Activator.getDefault().useJREHttpClient();
+ }
+ }
+ }
+ throw new AuthenticationFailedException();
+ }
+
+ /**
+ * 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).
+ * @throws OperationCanceledException if the operation was canceled by the user.
+ * @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;
+ }
+
+ public long getLastModified(URI toDownload, IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ boolean promptUser = false;
+ boolean useJREHttp = false;
+ AuthenticationInfo loginDetails = null;
+ for (int i = RepositoryPreferences.getLoginRetryCount(); i > 0; i--) {
+ try {
+ loginDetails = Credentials.forLocation(toDownload, promptUser, loginDetails);
+ IConnectContext context = (loginDetails == null) ? null : ConnectContextFactory.createUsernamePasswordConnectContext(loginDetails.getUserName(), loginDetails.getPassword());
+ // get the remote info
+ FileInfoReader reader = new FileInfoReader(context);
+ return reader.getLastModified(toDownload, monitor);
+ } catch (UserCancelledException e) {
+ throw new OperationCanceledException();
+ } catch (CoreException e) {
+ // must translate this core exception as it is most likely not informative to a user
+ if (e.getStatus().getException() == null)
+ throw new CoreException(RepositoryStatus.forException(e, toDownload));
+ throw new CoreException(RepositoryStatus.forStatus(e.getStatus(), toDownload));
+ } catch (AuthenticationFailedException e) {
+ promptUser = true;
+ } catch (LoginCanceledException e) {
+ // same behavior as if user failed n attempts.
+ throw new AuthenticationFailedException();
+ } catch (JREHttpClientRequiredException e) {
+ if (!useJREHttp) {
+ useJREHttp = true; // only do this once
+ i++; // need an extra retry
+ Activator.getDefault().useJREHttpClient();
+ }
+ }
+
+ }
+ // reached maximum number of authentication retries without success
+ throw new AuthenticationFailedException();
+ }
+
+ public static DownloadStatus forStatus(IStatus original, URI toDownload) {
+ Throwable t = original.getException();
+ return forException(t, toDownload);
+ }
+
+ public static DownloadStatus forException(Throwable t, URI toDownload) {
+ if (t instanceof FileNotFoundException || (t instanceof IncomingFileTransferException && ((IncomingFileTransferException) t).getErrorCode() == 404))
+ 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 DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, NLS.bind(Messages.TransportErrorTranslator_UnableToConnectToRepository_0, toDownload), t);
+ if (t instanceof UnknownHostException)
+ 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 DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_INVALID_LOCATION, NLS.bind(Messages.TransportErrorTranslator_MalformedRemoteFileReference, toDownload), t);
+ }
+ int code = 0;
+
+ // default to report as read repository error
+ int provisionCode = ProvisionException.REPOSITORY_FAILED_READ;
+
+ if (t instanceof IncomingFileTransferException)
+ code = ((IncomingFileTransferException) t).getErrorCode();
+ else if (t instanceof BrowseFileTransferException)
+ code = ((BrowseFileTransferException) t).getErrorCode();
+
+ // Switch on error codes in the HTTP error code range.
+ // Note that 404 uses ARTIFACT_NOT_FOUND (as opposed to REPOSITORY_NOT_FOUND, which
+ // is determined higher up in the calling chain).
+ if (code == 401)
+ provisionCode = ProvisionException.REPOSITORY_FAILED_AUTHENTICATION;
+ else if (code == 404)
+ provisionCode = ProvisionException.ARTIFACT_NOT_FOUND;
+
+ // Add more specific translation here
+
+ return new DownloadStatus(IStatus.ERROR, Activator.ID, provisionCode, //
+ code == 0 ? NLS.bind(Messages.io_failedRead, toDownload) //
+ : RepositoryStatus.codeToMessage(code, toDownload.toString()), t);
+ }
+
+ @Override
+ public Object createService(IProvisioningAgent agent) {
+ if (instance == null)
+ return instance;
+ return instance;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/messages.properties b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/messages.properties
new file mode 100644
index 000000000..4e59267be
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/messages.properties
@@ -0,0 +1,76 @@
+###############################################################################
+# Copyright (c) 2007, 2010 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+# Cloudsmith Inc - additional messages
+# Sonatype Inc - ongoing implementation
+###############################################################################
+artifact_not_found=Artifact not found: {0}.
+
+io_failedRead=Unable to read repository at {0}.
+ecf_configuration_error=Transport initialization error.
+
+
+repoMan_internalError=Internal error.
+repo_loading = Loading the repository {0}
+
+CacheManager_Neither_0_nor_1_found=Neither {0} nor {1} found.
+CacheManager_AuthenticationFaileFor_0=Authentication failed for {0}.
+CacheManager_FailedCommunicationWithRepo_0=Communication with repository at {0} failed.
+CacheManage_ErrorRenamingCache=An error occurred while downloading {0}. The cache file {1} could not be renamed to {1}.
+
+exception_malformedRepoURI = The repository location ({0}) must be a URI.
+
+TransportErrorTranslator_400=Bad HTTP Request: {0}
+TransportErrorTranslator_401=Authentication Failed - Unauthorized: {0}
+TransportErrorTranslator_402=HTTP Payment Required: {0}
+TransportErrorTranslator_403=HTTP Access Forbidden: {0}
+TransportErrorTranslator_404=HTTP Remote File Not Found: {0}
+TransportErrorTranslator_405=HTTP Method Not Allowed: {0}
+TransportErrorTranslator_406=HTTP Request Not Acceptable: {0}
+TransportErrorTranslator_407=HTTP Proxy Authentication Required: {0}
+TransportErrorTranslator_408=HTTP Request Timeout: {0}
+TransportErrorTranslator_409=HTTP Conflict In Request: {0}
+TransportErrorTranslator_410=HTTP Remote File Permanently Removed: {0}
+TransportErrorTranslator_411=HTTP Length Required: {0}
+TransportErrorTranslator_412=HTTP Precondition Failed: {0}
+TransportErrorTranslator_413=HTTP Requested Entity Too Large: {0}
+TransportErrorTranslator_414=HTTP Request URI Too Long: {0}
+TransportErrorTranslator_415=HTTP Unsupported Media Type: {0}
+TransportErrorTranslator_416=HTTP Requested Range Not Satisfiable: {0}
+TransportErrorTranslator_417=HTTP Expectation Failed: {0}
+TransportErrorTranslator_418=HTTP Cannot provision coffee from a tea pot: {0}
+TransportErrorTranslator_422=HTTP (WebDav) Unprocessable Entity: {0}
+TransportErrorTranslator_423=HTTP (WebDAV) Locked: {0}
+TransportErrorTranslator_424=HTTP (WebDAV) Failed Dependency: {0}
+TransportErrorTranslator_425=HTTP Unordered Collection: {0}
+TransportErrorTranslator_426=HTTP Upgrade Required: {0}
+TransportErrorTranslator_449=HTTP Retry With Response: {0}
+TransportErrorTranslator_450=HTTP Blocked By Parental Control: {0}
+TransportErrorTranslator_500=HTTP Server ''Internal Error'': {0}
+TransportErrorTranslator_501=HTTP Server ''Not Implemented'': {0}
+TransportErrorTranslator_502=HTTP Server ''Bad Gateway'' : {0}
+TransportErrorTranslator_503=HTTP Server ''Service Unavailable'': {0}
+TransportErrorTranslator_504=HTTP Server ''Gateway Timeout'': {0}
+TransportErrorTranslator_505=HTTP Server ''HTTP Version Not Supported'': {0}
+TransportErrorTranslator_506=HTTP Server ''Variant Also Negotiates'': {0}
+TransportErrorTranslator_507=HTTP (WebDAV) ''Insufficient Storage'': {0}
+TransportErrorTranslator_508=HTTP Server ''Bandwidth Limit Exceeded'': {0}
+TransportErrorTranslator_510=HTTP Server ''Not Extended'': {0}
+TransportErrorTranslator_MalformedRemoteFileReference=Malformed reference to remote file: {0}
+TransportErrorTranslator_UnableToConnectToRepository_0=Unable to connect to repository {0}
+TransportErrorTranslator_UnknownErrorCode=HTTP Server Unknown HTTP Response Code ({0}):{1}
+TransportErrorTranslator_UnknownHost=Unknown Host: {0}
+fetching_0_from_1_2_at_3=Fetching {0} ({2} at {3}/s) from {1}
+fetching_0_from_1_2_of_3_at_4=Fetching {0} ({2} of {3} at {4}/s) from {1}
+FileTransport_reader=File Transport Reader
+FileTransport_cancelCheck=File Transport Cancel Handler
+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 login attempts.
+UnableToRead_0_UserCanceled=Unable to read repository at: {0}. Login canceled by user.
+RepositoryTransport_failedReadRepo=Error while reading from repository: {0}.

Back to the top