From 982553a5cb7122f2f96ba103a43eee3e60da42af Mon Sep 17 00:00:00 2001 From: Patrick Reinhart Date: Wed, 9 Feb 2022 09:17:49 -0600 Subject: [PATCH] SONAR-16026 Adds file lock retries when explode jar --- .../bootstrap/ScannerPluginJarExploder.java | 23 ++++++++++++++++++- .../ScannerPluginJarExploderTest.java | 21 +++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploder.java index 931a0727073..60da81630b1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploder.java @@ -22,6 +22,10 @@ package org.sonar.scanner.bootstrap; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; + import org.apache.commons.io.FileUtils; import org.sonar.api.utils.ZipUtils; import org.sonar.core.platform.ExplodedPlugin; @@ -54,7 +58,7 @@ public class ScannerPluginJarExploder extends PluginJarExploder { File lockFile = new File(cachedFile.getParentFile(), filename + "_unzip.lock"); if (!destDir.exists()) { try (FileOutputStream out = new FileOutputStream(lockFile)) { - java.nio.channels.FileLock lock = out.getChannel().lock(); + FileLock lock = createLockWithRetries(out.getChannel()); try { // Recheck in case of concurrent processes if (!destDir.exists()) { @@ -71,4 +75,21 @@ public class ScannerPluginJarExploder extends PluginJarExploder { } return destDir; } + + private static FileLock createLockWithRetries(FileChannel channel) throws IOException { + int tryCount = 0; + while (tryCount++ < 10) { + try { + return channel.lock(); + } catch (OverlappingFileLockException ofle) { + // ignore overlapping file exception + } + try { + Thread.sleep(200L * tryCount); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + throw new IOException("Unable to get lock after " + tryCount + " tries"); + } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploderTest.java index 09f5aee57cd..d46d868c0a6 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploderTest.java @@ -20,7 +20,9 @@ package org.sonar.scanner.bootstrap; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.nio.channels.FileLock; import org.apache.commons.io.FileUtils; import org.junit.Before; import org.junit.ClassRule; @@ -30,6 +32,7 @@ import org.sonar.core.platform.ExplodedPlugin; import org.sonar.core.platform.PluginInfo; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -72,6 +75,24 @@ public class ScannerPluginJarExploderTest { assertThat(new File(jar.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/org/sonar/plugins/checkstyle/CheckstyleVersion.class")).doesNotExist(); } + @Test + public void retry_on_locked_file() throws IOException { + File jar = loadFile("sonar-checkstyle-plugin-2.8.jar"); + File lockFile = new File(jar.getParentFile(), jar.getName() + "_unzip.lock"); + try (FileOutputStream out = new FileOutputStream(lockFile)) { + FileLock lock = out.getChannel().lock(); + try { + PluginInfo pluginInfo = PluginInfo.create(jar); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> underTest.explode(pluginInfo)) + .withMessage("Fail to open plugin [checkstyle]: " + jar) + .withCauseExactlyInstanceOf(IOException.class); + } finally { + lock.release(); + } + } + } + private File loadFile(String filename) throws IOException { File src = FileUtils.toFile(getClass().getResource(getClass().getSimpleName() + "/" + filename)); File dest = new File(temp.newFolder(), filename); -- 2.39.5