From 522cfe70a7d26da4703d59eab5e629c784d5e47f Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 31 Jan 2025 17:55:30 +0200 Subject: [PATCH] Avoid UUID.randomUUID() in file system related startup code This is done because bootstrapping the plumbing needed by the JDK to produce a UUID value is expensive, it thus doesn't make sense to pay this cost when the property isn't actually needed --- .../core/file/FileSystemOptionsConverter.java | 8 ++++ .../io/vertx/core/file/FileSystemOptions.java | 31 +++++++++++++- .../io/vertx/core/file/impl/FileCache.java | 12 +++--- .../core/file/impl/FileResolverImpl.java | 7 +++- .../io/vertx/tests/file/FileCacheTest.java | 18 ++++++++ .../cachedir/ExactDirDoesNotExistTest.java | 39 ++++++++++++++++++ .../cachedir/ExactDirExistsButIsFileTest.java | 35 ++++++++++++++++ .../file/cachedir/ExactDirExistsTest.java | 41 +++++++++++++++++++ .../cachedir/ExactDirParentIsAFileTest.java | 38 +++++++++++++++++ 9 files changed, 220 insertions(+), 9 deletions(-) create mode 100644 vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirDoesNotExistTest.java create mode 100644 vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirExistsButIsFileTest.java create mode 100644 vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirExistsTest.java create mode 100644 vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirParentIsAFileTest.java diff --git a/vertx-core/src/main/generated/io/vertx/core/file/FileSystemOptionsConverter.java b/vertx-core/src/main/generated/io/vertx/core/file/FileSystemOptionsConverter.java index 2f6f89420cc..903fa9bf9df 100644 --- a/vertx-core/src/main/generated/io/vertx/core/file/FileSystemOptionsConverter.java +++ b/vertx-core/src/main/generated/io/vertx/core/file/FileSystemOptionsConverter.java @@ -29,6 +29,11 @@ static void fromJson(Iterable> json, FileSys obj.setFileCacheDir((String)member.getValue()); } break; + case "exactFileCacheDir": + if (member.getValue() instanceof String) { + obj.setExactFileCacheDir((String)member.getValue()); + } + break; } } } @@ -43,5 +48,8 @@ static void toJson(FileSystemOptions obj, java.util.Map json) { if (obj.getFileCacheDir() != null) { json.put("fileCacheDir", obj.getFileCacheDir()); } + if (obj.getExactFileCacheDir() != null) { + json.put("exactFileCacheDir", obj.getExactFileCacheDir()); + } } } diff --git a/vertx-core/src/main/java/io/vertx/core/file/FileSystemOptions.java b/vertx-core/src/main/java/io/vertx/core/file/FileSystemOptions.java index 66fb5cfc2b6..ed78ee180c0 100644 --- a/vertx-core/src/main/java/io/vertx/core/file/FileSystemOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/file/FileSystemOptions.java @@ -45,9 +45,17 @@ public class FileSystemOptions { */ public static final String DEFAULT_FILE_CACHING_DIR = SysProps.FILE_CACHE_DIR.get(); + /** + * The default exact file caching dir, which is {@code null} as {@link FileSystemOptions#setExactFileCacheDir} + * is meant to be used by advanced integrations that can guarantee on their own that the cache dir + * will be unique + */ + public static final String DEFAULT_EXACT_FILE_CACHING_DIR = null; + private boolean classPathResolvingEnabled = DEFAULT_CLASS_PATH_RESOLVING_ENABLED; private boolean fileCachingEnabled = DEFAULT_FILE_CACHING_ENABLED; private String fileCacheDir = DEFAULT_FILE_CACHING_DIR; + private String exactFileCacheDir = DEFAULT_EXACT_FILE_CACHING_DIR; /** * Default constructor @@ -128,7 +136,7 @@ public FileSystemOptions setFileCachingEnabled(boolean fileCachingEnabled) { } /** - * @return the configured file cache dir + * @return the base name of the configured file cache dir. Vert.x will append a random value to this when determining the effective value */ public String getFileCacheDir() { return this.fileCacheDir; @@ -147,6 +155,26 @@ public FileSystemOptions setFileCacheDir(String fileCacheDir) { return this; } + /** + * @return the configured exact file cache dir to be used as is + */ + public String getExactFileCacheDir() { + return this.exactFileCacheDir; + } + + /** + * When vert.x reads a file that is packaged with the application it gets + * extracted to this directory first and subsequent reads will use the extracted + * file to get better IO performance. + * + * @param exactFileCacheDir the value + * @return a reference to this, so the API can be used fluently + */ + public FileSystemOptions setExactFileCacheDir(String exactFileCacheDir) { + this.exactFileCacheDir = exactFileCacheDir; + return this; + } + @Override public String toString() { @@ -154,6 +182,7 @@ public String toString() { "classPathResolvingEnabled=" + classPathResolvingEnabled + ", fileCachingEnabled=" + fileCachingEnabled + ", fileCacheDir=" + fileCacheDir + + ", exactFileCacheDir=" + exactFileCacheDir + '}'; } } diff --git a/vertx-core/src/main/java/io/vertx/core/file/impl/FileCache.java b/vertx-core/src/main/java/io/vertx/core/file/impl/FileCache.java index 5156e9e6f14..90684bbaa7a 100644 --- a/vertx-core/src/main/java/io/vertx/core/file/impl/FileCache.java +++ b/vertx-core/src/main/java/io/vertx/core/file/impl/FileCache.java @@ -18,7 +18,6 @@ import java.io.InputStream; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; @@ -27,8 +26,8 @@ public class FileCache { - static FileCache setupCache(String fileCacheDir) { - FileCache cache = new FileCache(setupCacheDir(fileCacheDir)); + public static FileCache setupCache(String fileCacheDir, boolean isEffectiveValue) { + FileCache cache = new FileCache(setupCacheDir(fileCacheDir, isEffectiveValue)); // Add shutdown hook to delete on exit cache.registerShutdownHook(); return cache; @@ -37,7 +36,7 @@ static FileCache setupCache(String fileCacheDir) { /** * Prepares the cache directory to be used in the application. */ - static File setupCacheDir(String fileCacheDir) { + static File setupCacheDir(String fileCacheDir, boolean isEffectiveValue) { // ensure that the argument doesn't end with separator if (fileCacheDir.endsWith(File.separator)) { fileCacheDir = fileCacheDir.substring(0, fileCacheDir.length() - File.separator.length()); @@ -45,8 +44,7 @@ static File setupCacheDir(String fileCacheDir) { // the cacheDir will be suffixed a unique id to avoid eavesdropping from other processes/users // also this ensures that if process A deletes cacheDir, it won't affect process B - String cacheDirName = fileCacheDir + "-" + UUID.randomUUID(); - File cacheDir = new File(cacheDirName); + File cacheDir = isEffectiveValue ? new File(fileCacheDir) : new File(fileCacheDir + "-" + UUID.randomUUID()); // Create the cache directory try { if (Utils.isWindows()) { @@ -220,7 +218,7 @@ private void fileNameCheck(File file) throws IOException { } } - private File getCacheDir() { + public File getCacheDir() { File currentCacheDir = cacheDir; if (currentCacheDir == null) { throw new IllegalStateException("cacheDir has been removed. FileResolver is closing?"); diff --git a/vertx-core/src/main/java/io/vertx/core/file/impl/FileResolverImpl.java b/vertx-core/src/main/java/io/vertx/core/file/impl/FileResolverImpl.java index 41007a78a86..cd03bea95b5 100644 --- a/vertx-core/src/main/java/io/vertx/core/file/impl/FileResolverImpl.java +++ b/vertx-core/src/main/java/io/vertx/core/file/impl/FileResolverImpl.java @@ -68,7 +68,12 @@ public FileResolverImpl(FileSystemOptions fileSystemOptions) { enableCPResolving = fileSystemOptions.isClassPathResolvingEnabled(); if (enableCPResolving) { - cache = FileCache.setupCache(fileSystemOptions.getFileCacheDir()); + String exactFileCacheDir = fileSystemOptions.getExactFileCacheDir(); + if (exactFileCacheDir != null) { + cache = FileCache.setupCache(exactFileCacheDir, true); + } else { + cache = FileCache.setupCache(fileSystemOptions.getFileCacheDir(), false); + } } else { cache = null; } diff --git a/vertx-core/src/test/java/io/vertx/tests/file/FileCacheTest.java b/vertx-core/src/test/java/io/vertx/tests/file/FileCacheTest.java index 2727cfc4f13..3527c2bc18c 100644 --- a/vertx-core/src/test/java/io/vertx/tests/file/FileCacheTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/file/FileCacheTest.java @@ -11,7 +11,9 @@ package io.vertx.tests.file; import io.vertx.core.VertxException; +import io.vertx.core.file.FileSystemOptions; import io.vertx.core.file.impl.FileCache; +import io.vertx.core.file.impl.FileResolverImpl; import io.vertx.test.core.VertxTestBase; import org.junit.Test; @@ -40,4 +42,20 @@ public void testMutateCacheContentOnly() throws IOException { assertEquals("protected", new String(Files.readAllBytes(other.toPath()))); } } + + @Test + public void testGetTheExactCacheDirWithoutHacks() throws IOException { + String cacheBaseDir; + try { + cacheBaseDir = new File(System.getProperty("java.io.tmpdir", ".") + File.separator + "vertx-cache").getCanonicalPath(); + } catch (IOException e) { + throw new IllegalStateException("Cannot resolve the canonical path to the cache dir", e); + } + + String cacheDir = FileCache.setupCache(cacheBaseDir + "-exact", true).getCacheDir().getCanonicalPath(); + assertTrue(cacheDir.startsWith(cacheBaseDir + "-")); + // strip the remaining + String remaining = cacheDir.substring(cacheBaseDir.length() + 1); + assertEquals(remaining, "exact"); + } } diff --git a/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirDoesNotExistTest.java b/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirDoesNotExistTest.java new file mode 100644 index 00000000000..c808ee46801 --- /dev/null +++ b/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirDoesNotExistTest.java @@ -0,0 +1,39 @@ +package io.vertx.tests.file.cachedir; + +import io.vertx.core.VertxOptions; +import io.vertx.core.file.FileSystemOptions; +import io.vertx.core.internal.VertxInternal; +import io.vertx.core.spi.file.FileResolver; +import io.vertx.test.core.VertxTestBase; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.Assert; +import org.junit.Test; + +public class ExactDirDoesNotExistTest extends VertxTestBase { + + private final Path cacheBaseDir; + + public ExactDirDoesNotExistTest() throws IOException { + cacheBaseDir = Files.createTempDirectory("cache-does-not-exist"); + Files.deleteIfExists(cacheBaseDir); + Assert.assertFalse(Files.exists(cacheBaseDir)); + } + + @Override + protected VertxOptions getOptions() { + return new VertxOptions(super.getOptions()) + .setFileSystemOptions(new FileSystemOptions().setFileCachingEnabled(true).setExactFileCacheDir(cacheBaseDir.toAbsolutePath().toString())); + } + + @Test + public void test() throws IOException { + try (FileResolver fileResolver = ((VertxInternal) vertx).fileResolver()) { + File file = fileResolver.resolveFile("conf.json"); + Assert.assertEquals(cacheBaseDir.resolve("conf.json").toFile(), file); + } + } +} diff --git a/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirExistsButIsFileTest.java b/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirExistsButIsFileTest.java new file mode 100644 index 00000000000..cd978c26f87 --- /dev/null +++ b/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirExistsButIsFileTest.java @@ -0,0 +1,35 @@ +package io.vertx.tests.file.cachedir; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.file.FileSystemOptions; +import io.vertx.core.internal.VertxInternal; +import io.vertx.core.spi.file.FileResolver; +import io.vertx.test.core.VertxTestBase; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.Assert; +import org.junit.Test; + +public class ExactDirExistsButIsFileTest { + + private final Path cacheBaseDir; + + public ExactDirExistsButIsFileTest() throws IOException { + cacheBaseDir = Files.createTempDirectory("cache-exists-but-is-file"); + Files.deleteIfExists(cacheBaseDir); + Files.createFile(cacheBaseDir); + Assert.assertTrue(Files.exists(cacheBaseDir)); + Assert.assertFalse(Files.isDirectory(cacheBaseDir)); + } + + @Test + public void test() { + Assert.assertThrows(IllegalStateException.class, () -> { + Vertx.builder().with(new VertxOptions().setFileSystemOptions(new FileSystemOptions().setFileCachingEnabled(true).setExactFileCacheDir(cacheBaseDir.toAbsolutePath().toString()))).build(); + }); + } +} diff --git a/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirExistsTest.java b/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirExistsTest.java new file mode 100644 index 00000000000..7d53d48c424 --- /dev/null +++ b/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirExistsTest.java @@ -0,0 +1,41 @@ +package io.vertx.tests.file.cachedir; + +import io.vertx.core.VertxOptions; +import io.vertx.core.file.FileSystemOptions; +import io.vertx.core.internal.VertxInternal; +import io.vertx.core.spi.file.FileResolver; +import io.vertx.test.core.VertxTestBase; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.Assert; +import org.junit.Test; + +public class ExactDirExistsTest extends VertxTestBase { + + private final Path cacheBaseDir; + + public ExactDirExistsTest() throws IOException { + cacheBaseDir = Paths.get(System.getProperty("java.io.tmpdir", "."), "cache-exists"); + Files.deleteIfExists(cacheBaseDir); + Files.createDirectories(cacheBaseDir); + Assert.assertTrue(Files.exists(cacheBaseDir)); + Assert.assertTrue(Files.isDirectory(cacheBaseDir)); + } + + @Override + protected VertxOptions getOptions() { + return new VertxOptions(super.getOptions()) + .setFileSystemOptions(new FileSystemOptions().setFileCachingEnabled(true).setExactFileCacheDir(cacheBaseDir.toAbsolutePath().toString())); + } + + @Test + public void test() throws IOException { + try (FileResolver fileResolver = ((VertxInternal) vertx).fileResolver()) { + File file = fileResolver.resolveFile("conf.json"); + Assert.assertEquals(cacheBaseDir.resolve("conf.json").toFile(), file); + } + } +} diff --git a/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirParentIsAFileTest.java b/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirParentIsAFileTest.java new file mode 100644 index 00000000000..b4fce98327a --- /dev/null +++ b/vertx-core/src/test/java/io/vertx/tests/file/cachedir/ExactDirParentIsAFileTest.java @@ -0,0 +1,38 @@ +package io.vertx.tests.file.cachedir; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.file.FileSystemOptions; +import io.vertx.core.internal.VertxInternal; +import io.vertx.core.spi.file.FileResolver; +import io.vertx.test.core.VertxTestBase; +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystemException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.Assert; +import org.junit.Test; + +public class ExactDirParentIsAFileTest { + + private final Path cacheBaseDir; + + public ExactDirParentIsAFileTest() throws IOException { + Path cacheBaseDirParent = Paths.get(System.getProperty("java.io.tmpdir", "."), "cache-parent"); + Files.deleteIfExists(cacheBaseDirParent); + Files.createFile(cacheBaseDirParent); + Assert.assertTrue(Files.exists(cacheBaseDirParent)); + Assert.assertFalse(Files.isDirectory(cacheBaseDirParent)); + this.cacheBaseDir = cacheBaseDirParent.resolve("actual-cache"); + Assert.assertFalse(Files.exists(cacheBaseDir)); + } + + @Test + public void test() { + Assert.assertThrows(IllegalStateException.class, () -> { + Vertx.builder().with(new VertxOptions().setFileSystemOptions(new FileSystemOptions().setFileCachingEnabled(true).setExactFileCacheDir(cacheBaseDir.toAbsolutePath().toString()))).build(); + }); + } +}