com.ovea.tajin.io.FileWatcher.groovy Maven / Gradle / Ivy
/**
* 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