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

com.ovea.tajin.io.FileWatcher.groovy Maven / Gradle / Ivy

There is a newer version: 1.0.b13
Show newest version
/**
 * Copyright (C) 2011 Ovea 
 *
 * 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.ovea.tajin.io

import java.nio.file.*
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.locks.ReadWriteLock
import java.util.concurrent.locks.ReentrantReadWriteLock

import static java.nio.file.StandardWatchEventKinds.*

/**
 * @author Mathieu Carbou ([email protected])
 * @date 2013-01-10
 */
class FileWatcher {

    private final WatchService watchService = FileSystems.default.newWatchService()
    private final Map watched = new HashMap<>()
    private final ReadWriteLock lock = new ReentrantReadWriteLock()
    private final AtomicReference watcher = new AtomicReference<>()

    void watch(Collection files, Closure listener) {
        lock.writeLock().lock()
        try {
            files.collect { it.absoluteFile }.unique().each {
                def folder = (it.directory ? it : it.parentFile).canonicalFile
                def filename = it.directory ? '*' : it.name
                Bucket bucket = watched.find { k, v -> v.folder == folder }?.value
                if (!bucket) {
                    bucket = new Bucket(folder)
                    watched.put(folder.toPath().register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE), bucket)
                }
                if (!bucket.listeners[filename]) {
                    bucket.listeners[filename] = new CopyOnWriteArrayList<>()
                }
                bucket.listeners[filename] << listener
            }
            Thread t = watcher.get()
            if (watched && (!t || t.interrupted)) {
                watcher.set(Thread.start(FileWatcher.name, {
                    while (!Thread.currentThread().interrupted) {
                        try {
                            WatchKey key = watchService.take()
                            lock.readLock().lock()
                            Bucket desc
                            try {
                                desc = watched.get(key)
                            } finally {
                                lock.readLock().unlock()
                            }
                            if (key.valid && desc) {
                                key.pollEvents().each { WatchEvent evt ->
                                    def f = evt.context().toFile().name
                                    ((desc.listeners[f] ?: []) + (desc.listeners['*'] ?: []))*.call(new Event(evt.kind().name(), new File(desc.folder, f).absoluteFile))
                                }
                            }
                            key.reset()
                        } catch (InterruptedException ignored) {
                            Thread.currentThread().interrupt()
                            watcher.set(null)
                        }
                    }
                }))
            }
        } finally {
            lock.writeLock().unlock()
        }
    }

    void unwatch(Collection files) {
        lock.writeLock().lock()
        try {
            files.collect { it.absoluteFile }.unique().each {
                def folder = (it.directory ? it : it.parentFile).canonicalFile
                Map.Entry entry = watched.find { k, v -> v.folder == folder }
                if (entry) {
                    if (it.directory) {
                        entry.value.listeners.remove('*')
                    } else {
                        entry.value.listeners.remove(it.name)
                    }
                    if (!entry.value.listeners) {
                        entry.key.cancel()
                        watched.remove(entry.key)
                    }
                }
            }
            Thread t = watcher.get()
            if (!watched && t && !t.interrupted) {
                t.interrupt()
                watcher.set(null)
            }
        } finally {
            lock.writeLock().unlock()
        }
    }

    @Override
    String toString() { "FileWatcher: " + watched.values() }

    private static class Bucket {
        final File folder
        final Map>> listeners = new HashMap<>()

        Bucket(File folder) {
            this.folder = folder
        }

        @Override
        String toString() { "${folder}:${listeners}" }
    }

    static class Event {

        static enum Kind {
            ENTRY_CREATE,
            ENTRY_MODIFY,
            ENTRY_DELETE,
            UNKNOWN,
        }

        final Kind kind
        final File target

        Event(String type, File target) {
            this.kind = Kind.values().find { it.name() == type } ?: Kind.UNKNOWN
            this.target = target
        }

        @Override
        public String toString() {
            return "Event{" +
                "kind='" + kind + '\'' +
                ", target=" + target
            '}';
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy