P2 should honor retry property on time outs for repository metadata

Bug: 520461
Signed-off-by: Laurent Goubet <laurent.goubet@obeo.fr>
Change-Id: Iaa96aea34026c6cd1c539f651ff3d6aef7ca168a
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/CacheManager.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/CacheManager.java
index 99b3e9f..5b5b1cf 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/CacheManager.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/CacheManager.java
@@ -27,6 +27,7 @@
 import org.eclipse.equinox.p2.core.IAgentLocation;
 import org.eclipse.equinox.p2.core.ProvisionException;
 import org.eclipse.equinox.p2.repository.IRepository;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
 import org.eclipse.osgi.util.NLS;
 
 /**
@@ -189,7 +190,7 @@
 			// bug 269588 - server may return 0 when file exists, so extra flag is needed
 			boolean useJar = true;
 			try {
-				lastModifiedRemote = transport.getLastModified(jarLocation, submonitor.newChild(1));
+				lastModifiedRemote = getLastModified(jarLocation, submonitor.newChild(1));
 				if (lastModifiedRemote <= 0)
 					LogHelper.log(new Status(IStatus.WARNING, Activator.ID, "Server returned lastModified <= 0 for " + jarLocation)); //$NON-NLS-1$
 			} catch (AuthenticationFailedException e) {
@@ -226,7 +227,7 @@
 				// (Status is reported based on finding the XML file as giving up on certain errors
 				// when checking for the jar may not be correct).
 				try {
-					lastModifiedRemote = transport.getLastModified(xmlLocation, submonitor.newChild(1));
+					lastModifiedRemote = getLastModified(xmlLocation, submonitor.newChild(1));
 					// if lastModifiedRemote is 0 - something is wrong in the communication stack, as 
 					// a FileNotFound exception should have been thrown.
 					// bug 269588 - server may return 0 when file exists - site is not correctly configured
@@ -267,6 +268,24 @@
 		}
 	}
 
+	private long getLastModified(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException {
+		CoreException exception = null;
+		long lastModifiedRemote = -1L;
+		do {
+			try {
+				lastModifiedRemote = transport.getLastModified(location, monitor);
+			} catch (CoreException e) {
+				if (e.getStatus() == null) {
+					throw e;
+				}
+				exception = e;
+			}
+		} while (exception != null && exception.getStatus() != null && exception.getStatus().getCode() == IArtifactRepository.CODE_RETRY);
+		if (exception != null)
+			throw exception;
+		return lastModifiedRemote;
+	}
+
 	/**
 	 * Deletes the local cache file(s) for the given repository
 	 * @param repositoryLocation
@@ -402,6 +421,9 @@
 		try {
 			submonitor.setWorkRemaining(1000);
 			result = transport.download(remoteFile, stream, submonitor.newChild(1000));
+			while (result.getCode() == IArtifactRepository.CODE_RETRY) {
+				result = transport.download(remoteFile, stream, submonitor.newChild(1000));
+			}
 		} catch (OperationCanceledException e) {
 			// need to pick up the status - a new operation canceled exception is thrown at the end
 			// as status will be CANCEL.
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/AbstractRepositoryManager.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/AbstractRepositoryManager.java
index 06a338c..81083d9 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/AbstractRepositoryManager.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/AbstractRepositoryManager.java
@@ -34,6 +34,7 @@
 import org.eclipse.equinox.p2.query.*;
 import org.eclipse.equinox.p2.repository.IRepository;
 import org.eclipse.equinox.p2.repository.IRepositoryManager;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
 import org.eclipse.equinox.security.storage.EncodingUtils;
 import org.eclipse.osgi.util.NLS;
 import org.osgi.service.prefs.BackingStoreException;
@@ -727,6 +728,9 @@
 		ByteArrayOutputStream index = new ByteArrayOutputStream();
 		IStatus indexFileStatus = null;
 		indexFileStatus = getTransport().download(indexFileURI, index, monitor);
+		while (indexFileStatus.getCode() == IArtifactRepository.CODE_RETRY) {
+			indexFileStatus = getTransport().download(indexFileURI, index, monitor);
+		}
 		if (indexFileStatus != null && indexFileStatus.isOK())
 			return LocationProperties.create(new ByteArrayInputStream(index.toByteArray()));
 		return LocationProperties.createEmptyIndexFile();
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF
index bbceef3..2bf8a89 100644
--- a/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.equinox.p2.transport.ecf
-Bundle-Version: 1.3.0.qualifier
+Bundle-Version: 1.3.100.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-11
 Require-Bundle: org.eclipse.ecf;bundle-version="3.1.0",
  org.eclipse.ecf.filetransfer;bundle-version="4.0.0",
diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml b/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml
index b6fbd62..6447c88 100644
--- a/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml
+++ b/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml
@@ -9,6 +9,6 @@
   </parent>
   <groupId>org.eclipse.equinox</groupId>
   <artifactId>org.eclipse.equinox.p2.transport.ecf</artifactId>
-  <version>1.3.0-SNAPSHOT</version>
+  <version>1.3.100-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 </project>
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
index ba13b7f..a705ddc 100644
--- 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
@@ -192,8 +192,8 @@
 				// 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));
+					throw new CoreException(forException(e, toDownload));
+				throw new CoreException(forStatus(e.getStatus(), toDownload));
 			} catch (LoginCanceledException e) {
 				// i.e. same behavior when user cancels as when failing n attempts.
 				throw new AuthenticationFailedException();
@@ -253,8 +253,8 @@
 				// 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));
+					throw new CoreException(forException(e, toDownload));
+				throw new CoreException(forStatus(e.getStatus(), toDownload));
 			} catch (AuthenticationFailedException e) {
 				promptUser = true;
 			} catch (LoginCanceledException e) {
@@ -278,6 +278,8 @@
 			return true;
 		else if (t instanceof SocketException)
 			return true;
+		else if (t instanceof IncomingFileTransferException && ((IncomingFileTransferException) t).getErrorCode() == 503)
+			return true;
 		return false;
 	}
 
@@ -308,7 +310,7 @@
 								retryCount = Integer.valueOf(alreadyRetryCount.intValue() + 1);
 							}
 						}
-						if (retryCount != null) {
+						if (retryCount != null && retryCount.intValue() <= retry) {
 							socketExceptionRetry.put(toDownload, retryCount);
 							return new DownloadStatus(IStatus.ERROR, Activator.ID, IArtifactRepository.CODE_RETRY,
 									NLS.bind(Messages.connection_to_0_failed_on_1_retry_attempt_2, new String[] {