com.github.phantomthief.zookeeper.ZkBasedNodeResource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zkconfig-resources Show documentation
Show all versions of zkconfig-resources Show documentation
A ZooKeeper based configuration resources holder
/**
*
*/
package com.github.phantomthief.zookeeper;
import java.io.Closeable;
import java.io.IOException;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author w.vela
*/
public final class ZkBasedNodeResource implements Closeable {
private static Logger logger = LoggerFactory.getLogger(ZkBasedNodeResource.class);
private final BiFunction factory;
private final Supplier cacheFactory;
private final Predicate cleanup;
private final long waitStopPeriod;
private final T emptyObject;
/**
* @param factory
* @param cacheFactory
* @param cleanup
* @param waitStopPeriod
* @param emptyObject
*/
private ZkBasedNodeResource(BiFunction factory,
Supplier cacheFactory, Predicate cleanup, long waitStopPeriod,
T emptyObject) {
this.factory = factory;
this.cacheFactory = cacheFactory;
this.cleanup = cleanup;
this.waitStopPeriod = waitStopPeriod;
this.emptyObject = emptyObject;
}
private volatile T resource;
public T get() {
if (resource == null) {
synchronized (ZkBasedNodeResource.this) {
if (resource == null) {
NodeCache cache = cacheFactory.get();
ChildData currentData = cache.getCurrentData();
if (currentData == null || currentData.getData() == null) {
return emptyObject;
}
resource = factory.apply(currentData.getData(), currentData.getStat());
cache.getListenable().addListener(() -> {
T oldResource = null;
synchronized (ZkBasedNodeResource.this) {
ChildData data = cache.getCurrentData();
oldResource = resource;
if (data != null && data.getData() != null) {
resource = factory.apply(data.getData(), data.getStat());
} else {
resource = emptyObject;
}
}
cleanup(oldResource);
});
}
}
}
return resource;
}
/**
* @param oldResource
*/
private void cleanup(T oldResource) {
if (oldResource != null && oldResource != emptyObject) {
if (cleanup == null) {
return;
}
Thread cleanupThread = new Thread(() -> {
do {
if (waitStopPeriod > 0) {
try {
Thread.sleep(waitStopPeriod);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (cleanup.test(oldResource)) {
break;
}
} while (true);
logger.info("successfully close old resource:{}", oldResource);
});
cleanupThread.setName("old [" + oldResource.getClass().getSimpleName()
+ "] cleanup thread-[" + cleanupThread.getId() + "]");
cleanupThread.setUncaughtExceptionHandler((t, e) -> {
logger.error("fail to cleanup resource.", e);
});
cleanupThread.start();
}
}
@Override
public void close() throws IOException {
synchronized (ZkBasedNodeResource.this) {
if (resource != null && resource != emptyObject && cleanup != null) {
cleanup.test(resource);
}
}
}
public static final class Builder {
private BiFunction factory;
private Supplier cacheFactory;
private Predicate cleanup;
private long waitStopPeriod;
private E emptyObject;
public Builder withFactory(BiFunction factory) {
this.factory = factory;
return this;
}
public Builder withFactory(Function factory) {
this.factory = (data, stat) -> factory.apply(data);
return this;
}
public Builder withStringFactory(BiFunction factory) {
this.factory = (data, stat) -> factory.apply(data == null ? null : new String(data),
stat);
return this;
}
public Builder withStringFactory(Function factory) {
this.factory = (data, stat) -> factory.apply(data == null ? null : new String(data));
return this;
}
public Builder withCacheFactory(Supplier cacheFactory) {
this.cacheFactory = cacheFactory;
return this;
}
public Builder withCacheFactory(String path, CuratorFramework curator) {
return withCacheFactory(path, () -> curator);
}
public Builder withCacheFactory(String path, Supplier curatorFactory) {
this.cacheFactory = () -> {
CuratorFramework thisClient = curatorFactory.get();
if (thisClient.getState() != CuratorFrameworkState.STARTED) {
thisClient.start();
}
NodeCache buildingCache = new NodeCache(thisClient, path);
try {
buildingCache.start();
buildingCache.rebuild();
return buildingCache;
} catch (Throwable e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(e);
}
}
};
return this;
}
public Builder withCleanup(Consumer cleanup) {
this.cleanup = t -> {
try {
cleanup.accept(t);
return true;
} catch (Throwable e) {
logger.error("Ops. fail to close:{}", t, e);
return false;
}
};
return this;
}
public Builder withCleanup(Predicate cleanup) {
this.cleanup = cleanup;
return this;
}
public Builder withWaitStopPeriod(long waitStopPeriod) {
this.waitStopPeriod = waitStopPeriod;
return this;
}
public Builder withEmptyObject(E emptyObject) {
this.emptyObject = emptyObject;
return this;
}
public ZkBasedNodeResource build() {
ensure();
return new ZkBasedNodeResource<>(factory, cacheFactory, cleanup, waitStopPeriod,
emptyObject);
}
private void ensure() {
if (factory == null) {
throw new NullPointerException("factory is null.");
}
if (cacheFactory == null) {
throw new NullPointerException("cache factory is null.");
}
if (cleanup == null) {
withCleanup(t -> {
if (t instanceof Closeable) {
try {
((Closeable) t).close();
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(e);
}
}
}
});
}
}
}
public static final Builder newBuilder() {
return new Builder<>();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy