I can no longer edit this post (why!), so here is the latest version of my file “WatchFileChanges.java”
It contains a modified book version and one which uses Optional (an interesting modification that has to be attempted with an IDE to get unexpected lessons in type inference! )
package chapter3;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.*;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// ---
// Described in "Functional Programming in Java" on page 64.
// Derived from the original "compare/fpij/WatchFileChange.java".
// This file is "src/test/java/chapter3/WatchFileChanges.java"
// Instead of having a main() we code it into a JUnit test case.
// ---
public class WatchFileChanges {
private final static String where = "/home/aloy";
private final static Path thePath = Paths.get(where);
private String stringAddClass(final Object o) {
assert o != null;
return o.getClass().getName() + ": " + o;
}
// As in the book
@Test
public void watchFileChange() throws IOException, InterruptedException {
String txt; // cannot be made final
// Try-with-resources to close the WatchService at the end
// (and thus cancel all the WatchKeys registered with it)
try (final WatchService watchService = thePath.getFileSystem().newWatchService()) {
try {
// No need to retain the WatchKey returned by path.register()
thePath.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("Report the first change on '" + thePath + "' within the next 1 minute...");
// poll() "retrieves and removes the next watch key", i.e. returns with a WatchKey once a
// change has been detected. Otherwise, it times out returning null.
// Note that we do NOT catch the InterruptedException, but leave it up the stack.
// If someone interrupted us, there must be reasons.
WatchKey watchKey = watchService.poll(1, TimeUnit.MINUTES);
if (watchKey == null) {
txt = "Nothing happened at all, the WatchKey is null!";
} else {
txt = "Changes in: " +
watchKey.pollEvents()
.stream()
.map(event -> event.context().toString()) // MAKE STRING!
.collect(Collectors.joining(", "));
}
} catch (NoSuchFileException ex) {
txt = "Looks like there is no filesystem entry '" + thePath + "'";
}
}
System.out.println(txt);
}
// More interesting, using Optional<>
@Test
void watchingAFileChangeFirstMoreInteresting() throws IOException, InterruptedException {
final String txt;
try (final WatchService watchService = thePath.getFileSystem().newWatchService()) {
thePath.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("Report the first change on '" + thePath + "' within the next 1 minute...");
// poll() "retrieves and removes the next watch key", i.e. returns with a WatchKey once a
// change has been detected. Otherwise, it times out returning null.
// Note that we do NOT catch the InterruptedException, but leave it up the stack.
// If someone interrupted us, there must be reasons.
final WatchKey watchKey = watchService.poll(1, TimeUnit.MINUTES);
if (watchKey == null) {
txt = "Nothing happened at all, the WatchKey is (null)!";
} else {
// The typing here is special as WatchEvent<?> may be parametrized by different actual types
{
Stream<WatchEvent<?>> watchKeyStream = watchKey.pollEvents().stream();
Stream<Object> contextStream = watchKeyStream.map(WatchEvent::context);
Stream<Optional<Object>> optContextStream = contextStream.map(Optional::of);
List<Optional<Object>> list = optContextStream.toList();
// The map() makes a string including the class name of
// whatever the "Stream<Optional<Object>>" gave us
txt = "Changes in: " +
list.stream()
.map(opt -> opt.map(o -> stringAddClass(o)).orElse("(null)"))
.collect(Collectors.joining(", "));
}
/* This does not pass typecheck:
{
List<Optional<Object>> list = watchKey.pollEvents().stream()
.map(WatchEvent::context)
.map(Optional::of)
.toList();
String txt = list.stream()
.map(opt -> opt.map(o -> o.getClass().getName() + ": " + o).orElse("(null)"))
.collect(Collectors.joining("\n"));
System.out.println(txt);
}
*/
}
}
System.out.println(txt);
}
}