Question

I'd like to be notified when a file has been changed in the file system. I have found nothing but a thread that polls the lastModified File property and clearly this solution is not optimal.

Was it helpful?

Solution

At the low level the only way to model this utility is to have a thread polling on a directory and keeping a watch on the attributes of the file. But you can use patterns to develop a adapter for such a utility.

For example j2ee application servers like Tomcat and others have a auto load feature where in as soon as deployment descriptor changes or servlet class changes the application restarts.

You can use the libraries from such servers as most of the code of tomcat is reusable and opensource.

OTHER TIPS

I've written a log file monitor before, and I found that the impact on system performance of polling the attributes of a single file, a few times a second, is actually very small.

Java 7, as part of NIO.2 has added the WatchService API

The WatchService API is designed for applications that need to be notified about file change events.

I use the VFS API from Apache Commons, here is an example of how to monitor a file without much impact in performance:

DefaultFileMonitor

There is a lib called jnotify that wraps inotify on linux and has also support for windows. Never used it and I don't know how good it is, but it's worth a try I'd say.

Java commons-io has a FileAlterationObserver. it does polling in combination with a FileAlterationMonitor. Similar to commons VFS. The advantag is that it has much less dependencies.

edit: Less dependencies is not true, they are optional for VFS. But it uses java File instead of the VFS abstraction layer.

"More NIO features" has file watch functionality, with implementation dependent upon the underlying OS. Should be in JDK7.

I run this snippet of code every time I go to read the properties file, only actually reading the file if it has been modified since the last time I read it. Hope this helps someone.

private long timeStamp;
private File file;

private boolean isFileUpdated( File file ) {
  this.file = file;
  this.timeStamp = file.lastModified();

  if( this.timeStamp != timeStamp ) {
    this.timeStamp = timeStamp;
    //Yes, file is updated
    return true;
  }
  //No, file is not updated
  return false;
}

Similar approach is used in Log4J FileWatchdog.

You can listen file changes using a FileReader. Plz see the example below

// File content change listener 
private String fname;
private Object lck = new Object();
... 
public void run()
{
    try
    {
        BufferedReader br = new BufferedReader( new FileReader( fname ) );
        String s;
        StringBuilder buf = new StringBuilder();
        while( true )
        {
            s = br.readLine();
            if( s == null )
            {
                synchronized( lck )
                {
                    lck.wait( 500 );
                }
            }
            else
            {
               System.out.println( "s = " + s );
            }

        }
    }
    catch( Exception e )
    {
        e.printStackTrace();
    }
}

If you are willing to part with some money, JNIWrapper is a useful library with a Winpack, you will be able to get file system events on certain files. Unfortunately windows only.

See https://www.teamdev.com/jniwrapper.

Otherwise, resorting to native code is not always a bad thing especially when the best on offer is a polling mechanism as against a native event.

I've noticed that Java file system operations can be slow on some computers and can easily affect the application's performance if not handled well.

You may also consider the Apache Commons JCI (Java Compiler Interface). Although this API seems to be focused on dynamic compilation of classes, it also includes classes in its API that monitors file changes.

Example: http://commons.apache.org/jci/usage.html

Spring Integration provides a nice mechanism for watching Directories and files: http://static.springsource.org/spring-integration/reference/htmlsingle/#files. Pretty sure it's cross platform (I've used it on mac, linux, and windows).

There is a commercial cross-desktop library for files and folders watching called JxFileWatcher. It can be downloaded from here: http://www.teamdev.com/jxfilewatcher/

Also you can see it in action online: http://www.teamdev.com/jxfilewatcher/onlinedemo/

Polling the last modified file property is a simple yet effective solution though. Just define a class extending my FileChangedWatcher and implement the onModified() method:

import java.io.File;

public abstract class FileChangedWatcher
{
    private File file;

    public FileChangedWatcher(String filePath)
    {
        file = new File(filePath);
    }

    public void watch() throws InterruptedException
    {
        long currentModifiedDate = file.lastModified();

        while (true)
        {
            long newModifiedDate = file.lastModified();

            if (newModifiedDate != currentModifiedDate)
            {
                currentModifiedDate = newModifiedDate;
                onModified();
            }

            Thread.sleep(100);
        }
    }

    public String getFilePath()
    {
        return file.getAbsolutePath();
    }

    protected abstract void onModified();
}

Similar to the other answers, here's how I did it using File, Timer, and TimerTask to let this run as a background thread polling at set intervals.

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

public class FileModifiedWatcher
{
  private static File file;
  private static int pollingInterval;
  private static Timer fileWatcher;
  private static long lastReadTimeStamp = 0L;

  public static boolean init(String _file, int _pollingInterval)
  {
    file =  new File(_file);
    pollingInterval = _pollingInterval; // In seconds

    watchFile();

    return true;
  }

  private static void watchFile()
  {
    if ( null == fileWatcher )
    {
      System.out.println("START");

      fileWatcher = new Timer();

      fileWatcher.scheduleAtFixedRate(new TimerTask()
      {
        @Override
        public void run()
        {

          if ( file.lastModified() > lastReadTimeStamp )
          {
            System.out.println("File Modified");
          }

          lastReadTimeStamp = System.currentTimeMillis();
        }
      }, 0, 1000 * pollingInterval);
    }

  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top