From 3709b2742931f0956704c5174412933c83b3fe61 Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Sun, 5 Feb 2023 15:23:02 +0800 Subject: [PATCH] improved Application class --- .../java/net/woggioni/jwo/Application.java | 190 +++++++++++++----- src/main/java/net/woggioni/jwo/JWO.java | 34 ---- 2 files changed, 141 insertions(+), 83 deletions(-) diff --git a/src/main/java/net/woggioni/jwo/Application.java b/src/main/java/net/woggioni/jwo/Application.java index 1ec8797..5d7f58a 100644 --- a/src/main/java/net/woggioni/jwo/Application.java +++ b/src/main/java/net/woggioni/jwo/Application.java @@ -1,7 +1,8 @@ package net.woggioni.jwo; import lombok.AccessLevel; -import lombok.NoArgsConstructor; +import lombok.Builder; +import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -12,9 +13,27 @@ import java.nio.file.Paths; import java.util.Optional; import java.util.stream.Stream; +import static net.woggioni.jwo.JWO.optional2Stream; +import static net.woggioni.jwo.JWO.streamCat; + @Slf4j -@NoArgsConstructor(access = AccessLevel.PRIVATE) -class Application { +@Builder(builderMethodName = "", builderClassName = "Builder") +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class Application { + private final String name; + private final String configurationDirectoryPropertyKey; + private final String cacheDirectoryPropertyKey; + private final String dataDirectoryPropertyKey; + private final String configurationDirectoryEnvVar; + private final String cacheDirectoryEnvVar; + private final String dataDirectoryEnvVar; + + private static final String MAC_FOLDER_LIBRARY = "Library"; + private static final String MAC_FOLDER_APPLICATION_SUPPORT = "Application Support"; + + public static Builder builder(String name) { + return new Builder().name(name); + } private static boolean validateConfigurationDirectory(Path candidate) { try { @@ -46,72 +65,145 @@ class Application { .filter(Application::validateConfigurationDirectory) .peek(p -> log.debug(successMessage, p)) .findFirst() - .orElseThrow((Sup)() -> new FileNotFoundException(errorMessage)); + .orElseThrow((Sup) () -> new FileNotFoundException(errorMessage)); + } + + private static boolean validateWritableDirectory(Path candidate) { + try { + if (!Files.exists(candidate)) { + Files.createDirectories(candidate); + log.trace("Created and selected directory '{}'", candidate); + return true; + } else if (!Files.isDirectory(candidate)) { + log.trace("Directory '{}' discarded because it is not a directory", candidate); + return false; + } else if (!Files.isWritable(candidate)) { + log.trace("Directory '{}' discarded because it is not writable", candidate); + return false; + } else { + log.trace("Selected existing directory '{}'", candidate); + return true; + } + } catch (Exception ioe) { + log.trace( + String.format("Directory '%s' discarded: %s", candidate.toString(), ioe.getMessage()), + ioe + ); + return false; + } } @SneakyThrows - private static Path computeCacheDirectory(String appName, String jvmPropertyKey) { - Stream candidates; - if(OS.isUnix) { - candidates = JWO.optional2Stream( - Optional.ofNullable(System.getProperty(jvmPropertyKey)).map(Paths::get), - Optional.ofNullable(System.getenv("XDG_CACHE_HOME")).map(prefix -> Paths.get(prefix, appName)), - Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, ".cache", appName)), - Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + appName, "cache")) + private Path selectWritableDirectory(Stream candidates, String successMessage, String errorMessage) { + return candidates + .filter(Application::validateWritableDirectory) + .peek(p -> log.debug(successMessage, p)) + .findFirst() + .orElseThrow((Sup) () -> new FileNotFoundException(errorMessage)); + } + + @SneakyThrows + public Path computeCacheDirectory() { + Stream commonCandidates = optional2Stream( + Optional.ofNullable(cacheDirectoryPropertyKey).map(System::getProperty).map(Paths::get), + Optional.ofNullable(cacheDirectoryEnvVar).map(System::getProperty).map(Paths::get) + ); + Stream osSpecificCandidates; + if (OS.isMac) { + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getProperty("user.home")) + .map(prefix -> Paths.get(prefix, MAC_FOLDER_LIBRARY, MAC_FOLDER_APPLICATION_SUPPORT, name, "cache")), + Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + name, "cache")) ); - } else if(OS.isMac) { - candidates = JWO.optional2Stream( - Optional.ofNullable(System.getProperty("user.home")) - .map(prefix -> Paths.get(prefix, "Library", "Application support", appName, "cache")), - Optional.ofNullable(System.getProperty("user.home")) - .map(prefix -> Paths.get(prefix, "." + appName, "cache")) + } else if (OS.isUnix) { + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getenv("XDG_CACHE_HOME")).map(prefix -> Paths.get(prefix, name)), + Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, ".cache", name)), + Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + name, "cache")) ); - } else if(OS.isWindows) { - candidates = JWO.optional2Stream( - Optional.ofNullable(System.getenv("LOCALAPPDATA")) - .map(prefix -> Paths.get(prefix, appName, "cache")), - Optional.ofNullable(System.getProperty("user.home")) - .map(prefix -> Paths.get(prefix, "Application Data", "Local Settings", "Application Data", appName, "cache"))); + } else if (OS.isWindows) { + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getenv("LOCALAPPDATA")) + .map(prefix -> Paths.get(prefix, name, "cache")), + Optional.ofNullable(System.getProperty("user.home")) + .map(prefix -> Paths.get(prefix, "Application Data", "Local Settings", "Application Data", name, "cache"))); } else { - candidates = JWO.optional2Stream( - Optional.ofNullable(System.getProperty("user.home")) - .map(prefix -> Paths.get(prefix, "." + appName, "cache"))); + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getProperty("user.home")) + .map(prefix -> Paths.get(prefix, "." + name, "cache"))); } - return selectCandidate(candidates, + return selectWritableDirectory(streamCat(commonCandidates, osSpecificCandidates), "Using cache directory '{}'", "Unable to find a usable cache directory"); } + @SneakyThrows + public Path computeDataDirectory() { + Stream commonCandidates = optional2Stream( + Optional.ofNullable(dataDirectoryPropertyKey).map(System::getProperty).map(Paths::get), + Optional.ofNullable(dataDirectoryEnvVar).map(System::getProperty).map(Paths::get) + ); + Stream osSpecificCandidates; + if (OS.isMac) { + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getProperty("user.home")) + .map(prefix -> Paths.get(prefix, MAC_FOLDER_LIBRARY, MAC_FOLDER_APPLICATION_SUPPORT, name)), + Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + name)) + ); + } else if (OS.isUnix) { + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getenv("XDG_DATA_HOME")).map(prefix -> Paths.get(prefix, name)), + Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, ".local", "share", name)), + Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + name)) + ); + } else if (OS.isWindows) { + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getenv("LOCALAPPDATA")) + .map(prefix -> Paths.get(prefix, name)), + Optional.ofNullable(System.getProperty("user.home")) + .map(prefix -> Paths.get(prefix, "Application Data", "Local Settings", "Application Data", name))); + } else { + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getProperty("user.home")) + .map(prefix -> Paths.get(prefix, "." + name))); + } + return selectWritableDirectory(streamCat(commonCandidates, osSpecificCandidates), + "Using data directory '{}'", + "Unable to find a usable data directory"); + } @SneakyThrows - private static Path computeConfigurationDirectory(String appName, String jvmPropertyKey) { - Stream candidates; - if(OS.isUnix) { - candidates = JWO.optional2Stream( - Optional.ofNullable(System.getProperty(jvmPropertyKey)).map(Paths::get), - Optional.ofNullable(System.getenv("XDG_CONFIG_HOME")).map(prefix -> Paths.get(prefix, appName)), - Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, ".config", appName)), - Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + appName, "config")) - ); - } else if(OS.isMac) { - candidates = JWO.optional2Stream( + public Path computeConfigurationDirectory() { + Stream commonCandidates = optional2Stream( + Optional.ofNullable(configurationDirectoryPropertyKey).map(System::getProperty).map(Paths::get), + Optional.ofNullable(configurationDirectoryEnvVar).map(System::getProperty).map(Paths::get) + ); + Stream osSpecificCandidates; + if (OS.isMac) { + osSpecificCandidates = optional2Stream( Optional.ofNullable(System.getProperty("user.home")) - .map(prefix -> Paths.get(prefix, "Library", "Application support", appName, "config")), - Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + appName, "config")) + .map(prefix -> Paths.get(prefix, MAC_FOLDER_LIBRARY, MAC_FOLDER_APPLICATION_SUPPORT, name, "config")), + Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + name, "config")) ); - } else if(OS.isWindows) { - candidates = JWO.optional2Stream( + } else if (OS.isUnix) { + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getenv("XDG_CONFIG_HOME")).map(prefix -> Paths.get(prefix, name)), + Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, ".config", name)), + Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + name, "config")) + ); + } else if (OS.isWindows) { + osSpecificCandidates = optional2Stream( Optional.ofNullable(System.getenv("LOCALAPPDATA")) - .map(prefix -> Paths.get(prefix, appName, "config")), + .map(prefix -> Paths.get(prefix, name, "config")), Optional.ofNullable(System.getProperty("user.home")) - .map(prefix -> Paths.get(prefix, "Application Data", "Local Settings", "Application Data", appName, "config"))); + .map(prefix -> Paths.get(prefix, "Application Data", "Local Settings", "Application Data", name, "config"))); } else { - candidates = JWO.optional2Stream(Optional.ofNullable(System.getProperty("user.home")) - .map(prefix -> Paths.get(prefix, "." + appName, "config"))); + osSpecificCandidates = optional2Stream( + Optional.ofNullable(System.getProperty("user.home") + ).map(prefix -> Paths.get(prefix, "." + name, "config"))); } - return selectCandidate(candidates, + return selectWritableDirectory(streamCat(commonCandidates, osSpecificCandidates), "Using configuration directory '{}'", "Unable to find a usable configuration directory"); } - } diff --git a/src/main/java/net/woggioni/jwo/JWO.java b/src/main/java/net/woggioni/jwo/JWO.java index 2df0e95..6550694 100644 --- a/src/main/java/net/woggioni/jwo/JWO.java +++ b/src/main/java/net/woggioni/jwo/JWO.java @@ -426,40 +426,6 @@ public class JWO { return sb.toString(); } - @SneakyThrows - public static Path computeCacheDirectory(String appName) { - return Stream.of( - Optional.ofNullable(System.getProperty("user.home")) - .map(prefix -> Paths.get(prefix, ".cache", appName)), - Optional.ofNullable(System.getProperty("java.io.tmpdir")).map(Paths::get).map(p -> p.resolve(appName)), - Optional.of(Paths.get("/tmp", appName))) - .flatMap(JWO::optional2Stream) - .filter(JWO::validateCacheDirectory) - .findFirst() - .orElseThrow(() -> newThrowable(FileNotFoundException.class, "Unable to find a usable cache directory")); - } - - private static boolean validateCacheDirectory(Path candidate) { - try { - if (!Files.exists(candidate)) { - Files.createDirectories(candidate); - return true; - } else if (!Files.isDirectory(candidate)) { - log.debug("Cache directory '{}' discarded because it is not a directory", candidate.toString()); - return false; - } else if (!Files.isWritable(candidate)) { - log.debug("Cache directory '{}' discarded because it is not writable", candidate.toString()); - return false; - } else { - log.info("Using cache directory '{}'", candidate.toString()); - return true; - } - } catch (Exception ioe) { - log.debug(String.format("Cache directory '%s' discarded: %s", candidate.toString(), ioe.getMessage()), ioe); - return false; - } - } - public static Optional cast(T value, Class cls) { if (cls.isInstance(value)) { return Optional.of((U) value);