Skip to content

Instantly share code, notes, and snippets.

@Leo40Git
Created June 19, 2022 18:04
Show Gist options
  • Save Leo40Git/f0a44aa687b7c08e4c8568e900070024 to your computer and use it in GitHub Desktop.
Save Leo40Git/f0a44aa687b7c08e4c8568e900070024 to your computer and use it in GitHub Desktop.
File Watcher Service prototype
package adudecalledleo.filewatch;
import java.nio.file.Path;
@FunctionalInterface
public interface FileListener {
void onChange(Path path);
}
package adudecalledleo.filewatch;
import java.nio.file.Path;
import java.util.*;
final class FileListenerMultimap {
private final Map<Path, List<FileListener>> backingMap;
public FileListenerMultimap() {
backingMap = new HashMap<>();
}
public void put(Path key, FileListener value) {
backingMap.computeIfAbsent(key, path -> new ArrayList<>()).add(value);
}
public List<FileListener> get(Path key) {
return backingMap.getOrDefault(key, Collections.emptyList());
}
}
package adudecalledleo.filewatch;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
public class FileWatcher {
private final class DirectoryData {
private final Path path;
private final WatchKey pathWKey;
private final List<FileListener> listeners;
private final FileListenerMultimap specificListeners;
private DirectoryData(Path path) throws IOException {
this.path = path;
pathWKey = path.register(watchService, StandardWatchEventKinds.OVERFLOW, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
listeners = new ArrayList<>();
specificListeners = new FileListenerMultimap();
}
public void addListener(FileListener listener) {
listeners.add(listener);
}
public void addListenerForChild(Path fileName, FileListener listener) {
specificListeners.put(fileName, listener);
}
public Path getPath() {
return path;
}
public List<FileListener> getListenersForChild(Path fileName) {
return specificListeners.get(fileName);
}
public List<FileListener> getListeners() {
return listeners;
}
}
private final FileSystem fileSystem;
private final WatchService watchService;
private final Map<Path, DirectoryData> dirMap;
public FileWatcher(FileSystem fileSystem) throws IOException {
this.fileSystem = fileSystem;
watchService = fileSystem.newWatchService();
dirMap = new HashMap<>();
}
public void watch(Path path, FileListener listener) throws IOException {
if (path.getFileSystem() != fileSystem) {
throw new IllegalArgumentException("Path isn't in same file system as this watcher!");
}
Path parent = path.getParent();
DirectoryData dirDat = dirMap.get(parent);
if (dirDat == null) {
dirDat = new DirectoryData(parent);
dirMap.put(parent, dirDat);
}
dirDat.addListenerForChild(path.getFileName(), listener);
}
public void watchAll(Path directory, FileListener childListener) throws IOException {
if (directory.getFileSystem() != fileSystem) {
throw new IllegalArgumentException("Path isn't in same file system as this watcher!");
}
DirectoryData dirDat = dirMap.get(directory);
if (dirDat == null) {
dirDat = new DirectoryData(directory);
dirMap.put(directory, dirDat);
}
dirDat.addListener(childListener);
}
public void pump() throws InterruptedException {
WatchKey wKey = watchService.take();
Path path;
if (wKey.watchable() instanceof Path pathIn) {
path = pathIn;
} else {
wKey.cancel();
return;
}
DirectoryData dirDat = dirMap.get(path);
if (dirDat == null) {
wKey.cancel();
return;
}
LinkedHashSet<Path> dedupSet = new LinkedHashSet<>();
for (WatchEvent<?> wEvent : wKey.pollEvents()) {
if (wEvent.context() instanceof Path fileName) {
dedupSet.add(fileName);
}
}
for (Path fileName : dedupSet) {
Path fullPath = path.resolve(fileName);
for (FileListener listener : dirDat.getListeners()) {
listener.onChange(fullPath);
}
for (FileListener listener : dirDat.getListenersForChild(fileName)) {
listener.onChange(fullPath);
}
}
boolean valid = wKey.reset();
if (!valid) {
// TODO error or some shit
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment