From 3630f86f9aaa8edaf7b5ee84d1f858da5dd2b01b Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Wed, 24 Jun 2015 09:44:40 +0200 Subject: [PATCH] Fix lock on windows --- .../batch/bootstrap/TempFolderProvider.java | 17 +++++- .../bootstrap/TempFolderProviderTest.java | 31 +++++++--- .../org/sonar/home/cache/PersistentCache.java | 58 ++++++++++++++++--- .../sonar/home/cache/PersistentCacheTest.java | 2 +- 4 files changed, 87 insertions(+), 21 deletions(-) diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java index b1ee968f344..175009bd898 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.bootstrap; +import org.sonar.api.utils.System2; + import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.apache.commons.io.FileUtils; @@ -40,7 +42,16 @@ public class TempFolderProvider extends LifecycleProviderAdapter { private static final long CLEAN_MAX_AGE = TimeUnit.DAYS.toMillis(21); static final String TMP_NAME_PREFIX = ".sonartmp_"; + private System2 system; private DefaultTempFolder tempFolder; + + public TempFolderProvider() { + this(new System2()); + } + + TempFolderProvider(System2 system) { + this.system = system; + } public TempFolder provide(BootstrapProperties bootstrapProps) { if (tempFolder == null) { @@ -71,19 +82,19 @@ public class TempFolderProvider extends LifecycleProviderAdapter { return tempFolder; } - private static Path findHome(BootstrapProperties props) { + private Path findHome(BootstrapProperties props) { String home = props.property("sonar.userHome"); if (home != null) { return Paths.get(home); } - home = System.getenv("SONAR_USER_HOME"); + home = system.envVariable("SONAR_USER_HOME"); if (home != null) { return Paths.get(home); } - home = System.getProperty("user.home"); + home = system.property("user.home"); return Paths.get(home, ".sonar"); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java index 8c1e23b7b56..3192641f791 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java @@ -19,6 +19,7 @@ */ package org.sonar.batch.bootstrap; +import org.sonar.api.utils.System2; import org.apache.commons.io.FileUtils; import org.sonar.api.utils.TempFolder; import com.google.common.collect.ImmutableMap; @@ -32,6 +33,8 @@ import java.nio.file.attribute.FileTime; import java.util.Collections; import java.util.concurrent.TimeUnit; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Rule; import org.junit.Test; @@ -45,19 +48,21 @@ public class TempFolderProviderTest { @Test public void createTempFolderProps() throws Exception { - File workingDir = temp.getRoot(); + File workingDir = temp.newFolder(); TempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath()))); tempFolder.newDir(); tempFolder.newFile(); assertThat(getCreatedTempDir(workingDir)).exists(); assertThat(getCreatedTempDir(workingDir).list()).hasSize(2); + + FileUtils.deleteQuietly(workingDir); } @Test public void cleanUpOld() throws IOException { long creationTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(100); - File workingDir = temp.getRoot(); + File workingDir = temp.newFolder(); for (int i = 0; i < 3; i++) { File tmp = new File(workingDir, ".sonartmp_" + i); @@ -68,30 +73,37 @@ public class TempFolderProviderTest { tempFolderProvider.provide(new BootstrapProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath()))); // this also checks that all other temps were deleted assertThat(getCreatedTempDir(workingDir)).exists(); + + FileUtils.deleteQuietly(workingDir); } @Test public void createTempFolderSonarHome() throws Exception { // with sonar home, it will be in {sonar.home}/.sonartmp - File sonarHome = temp.getRoot(); - File workingDir = new File(sonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE); + File sonarHome = temp.newFolder(); + File workingDir = new File(sonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE).getAbsoluteFile(); TempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(ImmutableMap.of("sonar.userHome", sonarHome.getAbsolutePath()))); tempFolder.newDir(); tempFolder.newFile(); assertThat(getCreatedTempDir(workingDir)).exists(); assertThat(getCreatedTempDir(workingDir).list()).hasSize(2); + + FileUtils.deleteQuietly(sonarHome); } @Test public void createTempFolderDefault() throws Exception { - File userHome = temp.getRoot(); - System.setProperty("user.home", userHome.getAbsolutePath()); + System2 system = mock(System2.class); + tempFolderProvider = new TempFolderProvider(system); + File userHome = temp.newFolder(); + + when(system.envVariable("SONAR_USER_HOME")).thenReturn(null); + when(system.property("user.home")).thenReturn(userHome.getAbsolutePath().toString()); // if nothing is defined, it will be in {user.home}/.sonar/.sonartmp - File defaultSonarHome = new File(System.getProperty("user.home"), ".sonar"); + File defaultSonarHome = new File(userHome.getAbsolutePath(), ".sonar"); File workingDir = new File(defaultSonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE).getAbsoluteFile(); - try { TempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(Collections.emptyMap())); tempFolder.newDir(); @@ -99,11 +111,12 @@ public class TempFolderProviderTest { assertThat(getCreatedTempDir(workingDir)).exists(); assertThat(getCreatedTempDir(workingDir).list()).hasSize(2); } finally { - FileUtils.deleteDirectory(getCreatedTempDir(workingDir)); + FileUtils.deleteQuietly(workingDir); } } private File getCreatedTempDir(File workingDir) { + assertThat(workingDir).isDirectory(); assertThat(workingDir.listFiles()).hasSize(1); return workingDir.listFiles()[0]; } diff --git a/sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java b/sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java index 3ceeaa47f3d..6af55ef6271 100644 --- a/sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java +++ b/sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java @@ -26,6 +26,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; +import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; @@ -34,7 +35,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributes; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -60,7 +60,7 @@ public class PersistentCache { this.log = log; reconfigure(forceUpdate); - log.info("cache: " + baseDir + ", default expiration time (ms): " + defaultDurationToExpireMs); + log.debug("cache: " + baseDir + ", default expiration time (ms): " + defaultDurationToExpireMs); } public void reconfigure(boolean forceUpdate) { @@ -109,7 +109,8 @@ public class PersistentCache { public synchronized byte[] get(@Nonnull String obj, @Nullable Callable valueLoader) throws Exception { String key = getKey(obj); - try (FileLock l = lock()) { + try { + lock(); if (!forceUpdate) { byte[] cached = getCache(key); @@ -130,6 +131,8 @@ public class PersistentCache { } return value; } + } finally { + unlock(); } return null; @@ -140,10 +143,13 @@ public class PersistentCache { */ public synchronized void clear() { log.info("cache: clearing"); - try (FileLock l = lock()) { + try { + lock(); deleteCacheEntries(createClearFilter()); } catch (IOException e) { log.error("Error clearing cache", e); + } finally { + unlock(); } } @@ -152,16 +158,52 @@ public class PersistentCache { */ public synchronized void clean() { log.info("cache: cleaning"); - try (FileLock l = lock()) { + try { + lock(); deleteCacheEntries(createCleanFilter()); } catch (IOException e) { log.error("Error cleaning cache", e); + } finally { + unlock(); } } - private FileLock lock() throws IOException { - FileChannel ch = FileChannel.open(getLockPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); - return ch.lock(); + private void lock() throws IOException { + lock_raf = new RandomAccessFile(getLockPath().toFile(), "rw"); + lock_fc = lock_raf.getChannel(); + lock = lock_fc.lock(); + } + + private RandomAccessFile lock_raf; + private FileChannel lock_fc; + private FileLock lock; + + private void unlock() { + if (lock != null) { + try { + lock.release(); + } catch (IOException e) { + log.error("Error releasing lock", e); + } + } + if (lock_fc != null) { + try { + lock_fc.close(); + } catch (IOException e) { + log.error("Error closing file channel", e); + } + } + if (lock_raf != null) { + try { + lock_raf.close(); + } catch (IOException e) { + log.error("Error closing file", e); + } + } + + lock = null; + lock_raf = null; + lock_fc = null; } private String getKey(String uri) { diff --git a/sonar-home/src/test/java/org/sonar/home/cache/PersistentCacheTest.java b/sonar-home/src/test/java/org/sonar/home/cache/PersistentCacheTest.java index 8f8596e0705..e9abb0bc609 100644 --- a/sonar-home/src/test/java/org/sonar/home/cache/PersistentCacheTest.java +++ b/sonar-home/src/test/java/org/sonar/home/cache/PersistentCacheTest.java @@ -103,7 +103,7 @@ public class PersistentCacheTest { assertCacheHit(false); File root = tmp.getRoot(); - FileUtils.deleteDirectory(root); + FileUtils.deleteQuietly(root); // should re-create cache directory and start using the cache cache.reconfigure(false); -- 2.39.5