All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.takari.watcher.JdkDirectoryWatcher Maven / Gradle / Ivy

package io.takari.watcher;

import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;

public class JdkDirectoryWatcher extends DirectoryWatcher  {

  private final WatchService watchService;
  private final Map keys;
  private final boolean recursive;
  private boolean trace = false;
  private final DirectoryChangeListener listener;

  /**
   * Creates a WatchService and registers the given directory
   */
  JdkDirectoryWatcher(Path dir, DirectoryChangeListener listener) throws IOException {
    this.watchService = FileSystems.getDefault().newWatchService();
    this.keys = new HashMap();
    this.recursive = true;
    this.listener = listener;

    if (recursive) {
      registerAll(dir);
    } else {
      register(dir);
    }

    // enable trace after initial registration
    this.trace = true;
  }

  @SuppressWarnings("unchecked")
   WatchEvent cast(WatchEvent event) {
    return (WatchEvent) event;
  }

  /**
   * Register the given directory with the WatchService
   */
  private void register(Path dir) throws IOException {
    // This use of the SensitivityWatchEventModifier seems required for decent time.
    WatchKey key = dir.register(watchService, new WatchEvent.Kind[]{ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE});
    if (trace) {
      Path prev = keys.get(key);
      if (prev == null) {
        System.out.format("register: %s\n", dir);
      } else {
        if (!dir.equals(prev)) {
          System.out.format("update: %s -> %s\n", prev, dir);
        }
      }
    }
    keys.put(key, dir);
  }

  /**
   * Register the given directory, and all its sub-directories, with the
   * WatchService.
   */
  private void registerAll(final Path start) throws IOException {
    // register directory and sub-directories
    Files.walkFileTree(start, new SimpleFileVisitor() {
      @Override
      public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        register(dir);
        return FileVisitResult.CONTINUE;
      }
    });
  }

  /**
   * Process all events for keys queued to the watcher
   * @throws IOException 
   */
  @Override
  public void processEvents() throws IOException {
    for (;;) {

      // wait for key to be signalled
      WatchKey key;
      try {
        key = watchService.take();
      } catch (InterruptedException x) {
        return;
      }

      Path dir = keys.get(key);
      if (dir == null) {
        System.err.println("WatchKey not recognized!!");
        continue;
      }

      for (WatchEvent event : key.pollEvents()) {
        WatchEvent.Kind kind = event.kind();

        // TBD - provide example of how OVERFLOW event is handled
        if (kind == OVERFLOW) {
          continue;
        }

        // Context for directory entry event is the file name of entry
        WatchEvent ev = cast(event);
        Path name = ev.context();
        Path child = dir.resolve(name);

        if (kind == ENTRY_MODIFY) {
          listener.onModify(child);
        }

        // if directory is created, and watching recursively, then register it and its sub-directories
        if (kind == ENTRY_CREATE) {
          if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
            try {
              registerAll(child);
            } catch (IOException e) {
              // ignore to keep sample readbale
            }
          } else {
            listener.onCreate(child);
          }
        }
        if (kind == ENTRY_DELETE) {
          listener.onDelete(child);
        }
      }

      // reset key and remove from set if directory no longer accessible
      boolean valid = key.reset();
      if (!valid) {
        keys.remove(key);

        // all directories are inaccessible
        if (keys.isEmpty()) {
          break;
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy