Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: How to wait for fileChanged to execute?

I'd like to have my thread (the main/EDT) wait until changes to a file occur and then wait. DefaultFileMonitor extends Runnable and hence runs in a thread of its own. Here is a SSCE:

import java.io.File;
import org.apache.commons.vfs.*;
import org.apache.commons.vfs.impl.DefaultFileMonitor;

public class FileChangeListener implements FileListener {
    DefaultFileMonitor fm;
    public final static File logFile = new File("t.txt");

 public void startListening() throws FileSystemException {
    final FileSystemManager fsManager = VFS.getManager();
    final FileObject listendir = fsManager.toFileObject(logFile);

    fm = new DefaultFileMonitor(this);
    fm.addFile(listendir);
    fm.start();
}

     @Override
public void fileCreated(FileChangeEvent fce) throws Exception {
    fileChanged(fce);
}

@Override
public void fileDeleted(FileChangeEvent fce) throws Exception {
    //hmm..why deleted?
}

@Override
public void fileChanged(FileChangeEvent fce) throws Exception {
    System.out.println("fileChanged executed");
}
}

The main:

import java.io.PrintWriter;

public class App {

public static void main(String[] args) {
    FileChangeListener fcl = new FileChangeListener();
    try {
        fcl.startListening();
        final PrintWriter printWriter = new PrintWriter(FileChangeListener.logFile);
        printWriter.println("Hello Threads!");
        printWriter.close();

        //EXECUTE THE FOLLOWING ONLY AFTER fileChanged
        System.out.println("Mission complete.");
    } catch (Exception ex) {
    }
}
}
like image 896
simpatico Avatar asked Jun 18 '26 15:06

simpatico


2 Answers

Append the following to App.main(..) after printWriter.close():

            synchronized (fcl) {
                fcl.wait();
            }
            //EXECUTE THE FOLLOWING ONLY AFTER fileChanged
            System.out.println("Mission complete.");

and append the following to FileChangeListener.fileChanged(..) after System.out.println("fileChanged executed"):

synchronized (this) {
        this.notifyAll();
    }
like image 103
simpatico Avatar answered Jun 20 '26 06:06

simpatico


You could communicate between teh two using "Conditions" : http://download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Condition.html

Basically, create a new "shared" Condition (say fileChanged). Now, whenever the file changes (in fileChanged() trigger this condition (fileChanged.signal()). In your main code, wait for this condition to occur (fileChanged.await()).

Hope you get the idea.


For making the condition accessible to multiple code unit, here is what I can think (decreasing order of preference) :

  1. Assuming you are going to need as many conditions as many files you listen to, create a factory method getCondition(String file path/name/attribute) which will return the Condition object based on the file (its path or name or other attributes). Use this factory method to get the condition in all cases. The factory should internally create new Condition() instances for each new file to be listened to AND must throw away older instances as the processing of the files is complete (so probably you should add a destroy/deleteCondition(String file) method as well.)
  2. Store the condition as a public field in the listener class (kind of hack if you have the listener instance available).
  3. Store the condition as a public static field in the listener class (kind of hack if you have only one listener instance throughout).
like image 32
madhurtanwani Avatar answered Jun 20 '26 07:06

madhurtanwani



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!