単なるメモで-す。
import java.nio.file.Path; import java.nio.file.WatchEvent; public interface WatcherCallback { void onCall(Path path, WatchEvent<Path> event); }
import static java.nio.file.LinkOption.NOFOLLOW_LINKS; import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.nio.file.attribute.BasicFileAttributes; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; public class WatchKeys { private final WatchService watcher; private final Map<WatchKey, Path> watchKeys = new HashMap<>(); public WatchKeys(Path basedir, final WatchService watcher) throws IOException { this.watcher = watcher; Files.walkFileTree(basedir, new SimpleFileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { register(dir); return FileVisitResult.CONTINUE; } }); } public void register(final Path dir) throws IOException { if (dir == null) return; WatchKey key = dir.register(this.watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); this.watchKeys.put(key, dir); } public void sweep() { for (Iterator<Entry<WatchKey, Path>> it = watchKeys.entrySet().iterator(); it.hasNext();) { Entry<WatchKey, Path> entry = it.next(); if (Files.notExists(entry.getValue(), NOFOLLOW_LINKS)) { entry.getKey().cancel(); it.remove(); } } } public boolean isEmpty() { return watchKeys.isEmpty(); } public void remove(WatchKey key) { watchKeys.remove(key); } public Path get(WatchKey key) { return watchKeys.get(key); } }
import static java.nio.file.LinkOption.NOFOLLOW_LINKS; import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.OVERFLOW; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; public class FileChangeWatcher { private static final Logger log = Logger.getLogger(FileChangeWatcher.class.getName()); private final WatchService watcher; private final WatchKeys watchKeys; private final List<WatcherCallback> callbacks = new ArrayList<>(); public FileChangeWatcher(Path basedir, WatcherCallback...cb) { try { watcher = FileSystems.getDefault().newWatchService(); watchKeys = new WatchKeys(basedir, watcher); callbacks.addAll(Arrays.asList(cb)); } catch (IOException e) { throw new RuntimeException(e); } } public void start() { try (WatchService watchService = this.watcher;) { while (true) { WatchKey key; try { key = watchService.take(); // wait } catch (InterruptedException e) { log.log(Level.WARNING, "interrupted watch service.", e); return; } hundleEvent(key); boolean valid = key.reset(); if (!valid) this.watchKeys.remove(key); watchKeys.sweep(); if (watchKeys.isEmpty()) break; } } catch(IOException e) { throw new RuntimeException(e); } } private void hundleEvent(WatchKey key) throws IOException { for (WatchEvent<?> event: key.pollEvents()) { if (event.kind() == OVERFLOW) continue; WatchEvent<Path> watchEvent = cast(event); Path path = watchKeys.get(key).resolve(watchEvent.context()); if (Files.isDirectory(path, NOFOLLOW_LINKS)) { if (event.kind() == ENTRY_CREATE) watchKeys.register(path); } else if (Files.isRegularFile(path, NOFOLLOW_LINKS)) { for (WatcherCallback cb : this.callbacks) { cb.onCall(path, watchEvent); } } } } @SuppressWarnings("unchecked") private static <T> WatchEvent<T> cast(WatchEvent<?> event) { return (WatchEvent<T>)event; } }