Masoud Kalali has a software engineering degree and has been working on software development projects since 1998. He has experience with a variety of technologies (.NET, J2EE, CORBA, and COM+) on diverse platforms (Solaris, Linux, and Windows). His experience is in software architecture, design, and server-side development.

Masoud has published several articles at Java.net and Dzone. He has authored multiple refcards, published by Dzone, including but not limited to Using XML in Java, Java EE Security and GlassFish v3 refcardz. He is one of the founder members of NetBeans Dream Team and a GlassFish community spotlighted developer. Recently Masoud's new book, GlassFish Security has been published which covers GlassFish v3 security and Java EE 6 security.

Masoud's main area of research and interest includes service-oriented architecture and large scale systems' development and deployment and in his leisure time he enjoys photography, mountaineering and camping. Masoud's can be followed at his Twitter account.

Masoud has posted 82 posts at DZone. You can read more from them at their website. View Full User Profile

Introducing NIO.2 (JSR 203) Part 5: Watch Service and Change Notification

08.11.2010
| 9217 views |
  • submit to reddit

For a long time Java developers used in-house developed solutions to monitor the file system for changes. Some developed general purpose libraries to ease the task for others who deal with the same requirement.  Commercial and  free/ open source libraries like http://jnotify.sourceforge.net/, http://jpathwatch.wordpress.com/ and  http://www.teamdev.com/jxfilewatcher among others. Java 7 comes with NIO.2 or JSR 203 which provides a native file system watch service.

The  watch service provided in Java 7  uses the underlying file system functionalities to watch the file system for changes, so if we are running on Windows, MacOS or Linux… we are sure that the watch service is not imposing polling overhead on our application because the underlying OS and file system provides the required functionalities to allow Java to register for receiving notification on file system changes. If the underlying file system does not provide the watch-ability, which I doubt it for any mainstream file system,  Java will fall back to some rudimentary polling mechanism to keep the code working but the performance will degrade.

From the mentioned libraries the jpathwatch API is the same as the Java 7 APIs to make it easier to migrate an IO based application from older version of Java to Java 7 when its time arrives.

The whole story starts with WatchService which we register our interest in watching a path using it. The WatchService itself is an interface with several implementatins for different file system and operating systems. We have four class to work with when we are developing a system with file system watch capability.
  1. A Watchable: A watchable is an object of a class implementing the Watchable interface. In our case this is the Path class which is the one of the central classes in the NIO.2
  2. A set of event types: We use it to specify which types of events we are interested in. For example whether we want to receive creation, deletion, … events. In our case we will use StandardWatchEventKind which implements the WatchEvent.Kind<T>.
  3. An event modifier: An event modifier that qualifies how a Watchable is registered with a WatchService. In our case we will deal with nothing specific up to now as there is no implementation of this interface included in the JDK distribution.
  4. The Wacher: This is the watcher who watch some watchable. In our case the watcher watches the File System for changes. The abstract class is java.nio.file.WatchService but we will be using the FileSystem object to create a watcher for the File System.

Now that we know the basics, let’s see how a complete sample will look like and then we will break down the sample into different parts and discuss them one by one.

import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKind;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class WatchSer {
public static void main(String args[]) throws InterruptedException {
try {
FileSystem fs = FileSystems.getDefault();
WatchService ws = null;
try {
ws = fs.newWatchService();
} catch (IOException ex) {
Logger.getLogger(WatchSer.class.getName()).log(Level.SEVERE, null, ex);
}
Path path = fs.getPath("/home/masoud/Pictures");
path.register(ws, StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_MODIFY, StandardWatchEventKind.OVERFLOW, StandardWatchEventKind.ENTRY_DELETE);

WatchKey k = ws.take();

List> events = k.pollEvents();
for (WatchEvent object : events) {
if (object.kind() == StandardWatchEventKind.ENTRY_MODIFY) {
System.out.println("Modify: " + object.context().toString());
}
if (object.kind() == StandardWatchEventKind.ENTRY_DELETE) {
System.out.println("Delete: " + object.context().toString());
}
if (object.kind() == StandardWatchEventKind.ENTRY_CREATE) {
System.out.println("Created: " + object.context().toString());
}
}
} catch (IOException ex) {
Logger.getLogger(WatchSer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

At the beginning of the code we create a FileSystem object and then create a WatchService for the file system underneath the FileSystem object we created previously.

            FileSystem fs = FileSystems.getDefault();
WatchService ws = null;
try {
ws = fs.newWatchService();
} catch (IOException ex) {
Logger.getLogger(WatchSer.class.getName()).log(Level.SEVERE, null, ex);
}

At the next step we create a Path object which basically is our watchable, what we want to watch, and then register it with the WatchService object we created in the previous step.

  Path path = fs.getPath("/home/masoud/Pictures");
path.register(ws, StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_MODIFY, StandardWatchEventKind.OVERFLOW, StandardWatchEventKind.ENTRY_DELETE);

Next we get a key which represents the registration of a watchable with a watch service.
This WatchKey object represents our access door to events propagated by the WatchService object.

After getting the key, we can poll for the events arrived at the key. An File system event may get represented by multiple WatchService events. For example a rename is represented by deletion and creation events.

 WatchKey key = ws.take();
List> events = key.pollEvents();

Now that we have the list of events we can iterate over the events list and see whether we are intrested in the event or not.

for (WatchEvent object : events) {
if (object.kind() == StandardWatchEventKind.ENTRY_MODIFY) {
System.out.println("Modify: " + path.toRealPath(true)+"/"+ object.context().toString());

}
if (object.kind() == StandardWatchEventKind.ENTRY_DELETE) {
System.out.println("Delete: " + path.toRealPath(true)+"/"+ object.context().toString());
}
if (object.kind() == StandardWatchEventKind.ENTRY_CREATE) {
System.out.println("Created: " + path.toRealPath(true)+"/"+ object.context().toString());
}
}

In our sample code we are running the watch service in the application thread while we can use multiple threads to handle the events. One reason that the WatchService is not implemented using the Listener is the urge for using multiple threads to deal with the events in cases where there are thousands of events or event processing take longer than application threshold.
You can grab the latest version of Java 7 aka Dolphin from here and from the same page you can grab the latest JavaDoc which is generated from the same source code that is used to generate the binary bits. Other entries of this series are under the NIO.2 tag of my weblog.

From http://kalali.me/introducing-nio-2-jsr-203-part-5-watch-service-and-change-notification/

Published at DZone with permission of its author, Masoud Kalali.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Rosarin Roy replied on Wed, 2010/08/11 - 5:27pm

Nice post. In line 21, you should return or re-throw. Otherwise you will get NPE in line 26.

Abel Morelos replied on Fri, 2010/08/27 - 12:06pm

I remember creating my custom solution for this situation when working with Java, I couldn't use any of the mentioned frameworks, so I had to create my own "FileSystemObserver" component. In the other hand with .NET this has been really easy to do ince a long time ago.

King Sam replied on Fri, 2012/02/24 - 10:42am

I’m not java developer but I understand the functionality. I will try to implement it and integrate with my PHP web-app documents management. What I want to create is a syncing full-duplex from the remote folder and local folder (and vice versa).

I will need an authentication for registered user too.

What do you thinking about?
Do I expect some troubles for link a remote path?
Path path = fs.getPath(“URL.COM/home/masoud/Pictures”); ??

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.