Getting Started

To start using jpathwatch, download the library’s JAR file first via the download page and add it to your project.

In the class where you want to do file watching, import jpatchwatch’s package, like so:

import name.pachler.nio.file.*;

The first thing we’ll need to do is to create a WatchService. This object manages all operating system resources required for monitoring files. You’ll create it like this:

WatchService watchService = FileSystems.getDefault().newWatchService();

Now we need something we can watch. Assume we watch a directory called “/tmp” (or “c:\temp” on Windows). Before trying this, make sure it exists on your local hard drive.

jpathwatch needs a special object to be able to monitor a directory, called a Path. We’ll create one now for “/tmp”:

Path watchedPath = Paths.get("/tmp");

The path is only a representative for the directory, it doesn’t actually create a directory or change any files. To monitor this path for changes, we need to register it with the WatchService that we created above, like this:

WatchKey key = null;
try {
    key = path.register(watchService, StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_DELETE);
} catch (UnsupportedOperationException uox){
    System.err.println("file watching not supported!");
    // handle this error here
}catch (IOException iox){
    System.err.println("I/O errors");
    // handle this error here
}

This will associate the Path with the WatchService. Such a registration is represented by a WatchKey that register() creates for us. The ENTRY_CREATE and ENTRY_DELETE constants specify what we want to watch for – in this case files being created and deleted. These constants are called event kinds.

You surely have noticed the try/catch block. This is because registration can fail for a number of reasons, mainly because the platform you’re running on doesn’t support the event kinds you requested.

Now all we have to do now is to wait for changes. In this simple example, we’ll wait for changes in an infinite loop, like this:

for(;;){
    // take() will block until a file has been created/deleted
    WatchKey signalledKey;
    try {
        signalledKey = watchService.take();
    } catch (InterruptedException ix){
        // we'll ignore being interrupted
        continue;
    } catch (ClosedWatchServiceException cwse){
        // other thread closed watch service
        System.out.println("watch service closed, terminating.");
        break;
    }

    // get list of events from key
    List<WatchEvent<?>> list = signalledKey.pollEvents();

    // VERY IMPORTANT! call reset() AFTER pollEvents() to allow the
    // key to be reported again by the watch service
    signalledKey.reset();

    // we'll simply print what has happened; real applications
    // will do something more sensible here
    for(WatchEvent e : list){
        String message = "";
        if(e.kind() == StandardWatchEventKind.ENTRY_CREATE){
            Path context = (Path)e.context();
            message = context.toString() + " created";
        } else if(e.kind() == StandardWatchEventKind.ENTRY_DELETE){
            Path context = (Path)e.context();
            message = context.toString() + " deleted";
        } else if(e.kind() == StandardWatchEventKind.OVERFLOW){
            message = "OVERFLOW: more changes happened than we could retreive";
        }
        System.out.println(message);
    }
}

A couple of things happen here: The call to WatchService.take() is what actually waits for events. take() will not return until a file has been created or deleted (as we requested), or until the WatchService is closed.

When an event has occurred, the WatchKey returned by take is in the ready state (it is signalled), which means that the current thread received it for processing. Note that in our example, the returned key will be the same object that we received previously from register().

To find out what happened, we call WatchKey.pollEvents(), which returns a list of events. After we have that list, we call WatchKey.reset(), which resets the key’s signalled state and allows the key to be reported again by the WatchService.

The example then simply iterates over the list and examines each WatchEvent instance to find out what has happened.

  1. March 31, 2010 at 12:33

    Hi,

    Can you please let me know how to get full path.
    Path context = (Path)e.context(); This line gives only filename.

    Thanks,
    Am

    • March 31, 2010 at 21:58

      When you register a Path with a WatchService using Path.register(), you get a WatchKey as a result. Store both the WatchKey and the Path in a Map.
      Later, when you get the events by calling WatchKey.pollEvents(), you can get the path for that WatchKey by calling get() on your Map.

  2. Gareth
    April 8, 2010 at 23:00

    Hi,

    When implemenenting the code above, the following events get logged for :
    -> a delete – file.txt create, file.txt deleted, file.txt created, file.txt modified i.e. 4 events for a delete.
    -> a modification – file.txt modified, file.txt modified i.e 2 events for a modification.

    Thanks

    • April 8, 2010 at 23:30

      Hey Gareth,

      Which platform are you running on?

      • Gareth
        April 9, 2010 at 00:21

        Hi there,

        Running on Windows 7.

    • April 9, 2010 at 04:49

      A WatchService will only report what you specify in WatchEvent.Kind array passed to Path.register(). If you’re receiving other events as well, it can only be because:
      a) you have also subscribed to other event kinds (ENTRY_CREATE, ENTRY_MODIFY) and
      b) these events actually happened on the file after you’ve created the WatchKey with Path.register()

      The multiple modification events you’re seeing come from the fact that it is not specified how often such a notification happens. It could happen as often as for every byte that an application writes to a file, or only every couple of seconds, or something in between. So seeing multiple modify events is perfectly fine; in fact you can’t rely on whether you’ll see just a single one or multiple ones.

  3. April 16, 2010 at 19:37

    Hi

    I am trying to run the above example on Mac OS 10.6, however i get the following exception:

    java.lang.UnsatisfiedLinkError: no jpathwatch-native in java.library.path
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1792)

    Would it be possible to get some help with this? this library is very interesting and I would like to do a proof of concept for something that I am working on and this is kind of the last piece of the puzzle.

    Thanks
    Amin

    • April 16, 2010 at 23:40

      Hey Amin,
      There is currently an issue with 64 bit Macs in 0.90 and 0.91. The issue is already fixed in SVN (as of yesterday), but I haven’t done a release yet.

      The problem is that there simply is no library for 64 bit OSX, and jpathwatch doesn’t discriminate between the two.

      So until I release 0.92 (which should happen in the next view days), you can use the polling fallback in 0.91 (activated by calling BootStrapper.setForcePollingEnabled(true)), or boot your Mac in 32 bit mode (by holding the ‘3’ and ‘2’ keys down during boot, if I remember correctly; I don’t own a mac 😉

  4. Morris
    April 27, 2010 at 21:38

    I know that watchService.take() will not return until a modification has occurred but is there a way for the watchService to have a time limit of how long to wait for such a notification and return if it has not found anything?

    • April 30, 2010 at 00:20

      Use WatchService.poll(). It’s in the docs 😉

  5. Peter
    May 7, 2010 at 08:37

    Hello,
    how could I watch for coping ending? For example, I download huge file from ftp to my local dir. The file is downloaded for example for 1 hour. I wish to get info about new file after whole file is copied, not when it is initially created on my local machine when I start to download. Is it possible with jpathwatch?

    • May 7, 2010 at 09:52

      Someone on the mailing list asked the same question this week, I have explained it there in detail.
      Essentially you listen for ENTRY_MODIFIED events on the file. If there are no more events for a while (a couple of seconds probably), you’ll just assume the file has finished downloading.
      Very few OSs have support for actively tracking files being opened and closed, therefore there is no direct support (yet) in jpathwatch. Windows certinaly doesn’t support it.

  1. No trackbacks yet.
Comments are closed.