I've just been playing around with the Java 7 WatchService for monitoring a file for change.
Here's a little bit of code I knocked up:
WatchService watcher = FileSystems.getDefault().newWatchService();
Path path = Paths.get("c:\\testing");
path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
WatchKey key = watcher.take();
for (WatchEvent event : key.pollEvents()) {
System.out.println(event.kind() + ":" + event.context());
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
This seems to be working, and I get notifications as to when a file 'changethis.txt' gets modified.
However, in addition to being able to notify when a file changes, is there anyway of being notified as to the location within the file that the modification occurred?
I've had a look through the Java docs but I can't seem to find anything.
Is this possible using the WatchService, or would something custom have to be implemented?
Thanks
Okay, here is another answer as a variation of my previous one for changes at any file position (diff). Now the somewhat simpler case is files only being appended (tail).
How to build:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.tools</groupId>
<artifactId>SO_WatchServiceChangeLocationInFile</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<!-- Use snapshot because of the UTF-8 problem in https://issues.apache.org/jira/browse/IO-354 -->
<version>2.5-SNAPSHOT</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>apache.snapshots</id>
<url>http://repository.apache.org/snapshots/</url>
</repository>
</repositories>
</project>
As you can see, we use Apache Commons IO here. (Why a snapshot version? Follow the link in the XML comment if you are interested.)
Source code:
package de.scrum_master.app;
import org.apache.commons.io.input.Tailer;
import org.apache.commons.io.input.TailerListenerAdapter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
public class FileTailWatcher {
public static final String DEFAULT_WATCH_DIR = "watch-dir";
public static final int DEFAULT_WATCH_INTERVAL = 5;
private Path watchDir;
private int watchInterval;
private WatchService watchService;
public FileTailWatcher(Path watchDir, int watchInterval) throws IOException {
if (!Files.isDirectory(watchDir))
throw new IllegalArgumentException("Path '" + watchDir + "' is not a directory");
this.watchDir = watchDir;
this.watchInterval = watchInterval;
watchService = FileSystems.getDefault().newWatchService();
}
public static class MyTailerListener extends TailerListenerAdapter {
public void handle(String line) {
System.out.println(line);
}
}
public void run() throws InterruptedException, IOException {
try (DirectoryStream<Path> dirEntries = Files.newDirectoryStream(watchDir)) {
for (Path file : dirEntries)
createTailer(file);
}
watchDir.register(watchService, ENTRY_CREATE);
while (true) {
WatchKey watchKey = watchService.take();
for (WatchEvent<?> event : watchKey.pollEvents())
createTailer(watchDir.resolve((Path) event.context()));
watchKey.reset();
Thread.sleep(1000 * watchInterval);
}
}
private Tailer createTailer(Path path) {
if (Files.isDirectory(path))
return null;
System.out.println("Creating tailer: " + path);
return Tailer.create(
path.toFile(), // File to be monitored
Charset.defaultCharset(), // Character set (available since Commons IO 2.5)
new MyTailerListener(), // What should happen for new tail events?
1000, // Delay between checks in ms
true, // Tail from end of file, not from beginning
true, // Close & reopen files in between reads,
// otherwise file is locked on Windows and cannot be deleted
4096 // Read buffer size
);
}
public static void main(String[] args) throws IOException, InterruptedException {
String watchDirName = args.length > 0 ? args[0] : DEFAULT_WATCH_DIR;
int watchInterval = args.length > 2 ? Integer.getInteger(args[2]) : DEFAULT_WATCH_INTERVAL;
new FileTailWatcher(Paths.get(watchDirName), watchInterval).run();
}
}
Now try appending to existing files and/or creating new ones. Everything will be printed to standard output. In a production environment you would maybe display multiple windows or tabs, one for each log file. Whatever...
@Simon: I hope this one suits your situation better than the more general case and is worth a bounty. :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With