improved Application class

This commit is contained in:
2023-02-05 15:23:02 +08:00
parent b212fd2559
commit 3709b27429
2 changed files with 141 additions and 83 deletions

View File

@@ -1,7 +1,8 @@
package net.woggioni.jwo; package net.woggioni.jwo;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.Builder;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -12,9 +13,27 @@ import java.nio.file.Paths;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import static net.woggioni.jwo.JWO.optional2Stream;
import static net.woggioni.jwo.JWO.streamCat;
@Slf4j @Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE) @Builder(builderMethodName = "", builderClassName = "Builder")
class Application { @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) { private static boolean validateConfigurationDirectory(Path candidate) {
try { try {
@@ -46,72 +65,145 @@ class Application {
.filter(Application::validateConfigurationDirectory) .filter(Application::validateConfigurationDirectory)
.peek(p -> log.debug(successMessage, p)) .peek(p -> log.debug(successMessage, p))
.findFirst() .findFirst()
.orElseThrow((Sup<Throwable>)() -> new FileNotFoundException(errorMessage)); .orElseThrow((Sup<Throwable>) () -> 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 @SneakyThrows
private static Path computeCacheDirectory(String appName, String jvmPropertyKey) { private Path selectWritableDirectory(Stream<Path> candidates, String successMessage, String errorMessage) {
Stream<Path> candidates; return candidates
if(OS.isUnix) { .filter(Application::validateWritableDirectory)
candidates = JWO.optional2Stream( .peek(p -> log.debug(successMessage, p))
Optional.ofNullable(System.getProperty(jvmPropertyKey)).map(Paths::get), .findFirst()
Optional.ofNullable(System.getenv("XDG_CACHE_HOME")).map(prefix -> Paths.get(prefix, appName)), .orElseThrow((Sup<Throwable>) () -> new FileNotFoundException(errorMessage));
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"))
@SneakyThrows
public Path computeCacheDirectory() {
Stream<Path> commonCandidates = optional2Stream(
Optional.ofNullable(cacheDirectoryPropertyKey).map(System::getProperty).map(Paths::get),
Optional.ofNullable(cacheDirectoryEnvVar).map(System::getProperty).map(Paths::get)
);
Stream<Path> 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) { } else if (OS.isUnix) {
candidates = JWO.optional2Stream( osSpecificCandidates = optional2Stream(
Optional.ofNullable(System.getProperty("user.home")) Optional.ofNullable(System.getenv("XDG_CACHE_HOME")).map(prefix -> Paths.get(prefix, name)),
.map(prefix -> Paths.get(prefix, "Library", "Application support", appName, "cache")), Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, ".cache", name)),
Optional.ofNullable(System.getProperty("user.home")) Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + name, "cache"))
.map(prefix -> Paths.get(prefix, "." + appName, "cache"))
); );
} else if(OS.isWindows) { } else if (OS.isWindows) {
candidates = JWO.optional2Stream( osSpecificCandidates = optional2Stream(
Optional.ofNullable(System.getenv("LOCALAPPDATA")) Optional.ofNullable(System.getenv("LOCALAPPDATA"))
.map(prefix -> Paths.get(prefix, appName, "cache")), .map(prefix -> Paths.get(prefix, name, "cache")),
Optional.ofNullable(System.getProperty("user.home")) Optional.ofNullable(System.getProperty("user.home"))
.map(prefix -> Paths.get(prefix, "Application Data", "Local Settings", "Application Data", appName, "cache"))); .map(prefix -> Paths.get(prefix, "Application Data", "Local Settings", "Application Data", name, "cache")));
} else { } else {
candidates = JWO.optional2Stream( osSpecificCandidates = optional2Stream(
Optional.ofNullable(System.getProperty("user.home")) Optional.ofNullable(System.getProperty("user.home"))
.map(prefix -> Paths.get(prefix, "." + appName, "cache"))); .map(prefix -> Paths.get(prefix, "." + name, "cache")));
} }
return selectCandidate(candidates, return selectWritableDirectory(streamCat(commonCandidates, osSpecificCandidates),
"Using cache directory '{}'", "Using cache directory '{}'",
"Unable to find a usable cache directory"); "Unable to find a usable cache directory");
} }
@SneakyThrows
public Path computeDataDirectory() {
Stream<Path> commonCandidates = optional2Stream(
Optional.ofNullable(dataDirectoryPropertyKey).map(System::getProperty).map(Paths::get),
Optional.ofNullable(dataDirectoryEnvVar).map(System::getProperty).map(Paths::get)
);
Stream<Path> 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 @SneakyThrows
private static Path computeConfigurationDirectory(String appName, String jvmPropertyKey) { public Path computeConfigurationDirectory() {
Stream<Path> candidates; Stream<Path> commonCandidates = optional2Stream(
if(OS.isUnix) { Optional.ofNullable(configurationDirectoryPropertyKey).map(System::getProperty).map(Paths::get),
candidates = JWO.optional2Stream( Optional.ofNullable(configurationDirectoryEnvVar).map(System::getProperty).map(Paths::get)
Optional.ofNullable(System.getProperty(jvmPropertyKey)).map(Paths::get), );
Optional.ofNullable(System.getenv("XDG_CONFIG_HOME")).map(prefix -> Paths.get(prefix, appName)), Stream<Path> osSpecificCandidates;
Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, ".config", appName)), if (OS.isMac) {
Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + appName, "config")) osSpecificCandidates = optional2Stream(
);
} else if(OS.isMac) {
candidates = JWO.optional2Stream(
Optional.ofNullable(System.getProperty("user.home")) Optional.ofNullable(System.getProperty("user.home"))
.map(prefix -> Paths.get(prefix, "Library", "Application support", 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, "." + appName, "config")) Optional.ofNullable(System.getProperty("user.home")).map(prefix -> Paths.get(prefix, "." + name, "config"))
); );
} else if(OS.isWindows) { } else if (OS.isUnix) {
candidates = JWO.optional2Stream( 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")) 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")) 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 { } else {
candidates = JWO.optional2Stream(Optional.ofNullable(System.getProperty("user.home")) osSpecificCandidates = optional2Stream(
.map(prefix -> Paths.get(prefix, "." + appName, "config"))); Optional.ofNullable(System.getProperty("user.home")
).map(prefix -> Paths.get(prefix, "." + name, "config")));
} }
return selectCandidate(candidates, return selectWritableDirectory(streamCat(commonCandidates, osSpecificCandidates),
"Using configuration directory '{}'", "Using configuration directory '{}'",
"Unable to find a usable configuration directory"); "Unable to find a usable configuration directory");
} }
} }

View File

@@ -426,40 +426,6 @@ public class JWO {
return sb.toString(); 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 <T, U extends T> Optional<U> cast(T value, Class<U> cls) { public static <T, U extends T> Optional<U> cast(T value, Class<U> cls) {
if (cls.isInstance(value)) { if (cls.isInstance(value)) {
return Optional.of((U) value); return Optional.of((U) value);