Search code examples
javawatchservice

WatchService WatchEvent .context() method returns inconsistent relative path for file on ENTRY_MODIFY (goutputstream-####, Linux OS)


In this code, I'm hoping to update a HashMap with the most recent version of a given path's contents, with the absolute path as string being used as the key. The problem is that WatchEvent's .context() method is giving me a different relative path for the same file on each event.

Here is a snippet of code:

            else if(event.kind()==StandardWatchEventKinds.ENTRY_MODIFY)
            {
                /*Variable path is a Path of "//workspaces", set earlier.*/
                Path oldfilepath=path.resolve((Path)event.context()); /*problem line*/
                String oldfilepathstring = oldfilepath.toString();
                 FileReader oldIn = new FileReader(oldfilepathstring);
                 BufferedReader br = new BufferedReader(oldIn);
                 String line;
                 List<String> newfiletext=new LinkedList<>();
                  while((line = br.readLine())!=null)
                    newfiletext.add(line);

                 List<String> previousText=new LinkedList<>();
                 if((previousText = fileMappings.get(oldfilepathstring))!= null)
                 {
                      System.out.println("previoustext:\n"+previousText);
                      System.out.println("newfiletext:\n"+newfiletext);
                 }

                 fileMappings.put(oldfilepathstring, newfiletext);
                 System.out.println(fileMappings.keySet()+"\n"+fileMappings.values());
            }

        }

And here is sample output upon modifying the file b.txt in the watched directory from contents "abc" to "abc 123"

Note that all of this comes simply from opening file /workspaces/b.txt (which already exists) and modifying its contents.):


    run:
    ENTRY_CREATE:.goutputstream-BRC1HX
    ENTRY_MODIFY:.goutputstream-BRC1HX
    [/workspaces/.goutputstream-BRC1HX]
    [[]]
    ENTRY_MODIFY:.goutputstream-BRC1HX
    previoustext:
    []
    newfiletext:
    [abc]
    [/workspaces/.goutputstream-BRC1HX]
    [[abc]]
    ENTRY_CREATE:b.txt~
    ENTRY_CREATE:b.txt
    ENTRY_CREATE:.goutputstream-MFJ6HX
    ENTRY_MODIFY:.goutputstream-MFJ6HX
    [/workspaces/.goutputstream-MFJ6HX, /workspaces/.goutputstream-BRC1HX]
    [[], [abc]]
    ENTRY_MODIFY:.goutputstream-MFJ6HX
    previoustext:
    []
    newfiletext:
    [abc, 123]
    [/workspaces/.goutputstream-MFJ6HX, /workspaces/.goutputstream-BRC1HX]
    [[abc, 123], [abc]]
    ENTRY_CREATE:b.txt~
    ENTRY_CREATE:b.txt

The line of interest is Path oldfilepath=path.resolve((Path)event.context());

Note how oldfilepath has resolved to "/workspaces/.goutputstream-MFJ6HX", and later "/workspaces/.goutputstream-BRC1HX" for the same file.

event.context() is returning a different path for the same file after each modification. Is this a Linux issue, or a Java issue, and how exactly do I get a standard relative path (in this case, it'd be "b.txt") for this file?

It seems that when I perform a modify, I'm getting a sequence of create/modify/create events, and the ENTRY_CREATEs have the correct filename, wile the ENTRY_MODIFYs have a temp handle (I'm guessing to a temp version of the file used between saves.) I need to be able to capture file modification and pull the correct filename out of that event.

I understand that my filesystem may be doing temp file creation & processing under the hood while I'm just opening, modifying and saving the file, but how exactly do I extract proper filename out of the temp file that the event indicating ENTRY_MODIFY gives me? Is there some sort of method to group the events pertaining to this modify, so that I can just find the enclosing ENTRY_CREATE and get the filename from that? Or somehow traverse upward through the stack of calls leading to this ENTRY_CREATE?

I can see the filename in the enclosing ENTRY_CREATE events, surrounding each ENTRY_MODIFY, but I'm hoping that there's a more elegant way to do this than to somehow (get most recent event that wasn't ENTRY_MODIFY, and then get .context() from that.)

Thanks!


Solution

  • I faced the same question. I think it has nothing to do with any Linux problem or any Java issues. It is just the way the editor of b.txt (I assume gedit) handles the thing.

    Upon save it

    1. creates a new temp file ".goutputstream-xxxx" with some random xxxx (the create you see),

    2. writes the new content to this file (the modify you see),

    3. renames the original file to b.txt~ (the create you see),

    4. and renames the temp file to b.txt (the create you see)

    So I guess you have to watch out for both ENTRY_MODIFY and ENTRY_CREATE with respect to b.txt to really see all the file modifications.