Search code examples
javawatchservice

stop watcher service on application quit


I use gradle and application plugin to run the application that watches the changes in the directory.

My main class looks like this

public static void main(String[] args) throws IOException {
    WatcherThread thread = new WatcherThread(EXTENSION_FOLDER);
    thread.start();

    try(BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
        String input = null;
        ConsoleInputController controller = new ConsoleInputController(br);
        while (!QUIT_COMMAND.equals(StringUtils.trim(input))) {
            System.out.println(CONSOLE_TEMPLATE);
            System.out.println("input (to exit write [quit]):> ");

            input = br.readLine();
            controller.handleInput(input);
        }
    } catch (IOException exc) {
        LOGGER.error("Failed to process input.", exc);
    }

    thread.stopThread();
}

WatcherThread is a thread class that uses WatcherService (some wrapper over WatchService of java)

public class WatcherThread extends Thread {
    private static final Logger LOGGER = LoggerFactory.getLogger(WatcherThread.class);

    private boolean watch = true;
    private WatcherService watcherService;

    public WatcherThread(String searchingPath) throws IOException {
        watcherService = new WatcherService(Paths.get(searchingPath));
    }

    @Override
    public void run() {
        LOGGER.info("Artifact watching thread started.");
        while(watch) {
            if (!watcherService.watch()) {
                break;
            }
        }
        LOGGER.info("Artifact watching thread stopped.");
    }

    public void stopThread() {
        watch = false;
    }
}

WatcherService looks like this

public class WatcherService {
    private static final Logger LOGGER = LoggerFactory.getLogger(WatcherThread.class);

    private final WatchService watcher;
    private final Map<WatchKey, Path> keys;
    private boolean trace;

    WatcherService(Path dir) throws IOException {
        watcher = FileSystems.getDefault().newWatchService();
        keys = new HashMap<>();

        register(dir);

        trace = true;
    }

    private void register(Path dir) throws IOException {
        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        if (trace) {
            Path prev = keys.get(key);
            if (null == prev) {
                LOGGER.info("Register path: [{}].", dir);
            } else {
                if (!dir.equals(prev)) {
                    LOGGER.info("Updated path: [{}] -> [{}].", prev, dir);
                }
            }
        }
        keys.put(key, dir);
    }

    boolean watch() {
        WatchKey key;
        try {
            key = watcher.take();
        } catch (InterruptedException exc) {
            return false;
        }

        Path dir = keys.get(key);
        if (null == dir) {
            LOGGER.warn("WatchKey is not recognized!");
            return false;
        }

        // forEach?
        for (WatchEvent event: key.pollEvents()) {
            LOGGER.info("Polling events");
            WatchEvent.Kind kind = event.kind();
            if (OVERFLOW == kind) {
                continue;
            }

            WatchEvent<Path> ev = (WatchEvent<Path>) event;
            Path name = ev.context();
            Path child = dir.resolve(name);

            LOGGER.info("Event occurred [{}] in [{}].", event.kind().name(), child);
            WatchEventResolver.resolveEvent(ev, child);
        }

        boolean valid = key.reset();
        if (!valid) {
            keys.remove(key);

            if (keys.isEmpty()) {
                return false;
            }
        }

        return true;
    }
}

When I do not start my WatcherThread - console input works fine. I can quit for example without problems. But when I run thread and want to quit, it is waiting for couple of seconds and then only ends.

As far as I understand it is something with WatchService that cannot stop watching the directory.

How to stop it on quit application immediately?


Solution

  • It looks like you need an extra method in your WatcherService class, that calls to watcher.close(). Then, in your WatcherThread class you can call to that method inside stopThread().

    In the Javadoc for WatchService you can see that take() keeps waiting. You can force it to finish by closing it.