Utility for watching file changes using Java 7 WatchService. Written to allow myself working with file matching and file watching that feels more natural to me.
You could find it useful as a library, or just read source for examples of using Java 7 WatchService.
Latest release is 0.2.0 but you can build the current SNAPSHOT with maven and use that.
Main.java is a general purpose tool for direct command line usage.
To use it, just download latest java-watcher-0.2.0-shaded.jar
from maven central
or sonatype oss
or build the project with maven and use the shaded jar from target folder.
Without parameter it will display help information
> java -jar java-watcher-0.2.0-shaded.jar
Usage: folder script [arguments]
--burstDelay=x - number of miliseconds to wait before sending changes
(some programs may generate more than one chenge event in very short time when writing a file)
--include=pattern - can be used multiple times, defines an include pattern
--include=pattern - can be used multiple times, defines an include pattern
--exclude=pattern - can be used multiple times, defines an include pattern
Example patterns
*.txt - all files ending with .txt in root folder
**.txt - all files ending with .txt in all folders
nice/*.txt - all files ending with .txt in fodler "nice"
nice/**.txt - all files ending with .txt in all subfolders of "nice"
nice/first.txt - exactly that file
Example usage example.bat and example script in php to catch the changes example.php
java -jar java-watcher-0.2.0-shaded.jar testFolder http://localhost/test/example.php --burstDelay=50 --include=**.txt --exclude=**.html --exclude=**.doc
Add maven dependency or download from maven central or download from sonatype oss
<dependency>
<groupId>hr.hrg</groupId>
<artifactId>java-watcher</artifactId>
<version>0.2.0</version>
</dependency>
The library uses org.slf4j:slf4j-simple
for simple logging and you will likely will want to exclude it and use logback or log4j in your project.
<dependency>
<groupId>hr.hrg</groupId>
<artifactId>java-watcher</artifactId>
<version>0.2.0</version>
<exclusions>
<exclusion>
<artifactId>org.slf4j</artifactId>
<groupId>slf4j-simple</groupId>
</exclusion>
</exclusions>
</dependency>
SimpleCompileExample.java is an example showing how to compile sass file on change
// GlobWatcher implements AutoCloseable. Use it in try-with-resources or call .close() manually
try( GlobWatcher watcher = new GlobWatcher(Paths.get("./"), false) ){
// this one is configured for current folder without checking sub-folders (second param is false)
// if we do not define include rules, any file found will be accepted by the internal matcher
// and we are interested in .scss files only
watcher.includes("*.scss"); // this rule will match any .scss file directly in root folder
// init with intention to watch the files after that
watcher.init(true);
// no configuration should happen after the init or it will give unexpected results
// after init you can request all files that were found and for example recompile
// everything before going to watch mode
// Collection<Path> matchedFiles = watcher.getMatchedFiles();
while(!Thread.interrupted()){
Collection<FileChangeEntry<FileMatchGlob>> changedFiles = watcher.takeOrNull();
if(changedFiles == null) break; // interrupted
for (FileChangeEntry<FileMatchGlob> changed : changedFiles) {
compileSass(changed.getPath());
}
}
}
SimpleFindFiles.java is an e xample showing how to find files using {@link FileMatchGlob} by adding few include/exclude rules.
// create matcher on current folder also checking sub-folders
FileMatchGlob matcher = new FileMatchGlob(Paths.get("./"), true);
// if we do not define rules, then any file found will be accepted
// match any .scss file in root folder, and any .scss in subfolders
matcher.includes("*.scss","**/*.scss").excludes(".sass-cache");
FolderWatcher.fillMatcher(matcher);
for(Path path :matcher.getMatched()){
System.out.println("found: " path);
}
ComplexCompileExample.java is a more complex example showing :
- how to compile sass file on change
- how to compile all main files when an include file changes.
- how to avoid redundant compilation when burst change occurs (some editors might triger more than one change event in few miliseconds time)
// tweak this depending how responsive you want to be, but to still catch some duplicate changes
long burstDelay = 20;
// for collecting files to compile, and to skip duplicates
HashSet<Path> todo = new HashSet<>();
// GlobWatcher implements AutoCloseable. Use it in try-with-resources or call .close() manually
try( GlobWatcher watcher = new GlobWatcher(Paths.get("./scss"), true) ){
// "**.scss" matches in all folders and subfolders
watcher.includes("**.scss");
// create matcher on current folder without checking sub-folders for the source scss
FileMatchGlob sourceFiles = new FileMatchGlob(Paths.get("./"), false);
// if we do not define rules, then any file found will be accepted
// match any .scss file in root folder only
sourceFiles.includes("*.scss");
// add the additional matcher to listen for changes too
watcher.add(sourceFiles);
//start watching, no configuration should happen after this as it wil give unexpected results
watcher.init(true);
Collection<FileChangeEntry<FileMatchGlob>> changedFiles = null;
while(!Thread.interrupted()){
changedFiles = watcher.takeBatch(burstDelay);
if(changedFiles == null) break; // interrupted
for (FileChangeEntry<FileMatchGlob> changed : changedFiles) {
if(changed.getMatcher() == sourceFiles){
// a source file changed, add only it for recompilation
todo.add(changed.getPath());
}else{
// if any file in include folder changes, we want to recompile all source scss files
todo.addAll(sourceFiles.getMatched());
}
}
for(Path path: todo){
compileSass(path);
}
}
}
See the LICENSE file for license rights and limitations (MIT).