/**
 * 
 */
package com.github.phantomthief.zookeeper.util;

import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.slf4j.LoggerFactory.getLogger;

import java.util.Arrays;
import java.util.function.Function;

import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;

/**
 * @author w.vela
 */
public final class ZNodeModifyUtils {

    private static org.slf4j.Logger logger = getLogger(ZNodeModifyUtils.class);
    private static final long DEFAULT_WAIT = SECONDS.toMillis(1);
    private static final int INFINITY_LOOP = -1;

    private ZNodeModifyUtils() {
        throw new UnsupportedOperationException();
    }

    public static final <T> void modify(CuratorFramework client, String path,
            Function<T, T> changeFunction, Function<byte[], T> decoder,
            Function<T, byte[]> encoder) {
        Function<byte[], byte[]> realFunction = old -> {
            T decodedOld = decoder.apply(old);
            return encoder.apply(changeFunction.apply(decodedOld));
        };
        modify(client, path, realFunction, INFINITY_LOOP, DEFAULT_WAIT);
    }

    public static final boolean modify(CuratorFramework client, String path,
            Function<byte[], byte[]> changeFunction, int retryTimes, long retryWait) {
        int times = 0;
        do {
            try {
                Stat stat = new Stat();
                byte[] oldData = client.getData().storingStatIn(stat).forPath(path);
                byte[] newData = changeFunction.apply(oldData);
                client.setData().withVersion(stat.getVersion()).forPath(path, newData);
                if (logger.isDebugEnabled()) {
                    logger.debug("success update znode:{} from {} to {}", path,
                            Arrays.toString(oldData), Arrays.toString(newData));
                }
                return true;
            } catch (KeeperException.BadVersionException e) {
                logger.debug("bad version for znode:{}, retry.{}", path, times);
            } catch (KeeperException.NoNodeException e) {
                byte[] newData = changeFunction.apply(null);
                try {
                    client.create().creatingParentsIfNeeded().forPath(path, newData);
                    if (logger.isDebugEnabled()) {
                        logger.debug("success create znode:{} -> {}", path,
                                Arrays.toString(newData));
                    }
                    return true;
                } catch (KeeperException.NodeExistsException ex) {
                    logger.debug("node exist for znode:{}, retry.{}", path, times);
                } catch (Exception ex) {
                    logger.error("Ops.{}/{}", path, times, ex);
                }
            } catch (Exception e) {
                logger.error("Ops.{}/{}", path, times, e);
                times++;
                sleepUninterruptibly(retryWait, MILLISECONDS);
            }
        } while (times < retryTimes || retryTimes == INFINITY_LOOP);
        logger.warn("fail to change znode:{}, retry times:{}", path, times);
        return false;
    }
}

@@ -54,16 +54,81 @@
 .util;%0A%0A
+import static com.google.common.base.Preconditions.checkNotNull;%0A
 import s
@@ -206,16 +206,16 @@
 ptibly;%0A
-
 import s
@@ -568,16 +568,59 @@
 .Stat;%0A%0A
+import com.google.common.base.Throwables;%0A%0A
 /**%0A * @
@@ -981,21 +981,594 @@
  static 
-final
+void modify(CuratorFramework client, String path, byte%5B%5D value) %7B%0A        checkNotNull(client);%0A        checkNotNull(path);%0A        checkNotNull(value);%0A        try %7B%0A            client.setData().forPath(path, value);%0A        %7D catch (KeeperException.NoNodeException e) %7B%0A            try %7B%0A                client.create().creatingParentsIfNeeded().forPath(path, value);%0A            %7D catch (Exception e1) %7B%0A                throw Throwables.propagate(e1);%0A            %7D%0A        %7D catch (Exception e) %7B%0A            throw Throwables.propagate(e);%0A        %7D%0A    %7D%0A%0A    public static
  %3CT%3E voi

add modify.