172 lines
5.5 KiB
Java
172 lines
5.5 KiB
Java
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;
|
|
}
|
|
}
|