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

com.github.phantomthief.zookeeper.ZkBasedNodeResource Maven / Gradle / Ivy

There is a newer version: 1.1.28
Show newest version
/**
 * 
 */
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