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

org.k3a.observer.Observer Maven / Gradle / Ivy

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

import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
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.Logger;

/**
 * Created by  k3a
 * on 2018/6/29  14:17
 */
@SuppressWarnings({"WeakerAccess", "unused", "UnusedReturnValue"})
public class Observer {

    protected final AtomicBoolean running = new AtomicBoolean(false);

    protected final Logger LOGGER = Logger.getLogger(this.getClass().getName());

    protected volatile long minInterval = 50L;

    protected volatile int bathSize = 1;

    protected final ConcurrentMap TIMESTAMP = new ConcurrentHashMap<>();

    protected final Supplier notifierExecutorSupplier = () ->
            new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(Integer.MAX_VALUE),
                    new ThreadFactory() {
                        private final AtomicInteger threadNumber = new AtomicInteger(1);
                        private final String namePrefix = "Observer-notifier-";

                        @Override
                        public Thread newThread(Runnable r) {
                            Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
                            t.setDaemon(false);
                            return t;
                        }
                    },
                    (r, p) -> LOGGER.info("runnable: " + r + " is discarded by pool:" + p));

    protected volatile ExecutorService notifierExecutor = notifierExecutorSupplier.get();

    protected final int coreSize = Runtime.getRuntime().availableProcessors();

    protected final List> tasks = Collections.synchronizedList(new LinkedList<>());

    protected final Supplier regNewDirSupplier = () ->
            new ThreadPoolExecutor(coreSize, coreSize, 60L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(Integer.MAX_VALUE),
                    new ThreadFactory() {
                        private final AtomicInteger threadNumber = new AtomicInteger(1);
                        private final String namePrefix = "DirectoryObserver-register-";

                        @Override
                        public Thread newThread(Runnable r) {
                            Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
                            t.setDaemon(true);
                            return t;
                        }
                    },
                    (r, p) -> Logger.getLogger(this.getClass().getName()).info("runnable: " + r + " is discarded by pool:" + p));

    protected volatile ExecutorService regNewDirExecutor = regNewDirSupplier.get();

    protected final Supplier bootExecutorSupplier = () ->
            new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(Byte.MAX_VALUE),
                    new ThreadFactory() {
                        private final AtomicInteger threadNumber = new AtomicInteger(1);
                        private final String namePrefix = "StartAsync-";

                        @Override
                        public Thread newThread(Runnable r) {
                            Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
                            t.setDaemon(true);
                            return t;
                        }
                    },
                    (r, p) -> LOGGER.info("runnable: " + r + " is discarded by pool:" + p));

    protected volatile ExecutorService bootExecutor = bootExecutorSupplier.get();

    protected final ConcurrentMap> modifyHandlers = new ConcurrentHashMap<>();
    protected final ConcurrentMap> deleteHandlers = new ConcurrentHashMap<>();
    protected final ConcurrentMap> createHandlers = new ConcurrentHashMap<>();

    protected volatile Consumer commonModifyHandler = o -> LOGGER.info("NOTICE:this message is printed due to no modify handler was set, event source:" + o);

    protected volatile Consumer commonDeleteHandler = o -> LOGGER.info("NOTICE:this message is printed due to no delete handler was set, event source:" + o);

    protected volatile Consumer commonCreateHandler = o -> LOGGER.info("NOTICE:this message is printed due to no create handler was set, event source:" + o);

    protected volatile RejectObserving rejection = defaultRejection();

    protected volatile W watchService = defaultWatchServiceSupplier().get();

    protected volatile Supplier watchServiceSupplier = null;

    protected volatile Runnable notifier = null;

    protected volatile BiConsumer> register = null;

    protected volatile Consumer unRegister = null;

    /**
     * mill second
     */
    public Observer setMinInterval(long ms) {
        this.minInterval = ms;
        return this;
    }

    /**
     * batch notify
     */
    public Observer setBatchSize(int batch) {
        this.bathSize = batch;
        return this;
    }

    @SuppressWarnings("unchecked")
    public Observer register(O... observables) {
        return register(rejection, observables);
    }

    @SuppressWarnings("unchecked")
    public Observer register(RejectObserving reject, O... observables) {
        return register(reject, Arrays.asList(observables));
    }

    public Observer register(Collection paths) {
        return register(rejection, paths);
    }

    public Observer register(RejectObserving reject, Collection observables) {
        for (O o : observables)
            register(o, reject);
        return this;
    }

    public Observer register(O observable, RejectObserving reject) {
        tasks.add(CompletableFuture.runAsync(() -> (register == null ? register = defaultRegistry() : register).accept(observable, reject)));
        return this;
    }

    public Observer unRegister(O observable) {
        (unRegister == null ? unRegister = defaultCancel() : unRegister).accept(observable);
        return this;
    }

    public void start() throws InterruptedException {
        if (running.compareAndSet(false, true)) {
            CompletableFuture.allOf(tasks.toArray(new CompletableFuture[]{})).join();
            tasks.clear();
            //start this round
            notifierExecutor.execute(notifier == null ? notifier = defaultNotifier() : notifier);
        }
    }

    public void startAsync() {
        bootExecutor.execute(() -> {
            try {
                start();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
    }

    public void stop() throws IOException {
        if (running.compareAndSet(true, false)) {
            if (watchService != null) {
                watchService.close();
            }
            notifierExecutor.shutdown();
            regNewDirExecutor.shutdown();
            bootExecutor.shutdown();
            TIMESTAMP.clear();
        }
    }

    public void reset() throws IOException {
        stop();
        notifierExecutor = notifierExecutorSupplier.get();
        regNewDirExecutor = regNewDirSupplier.get();
        bootExecutor = bootExecutorSupplier.get();
        //so that it can be recovered
        watchService = watchServiceSupplier == null ? (watchServiceSupplier = defaultWatchServiceSupplier()).get() : watchServiceSupplier.get();
    }

    public Observer onModify(Consumer consumer) {
        this.commonModifyHandler = consumer;
        return this;
    }

    public Observer onModify(O path, Consumer consumer) {
        modifyHandlers.put(path, consumer);
        return this;
    }

    public Observer onDelete(Consumer consumer) {
        this.commonDeleteHandler = consumer;
        return this;
    }

    public Observer onDelete(O path, Consumer consumer) {
        this.deleteHandlers.put(path, consumer);
        return this;
    }

    public Observer onCreate(Consumer consumer) {
        this.commonCreateHandler = consumer;
        return this;
    }

    public Observer onCreate(O path, Consumer consumer) {
        this.createHandlers.put(path, consumer);
        return this;
    }

    /**
     * use this to change link service or override {@link Observer#defaultWatchServiceSupplier}
     */
    public Observer onWatch(Supplier watchServiceSupplier) {
        this.watchServiceSupplier = watchServiceSupplier;
        return this;
    }

    /**
     * use this to change Rejection or override {@link Observer#defaultRejection()}
     */
    public Observer onReject(RejectObserving reject) {
        this.rejection = reject;
        return this;
    }

    /**
     * use this to change notifier or override {@link Observer#defaultNotifier()}
     */
    public Observer onNotify(Runnable notifier) {
        this.notifier = notifier;
        return this;
    }

    /**
     * use this to change link or override {@link Observer#defaultRegistry()} ()}
     */
    public Observer onRegister(BiConsumer> register) {
        this.register = register;
        return this;
    }

    /**
     * use this to change link or override {@link Observer#defaultCancel()} ()}
     */
    public Observer onUnRegister(Consumer unRegister) {
        this.unRegister = unRegister;
        return this;
    }

    /**
     * handler event after customized action execute
     */
    protected void postEventHandler(WatchKey take, WatchEvent event, Path path, Path contextPath) {
    }

    /**
     * override this to change Rejection or use {@link Observer#onWatch}
     */
    protected RejectObserving defaultRejection() {
        //noinspection unchecked
        return RejectObserving.EXCEPTION;
    }

    /**
     * override this to change link service or use {@link Observer#onWatch}
     */
    protected Supplier defaultWatchServiceSupplier() {
        throw new IllegalStateException("watchService is required but has not been set up yet!");
    }

    /**
     * override this to change notifier or use {@link Observer#onNotify(Runnable)}
     */
    protected Runnable defaultNotifier() {
        throw new IllegalStateException("notifier is required but has not been set up yet!");
    }

    /**
     * override this to change link or use {@link Observer#onRegister(BiConsumer)}
     */
    protected BiConsumer> defaultRegistry() {
        throw new IllegalStateException("Register is required but has not been set up yet!");
    }

    /**
     * override this to change link or use {@link Observer#onUnRegister(Consumer)}
     */
    protected Consumer defaultCancel() {
        throw new IllegalStateException("UnRegister is required but has not been set up yet!");
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy