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;
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()) {
}
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");
+ }
}
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;
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;
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);