// Copyright © 2011-2014, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package fi.jumi.core.ipc.dirs;
import javax.annotation.concurrent.NotThreadSafe;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import static java.nio.file.StandardWatchEventKinds.*;
@NotThreadSafe
public class DirectoryObserver implements Runnable {
private final Path directory;
private final Listener listener;
private final Set<Path> seenFiles = new HashSet<>();
public DirectoryObserver(Path directory, Listener listener) {
checkDirectoryExists(directory);
this.directory = directory;
this.listener = listener;
}
private static void checkDirectoryExists(Path directory) {
if (!Files.exists(directory)) {
throw new IllegalArgumentException("Does not exist: " + directory);
}
if (!Files.isDirectory(directory)) {
throw new IllegalArgumentException("Not a directory: " + directory);
}
}
@Override
public void run() {
try {
WatchService watcher = watchDirectory(directory, ENTRY_CREATE);
handleAllFiles();
while (true) {
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException e) {
break;
}
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == OVERFLOW) {
handleAllFiles();
} else {
Path filename = (Path) event.context();
handleFileName(filename);
}
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
} catch (Throwable t) {
throw new RuntimeException("Error in watching directory " + directory, t);
}
}
private void handleAllFiles() throws IOException {
try (DirectoryStream<Path> paths = Files.newDirectoryStream(directory)) { // TODO: Java 8, use Files.list
for (Path path : paths) {
handleFileName(path.getFileName());
}
}
}
private void handleFileName(Path filename) {
if (seenFiles.contains(filename)) {
return;
}
seenFiles.add(filename);
listener.onFileNoticed(directory.resolve(filename));
}
protected WatchService watchDirectory(Path directory, WatchEvent.Kind<?>... events) throws IOException {
WatchService watchService = FileSystems.getDefault().newWatchService();
directory.register(watchService, events);
return watchService;
}
public interface Listener {
void onFileNoticed(Path path);
}
}