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

com.twosigma.webtau.cli.interactive.FileWatcher.groovy Maven / Gradle / Ivy

/*
 * Copyright 2018 TWO SIGMA OPEN SOURCE, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.twosigma.webtau.cli.interactive

import com.sun.nio.file.SensitivityWatchEventModifier
import com.twosigma.webtau.console.ConsoleOutputs
import com.twosigma.webtau.reporter.stacktrace.StackTraceUtils
import groovy.io.FileType

import java.nio.file.*
import java.util.concurrent.atomic.AtomicReference

import static java.nio.file.StandardWatchEventKinds.*

class FileWatcher {
    private final WatchService watchService
    private final Path rootDirToWatch
    private final Map pathByKey
    private final OnFileChangeHandler onFileChangeHandler

    private final AtomicReference isStopped = new AtomicReference()

    FileWatcher(Path rootDirToWatch, OnFileChangeHandler onFileChangeHandler) {
        this.onFileChangeHandler = onFileChangeHandler

        watchService = createWatchService()
        pathByKey = new HashMap<>()
        this.rootDirToWatch = rootDirToWatch.toAbsolutePath()

        registerDirs(this.rootDirToWatch)
    }

    void start() {
        try {
            startWatchLoop()
        } catch (InterruptedException e) {
            throw new RuntimeException(e)
        }
    }

    void stop() {
        isStopped.set(true)
    }

    private void startWatchLoop() throws InterruptedException {
        while (true) {
            try {
                watchCycle()
                if (isStopped.get()) {
                    return
                }
            } catch (RuntimeException e) {
                ConsoleOutputs.err(StackTraceUtils.renderStackTrace(e))
            }
        }
    }

    private void watchCycle() throws InterruptedException {
        final WatchKey key = watchService.take()
        try {
            final Path path = pathByKey.get(key)
            if (path == null) {
                ConsoleOutputs.err("bad watch key: ", key)
                return
            }

            key.pollEvents().each { e -> handleEvent(path, e) }
        } finally {
            boolean isValid = key.reset()
            if (!isValid) {
                pathByKey.remove(key)
            }
        }
    }

    private void handleEvent(Path parentPath, final WatchEvent watchEvent) {
        final WatchEvent.Kind kind = watchEvent.kind()
        if (kind == OVERFLOW) {
            return
        }

        @SuppressWarnings("unchecked")
        final Path relativePath = ((WatchEvent) watchEvent).context()
        final Path path = parentPath.resolve(relativePath)

        if (kind == ENTRY_MODIFY && !Files.isDirectory(path)) {
            handleModify(path)
        }
    }

    private void handleModify(final Path path) {
        onFileChangeHandler.onChange(path)
    }

    private void register(Path path) {
        if (! Files.isDirectory(path)) {
            path = path.getParent()
        }

        if (pathByKey.values().contains(path)) {
            return
        }

        final WatchKey key = path.register(watchService, [ENTRY_MODIFY] as WatchEvent.Kind[],
                SensitivityWatchEventModifier.HIGH)
        pathByKey.put(key, path)

        ConsoleOutputs.out("watching: ", path)
    }

    private void registerDirs(Path rootPath) {
        register(rootPath)
        rootPath.traverse type: FileType.DIRECTORIES, visit: {
            register(it)
        }
    }

    private static WatchService createWatchService() {
        return FileSystems.getDefault().newWatchService()
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy