Watchservice in windows 7 does not work
Asked Answered
M

2

2

This code works fine in Linux but not in Windows 7: to get file contents update I have to click on the output file. Where is the trick?

I am using Windows 7 prof, NetBeans IDE 8.0 RC1 (Build 201402242200) updated to version NetBeans 8.0 Patch 1.1, JDK 1.8

package watchfilethreadmod;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardWatchEventKinds.*;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class WatchFileThreadMod {

static class WatchFile {

    String fileName;
    long lastFilePos;
    RandomAccessFile file;

    public WatchFile(String _fileName, RandomAccessFile _file) {
        fileName = _fileName;
        lastFilePos = 0;
        file = _file;
    }
}

public static void shutDownListener(Thread thread) {
    Thread thr = thread;
    if (thr != null) {
        thr.interrupt();
    }
}

private static class MyWatchQueueReader implements Runnable {

    /**
     * the watchService that is passed in from above
     */
    private WatchService myWatcher;
    public ArrayList<WatchFile> threadFileToWatch;
    public String dirPath;

    public MyWatchQueueReader(String _dirPath, WatchService myWatcher, ArrayList<WatchFile> _threadFileToWatch) {
        this.myWatcher = myWatcher;
        this.threadFileToWatch = _threadFileToWatch;
        this.dirPath = _dirPath;

    }

    private void openFile(WatchFile obj) {

        try {
            System.out.println("Open file "+obj.fileName);
            obj.file = new RandomAccessFile(dirPath + "/" + obj.fileName, "r");                
        } catch (FileNotFoundException e) {
            obj.file = null;
            System.out.println("filename " + obj.fileName + " non trovato");

        }
        obj.lastFilePos = 0;

    }

    private void process(WatchEvent evt) {
        String thisLine;
        ArrayList<WatchFile> auxList = threadFileToWatch;
        for (WatchFile obj : auxList) {

            if (obj.fileName.equals(evt.context().toString())) {
                if (obj.file == null) {
                    openFile(obj);
                }

                try {
                    obj.file.seek(obj.lastFilePos);
                } catch (IOException e) {
                    System.err.println("Seek error: " + e);
                }
                try {     

                    thisLine = obj.file.readLine();
                    if ((thisLine == null)&&(evt.kind() == ENTRY_MODIFY)) {
                        System.out.printf("---> thisLine == null received %s event for file: %s\n",
                        evt.kind(), evt.context());
                        obj.file.close();
                        System.out.println("Close file "+obj.fileName);
                        openFile(obj);
                        thisLine = obj.file.readLine();
                    }

                    while (thisLine != null) { // while loop begins here                                                        
                        if (thisLine.length() > 0) {
                            if (thisLine.substring(thisLine.length() - 1).equals("*")) {
                                obj.lastFilePos = obj.file.getFilePointer();
                                System.out.println(obj.fileName + ": " + thisLine);
                            }
                        }
                        thisLine = obj.file.readLine();
                    } // end while 
                } // end try
                catch (IOException e) {
                    System.err.println("Error: " + e);
                }
            }
        }

    }

    /**
     * In order to implement a file watcher, we loop forever ensuring
     * requesting to take the next item from the file watchers queue.
     */
    @Override
    public void run() {
        try {
            // get the first event before looping
            WatchKey key = myWatcher.take();
            while (key != null) {
                // we have a polled event, now we traverse it and 
                // receive all the states from it
                for (WatchEvent event : key.pollEvents()) {
                    WatchEvent.Kind eventType = event.kind();
                    if (eventType == OVERFLOW) {
                        continue;
                    }
                    process(event);
                }
                key.reset();
                key = myWatcher.take();
            }
        } catch (InterruptedException e) {
            ArrayList<WatchFile> auxList = threadFileToWatch;
            for (WatchFile obj : auxList) {
                if (obj.file != null) {
                    try {
                        obj.file.close();
                        System.out.println("chiusura file " + obj.fileName);
                    } catch (IOException ex) {
                        System.out.println("errore in chiusura file");
                        Logger.getLogger(WatchFileThreadMod.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
            //e.printStackTrace();
        }
        System.out.println("Stopping thread");
    }
}

public static void main(String[] args) throws Exception {
    // get the directory we want to watch, using the Paths singleton class
    //Path toWatch = Paths.get(DIRECTORY_TO_WATCH);
    ArrayList<WatchFile> fileToWatch = new ArrayList<>();

    String filename;
    RandomAccessFile file;
    fileToWatch.add(new WatchFile("EURUSD.rlt", new RandomAccessFile(args[0] + "/EURUSD.rlt", "r")));

    filename = "EURCHF2.rlt";
    try {
        file = new RandomAccessFile(args[0] + "/" + filename, "r");
    } catch (FileNotFoundException e) {
        file = null;
        System.out.println("filename " + filename + " non trovato");
    }
    fileToWatch.add(new WatchFile(filename, file));
    fileToWatch = fileToWatch;

    Path toWatch = Paths.get(args[0]);
    if (toWatch == null) {
        throw new UnsupportedOperationException("Directory not found");
    }

    // Sanity check - Check if path is a folder
    try {
        Boolean isFolder = (Boolean) Files.getAttribute(toWatch,
                "basic:isDirectory", NOFOLLOW_LINKS);
        if (!isFolder) {
            throw new IllegalArgumentException("Path: " + toWatch + " is not a folder");
        }
    } catch (IOException ioe) {
        // Folder does not exists
        ioe.printStackTrace();
    }

    // make a new watch service that we can register interest in 
    // directories and files with.
    WatchService myWatcher = toWatch.getFileSystem().newWatchService();

    // start the file watcher thread below
    MyWatchQueueReader fileWatcher = new MyWatchQueueReader(args[0], myWatcher, fileToWatch);
    Thread processingThread = new Thread(fileWatcher, "FileWatcher");
    processingThread.start();

    toWatch.register(myWatcher, ENTRY_CREATE, ENTRY_MODIFY);  
}
}

Edit: reduced code as requested.

Edit 2: file path

enter image description here

Edit 3: Metatrader code I am using to write data

#property strict

int file_handle;
string InpFileName = _Symbol + ".rlt"; // File name
input string InpDirectoryName = "Data"; // Folder name

int OnInit()
{
ResetLastError();
file_handle = FileOpen(InpDirectoryName + "//" + InpFileName, FILE_SHARE_READ|FILE_WRITE|FILE_TXT|FILE_ANSI);
if(file_handle == INVALID_HANDLE) {
    PrintFormat("Failed to open %s file, Error code = %d", InpFileName, GetLastError());
    ExpertRemove();
}
return INIT_SUCCEEDED;
}

void OnTick()
{
//  file_handle = FileOpen(InpDirectoryName + "//" + InpFileName, FILE_SHARE_READ|FILE_WRITE|FILE_TXT|FILE_ANSI);
// Datetime), Bid, Volume
//  string s = FileRead()
string s = TimeToStr(TimeGMT()) + "|" + Bid + "|" + Volume[0];
FileWriteString(file_handle, s + "|*\r\n");
FileFlush(file_handle);
//FileClose(file_handle);

}

void OnDeinit(const int reason)
{
FileClose(file_handle);
}

Edit 4: Screencast to better show my issue: data updates only when I click on the output file

Watch Service does not update

Mancilla answered 19/6, 2014 at 12:39 Comment(11)
Java is platform independent:) Would be nice if you can tell us the error you got.Sturgill
I am aware Java is platform independent; I do not have any error, only the output file gets update only if I click on it. This problem is related to OS since in Linux this same code works perfectly.Mancilla
I think you should cut this down and create an SSCCE. There's too much code ... and I'd bet that a large proportion is not relevant to the root problem. TL;DR!!Osteogenesis
Code reduced as requestedMancilla
What filepath are you giving there? If you are giving it a non-platform independant path, it probably wouldn't work.Issykkul
This is the filepath: C:\Program Files (x86)\MT4 Exchange\MQL4\Files\Data , local directoryMancilla
The WatchService on Java works properly on Windows 7 - I've written applications that make use of it which work, detecting changes to files when they happen. The two most likely things you're likely running into are (1) folder virtualization issues where the writes are going somewhere into %APPDATA%\Local\VirtualStore or (2) different mechanisms for writing which don't alter the update date on the fileMallette
@Petesh - I do not use any virtualization, and I do FileFlush, as my above Edit3Mancilla
I have no familiarity with the MetaTrader platform; so I'm only making educated guesses; Have you checked if it works with a small native win32 application? i.e. does it detect the changes? If it doesn't work with a native windows app, then it suggests that MetaTrader is not doing the right thing.Mallette
@Petesh just done a little test following your suggestion: open with notepad my EURUS file then added two lines. When try to save I get "Access denied", so I can't save anything.Mancilla
To allow the write in notepad, open the file with FILE_SHARE_WRITE in MetaTrader. You could try refactoring your MetaTrader code so that it opens, seeks to the end of the file, appends the new data and then closes the file in the OnTick. You commented out code that kind-of does that (use FileSeek(file_handle, 0, SEEK_END); rather than whatever FileRead is). Your problem is most likely at the MetaTrader side's mechanism for writing the file and not at the java side. The 'click' effect is most likely a side effect of other software such as anti-virus.Mallette
B
4

First of all, a premise : I'm answering this question primarily for future users of WatchService, which (like me) could experience this problem (i.e. on some systems events are signaled way after they occur).

The problem is that the implementation of this feature in Java is native, so it is platform-dependant (take a look at https://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html, under the section 'platform dependencies').

In particular, on Windows 7 (and MacOSX afaict) the implementation uses polling to retrieve the changes from the filesystem, so you can't rely on the 'liveliness' of notifications from a WatchService. The notifications will eventually be signaled, but there are no guarantees on when it will happen. I don't have a rigorous solution to this problem, but after a lot of trial and error I can describe what works for me :

First, when writing to a file that is registered (i.e. 'watched'), I try to flush the content every time I can and update the 'last modified' attribute on the file, e.g. :

try (FileWriter writer = new FileWriter(outputFile)) {
    writer.write("The string to write");
    outputFile.setLastModified(System.currentTimeMillis());
    writer.flush();
}

Second, I try to 'trigger' the refresh from code (I know it's not good code, but in this case, I'm just happy it works 99% of the time)

Thread.sleep(2000);
// in case I've just created a file and I'm watching the ENTRY_CREATE event on outputDir
outputDir.list(); 

or (if watching ENTRY_MODIFY on a particular file in outputDir)

Thread.sleep(2000);
outputFile.length(); 

In both cases, a sleep call simply 'gives the time' to the mechanism underlying the WatchService to trigger, even though 2 seconds are probably a lot more than it is needed.

Buckram answered 17/11, 2015 at 15:20 Comment(0)
W
0

Probably missing quotes on file path.

Wane answered 19/6, 2014 at 18:13 Comment(2)
I have just edited my question by adding a picture of file pathMancilla
java -jar "C:\Users\utente\Desktop\WatchFileThreadMod\dist\WatchFileThreadMod.jar" "C:\Program Files (x86)\MT4 Exchange\MQL4\Files\Data"Mancilla

© 2022 - 2024 — McMap. All rights reserved.