]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16026 Adds file lock retries when explode jar
authorPatrick Reinhart <patrick@reini.net>
Wed, 9 Feb 2022 15:17:49 +0000 (09:17 -0600)
committersonartech <sonartech@sonarsource.com>
Wed, 9 Feb 2022 20:02:56 +0000 (20:02 +0000)
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploder.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerPluginJarExploderTest.java

index 931a0727073b4ec6bf5c72df57881ea4c29a78ca..60da81630b1ff5af9ef27d20e58d666c2629fd20 100644 (file)
@@ -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");
+  }
 }
index 09f5aee57cdaaf9c360d18d71644f84b4b126918..d46d868c0a6bd29956e11e92b0ad153ffcac9aff 100644 (file)
@@ -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);