Particles/src/main/java/com/alttd/storage/AutoReload.java

172 lines
5.5 KiB
Java
Raw Normal View History

package com.alttd.storage;
import com.alttd.config.ParticleConfig;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class AutoReload {
private final WatchService watchService;
private final Map<WatchKey, Path> keys;
private final Path rootDirectory;
private volatile boolean running = true;
public AutoReload(Path directory) throws IOException {
this.watchService = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<>();
this.rootDirectory = directory;
register(directory);
registerAll(directory);
}
private void registerAll(Path start) throws IOException {
Files.walkFileTree(start, new SimpleFileVisitor<>() {
@Override
public @NotNull FileVisitResult preVisitDirectory(@NotNull Path path, @NotNull BasicFileAttributes attrs) throws IOException {
if (path.toFile().isDirectory()) {
register(path);
}
return FileVisitResult.CONTINUE;
}
});
}
private void register(@NotNull Path dir) throws IOException {
WatchKey key = dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
keys.put(key, dir);
}
public void startWatching() {
log.info("Starting watch thread.");
Thread watchThread = new Thread(() -> {
log.info("Watch thread started.");
while (running) {
log.info("Watch thread loop start");
WatchKey key;
try {
key = watchService.take();
log.info("Watch thread loop key {}", key.toString());
} catch (InterruptedException e) {
log.error("Interrupted while waiting for key", e);
return;
}
if (!running) {
log.info("Exiting watch thread.");
return;
}
Path dir = keys.get(key);
if (dir == null) {
log.warn("Detected unknown key: {}. Ignoring.", key.toString());
continue;
}
detectChanges(key, dir);
if (!key.reset()) {
keys.remove(key);
if (keys.isEmpty()) {
log.info("No longer watching any directories. Exiting.");
break;
}
}
}
});
watchThread.start();
}
private void detectChanges(@NotNull WatchKey key, Path dir) {
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == StandardWatchEventKinds.OVERFLOW) {
log.warn("Detected overflow event. Ignoring.");
continue;
}
Path child = resolveEventPath(event, dir);
boolean isDirectory = Files.isDirectory(child);
if (shouldIgnoreDirectoryEvent(isDirectory, dir)) {
continue;
}
if (kind == StandardWatchEventKinds.ENTRY_CREATE && isDirectory) {
handleNewDirectoryCreation(child);
continue;
}
if (isDirectory) {
continue;
}
handleFileEvent(kind, child);
}
}
private @NotNull Path resolveEventPath(@NotNull WatchEvent<?> event, Path dir) {
Object context = event.context();
if (!(context instanceof Path path)) {
throw new IllegalArgumentException("Expected event context to be a Path, but got: " + context);
}
return dir.resolve(path);
}
private boolean shouldIgnoreDirectoryEvent(boolean isDirectory, Path dir) {
if (isDirectory && !dir.equals(rootDirectory)) {
log.warn("Detected directory {} outside of root directory. Ignoring.", dir);
return true;
}
return false;
}
private void handleNewDirectoryCreation(Path child) {
try {
log.info("Registering new directory: {}", child);
registerAll(child);
} catch (IOException e) {
log.error("Failed to register directory: {}", child);
}
}
private void handleFileEvent(WatchEvent.Kind<?> kind, Path child) {
if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
log.debug("Detected file modification: {}", child);
reloadFile(child);
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
log.debug("Detected file deletion: {}", child);
handleFileDeletion();
} else if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
log.debug("Detected file creation: {}", child);
reloadFile(child);
} else {
log.warn("Unknown event kind: {}", kind);
}
}
private void reloadFile(Path child) {
ParticleConfig.loadParticleFromFile(child.toFile());
}
private void handleFileDeletion() {
log.info("Detected file deletion. Reloading all particles.");
ParticleConfig.reload();
}
public void stop() {
running = false;
}
}