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

org.k3a.observer.impl.FileObserver 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.Observer;
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.Collections;
import java.util.LinkedList;
import java.util.List;
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/6/22  12:03
 */
@SuppressWarnings({"unused", "WeakerAccess"})
public class FileObserver extends LocalFileSystemObserver {

    private final List abandon = Collections.synchronizedList(new LinkedList<>());

    protected FileObserver() {
    }

    public FileObserver(Supplier s) {
        this.watchServiceSupplier = s;
    }

    public static Observer get() {
        return new FileObserver();
    }

    @Override
    public Supplier defaultWatchServiceSupplier() {
        return () -> {
            try {
                return FileSystems.getDefault().newWatchService();
            } catch (Throwable t) {
                throw new RuntimeException(t);
            }
        };
    }

    /**
     * ignore System which is defined as the name of system properties
     */
    @Override
    protected RejectObserving defaultRejection() {
        //noinspection unchecked
        return p -> {
            if (!"System".equals(p.toString())) {
                //noinspection unchecked
                RejectObserving.EXCEPTION.reject(p);
            }
        };
    }

    @Override
    protected Runnable defaultNotifier() {
        return () -> {
            WatchKey take = null;
            Path parent;
            do {
                try {
                    take = watchService.take();
                    parent = (Path) take.watchable();
                    if (!take.isValid()) {
                        unRegister(parent);
                        LOGGER.log(Level.WARNING, "cancel invalid watchKey :" + take);
                        continue;
                    }

                    //ignore double update
                    Thread.sleep(minInterval);
                    for (WatchEvent event : take.pollEvents()) {
                        final Path fullPath = parent.resolve((Path) event.context());
                        //unRegister
                        if (!abandon.isEmpty() && (abandon.contains(fullPath) || abandon.contains(parent))) {
                            take.cancel();
                            continue;
                        }
                        //ignore non-registered
                        if (!TIMESTAMP.keySet().contains(fullPath)) continue;
                        //ignore double update
                        final Long lastModified = TIMESTAMP.get(fullPath)[getEventOrder(event.kind())];
                        final long thisModified = Files.getLastModifiedTime(fullPath).toMillis();
                        if (lastModified < thisModified) {
                            try {
                                commonOnChangeHandler().accept(fullPath, event);
                            } finally {
                                TIMESTAMP.get(fullPath)[getEventOrder(event.kind())] = thisModified;
                            }
                        }
                    }
                } catch (ClosedWatchServiceException | InterruptedException e) {
                    //break 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) {
                    LOGGER.log(Level.WARNING, "\tat " + e.getStackTrace()[0]);
                } finally {
                    if (take != null)
                        take.reset();
                }
            } while (true);
        };
    }

    @Override
    public BiConsumer> defaultRegistry() {
        return (path, reject) -> {
            try {
                BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class);
                if (!attributes.isRegularFile()) {
                    reject.reject(path);
                    return;
                }

                final Path parent = path.getParent();

                if (SysHelper.isMacOs()) {
                    parent.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_CREATE,
                            StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE}, SensitivityWatchEventModifier.HIGH);
                } else {
                    parent.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 (Exception e) {
                reject.reject(path);
            }
        };
    }

    @Override
    public Consumer defaultCancel() {
        return path -> {
            try {
                BasicFileAttributes fileAttributes = Files.readAttributes(path, BasicFileAttributes.class);
                if (fileAttributes.isDirectory()) {
                    TIMESTAMP.keySet().removeIf(k -> k.getParent().equals(path));
                    abandon.add(path);
                } else if (fileAttributes.isRegularFile()) {
                    TIMESTAMP.remove(path);
                    //unRegister the directory only if no files under this directory is registered
                    Path parent = path.getParent();
                    if (TIMESTAMP.keySet().stream().noneMatch(p -> p.getParent().equals(parent))) {
                        abandon.add(path);
                    }
                }
            } catch (IOException e) {
                LOGGER.log(Level.WARNING, "\tat " + e.getStackTrace()[0]);
            }
        };
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy