File system events with Java 7

In the last post, I showed how to listen to Linux file system events using C, Ruby and Python.
In this post, we look at Java 7. Java 7 has several new classes in the java.nio.file package that let you listen to file system events. The number of events available is not as extensive as the ones in the C, Ruby and Python example.
If you want to use the inotify mechanism directly in Java, look at the following libraries:
JNotify
inotify-java

In this example, we only look at the features of the new classes that come with Java 7. For this test, I used jdk-7-ea-bin-b144 64 bit on a Linux machine. It should work exactly like that when Java 7 is final.

Here is the source code:


package com.markusjais;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

// Simple class to watch directory events.
class DirectoryWatcher implements Runnable {

    private Path path;

    public DirectoryWatcher(Path path) {
        this.path = path;
    }

    // print the events and the affected file
    private void printEvent(WatchEvent<?> event) {
        Kind<?> kind = event.kind();
        if (kind.equals(StandardWatchEventKinds.ENTRY_CREATE)) {
            Path pathCreated = (Path) event.context();
            System.out.println("Entry created:" + pathCreated);
        } else if (kind.equals(StandardWatchEventKinds.ENTRY_DELETE)) {
            Path pathDeleted = (Path) event.context();
            System.out.println("Entry deleted:" + pathDeleted);
        } else if (kind.equals(StandardWatchEventKinds.ENTRY_MODIFY)) {
            Path pathModified = (Path) event.context();
            System.out.println("Entry modified:" + pathModified);
        }
    }

    @Override
    public void run() {
        try {
            WatchService watchService = path.getFileSystem().newWatchService();
            path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
                    StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);

            // loop forever to watch directory
            while (true) {
                WatchKey watchKey;
                watchKey = watchService.take(); // this call is blocking until events are present

                // poll for file system events on the WatchKey
                for (final WatchEvent<?> event : watchKey.pollEvents()) {
                    printEvent(event);
                }

                // if the watched directed gets deleted, get out of run method
                if (!watchKey.reset()) {
                    System.out.println("No longer valid");
                    watchKey.cancel();
                    watchService.close();
                    break;
                }
            }

        } catch (InterruptedException ex) {
            System.out.println("interrupted. Goodbye");
            return;
        } catch (IOException ex) {
            ex.printStackTrace();  // don't do this in production code. Use a loggin framework
            return;
        }
    }
}

public class FileEventTest {

    public static void main(String[] args) throws InterruptedException {
        Path pathToWatch = FileSystems.getDefault().getPath("/tmp/java7");
        DirectoryWatcher dirWatcher = new DirectoryWatcher(pathToWatch);
        Thread dirWatcherThread = new Thread(dirWatcher);
        dirWatcherThread.start();
        
        // interrupt the program after 10 seconds to stop it.
        Thread.sleep(10000);
        dirWatcherThread.interrupt();

    }
}


This is a simple example on how to use the new classes. I created a new Thread that listens in an infinite loop for changes in the directory “/tmp/java7″. For each event (when a file is created, modified or deleted), the event and the file name is printed to Stdout. Note that this also works when creating or deleting directories.

Basically you create a WatchService, register the directory to watch (with the events to watch for), loop forever, create a WatchKey and poll on the WatchKey for events, then go over the events and do something with them, like printing as in this example. When done processing the events, reset the WatchKey so that it can contain new events.
The method reset returns true if the WatchKey is still valid. When you delete the watched directory, it returns false and in this example, the code breaks out of the while loop and terminates.

Note that in a real production system, you would probably not use System.out.println but do something else, like updating the directory view in a file manager, sending an email (for example, when watching a directory for activities that are not allowed, etc) or other actions.

In this example, I interrupt the program after 10 seconds. This is just to show you how to end watching a directory.

To test it, create the directory “/tmp/java7″ and then create, modify and delete a few files in it. To see the reset method in action, remove the directory. If you want to play longer than 10 seconds, just remove the call to interrupt at the end of the main method.

For more information, see the javadoc of Java 7:
http://download.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*