Retry loading config when locked by another process

When loading the config, a FileNotFoundException may occur if the file
exists but cannot be read (see [1]). This is the case on Windows with a
virus scanner checking the file. Therefore if the file exists and that
exception is thrown, retry multiple times, similar to how this was
already implemented for IOException.

[1] https://docs.oracle.com/javase/8/docs/api/java/io/FileNotFoundException.html

Bug: 529522
Change-Id: Ic5dc3b7b24bb0005d6256ed00513bc7c0b91e613
Signed-off-by: Michael Keppler <Michael.Keppler@gmx.de>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index afa8761..df42dc7 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -139,6 +139,7 @@
 configSubsectionContainsNullByte=config subsection name contains byte 0x00
 configValueContainsNullByte=config value contains byte 0x00
 configHandleIsStale=config file handle is stale, {0}. retry
+configHandleMayBeLocked=config file handle may be locked by other process, {0}. retry
 connectionFailed=connection failed
 connectionTimeOut=Connection time out: {0}
 contextMustBeNonNegative=context must be >= 0
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 4d0d051..bdaef5a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -200,6 +200,7 @@ public static JGitText get() {
 	/***/ public String configSubsectionContainsNullByte;
 	/***/ public String configValueContainsNullByte;
 	/***/ public String configHandleIsStale;
+	/***/ public String configHandleMayBeLocked;
 	/***/ public String connectionFailed;
 	/***/ public String connectionTimeOut;
 	/***/ public String contextMustBeNonNegative;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 2b31ebd..fc6f4a3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -148,7 +148,8 @@ public final File getFile() {
 	 */
 	@Override
 	public void load() throws IOException, ConfigInvalidException {
-		final int maxStaleRetries = 5;
+		final int maxRetries = 5;
+		int retryDelayMillis = 20;
 		int retries = 0;
 		while (true) {
 			final FileSnapshot oldSnapshot = snapshot;
@@ -177,6 +178,22 @@ public void load() throws IOException, ConfigInvalidException {
 				}
 				return;
 			} catch (FileNotFoundException noFile) {
+				// might be locked by another process (see exception Javadoc)
+				if (retries < maxRetries && configFile.exists()) {
+					if (LOG.isDebugEnabled()) {
+						LOG.debug(MessageFormat.format(
+								JGitText.get().configHandleMayBeLocked,
+								Integer.valueOf(retries)), noFile);
+					}
+					try {
+						Thread.sleep(retryDelayMillis);
+					} catch (InterruptedException e) {
+						Thread.currentThread().interrupt();
+					}
+					retries++;
+					retryDelayMillis *= 2; // max wait 1260 ms
+					continue;
+				}
 				if (configFile.exists()) {
 					throw noFile;
 				}
@@ -185,7 +202,7 @@ public void load() throws IOException, ConfigInvalidException {
 				return;
 			} catch (IOException e) {
 				if (FileUtils.isStaleFileHandle(e)
-						&& retries < maxStaleRetries) {
+						&& retries < maxRetries) {
 					if (LOG.isDebugEnabled()) {
 						LOG.debug(MessageFormat.format(
 								JGitText.get().configHandleIsStale,