fix 350014 to use a system property to set the retrying number if socket
timeout happened

Change-Id: I1bab6db693a3ee739e058bda1a4d00fbc8bd1eb8
diff --git a/bundles/org.eclipse.equinox.p2.tests/All p2 Tests.launch b/bundles/org.eclipse.equinox.p2.tests/All p2 Tests.launch
index e6bb76b..e28d160 100644
--- a/bundles/org.eclipse.equinox.p2.tests/All p2 Tests.launch
+++ b/bundles/org.eclipse.equinox.p2.tests/All p2 Tests.launch
@@ -57,7 +57,7 @@
 <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consolelog -console"/>
 <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.equinox.p2.tests"/>
 <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dorg.eclipse.equinox.p2.reconciler.tests.platform.archive=c:/dev/platform/zips/eclipse-platform-3.6M6-win32.zip&#13;&#10;-Dorg.eclipse.equinox.p2.reconciler.tests.35.platform.archive=c:/dev/platform/zips/eclipse-platform-3.5-win32.zip&#13;&#10;-Dorg.eclipse.equinox.p2.repository&#13;&#10;-Dorg.eclipse.equinox.p2.tests.current.build.repo=http://eclipsebuildserv/3.6-I-builds/&#13;&#13;&#10;-Xmx512m"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dorg.eclipse.equinox.p2.reconciler.tests.platform.archive=c:/dev/platform/zips/eclipse-platform-3.6M6-win32.zip&#13;&#10;-Dorg.eclipse.equinox.p2.reconciler.tests.35.platform.archive=c:/dev/platform/zips/eclipse-platform-3.5-win32.zip&#13;&#10;-Dorg.eclipse.equinox.p2.repository&#13;&#10;-Dorg.eclipse.equinox.p2.tests.current.build.repo=http://eclipsebuildserv/3.6-I-builds/&#13;&#13;&#10;-Xmx512m&#10;-Dorg.eclipse.ecf.provider.filetransfer.httpclient.browse.connectTimeout=10000&#10;-Dorg.eclipse.ecf.provider.filetransfer.httpclient.retrieve.connectTimeout=10000&#10;-Dorg.eclipse.ecf.provider.filetransfer.httpclient.retrieve.readTimeout=10000"/>
 <stringAttribute key="pde.version" value="3.3"/>
 <stringAttribute key="product" value="org.eclipse.sdk.ide"/>
 <booleanAttribute key="run_in_ui_thread" value="true"/>
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/AllTests.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/AllTests.java
index 74095af..7dade7b 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/AllTests.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/AllTests.java
@@ -35,6 +35,7 @@
 		suite.addTestSuite(MD5Tests.class);
 		suite.addTestSuite(MirrorSelectorTest.class);
 		suite.addTestSuite(MirrorRequestTest.class);
+		suite.addTestSuite(MirrorRequestTest2.class);
 		suite.addTestSuite(SimpleArtifactRepositoryTest.class);
 		suite.addTestSuite(TransferTest.class);
 		return suite;
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/MirrorRequestTest2.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/MirrorRequestTest2.java
new file mode 100644
index 0000000..0c9d67c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/MirrorRequestTest2.java
@@ -0,0 +1,157 @@
+package org.eclipse.equinox.p2.tests.artifact.repository;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import org.eclipse.core.runtime.*;
+import org.eclipse.equinox.internal.p2.artifact.repository.MirrorRequest;
+import org.eclipse.equinox.internal.p2.artifact.repository.MirrorSelector;
+import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
+import org.eclipse.equinox.internal.p2.repository.Transport;
+import org.eclipse.equinox.internal.p2.transport.ecf.RepositoryTransport;
+import org.eclipse.equinox.p2.core.ProvisionException;
+import org.eclipse.equinox.p2.metadata.IArtifactKey;
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.query.*;
+import org.eclipse.equinox.p2.repository.IRepository;
+import org.eclipse.equinox.p2.repository.artifact.*;
+import org.eclipse.equinox.p2.repository.artifact.spi.AbstractArtifactRepository;
+import org.eclipse.equinox.p2.tests.AbstractProvisioningTest;
+import org.eclipse.equinox.p2.tests.testserver.helper.AbstractTestServerClientCase;
+
+public class MirrorRequestTest2 extends AbstractTestServerClientCase {
+
+	private SimpleArtifactRepository sourceRepository;
+	private File targetLocation;
+	private SimpleArtifactRepository targetRepository;
+
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+		targetLocation = File.createTempFile("target", ".repo");
+		targetLocation.delete();
+		targetLocation.mkdirs();
+		targetRepository = new SimpleArtifactRepository(getAgent(), "TargetRepo", targetLocation.toURI(), null);
+
+		URI location = URI.create(getBaseURL() + "/mirrorrequest");
+
+		IArtifactRepositoryManager mgr = (IArtifactRepositoryManager) getAgent().getService(IArtifactRepositoryManager.SERVICE_NAME);
+		sourceRepository = (SimpleArtifactRepository) mgr.loadRepository(location, null);
+	}
+
+	public void tearDown() throws Exception {
+		IArtifactRepositoryManager mgr = (IArtifactRepositoryManager) getAgent().getService(IArtifactRepositoryManager.SERVICE_NAME);
+		mgr.removeRepository(targetLocation.toURI());
+		AbstractProvisioningTest.delete(targetLocation);
+		super.tearDown();
+	}
+
+	public void testRetryMirrorAfterTimeout() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
+		// call test
+		IArtifactKey key = new ArtifactKey("test.txt", "HelloWorldText", Version.parseVersion("1.0.0"));
+		MirrorRequest request = new MirrorRequest(key, targetRepository, null, null, (Transport) getAgent().getService(Transport.SERVICE_NAME));
+		MirrorRepo mirrorRepo = new MirrorRepo(sourceRepository);
+		Field field = sourceRepository.getClass().getDeclaredField("mirrors");
+		field.setAccessible(true);
+		field.set(sourceRepository, new MirrorSelector(mirrorRepo, (Transport) getAgent().getService(Transport.SERVICE_NAME)) {
+			private int count = 0;
+
+			@Override
+			public synchronized URI getMirrorLocation(URI inputLocation, IProgressMonitor monitor) {
+				if (count++ == 0) {
+					return inputLocation;
+				}
+				return URI.create(getBaseURL() + "/mirrorrequest/mirror-two/plugins/HelloWorldText_1.0.0.txt");
+			}
+
+			@Override
+			public synchronized boolean hasValidMirror() {
+				return true;
+			}
+		});
+
+		request.perform(mirrorRepo, new NullProgressMonitor());
+
+		// The download succeeded
+		assertTrue(request.getResult().toString(), request.getResult().isOK());
+	}
+
+	public void testTimeoutForgivableAfterTimeout() {
+		try {
+			System.setProperty(RepositoryTransport.TIMEOUT_RETRY, "4");
+			// call test
+			IArtifactKey key = new ArtifactKey("test.txt", "HelloWorldText", Version.parseVersion("1.0.0"));
+			MirrorRequest request = new MirrorRequest(key, targetRepository, null, null, (Transport) getAgent().getService(Transport.SERVICE_NAME));
+			request.perform(sourceRepository, new NullProgressMonitor());
+
+			// The download succeeded
+			assertTrue(request.getResult().toString(), request.getResult().isOK());
+		} finally {
+			System.clearProperty(RepositoryTransport.TIMEOUT_RETRY);
+		}
+	}
+
+	protected class MirrorRepo extends AbstractArtifactRepository {
+		SimpleArtifactRepository delegate;
+		int downloadCount = 0;
+
+		MirrorRepo(SimpleArtifactRepository repo) {
+			super(getAgent(), repo.getName(), repo.getType(), repo.getVersion(), repo.getLocation(), repo.getDescription(), repo.getProvider(), repo.getProperties());
+			delegate = repo;
+		}
+
+		@Override
+		public String getProperty(String key) {
+			return getProperties().get(key);
+		}
+
+		@Override
+		public synchronized Map<String, String> getProperties() {
+			Map<String, String> newProperties = new HashMap<String, String>(super.getProperties());
+			newProperties.put(IRepository.PROP_MIRRORS_URL, getBaseURL() + "/mirrorrequest/mirrors.xml");
+			newProperties.put(IRepository.PROP_MIRRORS_BASE_URL, getBaseURL() + "/mirrorrequest");
+			return newProperties;
+		}
+
+		public boolean contains(IArtifactDescriptor descriptor) {
+			return delegate.contains(descriptor);
+		}
+
+		public boolean contains(IArtifactKey key) {
+			return delegate.contains(key);
+		}
+
+		public IStatus getArtifact(IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) {
+			downloadCount++;
+			return delegate.getArtifact(descriptor, destination, monitor);
+		}
+
+		public IArtifactDescriptor[] getArtifactDescriptors(IArtifactKey key) {
+			return delegate.getArtifactDescriptors(key);
+		}
+
+		public IStatus getArtifacts(IArtifactRequest[] requests, IProgressMonitor monitor) {
+			return delegate.getArtifacts(requests, monitor);
+		}
+
+		public OutputStream getOutputStream(IArtifactDescriptor descriptor) throws ProvisionException {
+			return delegate.getOutputStream(descriptor);
+		}
+
+		public IStatus getRawArtifact(IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) {
+			return delegate.getRawArtifact(descriptor, destination, monitor);
+		}
+
+		public IQueryable<IArtifactDescriptor> descriptorQueryable() {
+			return delegate.descriptorQueryable();
+		}
+
+		public IQueryResult<IArtifactKey> query(IQuery<IArtifactKey> query, IProgressMonitor monitor) {
+			return delegate.query(query, monitor);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.equinox.p2.testserver/src/org/eclipse/equinox/p2/testserver/Activator.java b/bundles/org.eclipse.equinox.p2.testserver/src/org/eclipse/equinox/p2/testserver/Activator.java
index 746d634..90cbd16 100644
--- a/bundles/org.eclipse.equinox.p2.testserver/src/org/eclipse/equinox/p2/testserver/Activator.java
+++ b/bundles/org.eclipse.equinox.p2.testserver/src/org/eclipse/equinox/p2/testserver/Activator.java
@@ -17,6 +17,7 @@
 import org.eclipse.equinox.p2.testserver.servlets.ChopAndDelay;
 import org.eclipse.equinox.p2.testserver.servlets.ContentLengthLier;
 import org.eclipse.equinox.p2.testserver.servlets.FileMolester;
+import org.eclipse.equinox.p2.testserver.servlets.IntermittentTimeout;
 import org.eclipse.equinox.p2.testserver.servlets.LastModifiedLier;
 import org.eclipse.equinox.p2.testserver.servlets.Redirector;
 import org.eclipse.equinox.p2.testserver.servlets.StatusCodeResponse;
@@ -70,9 +71,11 @@
 			httpService.registerResources("/private", "/webfiles", secureHttpContext); //$NON-NLS-1$ //$NON-NLS-2$
 			httpService.registerResources("/never", "/webfiles", alwaysFail); //$NON-NLS-1$ //$NON-NLS-2$
 			httpService.registerResources("/flipflop", "/webfiles", flipFlop); //$NON-NLS-1$ //$NON-NLS-2$
+			//			httpService.registerResources("/mirrorrequest", "/webfiles/emptyJarRepo", null); //$NON-NLS-1$ //$NON-NLS-2$
 
 			httpService.registerServlet("/status", new StatusCodeResponse(), null, null); //$NON-NLS-1$
 			httpService.registerServlet("/timeout", new TimeOut(), null, null); //$NON-NLS-1$
+			httpService.registerServlet("/mirrorrequest", new IntermittentTimeout("/mirrorrequest", URI.create("http://localhost:" + System.getProperty("org.osgi.service.http.port", "8080") + "/public/emptyJarRepo")), null, null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
 			httpService.registerServlet("/redirect", new Redirector(), null, null); //$NON-NLS-1$
 
 			httpService.registerServlet("/truncated", new Truncator("/truncated", URI.create("/webfiles"), 50), null, null); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
diff --git a/bundles/org.eclipse.equinox.p2.testserver/src/org/eclipse/equinox/p2/testserver/servlets/IntermittentTimeout.java b/bundles/org.eclipse.equinox.p2.testserver/src/org/eclipse/equinox/p2/testserver/servlets/IntermittentTimeout.java
new file mode 100644
index 0000000..ac17e7a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.testserver/src/org/eclipse/equinox/p2/testserver/servlets/IntermittentTimeout.java
@@ -0,0 +1,83 @@
+package org.eclipse.equinox.p2.testserver.servlets;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URLConnection;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.equinox.p2.testserver.HttpConstants;
+
+public class IntermittentTimeout extends BasicResourceDelivery {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 2216234319571297257L;
+	int count = 1;
+
+	public IntermittentTimeout(String theAlias, URI thePath) {
+		super(theAlias, thePath);
+	}
+
+	protected void doDeliver(URLConnection conn, InputStream in, String filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
+		// set when the resource was modified
+		addDateHeader(response, HttpConstants.LAST_MODIFIED, getLastModified(conn));
+		int statusCode = HttpHeaderToStatus(conn.getHeaderField(0));
+
+		response.setStatus(statusCode != -1 ? HttpServletResponse.SC_OK : statusCode);
+
+		int contentlength = getContentLength(conn);
+		if (contentlength >= 0) {
+			response.setContentLength(contentlength);
+
+			String mimeType = computeMimeType(filename, conn);
+			response.setContentType(mimeType);
+
+			// We want to use a writer if we are sending text
+			if (mimeType.startsWith("text/")) //$NON-NLS-1$
+			{
+				PrintWriter writer = response.getWriter();
+
+				writer.flush(); /* write the headers and unbuffer the output */
+
+				doDelay(filename, 150);
+
+				BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+
+				char buffer[] = new char[4096];
+				int read;
+				while ((read = reader.read(buffer, 0, buffer.length)) != -1) {
+					writer.write(buffer, 0, read);
+				}
+			} else {
+				ServletOutputStream out = response.getOutputStream();
+
+				out.flush(); /* write the headers and unbuffer the output */
+
+				doDelay(filename, 150);
+
+				byte buffer[] = new byte[4096];
+				int read;
+				while ((read = in.read(buffer, 0, buffer.length)) != -1) {
+					out.write(buffer, 0, read);
+					out.flush();
+				}
+			}
+		}
+	}
+
+	private void doDelay(String filename, int seconds) {
+		if (filename.endsWith("emptyJarRepo/plugins/HelloWorldText_1.0.0.txt") && (count++ % 3 != 0)) {//$NON-NLS-1$
+			try {
+				Thread.sleep(1000 * seconds);
+			} catch (InterruptedException e) {
+				// ignore
+			}
+		}
+	}
+}
diff --git a/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/artifacts.xml b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/artifacts.xml
new file mode 100644
index 0000000..9143624
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/artifacts.xml
@@ -0,0 +1,52 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<?artifactRepository class='org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository' version='1.0.0'?>
+<repository name='HelloWorld Artifacts' type='org.eclipse.equinox.p2.artifact.repository.simpleRepository' version='1.0.0'>
+  <properties size='5'>
+    <property name='publishPackFilesAsSiblings' value='true'/>
+    <property name='p2.compressed' value='false'/>
+    <property name='p2.timestamp' value='1222368069078'/>
+    <property name='eclipse.p2.force.threading' value='true'/>
+    <property name='p2.mirrorsURL' value='mirrors.xml'/>
+  </properties>
+  <mappings size='6'>
+    <rule filter='(&amp; (classifier=osgi.bundle) (format=packed))' output='${repoUrl}/plugins/${id}_${version}.jar.pack.gz'/>
+    <rule filter='(&amp; (classifier=osgi.bundle))' output='${repoUrl}/plugins/${id}_${version}.jar'/>
+    <rule filter='(&amp; (classifier=binary))' output='${repoUrl}/binary/${id}_${version}'/>
+    <rule filter='(&amp; (classifier=org.eclipse.update.feature))' output='${repoUrl}/features/${id}_${version}.jar'/>
+    <rule filter='(&amp; (classifier=test.txt) (format=packed))' output='${repoUrl}/plugins/${id}_${version}.txt.pack.gz'/>
+    <rule filter='(&amp; (classifier=test.txt))' output='${repoUrl}/plugins/${id}_${version}.txt'/>
+  </mappings>
+  <artifacts size='4'>
+    <artifact classifier='org.eclipse.update.feature' id='HelloWorldFeature' version='1.0.0'>
+      <properties size='2'>
+        <property name='artifact.size' value='0'/>
+        <property name='download.size' value='0'/>
+      </properties>
+    </artifact>
+    <artifact classifier='test.txt' id='HelloWorldText' version='1.0.0'>
+      <properties size='3'>
+        <property name='artifact.size' value='12'/>
+        <property name='download.size' value='12'/>
+        <property name='download.md5' value='ed076287532e86365e841e92bfc50d8c'/>
+      </properties>
+    </artifact>
+    <artifact classifier='test.txt' id='fail_to_canonical' version='1.0.0'>
+      <properties size='3'>
+        <property name='artifact.size' value='12'/>
+        <property name='download.size' value='12'/>
+        <property name='download.md5' value='ed076287532e86365e841e92bfc50d8c'/>
+      </properties>
+    </artifact>
+    <artifact classifier='test.txt' id='fail_to_canonical' version='1.0.0'>
+       <processing size='1'>
+        <step id='org.eclipse.equinox.p2.processing.Pack200Unpacker' required='true'/>
+      </processing>
+      <properties size='4'>
+        <property name='artifact.size' value='12'/>
+        <property name='download.size' value='12'/>
+        <property name='download.md5' value='ed076287532e86365e841e92bfc50d8c'/>
+        <property name='format' value='packed'/>
+      </properties>
+    </artifact>
+  </artifacts>
+</repository>
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/mirror-two/plugins/HelloWorldText_1.0.0.txt b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/mirror-two/plugins/HelloWorldText_1.0.0.txt
new file mode 100644
index 0000000..c57eff5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/mirror-two/plugins/HelloWorldText_1.0.0.txt
@@ -0,0 +1 @@
+Hello World!
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/mirrors.xml b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/mirrors.xml
new file mode 100644
index 0000000..9faec8f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/mirrors.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<mirrors>
+	<mirror url="mirror-one" label="MirrorOne" />
+	<mirror url="mirror-two" label="MirrorTwo" />
+</mirrors>
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/plugins/HelloWorldText_1.0.0.txt b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/plugins/HelloWorldText_1.0.0.txt
new file mode 100644
index 0000000..c57eff5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/plugins/HelloWorldText_1.0.0.txt
@@ -0,0 +1 @@
+Hello World!
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/readme.txt b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/readme.txt
new file mode 100644
index 0000000..133499d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.testserver/webfiles/emptyJarRepo/readme.txt
@@ -0,0 +1,12 @@
+		
+Artifact: test.txt,HelloWorldText,1.0.0
+	Used by MirrorRequestTest2#testRetryMirrorAfterTimeout()
+		Mirror one: missing file
+		Mirror two: get it
+		Main: intermittent timeout
+		
+Artifact: test.txt,HelloWorldText,1.0.0
+	Used by MirrorRequestTest2#testTimeoutForgivableAfterTimeout()
+		Mirror one: missing file
+		Mirror two: won't use
+		Main: intermittent timeout		
\ No newline at end of file
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 bef589f..cf43309 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
@@ -16,8 +16,12 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.ConnectException;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
 import java.net.URI;
 import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
@@ -44,6 +48,7 @@
 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.equinox.p2.repository.artifact.IArtifactRepository;
 import org.eclipse.osgi.util.NLS;
 
 /**
@@ -54,6 +59,8 @@
 public class RepositoryTransport extends Transport implements IAgentServiceFactory {
 	private static RepositoryTransport instance;
 
+	public static final String TIMEOUT_RETRY = "org.eclipse.equinox.p2.transport.ecf.retry"; //$NON-NLS-1$
+	private static Map<URI, Integer> socketExceptionRetry = null;
 	/**
 	 * Returns an shared instance of Generic Transport
 	 */
@@ -226,12 +233,51 @@
 		throw new AuthenticationFailedException();
 	}
 
+	private static boolean isForgiveableException(Throwable t) {
+		if (t instanceof SocketTimeoutException)
+			return true;
+		else if (t instanceof SocketException)
+			return true;
+		return false;
+	}
+
 	public static DownloadStatus forStatus(IStatus original, URI toDownload) {
 		Throwable t = original.getException();
+		if (isForgiveableException(t) && original.getCode() == IArtifactRepository.CODE_RETRY)
+			return new DownloadStatus(original.getSeverity(), Activator.ID, original.getCode(), original.getMessage(), t);
 		return forException(t, toDownload);
 	}
 
 	public static DownloadStatus forException(Throwable t, URI toDownload) {
+		if (isForgiveableException(t)) {
+			String value = System.getProperty(TIMEOUT_RETRY);
+			if (value != null) {
+				try {
+					int retry = Integer.valueOf(value).intValue();
+					if (retry > 0) {
+						Integer retryCount = null;
+						if (socketExceptionRetry == null) {
+							socketExceptionRetry = new HashMap<URI, Integer>();
+							retryCount = new Integer(1);
+						} else {
+							Integer alreadyRetryCount = socketExceptionRetry.get(toDownload);
+							if (alreadyRetryCount == null)
+								retryCount = new Integer(1);
+							else if (alreadyRetryCount.intValue() < retry) {
+								retryCount = new Integer(alreadyRetryCount.intValue() + 1);
+							}
+						}
+						if (retryCount != null) {
+							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[] {toDownload.toString(), t.getMessage(), retryCount.toString()}), t); 
+						}
+					}
+				} catch (NumberFormatException e) {
+					// ignore
+				}
+			}
+		}
 		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)