Kotlin Watch Service

The java.nio.file package has a WatchService class that is used to watch for changes in a folder. This is a Kotlin program that demonstrates how to create a watch service that monitors a folder for changes and reports the changes.

package ch9.files

import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardWatchEventKinds
import java.nio.file.WatchService

private fun prompt(msg : String) : String {
    print("$msg => ")
    return readLine() ?: ""
}

private fun Path.watch() : WatchService {
    //Create a watch service
    val watchService = this.fileSystem.newWatchService()

    //Register the service, specifying which events to watch
    register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.OVERFLOW, StandardWatchEventKinds.ENTRY_DELETE)

    //Return the watch service
    return watchService
}

fun main(args : Array<String>){
    val folder = prompt("Enter a folder to watch")
    val path = Paths.get(folder)

    val watcher = path.watch()
    println("Press ctrl+c to exit")

    while(true){
        //The watcher blocks until an event is available
        val key = watcher.take()

        //Now go through each event on the folder
        key.pollEvents().forEach { it ->
            //Print output according to the event
            when(it.kind().name()){
                "ENTRY_CREATE" -> println("${it.context()} was created")
                "ENTRY_MODIFY" -> println("${it.context()} was modified")
                "OVERFLOW" -> println("${it.context()} overflow")
                "ENTRY_DELETE" -> println("${it.context()} was deleted")
            }
        }
        //Call reset() on the key to watch for future events
        key.reset()
    }
}

Here is what it looked like when run on my machine.

Enter a folder to watch => /users/stonesoup/downloads
Press ctrl+c to exit
bob.json was created
bob.json was deleted

While the program was running, I created a bob.json file in my Downloads folder and then deleted it.

Explanation

The first task is to register the Watch Service. The example program has an Path.watch() extension function that encapsulates creating a watch service, registering it, and then returning it to the caller. The Watch Service is obtained from Path.fileSystem.newWatchService() method (line 15). The next step is to register the Watch Service using the Path.register() method (line 18). When registering the Watch Service, we can pass in number of StandWatchEventKinds to tell the Watch Service what to watch.

The main method collects a path from the user (line 25), creates a Path object from the input (line 26), and then registers the Watch Service (line 28). At this point, we enter into an infinite loop and watch the target folder for changes.

The first action in the loop is watcher.take() (line 33). The take() method blocks the thread until an event happens. When a monitored watch event takes place, the take() method will return a WatchKey(). The WatchKey() holds any number of Watch Events that have happened since the last watch cycle.

The example program calls WatchKey.pollEvents().forEach and goes through each watch event (line 36). It uses the WatchEvent.kind().name property (line 38-43) to print output according to each event. Notice how the program combines a when() function to react to each kind of watch event (lines 38-43). When we are done processing all events, we call reset() on the WatchKey() so that the program can wait for the next event. We can also end the WatchService by calling cancel() on the WatchKey.

References

https://docs.oracle.com/javase/8/docs/api/?java/io/File.html

Leave a comment