
org.grails.io.watch.MacOsWatchServiceDirectoryWatcher Maven / Gradle / Ivy
package org.grails.io.watch;
import java.nio.file.WatchEvent.Kind;
import io.methvin.watchservice.MacOSXListeningWatchService;
import io.methvin.watchservice.WatchablePath;
import static io.methvin.watcher.DirectoryChangeEvent.EventType.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Implementation of a {@link AbstractDirectoryWatcher} that uses {@link java.nio.WatchService}.
* This implementation is used for Java 7 and later.
* @author Eric Helgeson
* @author David Estes
* @since 3.2
* @see DirectoryWatcher
*/
class MacOsWatchServiceDirectoryWatcher extends AbstractDirectoryWatcher {
private static final Logger LOG = LoggerFactory.getLogger(MacOsWatchServiceDirectoryWatcher.class);
private Map> watchKeyToExtensionsMap = new ConcurrentHashMap>();
private Set individualWatchedFiles = new HashSet();
private final WatchService watchService;
@SuppressWarnings("unchecked")
private static WatchEvent cast(WatchEvent> event) {
return (WatchEvent)event;
}
public MacOsWatchServiceDirectoryWatcher(){
try {
watchService = new MacOSXListeningWatchService();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void run() {
while (active) {
try {
WatchKey watchKey;
try {
watchKey = watchService.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent> watchEvent : watchKey.pollEvents()) {
WatchEvent.Kind> kind = watchEvent.kind();
if (kind == StandardWatchEventKinds.OVERFLOW) {
// TODO how is this supposed to be handled? I think the best thing to do is ignore it, but I'm not positive
LOG.warn("WatchService Overflow occurred");
continue;
}
WatchEvent pathWatchEvent = cast(watchEvent);
Path child = pathWatchEvent.context();
File childFile = child.toFile();
if(individualWatchedFiles.contains(child) || individualWatchedFiles.contains(child.normalize())){
if(kind == StandardWatchEventKinds.ENTRY_CREATE){
fireOnNew(childFile);
}else if(kind == StandardWatchEventKinds.ENTRY_MODIFY){
fireOnChange(childFile);
}else if(kind == StandardWatchEventKinds.ENTRY_DELETE){
// do nothing... there's no way to communicate deletions
}
}else{
List fileExtensions = watchKeyToExtensionsMap.get(watchKey);
if(fileExtensions==null){
// this event didn't match a file in individualWatchedFiles so it's a not an individual file we're interested in
// this event also didn't match a directory that we're interested in (if it did, fileExtentions wouldn't be null)
// so it must be event for a file we're not interested in. An example of how this can happen is:
// there's a directory with files in it like this:
// /images/a.png
// /images/b.png
// by using the addWatchFile method, /images/a.png is watched.
// Now, /images/b.png is changed. Because java.nio.file.WatchService watches directories, it gets a WatchEvent
// for /images/b.png. But we aren't interested in that.
LOG.debug("WatchService received an event for a file/directory that it's not interested in.");
}else{
if(kind==StandardWatchEventKinds.ENTRY_CREATE){
// new directory created, so watch its contents
addWatchDirectory(child,fileExtensions);
if(childFile.isDirectory() && childFile.exists()) {
final File[] files = childFile.listFiles();
if(files != null) {
for (File newFile : files) {
if(isValidFileToMonitor(newFile, fileExtensions)) {
fireOnNew(newFile);
}
}
}
}
}
if(isValidFileToMonitor(childFile,fileExtensions)){
if(kind == StandardWatchEventKinds.ENTRY_CREATE){
fireOnNew(childFile);
}else if(kind == StandardWatchEventKinds.ENTRY_MODIFY){
fireOnChange(childFile);
}else if(kind == StandardWatchEventKinds.ENTRY_DELETE){
// do nothing... there's no way to communicate deletions
}
}
}
}
}
watchKey.reset();
} catch (Exception e) {
LOG.error(e.toString());
// ignore
}
}
try {
watchService.close();
} catch (IOException e) {
LOG.debug("Exception while closing watchService", e);
}
}
@Override
public void addWatchFile(File fileToWatch) {
if(!isValidFileToMonitor(fileToWatch, Arrays.asList("*"))) return;
try {
if(!fileToWatch.exists()) return;
Path pathToWatch = fileToWatch.toPath().toAbsolutePath();
individualWatchedFiles.add(pathToWatch);
WatchablePath watchPath = new WatchablePath(pathToWatch);
Kind[] events = new Kind[] { StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY };
watchPath.register(watchService, events);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void addWatchDirectory(File dir, final List fileExtensions) {
Path dirPath = dir.toPath();
addWatchDirectory(dirPath, fileExtensions);
}
private void addWatchDirectory(Path dir, final List fileExtensions) {
if(!isValidDirectoryToMonitor(dir.toFile())){
return;
}
try {
//add the subdirectories too
Files.walkFileTree(dir, new SimpleFileVisitor() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException
{
if(!isValidDirectoryToMonitor(dir.toFile())){
return FileVisitResult.SKIP_SUBTREE;
}
WatchablePath watchPath = new WatchablePath(dir);
Kind[] events = new Kind[] { StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY };
WatchKey watchKey = watchPath.register(watchService, events);
final List originalFileExtensions = watchKeyToExtensionsMap.get(watchKey);
if(originalFileExtensions==null){
watchKeyToExtensionsMap.put(watchKey, fileExtensions);
}else{
final HashSet newFileExtensions = new HashSet(originalFileExtensions);
newFileExtensions.addAll(fileExtensions);
watchKeyToExtensionsMap.put(watchKey, Collections.unmodifiableList(new ArrayList(newFileExtensions)));
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy