org.k3a.observer.Observer Maven / Gradle / Ivy
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