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

org.k3a.observer.impl.DirectoryObserver Maven / Gradle / Ivy

There is a newer version: 1.0.2
Show newest version
package org.k3a.observer.impl;

import org.k3a.observer.LocalFileSystemObserver;
import org.k3a.observer.RejectObserving;
import org.k3a.observer.SensitivityWatchEventModifier;
import org.k3a.utils.SysHelper;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;

/**
 * Created by  k3a
 * on 2018/7/27  1:24
 * 

* Watch the change of Directory */ @SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) public class DirectoryObserver extends LocalFileSystemObserver { protected final Set recursively = new ConcurrentSkipListSet<>(); protected final AtomicInteger eventNum = new AtomicInteger(0); protected DirectoryObserver() { } public static DirectoryObserver get() { return new DirectoryObserver(); } protected void recursiveRegister(Path path, RejectObserving reject) { try { if (!Files.readAttributes(path, BasicFileAttributes.class).isDirectory()) return; CompletableFuture f = CompletableFuture.runAsync(() -> register(path, reject)); Files.list(path).parallel().forEach(e -> recursiveRegister(e, reject)); f.join(); recursively.add(path); } catch (Exception e) { LOGGER.log(Level.WARNING, "\tat " + e.getStackTrace()[0]); reject.reject(path); } } public DirectoryObserver registerRecursively(Path path, RejectObserving reject) { tasks.add(CompletableFuture.runAsync(() -> recursiveRegister(path, reject))); return this; } public DirectoryObserver registerRecursively(Path path) { //noinspection unchecked registerRecursively(path, defaultRejection()); return this; } @Override public void start() throws InterruptedException { CompletableFuture.allOf(tasks.toArray(new CompletableFuture[]{})).join(); super.start(); tasks.clear(); } @Override public void startAsync() { regNewDirExecutor.execute(() -> { try { start(); } catch (InterruptedException e) { throw new RuntimeException(e); } }); } @Override public Supplier defaultWatchServiceSupplier() { return () -> { try { return FileSystems.getDefault().newWatchService(); } catch (Exception t) { throw new RuntimeException(t); } }; } @Override protected Runnable defaultNotifier() { return () -> { WatchKey take = null; Path path; do { try { take = watchService.take(); path = (Path) take.watchable(); if (!take.isValid() || !TIMESTAMP.containsKey(path)) { take.cancel(); unRegister(path); LOGGER.log(Level.WARNING, "cancel invalid watchKey :" + take); continue; } //ignore double update Thread.sleep(minInterval); //handle events Long lastModified; for (WatchEvent event : take.pollEvents()) { final Long[] lastModifiedArr = TIMESTAMP.get(path); lastModified = lastModifiedArr[getEventOrder(event.kind())]; final long thisModified = Files.readAttributes(path, BasicFileAttributes.class).lastModifiedTime().toMillis(); //ignore double update if (lastModified != null && lastModified < thisModified && eventNum.incrementAndGet() == bathSize) { try { commonOnChangeHandler().accept(path, event); } finally { lastModifiedArr[getEventOrder(event.kind())] = thisModified; eventNum.set(0); postEventHandler(take, event, path, path.resolve((Path) event.context())); } } } } catch (ClosedWatchServiceException | InterruptedException e) { //break this round observing and return from this Thread break; } catch (NoSuchFileException e) { //usually happens when watched path is deleted LOGGER.log(Level.WARNING, "\tat " + e.getStackTrace()[0]); take.cancel(); unRegister((Path) take.watchable()); } catch (Exception e) { e.printStackTrace(); LOGGER.log(Level.WARNING, "\tat " + e.getStackTrace()[0]); } finally { if (take != null) take.reset(); } } while (true); }; } /** * register new path or cancel deleted key */ @Override protected void postEventHandler(WatchKey take, WatchEvent event, Path path, Path contextPath) { if (StandardWatchEventKinds.ENTRY_CREATE.equals(event.kind()) && contextPath.toFile().isDirectory()) { if (recursively.contains(path)) regNewDirExecutor.execute(() -> registerRecursively(contextPath)); else regNewDirExecutor.execute(() -> register(contextPath)); } else if (StandardWatchEventKinds.ENTRY_DELETE.equals(event.kind())) { take.cancel(); unRegister(contextPath); } } @Override protected RejectObserving defaultRejection() { //noinspection unchecked return RejectObserving.SILENTLY; } @Override public BiConsumer> defaultRegistry() { return (path, reject) -> { try { BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class); if (!attributes.isDirectory()) { reject.reject(path); return; } if (SysHelper.isMacOs()) { path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE}, SensitivityWatchEventModifier.HIGH); } else { path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE); } long lastModifiedTime = attributes.lastModifiedTime().toMillis(); TIMESTAMP.put(path, new Long[]{lastModifiedTime, lastModifiedTime, lastModifiedTime}); } catch (IOException e) { LOGGER.log(Level.WARNING, "\tat " + e.getStackTrace()[0]); reject.reject(path); } }; } @Override public Consumer defaultCancel() { return path -> { recursively.remove(path); TIMESTAMP.remove(path); }; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy