import java.nio.file.*; import static java.nio.file.StandardWatchEventKinds.*; sclass FileWatchService implements AutoCloseable { settable int interval = 50; // larger interval helps show less duplicate events (https://stackoverflow.com/questions/16777869/java-7-watchservice-ignoring-multiple-occurrences-of-the-same-event/34818620#34818620) int sleepSeconds = 60; // any drawback to this being very high? not sure WatchService watchService; new MultiMap> listeners; Thread pollThread; volatile bool closed; *() ctex { watchService = FileSystems.getDefault().newWatchService(); } public void close { set closed; dispose watchService; } WatchKey addListenerImpl(File path, IVF1 listener, WatchEvent.Kind... eventKinds) ctex { if (path == null) null; startPollThread(); WatchKey key = path.toPath().register(watchService, eventKinds); listeners.put(key, listener); ret key; } void addNonRecursiveListener(File path, IVF1 listener) { addListenerImpl(path, listener, jdk_watchService_allEventKinds()); } void addRecursiveListener(File path, IVF1 listener) { Set listeningTo = synchroSet(); IVF1 listener2 = new IVF1 { public void get(File f) { // TODO: remove listeners on deleted directory bool isDir = f.isDirectory(); if (isDir && listeningTo.add(f)) pcall-short { addListenerImpl(f, this, jdk_watchService_allEventKinds()); } callF(listener, f); } }; addListenerImpl(path, listener2, jdk_watchService_allEventKinds()); for (File subDir : findAllDirs(path)) addListenerImpl(subDir, listener2, jdk_watchService_allEventKinds()); } synchronized void startPollThread() { if (pollThread != null) ret; pollThread = startThread("File Watch Service", r { try { while (licensed() && !closed) { WatchKey key = watchService.poll(sleepSeconds, TimeUnit.SECONDS); if (key != null) { Path dir = cast key.watchable(); sleep(interval); L> events = key.pollEvents(); new LinkedHashSet changedFiles; for (WatchEvent event : reversedIterator(events)) pcall { final Path changed = (Path) event.context(); if (changed == null) continue; File full = newFile(dir.toFile(), changed.toFile().getPath()); changedFiles.add(full); //print("WatchService Changed: " + full + ", " + event.kind()); } for (File f : changedFiles) pcallFAll(listeners.get(key), f); key.reset(); } } } catch (ClosedWatchServiceException e) {} }); } }