Watching Files With Java NIO
Watching Files With Java NIO
Learn more about watching files with Java NIO.
Join the DZone community and get the full member experience.Join For Free
The java.nio.file package provides a file change notification API, called the Watch Service API. It enables us to register a folder with the watch service. When registering, we tell the service which types of events we are interested in: file creation, file modification, or file deletion.
You may also like: Java IO and NIO
When the service detects an event of interest, it is forwarded to the registered process and handled as needed. This is basically how it works:
- The first step is to create a new
WatchServiceby using the
newWatchService()method of the
- Next, we register a
Pathinstance for the folder to be monitored with the types of events that we are interested in.
- And at last, we implement an infinite loop to wait for incoming events. When an event occurs, the key is signaled and placed into the watcher's queue. After processing its events, we need to put it back into a
readystate by invoking its
reset()method. If it returns false, the key is no longer valid and the loop can exit.
This is the console output:
The WatchService API is fairly low level, allowing us to customize it. In this article, and following the Observer pattern, we are going to design a high-level API on top of this mechanism for listening to file events for a given folder. We will begin by creating a
FileEvent class, which extends the
java.util.EventObject from which all event state objects shall be derived. A
FileEvent instance is constructed with a reference to the source, which is logically the file upon which the event occurred upon.
Next, we create the
FileListener interface that must be implemented by an observer in order to be notified for file events. It extends the
java.util.EventListener interface, which is a tagging interface that all event listener interfaces must extend.
The last piece of the puzzle is to create the subject, which maintains the list of observers, and notifies them of any state changes, by calling one of their methods. We are going to name it
FileWatcher and given a folder, this is how an instance of this class is constructed.
It can implement the
Runnable interface, so we can start the watch process with a daemon thread when invoking its
watch() method if the folder exists.
In the implementation of its
run() method, a
WatchService instance is created to poll for events within a try-with-resources statement. We will keep a track of it using a static final list in the
FileWatcher class, so we can later invoke its
close() method to cause any thread waiting to retrieve keys, to throw the unchecked
ClosedWatchServiceException, which will interrupt the watch process in a clean way. Therefore, we will get no memory leak warnings when the application is being gracefully shutdown.
Whenever an event occurs, the file path is resolved and the listeners are notified accordingly. If it is the creation of a new folder, another
FileWatcher instance will be created for its monitoring.
Here is the complete listing of the
The final touch of our design can be the creation of a
FileAdapter class, which provides a default implementation of the
FileListener interface so that we can process only few of the events to save code.
FileAdapter class is very useful in my case, to reload a Groovy script when developing a servlet application within my IDE. When a file is modified and republished in the deployment directory, it is first deleted before being recreated. Therefore, the modification event — which is fired twice on my Windows platform — can be ignored and its deletion counterpart is unusable in my context.
This is because, currently, we can't unregister a servlet, filter, or listener from the web container. Thus, I found no reason yet to have such feature enabled in production. Also, in this use case, performance is not even a concern since it will be hard to have even five packages to be watched by a different
Even though it is said to use the
Thread.sleep() method in a unit test is generally a bad idea, we are going to use it to write a test case for the
FileWatcher class since we need a delay between the operations.
In my previous article, "Groovify Your Java Servlets (Part 2): Scripting the JVM", I shown how to instantiate an object from a script with the Groovy Script Engine using a simple
ScriptManager class. This one may be the perfect opportunity for me to correct its implementation, by replacing the deprecated
Class.newInstance() method with the
Class.getConstructor().newInstance() method in order to make it right without the exceptions thrown.
The class above can't load scripts located in the subdirectories of the given folder unless you pass the relative path in the script name argument. That is the reason why it is better to write it like this:
Further ReadingJava IO and NIO
Opinions expressed by DZone contributors are their own.