zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From an...@apache.org
Subject [14/36] zookeeper git commit: ZOOKEEPER-3032: MAVEN MIGRATION - branch-3.4 - zookeeper-server
Date Wed, 24 Oct 2018 09:32:30 GMT
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java b/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java
new file mode 100644
index 0000000..6ca538b
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java
@@ -0,0 +1,871 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.AsyncCallback.DataCallback;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.apache.zookeeper.data.Stat;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The command line client to ZooKeeper.
+ *
+ */
+@InterfaceAudience.Public
+public class ZooKeeperMain {
+    private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperMain.class);
+    static final Map<String,String> commandMap = new HashMap<String,String>( );
+
+    protected MyCommandOptions cl = new MyCommandOptions();
+    protected HashMap<Integer,String> history = new HashMap<Integer,String>( );
+    protected int commandCount = 0;
+    protected boolean printWatches = true;
+
+    protected ZooKeeper zk;
+    protected String host = "";
+
+    public boolean getPrintWatches( ) {
+        return printWatches;
+    }
+
+    static {
+        commandMap.put("connect", "host:port");
+        commandMap.put("close","");
+        commandMap.put("create", "[-s] [-e] path data acl");
+        commandMap.put("delete","path [version]");
+        commandMap.put("rmr","path");
+        commandMap.put("set","path data [version]");
+        commandMap.put("get","path [watch]");
+        commandMap.put("ls","path [watch]");
+        commandMap.put("ls2","path [watch]");
+        commandMap.put("getAcl","path");
+        commandMap.put("setAcl","path acl");
+        commandMap.put("stat","path [watch]");
+        commandMap.put("sync","path");
+        commandMap.put("setquota","-n|-b val path");
+        commandMap.put("listquota","path");
+        commandMap.put("delquota","[-n|-b] path");
+        commandMap.put("history","");
+        commandMap.put("redo","cmdno");
+        commandMap.put("printwatches", "on|off");
+        commandMap.put("quit","");
+        commandMap.put("addauth", "scheme auth");
+    }
+
+    static void usage() {
+        System.err.println("ZooKeeper -server host:port cmd args");
+        for (Map.Entry<String, String> entry : commandMap.entrySet()) {
+            System.err.println("\t" + entry.getKey() + " " + entry.getValue());
+        }
+    }
+
+    private class MyWatcher implements Watcher {
+        public void process(WatchedEvent event) {
+            if (getPrintWatches()) {
+                ZooKeeperMain.printMessage("WATCHER::");
+                ZooKeeperMain.printMessage(event.toString());
+            }
+        }
+    }
+
+    static private int getPermFromString(String permString) {
+        int perm = 0;
+        for (int i = 0; i < permString.length(); i++) {
+            switch (permString.charAt(i)) {
+            case 'r':
+                perm |= ZooDefs.Perms.READ;
+                break;
+            case 'w':
+                perm |= ZooDefs.Perms.WRITE;
+                break;
+            case 'c':
+                perm |= ZooDefs.Perms.CREATE;
+                break;
+            case 'd':
+                perm |= ZooDefs.Perms.DELETE;
+                break;
+            case 'a':
+                perm |= ZooDefs.Perms.ADMIN;
+                break;
+            default:
+                System.err
+                .println("Unknown perm type: " + permString.charAt(i));
+            }
+        }
+        return perm;
+    }
+
+    private static void printStat(Stat stat) {
+        System.err.println("cZxid = 0x" + Long.toHexString(stat.getCzxid()));
+        System.err.println("ctime = " + new Date(stat.getCtime()).toString());
+        System.err.println("mZxid = 0x" + Long.toHexString(stat.getMzxid()));
+        System.err.println("mtime = " + new Date(stat.getMtime()).toString());
+        System.err.println("pZxid = 0x" + Long.toHexString(stat.getPzxid()));
+        System.err.println("cversion = " + stat.getCversion());
+        System.err.println("dataVersion = " + stat.getVersion());
+        System.err.println("aclVersion = " + stat.getAversion());
+        System.err.println("ephemeralOwner = 0x"
+        		+ Long.toHexString(stat.getEphemeralOwner()));
+        System.err.println("dataLength = " + stat.getDataLength());
+        System.err.println("numChildren = " + stat.getNumChildren());
+    }
+
+    /**
+     * A storage class for both command line options and shell commands.
+     *
+     */
+    static class MyCommandOptions {
+
+        private Map<String,String> options = new HashMap<String,String>();
+        private List<String> cmdArgs = null;
+        private String command = null;
+        public static final Pattern ARGS_PATTERN = Pattern.compile("\\s*([^\"\']\\S*|\"[^\"]*\"|'[^']*')\\s*");
+        public static final Pattern QUOTED_PATTERN = Pattern.compile("^([\'\"])(.*)(\\1)$");
+
+        public MyCommandOptions() {
+          options.put("server", "localhost:2181");
+          options.put("timeout", "30000");
+        }
+
+        public String getOption(String opt) {
+            return options.get(opt);
+        }
+
+        public String getCommand( ) {
+            return command;
+        }
+
+        public String getCmdArgument( int index ) {
+            return cmdArgs.get(index);
+        }
+
+        public int getNumArguments( ) {
+            return cmdArgs.size();
+        }
+
+        public String[] getArgArray() {
+            return cmdArgs.toArray(new String[0]);
+        }
+
+        /**
+         * Parses a command line that may contain one or more flags
+         * before an optional command string
+         * @param args command line arguments
+         * @return true if parsing succeeded, false otherwise.
+         */
+        public boolean parseOptions(String[] args) {
+            List<String> argList = Arrays.asList(args);
+            Iterator<String> it = argList.iterator();
+
+            while (it.hasNext()) {
+                String opt = it.next();
+                try {
+                    if (opt.equals("-server")) {
+                        options.put("server", it.next());
+                    } else if (opt.equals("-timeout")) {
+                        options.put("timeout", it.next());
+                    } else if (opt.equals("-r")) {
+                        options.put("readonly", "true");
+                    }
+                } catch (NoSuchElementException e){
+                    System.err.println("Error: no argument found for option "
+                            + opt);
+                    return false;
+                }
+
+                if (!opt.startsWith("-")) {
+                    command = opt;
+                    cmdArgs = new ArrayList<String>( );
+                    cmdArgs.add( command );
+                    while (it.hasNext()) {
+                        cmdArgs.add(it.next());
+                    }
+                    return true;
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Breaks a string into command + arguments.
+         * @param cmdstring string of form "cmd arg1 arg2..etc"
+         * @return true if parsing succeeded.
+         */
+        public boolean parseCommand( String cmdstring ) {
+            Matcher matcher = ARGS_PATTERN.matcher(cmdstring);
+
+            List<String> args = new LinkedList<String>();
+            while (matcher.find()) {
+                String value = matcher.group(1);
+                if (QUOTED_PATTERN.matcher(value).matches()) {
+                    // Strip off the surrounding quotes
+                    value = value.substring(1, value.length() - 1);
+                }
+                args.add(value);
+            }
+            if (args.isEmpty()){
+                return false;
+            }
+            command = args.get(0);
+            cmdArgs = args;
+            return true;
+        }
+    }
+
+
+    /**
+     * Makes a list of possible completions, either for commands
+     * or for zk nodes if the token to complete begins with /
+     *
+     */
+
+
+    protected void addToHistory(int i,String cmd) {
+        history.put(i, cmd);
+    }
+
+    public static List<String> getCommands() {
+        return new LinkedList<String>(commandMap.keySet());
+    }
+
+    protected String getPrompt() {       
+        return "[zk: " + host + "("+zk.getState()+")" + " " + commandCount + "] ";
+    }
+
+    public static void printMessage(String msg) {
+        System.out.println("\n"+msg);
+    }
+
+    protected void connectToZK(String newHost) throws InterruptedException, IOException {
+        if (zk != null && zk.getState().isAlive()) {
+            zk.close();
+        }
+        host = newHost;
+        boolean readOnly = cl.getOption("readonly") != null;
+        zk = new ZooKeeper(host,
+                 Integer.parseInt(cl.getOption("timeout")),
+                 new MyWatcher(), readOnly);
+    }
+    
+    public static void main(String args[])
+        throws KeeperException, IOException, InterruptedException
+    {
+        ZooKeeperMain main = new ZooKeeperMain(args);
+        main.run();
+    }
+
+    public ZooKeeperMain(String args[]) throws IOException, InterruptedException {
+        cl.parseOptions(args);
+        System.out.println("Connecting to " + cl.getOption("server"));
+        connectToZK(cl.getOption("server"));
+        //zk = new ZooKeeper(cl.getOption("server"),
+//                Integer.parseInt(cl.getOption("timeout")), new MyWatcher());
+    }
+
+    public ZooKeeperMain(ZooKeeper zk) {
+      this.zk = zk;
+    }
+
+    @SuppressWarnings("unchecked")
+    void run() throws KeeperException, IOException, InterruptedException {
+        if (cl.getCommand() == null) {
+            System.out.println("Welcome to ZooKeeper!");
+
+            boolean jlinemissing = false;
+            // only use jline if it's in the classpath
+            try {
+                Class<?> consoleC = Class.forName("jline.ConsoleReader");
+                Class<?> completorC =
+                    Class.forName("org.apache.zookeeper.JLineZNodeCompletor");
+
+                System.out.println("JLine support is enabled");
+
+                Object console =
+                    consoleC.getConstructor().newInstance();
+
+                Object completor =
+                    completorC.getConstructor(ZooKeeper.class).newInstance(zk);
+                Method addCompletor = consoleC.getMethod("addCompletor",
+                        Class.forName("jline.Completor"));
+                addCompletor.invoke(console, completor);
+
+                String line;
+                Method readLine = consoleC.getMethod("readLine", String.class);
+                while ((line = (String)readLine.invoke(console, getPrompt())) != null) {
+                    executeLine(line);
+                }
+            } catch (ClassNotFoundException e) {
+                LOG.debug("Unable to start jline", e);
+                jlinemissing = true;
+            } catch (NoSuchMethodException e) {
+                LOG.debug("Unable to start jline", e);
+                jlinemissing = true;
+            } catch (InvocationTargetException e) {
+                LOG.debug("Unable to start jline", e);
+                jlinemissing = true;
+            } catch (IllegalAccessException e) {
+                LOG.debug("Unable to start jline", e);
+                jlinemissing = true;
+            } catch (InstantiationException e) {
+                LOG.debug("Unable to start jline", e);
+                jlinemissing = true;
+            }
+
+            if (jlinemissing) {
+                System.out.println("JLine support is disabled");
+                BufferedReader br =
+                    new BufferedReader(new InputStreamReader(System.in));
+
+                String line;
+                while ((line = br.readLine()) != null) {
+                    executeLine(line);
+                }
+            }
+        } else {
+            // Command line args non-null.  Run what was passed.
+            processCmd(cl);
+        }
+    }
+
+    public void executeLine(String line)
+    throws InterruptedException, IOException, KeeperException {
+      if (!line.equals("")) {
+        cl.parseCommand(line);
+        addToHistory(commandCount,line);
+        processCmd(cl);
+        commandCount++;
+      }
+    }
+
+    private static DataCallback dataCallback = new DataCallback() {
+
+        public void processResult(int rc, String path, Object ctx, byte[] data,
+                Stat stat) {
+            System.out.println("rc = " + rc + " path = " + path + " data = "
+                    + (data == null ? "null" : new String(data)) + " stat = ");
+            printStat(stat);
+        }
+
+    };
+
+    /**
+     * trim the quota tree to recover unwanted tree elements
+     * in the quota's tree
+     * @param zk the zookeeper client
+     * @param path the path to start from and go up and see if their
+     * is any unwanted parent in the path.
+     * @return true if sucessful
+     * @throws KeeperException
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    private static boolean trimProcQuotas(ZooKeeper zk, String path)
+        throws KeeperException, IOException, InterruptedException
+    {
+        if (Quotas.quotaZookeeper.equals(path)) {
+            return true;
+        }
+        List<String> children = zk.getChildren(path, false);
+        if (children.size() == 0) {
+            zk.delete(path, -1);
+            String parent = path.substring(0, path.lastIndexOf('/'));
+            return trimProcQuotas(zk, parent);
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * this method deletes quota for a node.
+     * @param zk the zookeeper client
+     * @param path the path to delete quota for
+     * @param bytes true if number of bytes needs to
+     * be unset
+     * @param numNodes true if number of nodes needs
+     * to be unset
+     * @return true if quota deletion is successful
+     * @throws KeeperException
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public static boolean delQuota(ZooKeeper zk, String path,
+            boolean bytes, boolean numNodes)
+        throws KeeperException, IOException, InterruptedException
+    {
+        String parentPath = Quotas.quotaZookeeper + path;
+        String quotaPath = Quotas.quotaZookeeper + path + "/" + Quotas.limitNode;
+        if (zk.exists(quotaPath, false) == null) {
+            System.out.println("Quota does not exist for " + path);
+            return true;
+        }
+        byte[] data = null;
+        try {
+            data = zk.getData(quotaPath, false, new Stat());
+        } catch(KeeperException.NoNodeException ne) {
+            System.err.println("quota does not exist for " + path);
+            return true;
+        }
+        StatsTrack strack = new StatsTrack(new String(data));
+        if (bytes && !numNodes) {
+            strack.setBytes(-1L);
+            zk.setData(quotaPath, strack.toString().getBytes(), -1);
+        } else if (!bytes && numNodes) {
+            strack.setCount(-1);
+            zk.setData(quotaPath, strack.toString().getBytes(), -1);
+        } else if (bytes && numNodes) {
+            // delete till you can find a node with more than
+            // one child
+            List<String> children = zk.getChildren(parentPath, false);
+            /// delete the direct children first
+            for (String child: children) {
+                zk.delete(parentPath + "/" + child, -1);
+            }
+            // cut the tree till their is more than one child
+            trimProcQuotas(zk, parentPath);
+        }
+        return true;
+    }
+
+    private static void checkIfParentQuota(ZooKeeper zk, String path)
+        throws InterruptedException, KeeperException
+    {
+        final String[] splits = path.split("/");
+        String quotaPath = Quotas.quotaZookeeper;
+        for (String str: splits) {
+            if (str.length() == 0) {
+                // this should only be for the beginning of the path
+                // i.e. "/..." - split(path)[0] is empty string before first '/'
+                continue;
+            }
+            quotaPath += "/" + str;
+            List<String> children =  null;
+            try {
+                children = zk.getChildren(quotaPath, false);
+            } catch(KeeperException.NoNodeException ne) {
+                LOG.debug("child removed during quota check", ne);
+                return;
+            }
+            if (children.size() == 0) {
+                return;
+            }
+            for (String child: children) {
+                if (Quotas.limitNode.equals(child)) {
+                    throw new IllegalArgumentException(path + " has a parent "
+                            + quotaPath + " which has a quota");
+                }
+            }
+        }
+    }
+
+    /**
+     * this method creates a quota node for the path
+     * @param zk the ZooKeeper client
+     * @param path the path for which quota needs to be created
+     * @param bytes the limit of bytes on this path
+     * @param numNodes the limit of number of nodes on this path
+     * @return true if its successful and false if not.
+     */
+    public static boolean createQuota(ZooKeeper zk, String path,
+            long bytes, int numNodes)
+        throws KeeperException, IOException, InterruptedException
+    {
+        // check if the path exists. We cannot create
+        // quota for a path that already exists in zookeeper
+        // for now.
+        Stat initStat = zk.exists(path, false);
+        if (initStat == null) {
+            throw new IllegalArgumentException(path + " does not exist.");
+        }
+        // now check if their is already existing
+        // parent or child that has quota
+
+        String quotaPath = Quotas.quotaZookeeper;
+        // check for more than 2 children --
+        // if zookeeper_stats and zookeeper_qutoas
+        // are not the children then this path
+        // is an ancestor of some path that
+        // already has quota
+        String realPath = Quotas.quotaZookeeper + path;
+        try {
+            List<String> children = zk.getChildren(realPath, false);
+            for (String child: children) {
+                if (!child.startsWith("zookeeper_")) {
+                    throw new IllegalArgumentException(path + " has child " +
+                            child + " which has a quota");
+                }
+            }
+        } catch(KeeperException.NoNodeException ne) {
+            // this is fine
+        }
+
+        //check for any parent that has been quota
+        checkIfParentQuota(zk, path);
+
+        // this is valid node for quota
+        // start creating all the parents
+        if (zk.exists(quotaPath, false) == null) {
+            try {
+                zk.create(Quotas.procZookeeper, null, Ids.OPEN_ACL_UNSAFE,
+                        CreateMode.PERSISTENT);
+                zk.create(Quotas.quotaZookeeper, null, Ids.OPEN_ACL_UNSAFE,
+                        CreateMode.PERSISTENT);
+            } catch(KeeperException.NodeExistsException ne) {
+                // do nothing
+            }
+        }
+
+        // now create the direct children
+        // and the stat and quota nodes
+        String[] splits = path.split("/");
+        StringBuilder sb = new StringBuilder();
+        sb.append(quotaPath);
+        for (int i=1; i<splits.length; i++) {
+            sb.append("/" + splits[i]);
+            quotaPath = sb.toString();
+            try {
+                zk.create(quotaPath, null, Ids.OPEN_ACL_UNSAFE ,
+                        CreateMode.PERSISTENT);
+            } catch(KeeperException.NodeExistsException ne) {
+                //do nothing
+            }
+        }
+        String statPath = quotaPath + "/" + Quotas.statNode;
+        quotaPath = quotaPath + "/" + Quotas.limitNode;
+        StatsTrack strack = new StatsTrack(null);
+        strack.setBytes(bytes);
+        strack.setCount(numNodes);
+        try {
+            zk.create(quotaPath, strack.toString().getBytes(),
+                    Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+            StatsTrack stats = new StatsTrack(null);
+            stats.setBytes(0L);
+            stats.setCount(0);
+            zk.create(statPath, stats.toString().getBytes(),
+                    Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        } catch(KeeperException.NodeExistsException ne) {
+            byte[] data = zk.getData(quotaPath, false , new Stat());
+            StatsTrack strackC = new StatsTrack(new String(data));
+            if (bytes != -1L) {
+                strackC.setBytes(bytes);
+            }
+            if (numNodes != -1) {
+                strackC.setCount(numNodes);
+            }
+            zk.setData(quotaPath, strackC.toString().getBytes(), -1);
+        }
+        return true;
+    }
+
+    protected boolean processCmd(MyCommandOptions co)
+        throws KeeperException, IOException, InterruptedException
+    {
+        try {
+            return processZKCmd(co);
+        } catch (IllegalArgumentException e) {
+            System.err.println("Command failed: " + e);
+        } catch (KeeperException.NoNodeException e) {
+            System.err.println("Node does not exist: " + e.getPath());
+        } catch (KeeperException.NoChildrenForEphemeralsException e) {
+            System.err.println("Ephemerals cannot have children: "
+                    + e.getPath());
+        } catch (KeeperException.NodeExistsException e) {
+            System.err.println("Node already exists: " + e.getPath());
+        } catch (KeeperException.NotEmptyException e) {
+            System.err.println("Node not empty: " + e.getPath());
+        } catch (KeeperException.NotReadOnlyException e) {
+            System.err.println("Not a read-only call: " + e.getPath());
+        }catch (KeeperException.InvalidACLException  e) {
+            System.err.println("Acl is not valid : "+e.getPath());
+        }catch (KeeperException.NoAuthException  e) {
+            System.err.println("Authentication is not valid : "+e.getPath());
+        }catch (KeeperException.BadArgumentsException   e) {
+            System.err.println("Arguments are not valid : "+e.getPath());
+        }catch (KeeperException.BadVersionException e) {
+            System.err.println("version No is not valid : "+e.getPath());
+        }
+        return false;
+    }
+
+    protected boolean processZKCmd(MyCommandOptions co)
+        throws KeeperException, IOException, InterruptedException
+    {
+        Stat stat = new Stat();
+        String[] args = co.getArgArray();
+        String cmd = co.getCommand();
+        if (args.length < 1) {
+            usage();
+            return false;
+        }
+
+        if (!commandMap.containsKey(cmd)) {
+            usage();
+            return false;
+        }
+        
+        boolean watch = args.length > 2;
+        String path = null;
+        List<ACL> acl = Ids.OPEN_ACL_UNSAFE;
+        LOG.debug("Processing " + cmd);
+
+        if (cmd.equals("quit")) {
+            System.out.println("Quitting...");
+            zk.close();
+            System.exit(0);
+        } else if (cmd.equals("redo") && args.length >= 2) {
+            Integer i = Integer.decode(args[1]);
+            if (commandCount <= i || i < 0){ // don't allow redoing this redo
+                System.out.println("Command index out of range");
+                return false;
+            }
+            cl.parseCommand(history.get(i));
+            if (cl.getCommand().equals( "redo" )){
+                System.out.println("No redoing redos");
+                return false;
+            }
+            history.put(commandCount, history.get(i));
+            processCmd( cl);
+        } else if (cmd.equals("history")) {
+            for (int i=commandCount - 10;i<=commandCount;++i) {
+                if (i < 0) continue;
+                System.out.println(i + " - " + history.get(i));
+            }
+        } else if (cmd.equals("printwatches")) {
+            if (args.length == 1) {
+                System.out.println("printwatches is " + (printWatches ? "on" : "off"));
+            } else {
+                printWatches = args[1].equals("on");
+            }
+        } else if (cmd.equals("connect")) {
+            if (args.length >=2) {
+                connectToZK(args[1]);
+            } else {
+                connectToZK(host);
+            }
+        }
+        
+        // Below commands all need a live connection
+        if (zk == null || !zk.getState().isAlive()) {
+            System.out.println("Not connected");
+            return false;
+        }
+        
+        if (cmd.equals("create") && args.length >= 3) {
+            int first = 0;
+            CreateMode flags = CreateMode.PERSISTENT;
+            if ((args[1].equals("-e") && args[2].equals("-s"))
+                    || (args[1]).equals("-s") && (args[2].equals("-e"))) {
+                first+=2;
+                flags = CreateMode.EPHEMERAL_SEQUENTIAL;
+            } else if (args[1].equals("-e")) {
+                first++;
+                flags = CreateMode.EPHEMERAL;
+            } else if (args[1].equals("-s")) {
+                first++;
+                flags = CreateMode.PERSISTENT_SEQUENTIAL;
+            }
+            if (args.length == first + 4) {
+                acl = parseACLs(args[first+3]);
+            }
+            path = args[first + 1];
+            String newPath = zk.create(path, args[first+2].getBytes(), acl,
+                    flags);
+            System.err.println("Created " + newPath);
+        } else if (cmd.equals("delete") && args.length >= 2) {
+            path = args[1];
+            zk.delete(path, watch ? Integer.parseInt(args[2]) : -1);
+        } else if (cmd.equals("rmr") && args.length >= 2) {
+            path = args[1];
+            ZKUtil.deleteRecursive(zk, path);
+        } else if (cmd.equals("set") && args.length >= 3) {
+            path = args[1];
+            stat = zk.setData(path, args[2].getBytes(),
+                    args.length > 3 ? Integer.parseInt(args[3]) : -1);
+            printStat(stat);
+        } else if (cmd.equals("aget") && args.length >= 2) {
+            path = args[1];
+            zk.getData(path, watch, dataCallback, path);
+        } else if (cmd.equals("get") && args.length >= 2) {
+            path = args[1];
+            byte data[] = zk.getData(path, watch, stat);
+            data = (data == null)? "null".getBytes() : data;
+            System.out.println(new String(data));
+            printStat(stat);
+        } else if (cmd.equals("ls") && args.length >= 2) {
+            path = args[1];
+            List<String> children = zk.getChildren(path, watch);
+            System.out.println(children);
+        } else if (cmd.equals("ls2") && args.length >= 2) {
+            path = args[1];
+            List<String> children = zk.getChildren(path, watch, stat);
+            System.out.println(children);
+            printStat(stat);
+        } else if (cmd.equals("getAcl") && args.length >= 2) {
+            path = args[1];
+            acl = zk.getACL(path, stat);
+            for (ACL a : acl) {
+                System.out.println(a.getId() + ": "
+                        + getPermString(a.getPerms()));
+            }
+        } else if (cmd.equals("setAcl") && args.length >= 3) {
+            path = args[1];
+            stat = zk.setACL(path, parseACLs(args[2]),
+                    args.length > 4 ? Integer.parseInt(args[3]) : -1);
+            printStat(stat);
+        } else if (cmd.equals("stat") && args.length >= 2) {
+            path = args[1];
+            stat = zk.exists(path, watch);
+            if (stat == null) {
+              throw new KeeperException.NoNodeException(path);
+            }
+            printStat(stat);
+        } else if (cmd.equals("listquota") && args.length >= 2) {
+            path = args[1];
+            String absolutePath = Quotas.quotaZookeeper + path + "/" + Quotas.limitNode;
+            byte[] data =  null;
+            try {
+                System.err.println("absolute path is " + absolutePath);
+                data = zk.getData(absolutePath, false, stat);
+                StatsTrack st = new StatsTrack(new String(data));
+                System.out.println("Output quota for " + path + " "
+                        + st.toString());
+
+                data = zk.getData(Quotas.quotaZookeeper + path + "/" +
+                        Quotas.statNode, false, stat);
+                System.out.println("Output stat for " + path + " " +
+                        new StatsTrack(new String(data)).toString());
+            } catch(KeeperException.NoNodeException ne) {
+                System.err.println("quota for " + path + " does not exist.");
+            }
+        } else if (cmd.equals("setquota") && args.length >= 4) {
+            String option = args[1];
+            String val = args[2];
+            path = args[3];
+            System.err.println("Comment: the parts are " +
+                               "option " + option +
+                               " val " + val +
+                               " path " + path);
+            if ("-b".equals(option)) {
+                // we are setting the bytes quota
+                createQuota(zk, path, Long.parseLong(val), -1);
+            } else if ("-n".equals(option)) {
+                // we are setting the num quota
+                createQuota(zk, path, -1L, Integer.parseInt(val));
+            } else {
+                usage();
+            }
+
+        } else if (cmd.equals("delquota") && args.length >= 2) {
+            //if neither option -n or -b is specified, we delete
+            // the quota node for thsi node.
+            if (args.length == 3) {
+                //this time we have an option
+                String option = args[1];
+                path = args[2];
+                if ("-b".equals(option)) {
+                    delQuota(zk, path, true, false);
+                } else if ("-n".equals(option)) {
+                    delQuota(zk, path, false, true);
+                }
+            } else if (args.length == 2) {
+                path = args[1];
+                // we dont have an option specified.
+                // just delete whole quota node
+                delQuota(zk, path, true, true);
+            } else if (cmd.equals("help")) {
+                usage();
+            }
+        } else if (cmd.equals("close")) {
+                zk.close();
+        } else if (cmd.equals("sync") && args.length >= 2) {
+            path = args[1];
+            zk.sync(path, new AsyncCallback.VoidCallback() { public void processResult(int rc, String path, Object ctx) { System.out.println("Sync returned " + rc); } }, null );
+        } else if (cmd.equals("addauth") && args.length >=2 ) {
+            byte[] b = null;
+            if (args.length >= 3)
+                b = args[2].getBytes();
+
+            zk.addAuthInfo(args[1], b);
+        } else if (!commandMap.containsKey(cmd)) {
+            usage();
+        }
+        return watch;
+    }
+
+    private static String getPermString(int perms) {
+        StringBuilder p = new StringBuilder();
+        if ((perms & ZooDefs.Perms.CREATE) != 0) {
+            p.append('c');
+        }
+        if ((perms & ZooDefs.Perms.DELETE) != 0) {
+            p.append('d');
+        }
+        if ((perms & ZooDefs.Perms.READ) != 0) {
+            p.append('r');
+        }
+        if ((perms & ZooDefs.Perms.WRITE) != 0) {
+            p.append('w');
+        }
+        if ((perms & ZooDefs.Perms.ADMIN) != 0) {
+            p.append('a');
+        }
+        return p.toString();
+    }
+
+    private static List<ACL> parseACLs(String aclString) {
+        List<ACL> acl;
+        String acls[] = aclString.split(",");
+        acl = new ArrayList<ACL>();
+        for (String a : acls) {
+            int firstColon = a.indexOf(':');
+            int lastColon = a.lastIndexOf(':');
+            if (firstColon == -1 || lastColon == -1 || firstColon == lastColon) {
+                System.err
+                .println(a + " does not have the form scheme:id:perm");
+                continue;
+            }
+            ACL newAcl = new ACL();
+            newAcl.setId(new Id(a.substring(0, firstColon), a.substring(
+                    firstColon + 1, lastColon)));
+            newAcl.setPerms(getPermFromString(a.substring(lastColon + 1)));
+            acl.add(newAcl);
+        }
+        return acl;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperTestable.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperTestable.java b/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperTestable.java
new file mode 100644
index 0000000..775d1a2
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperTestable.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class ZooKeeperTestable implements Testable {
+    private static final Logger LOG = LoggerFactory
+            .getLogger(ZooKeeperTestable.class);
+
+    private final ZooKeeper zooKeeper;
+    private final ClientCnxn clientCnxn;
+
+    ZooKeeperTestable(ZooKeeper zooKeeper, ClientCnxn clientCnxn) {
+        this.zooKeeper = zooKeeper;
+        this.clientCnxn = clientCnxn;
+    }
+
+    @Override
+    public void injectSessionExpiration() {
+        LOG.info("injectSessionExpiration() called");
+
+        clientCnxn.eventThread.queueEvent(new WatchedEvent(
+                Watcher.Event.EventType.None,
+                Watcher.Event.KeeperState.Expired, null));
+        clientCnxn.eventThread.queueEventOfDeath();
+        clientCnxn.sendThread.getClientCnxnSocket().wakeupCnxn();
+        clientCnxn.state = ZooKeeper.States.CLOSED;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/client/ConnectStringParser.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/client/ConnectStringParser.java b/zookeeper-server/src/main/java/org/apache/zookeeper/client/ConnectStringParser.java
new file mode 100644
index 0000000..ff0425a
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/client/ConnectStringParser.java
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.client;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+
+import org.apache.zookeeper.common.PathUtils;
+
+/**
+ * A parser for ZooKeeper Client connect strings.
+ * 
+ * This class is not meant to be seen or used outside of ZooKeeper itself.
+ * 
+ * The chrootPath member should be replaced by a Path object in issue
+ * ZOOKEEPER-849.
+ * 
+ * @see org.apache.zookeeper.ZooKeeper
+ */
+public final class ConnectStringParser {
+    private static final int DEFAULT_PORT = 2181;
+
+    private final String chrootPath;
+
+    private final ArrayList<InetSocketAddress> serverAddresses = new ArrayList<InetSocketAddress>();
+
+    /**
+     * 
+     * @throws IllegalArgumentException
+     *             for an invalid chroot path.
+     */
+    public ConnectStringParser(String connectString) {
+        // parse out chroot, if any
+        int off = connectString.indexOf('/');
+        if (off >= 0) {
+            String chrootPath = connectString.substring(off);
+            // ignore "/" chroot spec, same as null
+            if (chrootPath.length() == 1) {
+                this.chrootPath = null;
+            } else {
+                PathUtils.validatePath(chrootPath);
+                this.chrootPath = chrootPath;
+            }
+            connectString = connectString.substring(0, off);
+        } else {
+            this.chrootPath = null;
+        }
+
+        String hostsList[] = connectString.split(",");
+        for (String host : hostsList) {
+            int port = DEFAULT_PORT;
+            int pidx = host.lastIndexOf(':');
+            if (pidx >= 0) {
+                // otherwise : is at the end of the string, ignore
+                if (pidx < host.length() - 1) {
+                    port = Integer.parseInt(host.substring(pidx + 1));
+                }
+                host = host.substring(0, pidx);
+            }
+            serverAddresses.add(InetSocketAddress.createUnresolved(host, port));
+        }
+    }
+
+    public String getChrootPath() {
+        return chrootPath;
+    }
+
+    public ArrayList<InetSocketAddress> getServerAddresses() {
+        return serverAddresses;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/client/FourLetterWordMain.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/client/FourLetterWordMain.java b/zookeeper-server/src/main/java/org/apache/zookeeper/client/FourLetterWordMain.java
new file mode 100644
index 0000000..a4175e4
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/client/FourLetterWordMain.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.client;
+
+import org.apache.log4j.Logger;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+
+import org.apache.yetus.audience.InterfaceAudience;
+
+@InterfaceAudience.Public
+public class FourLetterWordMain {
+    //in milliseconds, socket should connect/read within this period otherwise SocketTimeoutException
+    private static final int DEFAULT_SOCKET_TIMEOUT = 5000;
+    protected static final Logger LOG = Logger.getLogger(FourLetterWordMain.class);
+
+    /**
+     * Send the 4letterword
+     * @param host the destination host
+     * @param port the destination port
+     * @param cmd the 4letterword
+     * @return server response
+     * @throws java.io.IOException
+     */
+    public static String send4LetterWord(String host, int port, String cmd)
+            throws IOException
+    {
+        return send4LetterWord(host, port, cmd, DEFAULT_SOCKET_TIMEOUT);
+    }
+    /**
+     * Send the 4letterword
+     * @param host the destination host
+     * @param port the destination port
+     * @param cmd the 4letterword
+     * @param timeout in milliseconds, maximum time to wait while connecting/reading data
+     * @return server response
+     * @throws java.io.IOException
+     */
+    public static String send4LetterWord(String host, int port, String cmd, int timeout)
+            throws IOException
+    {
+        LOG.info("connecting to " + host + " " + port);
+        Socket sock = new Socket();
+        InetSocketAddress hostaddress= host != null ? new InetSocketAddress(host, port) :
+            new InetSocketAddress(InetAddress.getByName(null), port);
+        BufferedReader reader = null;
+        try {
+            sock.setSoTimeout(timeout);
+            sock.connect(hostaddress, timeout);
+            OutputStream outstream = sock.getOutputStream();
+            outstream.write(cmd.getBytes());
+            outstream.flush();
+            // this replicates NC - close the output stream before reading
+            sock.shutdownOutput();
+
+            reader =
+                    new BufferedReader(
+                            new InputStreamReader(sock.getInputStream()));
+            StringBuilder sb = new StringBuilder();
+            String line;
+            while((line = reader.readLine()) != null) {
+                sb.append(line + "\n");
+            }
+            return sb.toString();
+        } catch (SocketTimeoutException e) {
+            throw new IOException("Exception while executing four letter word: " + cmd, e);
+        } finally {
+            sock.close();
+            if (reader != null) {
+                reader.close();
+            }
+        }
+    }
+    
+    public static void main(String[] args)
+            throws IOException
+    {
+        if (args.length != 3) {
+            System.out.println("Usage: FourLetterWordMain <host> <port> <cmd>");
+        } else {
+            System.out.println(send4LetterWord(args[0], Integer.parseInt(args[1]), args[2]));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/client/HostProvider.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/client/HostProvider.java b/zookeeper-server/src/main/java/org/apache/zookeeper/client/HostProvider.java
new file mode 100644
index 0000000..61d9108
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/client/HostProvider.java
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.client;
+
+import org.apache.yetus.audience.InterfaceAudience;
+
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+
+/**
+ * A set of hosts a ZooKeeper client should connect to.
+ * 
+ * Classes implementing this interface must guarantee the following:
+ * 
+ * * Every call to next() returns an InetSocketAddress. So the iterator never
+ * ends.
+ * 
+ * * The size() of a HostProvider may never be zero.
+ * 
+ * A HostProvider must return resolved InetSocketAddress instances on next(),
+ * but it's up to the HostProvider, when it wants to do the resolving.
+ * 
+ * Different HostProvider could be imagined:
+ * 
+ * * A HostProvider that loads the list of Hosts from an URL or from DNS 
+ * * A HostProvider that re-resolves the InetSocketAddress after a timeout. 
+ * * A HostProvider that prefers nearby hosts.
+ */
+@InterfaceAudience.Public
+public interface HostProvider {
+    public int size();
+
+    /**
+     * The next host to try to connect to.
+     *
+     * For a spinDelay of 0 there should be no wait.
+     *
+     * @param spinDelay Milliseconds to wait if all hosts have been tried once.
+     * @return The next host to try to connect to with resolved address. If the host is not resolvable, the unresolved
+     * address will be returned.
+     */
+    public InetSocketAddress next(long spinDelay);
+
+    /**
+     * Notify the HostProvider of a successful connection.
+     * 
+     * The HostProvider may use this notification to reset it's inner state.
+     */
+    public void onConnected();
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/client/StaticHostProvider.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/client/StaticHostProvider.java b/zookeeper-server/src/main/java/org/apache/zookeeper/client/StaticHostProvider.java
new file mode 100644
index 0000000..0005942
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/client/StaticHostProvider.java
@@ -0,0 +1,179 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.client;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Most simple HostProvider, resolves on every next() call.
+ *
+ * Please be aware that although this class doesn't do any DNS caching, there're multiple levels of caching already
+ * present across the stack like in JVM, OS level, hardware, etc. The best we could do here is to get the most recent
+ * address from the underlying system which is considered up-to-date.
+ *
+ */
+@InterfaceAudience.Public
+public final class StaticHostProvider implements HostProvider {
+    public interface Resolver {
+        InetAddress[] getAllByName(String name) throws UnknownHostException;
+    }
+
+    private static final Logger LOG = LoggerFactory
+            .getLogger(StaticHostProvider.class);
+
+    private final List<InetSocketAddress> serverAddresses = new ArrayList<InetSocketAddress>(5);
+
+    private int lastIndex = -1;
+
+    private int currentIndex = -1;
+
+    private Resolver resolver;
+
+    /**
+     * Constructs a SimpleHostSet.
+     *
+     * @param serverAddresses
+     *            possibly unresolved ZooKeeper server addresses
+     * @throws IllegalArgumentException
+     *             if serverAddresses is empty or resolves to an empty list
+     */
+    public StaticHostProvider(Collection<InetSocketAddress> serverAddresses) {
+        this.resolver = new Resolver() {
+            @Override
+            public InetAddress[] getAllByName(String name) throws UnknownHostException {
+                return InetAddress.getAllByName(name);
+            }
+        };
+        init(serverAddresses);
+    }
+
+    /**
+     * Introduced for testing purposes. getAllByName() is a static method of InetAddress, therefore cannot be easily mocked.
+     * By abstraction of Resolver interface we can easily inject a mocked implementation in tests.
+     *
+     * @param serverAddresses
+     *            possibly unresolved ZooKeeper server addresses
+     * @param resolver
+     *            custom resolver implementation
+     * @throws IllegalArgumentException
+     *             if serverAddresses is empty or resolves to an empty list
+     */
+    public StaticHostProvider(Collection<InetSocketAddress> serverAddresses, Resolver resolver) {
+        this.resolver = resolver;
+        init(serverAddresses);
+    }
+
+    /**
+     * Common init method for all constructors.
+     * Resolve all unresolved server addresses, put them in a list and shuffle.
+     */
+    private void init(Collection<InetSocketAddress> serverAddresses) {
+        if (serverAddresses.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "A HostProvider may not be empty!");
+        }
+
+        this.serverAddresses.addAll(serverAddresses);
+        Collections.shuffle(this.serverAddresses);
+    }
+
+    /**
+     * Evaluate to a hostname if one is available and otherwise it returns the
+     * string representation of the IP address.
+     *
+     * In Java 7, we have a method getHostString, but earlier versions do not support it.
+     * This method is to provide a replacement for InetSocketAddress.getHostString().
+     *
+     * @param addr
+     * @return Hostname string of address parameter
+     */
+    private String getHostString(InetSocketAddress addr) {
+        String hostString = "";
+
+        if (addr == null) {
+            return hostString;
+        }
+        if (!addr.isUnresolved()) {
+            InetAddress ia = addr.getAddress();
+
+            // If the string starts with '/', then it has no hostname
+            // and we want to avoid the reverse lookup, so we return
+            // the string representation of the address.
+            if (ia.toString().startsWith("/")) {
+                hostString = ia.getHostAddress();
+            } else {
+                hostString = addr.getHostName();
+            }
+        } else {
+            // According to the Java 6 documentation, if the hostname is
+            // unresolved, then the string before the colon is the hostname.
+            String addrString = addr.toString();
+            hostString = addrString.substring(0, addrString.lastIndexOf(':'));
+        }
+
+        return hostString;
+    }
+
+    public int size() {
+        return serverAddresses.size();
+    }
+
+    public InetSocketAddress next(long spinDelay) {
+        currentIndex = ++currentIndex % serverAddresses.size();
+        if (currentIndex == lastIndex && spinDelay > 0) {
+            try {
+                Thread.sleep(spinDelay);
+            } catch (InterruptedException e) {
+                LOG.warn("Unexpected exception", e);
+            }
+        } else if (lastIndex == -1) {
+            // We don't want to sleep on the first ever connect attempt.
+            lastIndex = 0;
+        }
+
+        InetSocketAddress curAddr = serverAddresses.get(currentIndex);
+        try {
+            String curHostString = getHostString(curAddr);
+            List<InetAddress> resolvedAddresses = new ArrayList<InetAddress>(Arrays.asList(this.resolver.getAllByName(curHostString)));
+            if (resolvedAddresses.isEmpty()) {
+                return curAddr;
+            }
+            Collections.shuffle(resolvedAddresses);
+            return new InetSocketAddress(resolvedAddresses.get(0), curAddr.getPort());
+        } catch (UnknownHostException e) {
+            return curAddr;
+        }
+    }
+
+    @Override
+    public void onConnected() {
+        lastIndex = currentIndex;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperSaslClient.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperSaslClient.java
new file mode 100644
index 0000000..af3303c
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperSaslClient.java
@@ -0,0 +1,439 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.client;
+
+import java.io.IOException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginException;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+import org.apache.zookeeper.AsyncCallback;
+import org.apache.zookeeper.ClientCnxn;
+import org.apache.zookeeper.Environment;
+import org.apache.zookeeper.Login;
+import org.apache.zookeeper.SaslClientCallbackHandler;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.proto.GetSASLRequest;
+import org.apache.zookeeper.proto.SetSASLResponse;
+import org.apache.zookeeper.util.SecurityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class manages SASL authentication for the client. It
+ * allows ClientCnxn to authenticate using SASL with a Zookeeper server.
+ */
+public class ZooKeeperSaslClient {
+    public static final String LOGIN_CONTEXT_NAME_KEY = "zookeeper.sasl.clientconfig";
+    public static final String ENABLE_CLIENT_SASL_KEY = "zookeeper.sasl.client";
+    public static final String ENABLE_CLIENT_SASL_DEFAULT = "true";
+    private static volatile boolean initializedLogin = false; 
+
+    /**
+     * Returns true if the SASL client is enabled. By default, the client
+     * is enabled but can be disabled by setting the system property
+     * <code>zookeeper.sasl.client</code> to <code>false</code>. See
+     * ZOOKEEPER-1657 for more information.
+     *
+     * @return If the SASL client is enabled.
+     */
+    public static boolean isEnabled() {
+        return Boolean.valueOf(System.getProperty(ENABLE_CLIENT_SASL_KEY, ENABLE_CLIENT_SASL_DEFAULT));
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslClient.class);
+    private static Login login = null;
+    private SaslClient saslClient;
+    private boolean isSASLConfigured = true;
+
+    private byte[] saslToken = new byte[0];
+
+    public enum SaslState {
+        INITIAL,INTERMEDIATE,COMPLETE,FAILED
+    }
+
+    private SaslState saslState = SaslState.INITIAL;
+
+    private boolean gotLastPacket = false;
+    /** informational message indicating the current configuration status */
+    private final String configStatus;
+
+    public SaslState getSaslState() {
+        return saslState;
+    }
+
+    public String getLoginContext() {
+        if (login != null)
+            return login.getLoginContextName();
+        return null;
+    }
+
+    public ZooKeeperSaslClient(final String serverPrincipal)
+            throws LoginException {
+        /**
+         * ZOOKEEPER-1373: allow system property to specify the JAAS
+         * configuration section that the zookeeper client should use.
+         * Default to "Client".
+         */
+        String clientSection = System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client");
+        // Note that 'Configuration' here refers to javax.security.auth.login.Configuration.
+        AppConfigurationEntry entries[] = null;
+        RuntimeException runtimeException = null;
+        try {
+            entries = Configuration.getConfiguration().getAppConfigurationEntry(clientSection);
+        } catch (SecurityException e) {
+            // handle below: might be harmless if the user doesn't intend to use JAAS authentication.
+            runtimeException = e;
+        } catch (IllegalArgumentException e) {
+            // third party customized getAppConfigurationEntry could throw IllegalArgumentException when JAAS
+            // configuration isn't set. We can reevaluate whether to catch RuntimeException instead when more 
+            // different types of RuntimeException found
+            runtimeException = e;
+        }
+        if (entries != null) {
+            this.configStatus = "Will attempt to SASL-authenticate using Login Context section '" + clientSection + "'";
+            this.saslClient = createSaslClient(serverPrincipal, clientSection);
+        } else {
+            // Handle situation of clientSection's being null: it might simply because the client does not intend to 
+            // use SASL, so not necessarily an error.
+            saslState = SaslState.FAILED;
+            String explicitClientSection = System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY);
+            if (explicitClientSection != null) {
+                // If the user explicitly overrides the default Login Context, they probably expected SASL to
+                // succeed. But if we got here, SASL failed.
+                if (runtimeException != null) {
+                    throw new LoginException("Zookeeper client cannot authenticate using the " + explicitClientSection +
+                            " section of the supplied JAAS configuration: '" +
+                            System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " +
+                            "RuntimeException: " + runtimeException);
+                } else {
+                    throw new LoginException("Client cannot SASL-authenticate because the specified JAAS configuration " +
+                            "section '" + explicitClientSection + "' could not be found.");
+                }
+            } else {
+                // The user did not override the default context. It might be that they just don't intend to use SASL,
+                // so log at INFO, not WARN, since they don't expect any SASL-related information.
+                String msg = "Will not attempt to authenticate using SASL ";
+                if (runtimeException != null) {
+                    msg += "(" + runtimeException + ")";
+                } else {
+                    msg += "(unknown error)";
+                }
+                this.configStatus = msg;
+                this.isSASLConfigured = false;
+            }
+            if (System.getProperty(Environment.JAAS_CONF_KEY)  != null) {
+                // Again, the user explicitly set something SASL-related, so they probably expected SASL to succeed.
+                if (runtimeException != null) {
+                    throw new LoginException("Zookeeper client cannot authenticate using the '" +
+                            System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") +
+                            "' section of the supplied JAAS configuration: '" +
+                            System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " +
+                            "RuntimeException: " + runtimeException);
+                } else {
+                    throw new LoginException("No JAAS configuration section named '" +
+                            System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") +
+                            "' was found in specified JAAS configuration file: '" +
+                            System.getProperty(Environment.JAAS_CONF_KEY) + "'.");
+                }
+            }
+        }
+    }
+    
+    /**
+     * @return informational message indicating the current configuration status.
+     */
+    public String getConfigStatus() {
+        return configStatus;
+    }
+
+    public boolean isComplete() {
+        return (saslState == SaslState.COMPLETE);
+    }
+
+    public boolean isFailed() {
+        return (saslState == SaslState.FAILED);
+    }
+
+    public static class ServerSaslResponseCallback implements AsyncCallback.DataCallback {
+        public void processResult(int rc, String path, Object ctx, byte data[], Stat stat) {
+            // processResult() is used by ClientCnxn's sendThread to respond to
+            // data[] contains the Zookeeper Server's SASL token.
+            // ctx is the ZooKeeperSaslClient object. We use this object's respondToServer() method
+            // to reply to the Zookeeper Server's SASL token
+            ZooKeeperSaslClient client = ((ClientCnxn)ctx).zooKeeperSaslClient;
+            if (client == null) {
+                LOG.warn("sasl client was unexpectedly null: cannot respond to Zookeeper server.");
+                return;
+            }
+            byte[] usedata = data;
+            if (data != null) {
+                LOG.debug("ServerSaslResponseCallback(): saslToken server response: (length="+usedata.length+")");
+            }
+            else {
+                usedata = new byte[0];
+                LOG.debug("ServerSaslResponseCallback(): using empty data[] as server response (length="+usedata.length+")");
+            }
+            client.respondToServer(usedata, (ClientCnxn)ctx);
+        }
+    }
+
+    private SaslClient createSaslClient(final String servicePrincipal,
+                                                     final String loginContext) throws LoginException {
+        try {
+            if (!initializedLogin) {
+                synchronized (ZooKeeperSaslClient.class) {
+                    if (login == null) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("JAAS loginContext is: " + loginContext);
+                        }
+                        // note that the login object is static: it's shared amongst all zookeeper-related connections.
+                        // in order to ensure the login is initialized only once, it must be synchronized the code snippet.
+                        login = new Login(loginContext, new SaslClientCallbackHandler(null, "Client"));
+                        login.startThreadIfNeeded();
+                        initializedLogin = true;
+                    }
+                }
+            }
+            return SecurityUtils.createSaslClient(login.getSubject(),
+                    servicePrincipal, "zookeeper", "zk-sasl-md5", LOG, "Client");
+        } catch (LoginException e) {
+            // We throw LoginExceptions...
+            throw e;
+        } catch (Exception e) {
+            // ..but consume (with a log message) all other types of exceptions.
+            LOG.error("Exception while trying to create SASL client: " + e);
+            return null;
+        }
+    }
+
+    public void respondToServer(byte[] serverToken, ClientCnxn cnxn) {
+        if (saslClient == null) {
+            LOG.error("saslClient is unexpectedly null. Cannot respond to server's SASL message; ignoring.");
+            return;
+        }
+
+        if (!(saslClient.isComplete())) {
+            try {
+                saslToken = createSaslToken(serverToken);
+                if (saslToken != null) {
+                    sendSaslPacket(saslToken, cnxn);
+                }
+            } catch (SaslException e) {
+                LOG.error("SASL authentication failed using login context '" +
+                        this.getLoginContext() + "' with exception: {}", e);
+                saslState = SaslState.FAILED;
+                gotLastPacket = true;
+            }
+        }
+
+        if (saslClient.isComplete()) {
+            // GSSAPI: server sends a final packet after authentication succeeds
+            // or fails.
+            if ((serverToken == null) && (saslClient.getMechanismName().equals("GSSAPI")))
+                gotLastPacket = true;
+            // non-GSSAPI: no final packet from server.
+            if (!saslClient.getMechanismName().equals("GSSAPI")) {
+                gotLastPacket = true;
+            }
+            // SASL authentication is completed, successfully or not:
+            // enable the socket's writable flag so that any packets waiting for authentication to complete in
+            // the outgoing queue will be sent to the Zookeeper server.
+            cnxn.enableWrite();
+        }
+    }
+
+    private byte[] createSaslToken() throws SaslException {
+        saslState = SaslState.INTERMEDIATE;
+        return createSaslToken(saslToken);
+    }
+
+    private byte[] createSaslToken(final byte[] saslToken) throws SaslException {
+        if (saslToken == null) {
+            // TODO: introspect about runtime environment (such as jaas.conf)
+            saslState = SaslState.FAILED;
+            throw new SaslException("Error in authenticating with a Zookeeper Quorum member: the quorum member's saslToken is null.");
+        }
+
+        Subject subject = login.getSubject();
+        if (subject != null) {
+            synchronized(login) {
+                try {
+                    final byte[] retval =
+                        Subject.doAs(subject, new PrivilegedExceptionAction<byte[]>() {
+                                public byte[] run() throws SaslException {
+                                    LOG.debug("saslClient.evaluateChallenge(len="+saslToken.length+")");
+                                    return saslClient.evaluateChallenge(saslToken);
+                                }
+                            });
+                    return retval;
+                }
+                catch (PrivilegedActionException e) {
+                    String error = "An error: (" + e + ") occurred when evaluating Zookeeper Quorum Member's " +
+                      " received SASL token.";
+                    // Try to provide hints to use about what went wrong so they can fix their configuration.
+                    // TODO: introspect about e: look for GSS information.
+                    final String UNKNOWN_SERVER_ERROR_TEXT =
+                      "(Mechanism level: Server not found in Kerberos database (7) - UNKNOWN_SERVER)";
+                    if (e.toString().indexOf(UNKNOWN_SERVER_ERROR_TEXT) > -1) {
+                        error += " This may be caused by Java's being unable to resolve the Zookeeper Quorum Member's" +
+                          " hostname correctly. You may want to try to adding" +
+                          " '-Dsun.net.spi.nameservice.provider.1=dns,sun' to your client's JVMFLAGS environment.";
+                    }
+                    error += " Zookeeper Client will go to AUTH_FAILED state.";
+                    LOG.error(error);
+                    saslState = SaslState.FAILED;
+                    throw new SaslException(error);
+                }
+            }
+        }
+        else {
+            throw new SaslException("Cannot make SASL token without subject defined. " +
+              "For diagnosis, please look for WARNs and ERRORs in your log related to the Login class.");
+        }
+    }
+
+    private void sendSaslPacket(byte[] saslToken, ClientCnxn cnxn)
+      throws SaslException{
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("ClientCnxn:sendSaslPacket:length="+saslToken.length);
+        }
+
+        GetSASLRequest request = new GetSASLRequest();
+        request.setToken(saslToken);
+        SetSASLResponse response = new SetSASLResponse();
+        ServerSaslResponseCallback cb = new ServerSaslResponseCallback();
+
+        try {
+            cnxn.sendPacket(request,response,cb, ZooDefs.OpCode.sasl);
+        } catch (IOException e) {
+            throw new SaslException("Failed to send SASL packet to server.",
+                e);
+        }
+    }
+
+    private void sendSaslPacket(ClientCnxn cnxn) throws SaslException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("ClientCnxn:sendSaslPacket:length="+saslToken.length);
+        }
+        GetSASLRequest request = new GetSASLRequest();
+        request.setToken(createSaslToken());
+        SetSASLResponse response = new SetSASLResponse();
+        ServerSaslResponseCallback cb = new ServerSaslResponseCallback();
+        try {
+            cnxn.sendPacket(request,response,cb, ZooDefs.OpCode.sasl);
+        } catch (IOException e) {
+            throw new SaslException("Failed to send SASL packet to server due " +
+              "to IOException:", e);
+        }
+    }
+
+    // used by ClientCnxn to know whether to emit a SASL-related event: either AuthFailed or SaslAuthenticated,
+    // or none, if not ready yet. Sets saslState to COMPLETE as a side-effect.
+    public KeeperState getKeeperState() {
+        if (saslClient != null) {
+            if (saslState == SaslState.FAILED) {
+              return KeeperState.AuthFailed;
+            }
+            if (saslClient.isComplete()) {
+                if (saslState == SaslState.INTERMEDIATE) {
+                    saslState = SaslState.COMPLETE;
+                    return KeeperState.SaslAuthenticated;
+                }
+            }
+        }
+        // No event ready to emit yet.
+        return null;
+    }
+
+    // Initialize the client's communications with the Zookeeper server by sending the server the first
+    // authentication packet.
+    public void initialize(ClientCnxn cnxn) throws SaslException {
+        if (saslClient == null) {
+            saslState = SaslState.FAILED;
+            throw new SaslException("saslClient failed to initialize properly: it's null.");
+        }
+        if (saslState == SaslState.INITIAL) {
+            if (saslClient.hasInitialResponse()) {
+                sendSaslPacket(cnxn);
+            }
+            else {
+                byte[] emptyToken = new byte[0];
+                sendSaslPacket(emptyToken, cnxn);
+            }
+            saslState = SaslState.INTERMEDIATE;
+        }
+    }
+
+    public boolean clientTunneledAuthenticationInProgress() {
+    	if (!isSASLConfigured) {
+    	    return false;
+        } 
+        // TODO: Rather than checking a disjunction here, should be a single member
+        // variable or method in this class to determine whether the client is
+        // configured to use SASL. (see also ZOOKEEPER-1455).
+        try {
+  	    if ((System.getProperty(Environment.JAAS_CONF_KEY) != null) ||
+              ((javax.security.auth.login.Configuration.getConfiguration() != null) &&
+                  (javax.security.auth.login.Configuration.getConfiguration().
+                       getAppConfigurationEntry(System.
+                       getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY,"Client")) 
+                           != null))) {
+                // Client is configured to use a valid login Configuration, so
+                // authentication is either in progress, successful, or failed.
+
+                // 1. Authentication hasn't finished yet: we must wait for it to do so.
+                if ((isComplete() == false) &&
+                    (isFailed() == false)) {
+                    return true;
+                }
+
+                // 2. SASL authentication has succeeded or failed..
+                if (isComplete() || isFailed()) {
+                    if (gotLastPacket == false) {
+                        // ..but still in progress, because there is a final SASL
+                        // message from server which must be received.
+                    return true;
+                    }
+                }
+            }
+            // Either client is not configured to use a tunnelled authentication
+            // scheme, or tunnelled authentication has completed (successfully or
+            // not), and all server SASL messages have been received.
+            return false;
+        } catch (SecurityException e) {
+            // Thrown if the caller does not have permission to retrieve the Configuration.
+            // In this case, simply returning false is correct.
+            if (LOG.isDebugEnabled() == true) {
+                LOG.debug("Could not retrieve login configuration: " + e);
+            }
+            return false;
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/common/AtomicFileOutputStream.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/common/AtomicFileOutputStream.java b/zookeeper-server/src/main/java/org/apache/zookeeper/common/AtomicFileOutputStream.java
new file mode 100644
index 0000000..2584d3f
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/common/AtomicFileOutputStream.java
@@ -0,0 +1,126 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.common;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+ * This code is originally from HDFS, see the similarly named files there
+ * in case of bug fixing, history, etc...
+ */
+
+/**
+ * A FileOutputStream that has the property that it will only show up at its
+ * destination once it has been entirely written and flushed to disk. While
+ * being written, it will use a .tmp suffix.
+ *
+ * When the output stream is closed, it is flushed, fsynced, and will be moved
+ * into place, overwriting any file that already exists at that location.
+ *
+ * <b>NOTE</b>: on Windows platforms, it will not atomically replace the target
+ * file - instead the target file is deleted before this one is moved into
+ * place.
+ */
+public class AtomicFileOutputStream extends FilterOutputStream {
+    private static final String TMP_EXTENSION = ".tmp";
+
+    private final static Logger LOG = LoggerFactory
+            .getLogger(AtomicFileOutputStream.class);
+
+    private final File origFile;
+    private final File tmpFile;
+
+    public AtomicFileOutputStream(File f) throws FileNotFoundException {
+        // Code unfortunately must be duplicated below since we can't assign
+        // anything
+        // before calling super
+        super(new FileOutputStream(new File(f.getParentFile(), f.getName()
+                + TMP_EXTENSION)));
+        origFile = f.getAbsoluteFile();
+        tmpFile = new File(f.getParentFile(), f.getName() + TMP_EXTENSION)
+                .getAbsoluteFile();
+    }
+
+    /**
+     * The default write method in FilterOutputStream does not call the write
+     * method of its underlying input stream with the same arguments. Instead
+     * it writes the data byte by byte, override it here to make it more
+     * efficient.
+     */
+    @Override
+    public void write(byte b[], int off, int len) throws IOException {
+        out.write(b, off, len);
+    }
+
+    @Override
+    public void close() throws IOException {
+        boolean triedToClose = false, success = false;
+        try {
+            flush();
+            ((FileOutputStream) out).getChannel().force(true);
+
+            triedToClose = true;
+            super.close();
+            success = true;
+        } finally {
+            if (success) {
+                boolean renamed = tmpFile.renameTo(origFile);
+                if (!renamed) {
+                    // On windows, renameTo does not replace.
+                    if (!origFile.delete() || !tmpFile.renameTo(origFile)) {
+                        throw new IOException(
+                                "Could not rename temporary file " + tmpFile
+                                        + " to " + origFile);
+                    }
+                }
+            } else {
+                if (!triedToClose) {
+                    // If we failed when flushing, try to close it to not leak
+                    // an FD
+                    IOUtils.closeStream(out);
+                }
+                // close wasn't successful, try to delete the tmp file
+                if (!tmpFile.delete()) {
+                    LOG.warn("Unable to delete tmp file " + tmpFile);
+                }
+            }
+        }
+    }
+
+    /**
+     * Close the atomic file, but do not "commit" the temporary file on top of
+     * the destination. This should be used if there is a failure in writing.
+     */
+    public void abort() {
+        try {
+            super.close();
+        } catch (IOException ioe) {
+            LOG.warn("Unable to abort file " + tmpFile, ioe);
+        }
+        if (!tmpFile.delete()) {
+            LOG.warn("Unable to delete tmp file during abort " + tmpFile);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/common/IOUtils.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/common/IOUtils.java b/zookeeper-server/src/main/java/org/apache/zookeeper/common/IOUtils.java
new file mode 100644
index 0000000..16aea4e
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/common/IOUtils.java
@@ -0,0 +1,123 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.common;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import org.slf4j.Logger;
+
+/*
+ * This code is originally from HDFS, see the similarly named files there
+ * in case of bug fixing, history, etc...
+ */
+
+public class IOUtils {
+    /**
+     * Closes the stream ignoring {@link IOException}. Must only be called in
+     * cleaning up from exception handlers.
+     * 
+     * @param stream
+     *            the Stream to close
+     */
+    public static void closeStream(Closeable stream) {
+        cleanup(null, stream);
+    }
+
+    /**
+     * Close the Closeable objects and <b>ignore</b> any {@link IOException} or
+     * null pointers. Must only be used for cleanup in exception handlers.
+     * 
+     * @param log
+     *            the log to record problems to at debug level. Can be null.
+     * @param closeables
+     *            the objects to close
+     */
+    public static void cleanup(Logger log, Closeable... closeables) {
+        for (Closeable c : closeables) {
+            if (c != null) {
+                try {
+                    c.close();
+                } catch (IOException e) {
+                    if (log != null) {
+                        log.warn("Exception in closing " + c, e);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Copies from one stream to another.
+     * 
+     * @param in
+     *            InputStrem to read from
+     * @param out
+     *            OutputStream to write to
+     * @param buffSize
+     *            the size of the buffer
+     * @param close
+     *            whether or not close the InputStream and OutputStream at the
+     *            end. The streams are closed in the finally clause.
+     */
+    public static void copyBytes(InputStream in, OutputStream out,
+            int buffSize, boolean close) throws IOException {
+        try {
+            copyBytes(in, out, buffSize);
+            if (close) {
+                out.close();
+                out = null;
+                in.close();
+                in = null;
+            }
+        } finally {
+            if (close) {
+                closeStream(out);
+                closeStream(in);
+            }
+        }
+    }
+
+    /**
+     * Copies from one stream to another.
+     * 
+     * @param in
+     *            InputStrem to read from
+     * @param out
+     *            OutputStream to write to
+     * @param buffSize
+     *            the size of the buffer
+     */
+    public static void copyBytes(InputStream in, OutputStream out, int buffSize)
+            throws IOException {
+        PrintStream ps = out instanceof PrintStream ? (PrintStream) out : null;
+        byte buf[] = new byte[buffSize];
+        int bytesRead = in.read(buf);
+        while (bytesRead >= 0) {
+            out.write(buf, 0, bytesRead);
+            if ((ps != null) && ps.checkError()) {
+                throw new IOException("Unable to write to output stream.");
+            }
+            bytesRead = in.read(buf);
+        }
+    }
+
+}


Mime
View raw message