Search code examples
javanio

Change the watcher service base on user action


I am developing a Java Watcher Service for the root directory and sub directory for any new folder and file creation. My root directory is C:/REST API/source/. Here are my folder structure that might look like and only the root directory was created by default and the sub directory was created by end user

C:/REST API/source/
  - /source/new folder
    -/source/new folder/new folder
  -/source/new folder(2)

My program will register C:/REST API/source/ as a root directory, if it's detected there's a new folder created in C:/REST API/source/, it's will register the path which is C:/REST API/source/new folder. The same process being tested for /source/new folder/new folder and it's work well. But when I try to create new folder under /source/ directory which is my root directory again, I found out the path is not correct

This is my compiler statement

1
2
3
a
b
C:\REST API\source
c
d
e
C:\REST API\source
4
5
6
7
8
9
New folder
C:\REST API\source\New folder
file:///C:/REST%20API/source/New%20folder/
10
C:\REST API\source\New folder
11
12
13
aa
bb
C:\REST API\source\New folder
cc
dd
ee
14
C:\REST API\source\New folder

The new file :C:\REST API\source\New folderEvent :ENTRY_CREATE
5
6
7
8
9
New folder
C:\REST API\source\New folder\New folder
file:///C:/REST%20API/source/New%20folder/New%20folder/
10
C:\REST API\source\New folder\New folder
11
12
13
aa
bb
C:\REST API\source\New folder\New folder
cc
dd
ee
14
C:\REST API\source\New folder\New folder

The new file :C:\REST API\source\New folder\New folderEvent :ENTRY_CREATE
5
6
7
8
9
New folder (2)
C:\REST API\source\New folder\New folder\New folder (2)
file:///C:/REST%20API/source/New%20folder/New%20folder/New%20folder%20(2)
10
C:\REST API\source\New folder\New folder\New folder (2)
11

The new file :C:\REST API\source\New folder\New folder\New folder (2)Event :ENTRY_CREATE
5



Will it able to register back the root directory or detect which directory have been access by the user ,so the watcher service will register again base on the current directory access by the user if the current directory have been created by the user previously. Because what I have found out it's cause by the path=child. If I didn't overwrite the child to path variable, I am not able to retrieve back the file path if I put the file into the monitored directory

public class fileStatus {

    public static void main(String [] args) throws FileNotFoundException, IOException, JSONException, InterruptedException
    {
     try(WatchService svc = FileSystems.getDefault().newWatchService()) 
        {
         System.out.println("1");
            Map<WatchKey, Path> keyMap = new HashMap<>();
            System.out.println("2");
            Path path = Paths.get("C:/REST API/source/");
            System.out.println("3");
            fileStatus.registerAll(path,keyMap,svc);
            System.out.println(path);
            System.out.println("4");
            WatchKey wk ;
           do 
           {
               System.out.println("5");
                wk = svc.take();
                System.out.println("6");
                for(WatchEvent<?> event : wk.pollEvents())
                {
                    System.out.println("7");
                    WatchEvent.Kind<?> type = event.kind();
                    System.out.println("8");
                    Path fileName = (Path)event.context();
                    System.out.println("9");
                    System.out.println(fileName);
                    Path child = path.resolve(fileName);
                    URI uri = child.toUri();
                    System.out.println(child);
                    System.out.println(uri);
                    System.out.println("10");
                    Path newPath = Paths.get(uri);             
                    System.out.println(newPath);

                    System.out.println("11");
                    if (Files.isDirectory(newPath, LinkOption.NOFOLLOW_LINKS))
                    {
                         System.out.println("12");
                        if(type == StandardWatchEventKinds.ENTRY_CREATE)
                        {
                             System.out.println("13");
                             register(newPath,keyMap,svc);
                             System.out.println("14");
                             System.out.println(newPath); 
                             child=newPath;
                        }
                    }
                    System.out.println("\nThe new file :"+child+ "Event :" +type);  
                    path = child ;

                }
           }while(wk.reset());
        }
     catch(IOException e)
     {
         e.printStackTrace();
     }

    }

    private static Path register(Path newPath, Map<WatchKey, Path> keyMap, WatchService svc) throws IOException 
    {
        System.out.println("aa");
        Files.walkFileTree(newPath,new SimpleFileVisitor<Path>()
        {

           public FileVisitResult preVisitDirectory(Path newPath, BasicFileAttributes attrs) throws IOException
           {
               System.out.println("bb");
               System.out.println(newPath);
               if(attrs.isDirectory())
               {
                   System.out.println("cc");
                   keyMap.put(newPath.register(svc, StandardWatchEventKinds.ENTRY_CREATE),newPath);
               }
               System.out.println("dd");
               return FileVisitResult.CONTINUE;
           }
        });
        System.out.println("ee");
return newPath;

    }

    private static Path registerAll(Path path, Map<WatchKey, Path> keyMap, WatchService svc) throws IOException 
    {
        System.out.println("a");
        Files.walkFileTree(path,new SimpleFileVisitor<Path>()
                {

                   public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) throws IOException
                   {
                       System.out.println("b");
                       System.out.println(path);
                       if(attrs.isDirectory())
                       {
                           System.out.println("c");
                           keyMap.put(path.register(svc, StandardWatchEventKinds.ENTRY_CREATE),path);
                       }
                       System.out.println("d");
                       return FileVisitResult.CONTINUE;
                   }
                });
        System.out.println("e");
        return path;
    }

}

Solution

  • The issue with the code is that path is set to child in the end of while loop. It means that if

    1. Directory a/ is tracked by default, the path is set to a
    2. Then directory a/b/ is created and path is set to a/b/
    3. Then directory a/c/ is created. It returns c from (Path) event.context(). But it is matched against path a/b/ by the next code path.resolve(fileName). It gives you a/b/c/ instead of a/c

    Unfortunately WatchService doesn't allow to get directory which populated the event. You can create separate WatchService for every existing/new directory, but this could be overkill.

    I would propose to use special Sun's class ExtendedWatchEventModifier.FILE_TREE to track directory with all its sub-directories. In this case, if a is tracked and a/b/c is created, than the Path got from WatchEvent will be b/c and you'll be able to resolve it using the root path. Please, see the rough code below:

    import com.sun.nio.file.ExtendedWatchEventModifier;
    import org.json.JSONException;
    
    import java.io.IOException;
    import java.net.URI;
    import java.nio.file.*;
    import java.nio.file.attribute.BasicFileAttributes;
    
    public class fileStatus {
    
        public static void main(String[] args) throws IOException, JSONException, InterruptedException {
            try (WatchService svc = FileSystems.getDefault().newWatchService()) {
                final Path path = Paths.get("C:/ADovzhenko/watch_dir");
                registerAll(path, svc);
                WatchKey wk;
                do {
                    wk = svc.take();
                    for (WatchEvent<?> event : wk.pollEvents()) {
                        WatchEvent.Kind<?> type = event.kind();
                        Path child = path.resolve((Path) event.context());
                        URI uri = child.toUri();
                        System.out.println("Created: " + child);
                    }
                } while (wk.reset());
            }
        }
    
        private static Path registerAll(Path path, final WatchService svc) throws IOException {
            //Register folder and its sub-folders
            path.register(svc, new WatchEvent.Kind<?>[]{StandardWatchEventKinds.ENTRY_CREATE}, ExtendedWatchEventModifier.FILE_TREE);
    
            //Print all existing directories
            Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
                public FileVisitResult preVisitDirectory(final Path dir, BasicFileAttributes attrs) throws IOException {
                    if (attrs.isDirectory()) {
                        System.out.println("Existing: " + dir);
                        return FileVisitResult.CONTINUE;
                    }
                    //In case if print of non-directory is required
                    //System.out.println("Existing: " + dir);
                    return FileVisitResult.SKIP_SIBLINGS;
                }
            });
            return path;
        }
    
    }