Created
June 19, 2022 18:04
-
-
Save Leo40Git/f0a44aa687b7c08e4c8568e900070024 to your computer and use it in GitHub Desktop.
File Watcher Service prototype
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package adudecalledleo.filewatch; | |
import java.nio.file.Path; | |
@FunctionalInterface | |
public interface FileListener { | |
void onChange(Path path); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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