001package com.dev9.mvnwatcher.event; 002 003import com.google.common.annotations.VisibleForTesting; 004import com.google.common.eventbus.EventBus; 005 006import java.io.IOException; 007import java.nio.file.*; 008import java.nio.file.attribute.BasicFileAttributes; 009import java.util.List; 010import java.util.Objects; 011import java.util.concurrent.Callable; 012import java.util.concurrent.ExecutionException; 013import java.util.concurrent.FutureTask; 014import java.util.concurrent.TimeUnit; 015 016import static java.nio.file.StandardWatchEventKinds.*; 017 018/** 019 * Based on code written originally by bbejeck 020 */ 021 022public class DirectoryEventWatcherImpl implements DirectoryEventWatcher { 023 024 private FutureTask<Integer> watchTask; 025 private EventBus eventBus; 026 private WatchService watchService; 027 private volatile boolean keepWatching = true; 028 029 public DirectoryEventWatcherImpl(EventBus eventBus) throws IOException { 030 this.eventBus = Objects.requireNonNull(eventBus); 031 if (watchService == null) { 032 watchService = FileSystems.getDefault().newWatchService(); 033 } 034 } 035 036 @Override 037 public void start() throws IOException { 038 createWatchTask(); 039 startWatching(); 040 } 041 042 public void add(Path p) throws IOException { 043 Objects.requireNonNull(p); 044 if (!p.toFile().exists()) 045 throw new IllegalArgumentException("Path " + p.toFile().getAbsolutePath() + " does not actually exist."); 046 047 Files.walkFileTree(p, new WatchServiceRegisteringVisitor()); 048 } 049 050 @Override 051 public boolean isRunning() { 052 return watchTask != null && !watchTask.isDone(); 053 } 054 055 @Override 056 public void stop() { 057 keepWatching = false; 058 } 059 060 @VisibleForTesting 061 public Integer getEventCount() { 062 try { 063 return watchTask.get(); 064 } catch (InterruptedException | ExecutionException e) { 065 throw new RuntimeException(e); 066 } 067 } 068 069 070 private void createWatchTask() { 071 watchTask = new FutureTask<>(new Callable<Integer>() { 072 private int totalEventCount; 073 074 @Override 075 public Integer call() throws Exception { 076 077 // Loop to keep watching until shutdown 078 while (keepWatching) { 079 WatchKey watchKey = watchService.poll(500, TimeUnit.MILLISECONDS); 080 if (watchKey != null) { 081 List<WatchEvent<?>> events = watchKey.pollEvents(); 082 Path watched = (Path) watchKey.watchable(); 083 PathEvents pathEvents = new PathEvents(watchKey.isValid(), watched); 084 for (WatchEvent event : events) { 085 pathEvents.add(new PathEvent((Path) event.context(), event.kind())); 086 totalEventCount++; 087 } 088 watchKey.reset(); 089 eventBus.post(pathEvents); 090 } 091 } 092 093 // Fell out of the watch loop, shut down the service. 094 watchService.close(); 095 096 return totalEventCount; 097 } 098 }); 099 } 100 101 private void startWatching() { 102 new Thread(watchTask).start(); 103 } 104 105 private class WatchServiceRegisteringVisitor extends SimpleFileVisitor<Path> { 106 @Override 107 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { 108 Objects.requireNonNull(dir); 109 Objects.requireNonNull(watchService); 110 111 dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); 112 return FileVisitResult.CONTINUE; 113 } 114 } 115}