ZOOKEEPER-1898: ZooKeeper Java cli shell always returns 0 as exit code (Abraham Fine via phunt)

git-svn-id: https://svn.apache.org/repos/asf/zookeeper/trunk@1754188 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/CHANGES.txt b/CHANGES.txt
index 11f6252..764b4ca 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -329,6 +329,9 @@
   ZOOKEEPER-2459: Update NOTICE file with Netty notice
   (fpj via phunt)
 
+  ZOOKEEPER-1898: ZooKeeper Java cli shell always returns "0" as exit code
+  (Abraham Fine via phunt)
+
 IMPROVEMENTS:
   ZOOKEEPER-2024 Major throughput improvement with mixed workloads (Kfir Lev-Ari via shralex)
 
diff --git a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java
index f722693..25d61a4 100644
--- a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java
+++ b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java
@@ -34,6 +34,9 @@
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 
+import org.apache.zookeeper.cli.CliException;
+import org.apache.zookeeper.cli.CommandNotFoundException;
+import org.apache.zookeeper.cli.MalformedCommandException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.zookeeper.ZooDefs.Ids;
@@ -42,7 +45,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.cli.ParseException;
 import org.apache.zookeeper.cli.AddAuthCommand;
 import org.apache.zookeeper.cli.CliCommand;
 import org.apache.zookeeper.cli.CloseCommand;
@@ -79,6 +81,7 @@
     protected HashMap<Integer,String> history = new HashMap<Integer,String>( );
     protected int commandCount = 0;
     protected boolean printWatches = true;
+    protected int exitCode = 0;
 
     protected ZooKeeper zk;
     protected String host = "";
@@ -283,8 +286,7 @@
                  new MyWatcher(), readOnly);
     }
     
-    public static void main(String args[])
-        throws KeeperException, IOException, InterruptedException
+    public static void main(String args[]) throws CliException, IOException, InterruptedException
     {
         ZooKeeperMain main = new ZooKeeperMain(args);
         main.run();
@@ -302,7 +304,7 @@
       this.zk = zk;
     }
 
-    void run() throws KeeperException, IOException, InterruptedException {
+    void run() throws CliException, IOException, InterruptedException {
         if (cl.getCommand() == null) {
             System.out.println("Welcome to ZooKeeper!");
 
@@ -360,10 +362,10 @@
             // Command line args non-null.  Run what was passed.
             processCmd(cl);
         }
+        System.exit(exitCode);
     }
 
-    public void executeLine(String line)
-    throws InterruptedException, IOException, KeeperException {
+    public void executeLine(String line) throws CliException, InterruptedException, IOException {
       if (!line.equals("")) {
         cl.parseCommand(line);
         addToHistory(commandCount,line);
@@ -580,80 +582,51 @@
         return true;
     }
 
-    protected boolean processCmd(MyCommandOptions co)
-        throws KeeperException, IOException, InterruptedException
-    {
+    protected boolean processCmd(MyCommandOptions co) throws CliException, IOException, InterruptedException {
+        boolean watch = false;
         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());
-        }catch (KeeperException.ReconfigInProgress e) {
-            System.err.println("Another reconfiguration is in progress -- concurrent " +
-                   "reconfigs not supported (yet)");
-        }catch (KeeperException.NewConfigNoQuorum e) {
-            System.err.println("No quorum of new config is connected and " +
-                   "up-to-date with the leader of last commmitted config - try invoking reconfiguration after " +
-                   "new servers are connected and synced");
+            watch = processZKCmd(co);
+            exitCode = 0;
+        } catch (CliException ex) {
+            exitCode = ex.getExitCode();
+            System.err.println(ex.getMessage());
         }
-        return false;
+        return watch;
     }
 
-    protected boolean processZKCmd(MyCommandOptions co)
-        throws KeeperException, IOException, InterruptedException
-    {
+    protected boolean processZKCmd(MyCommandOptions co) throws CliException, IOException, InterruptedException {
         String[] args = co.getArgArray();
         String cmd = co.getCommand();
         if (args.length < 1) {
             usage();
-            return false;
+            throw new MalformedCommandException("No command entered");
         }
 
         if (!commandMap.containsKey(cmd)) {
             usage();
-            return false;
+            throw new CommandNotFoundException("Command not found " + cmd);
         }
         
         boolean watch = false;
         LOG.debug("Processing " + cmd);
-        
-        try {
+
+
         if (cmd.equals("quit")) {
             zk.close();
-            System.exit(0);
+            System.exit(exitCode);
         } else if (cmd.equals("redo") && args.length >= 2) {
             Integer i = Integer.decode(args[1]);
-            if (commandCount <= i){ // don't allow redoing this redo
-                System.out.println("Command index out of range");
-                return false;
+            if (commandCount <= i) { // don't allow redoing this redo
+                throw new MalformedCommandException("Command index out of range");
             }
             cl.parseCommand(history.get(i));
-            if (cl.getCommand().equals( "redo" )){
-                System.out.println("No redoing redos");
-                return false;
+            if (cl.getCommand().equals("redo")) {
+                throw new MalformedCommandException("No redoing redos");
             }
             history.put(commandCount, history.get(i));
-            processCmd( cl);
+            processCmd(cl);
         } else if (cmd.equals("history")) {
-            for (int i=commandCount - 10;i<=commandCount;++i) {
+            for (int i = commandCount - 10; i <= commandCount; ++i) {
                 if (i < 0) continue;
                 System.out.println(i + " - " + history.get(i));
             }
@@ -664,12 +637,12 @@
                 printWatches = args[1].equals("on");
             }
         } else if (cmd.equals("connect")) {
-            if (args.length >=2) {
+            if (args.length >= 2) {
                 connectToZK(args[1]);
             } else {
-                connectToZK(host);                
+                connectToZK(host);
             }
-        } 
+        }
         
         // Below commands all need a live connection
         if (zk == null || !zk.getState().isAlive()) {
@@ -685,12 +658,6 @@
         } else if (!commandMap.containsKey(cmd)) {
              usage();
         }
-
-        } catch (ParseException ex) {
-            System.err.println(ex.getMessage());
-            usage();
-            return false;
-        }
         return watch;
     }
 }
diff --git a/src/java/main/org/apache/zookeeper/cli/AddAuthCommand.java b/src/java/main/org/apache/zookeeper/cli/AddAuthCommand.java
index e488745..e2a333a 100644
--- a/src/java/main/org/apache/zookeeper/cli/AddAuthCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/AddAuthCommand.java
@@ -38,20 +38,25 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        CommandLine cl = parser.parse(options, cmdArgs);
+        CommandLine cl;
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
+
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, IOException, 
-                                 InterruptedException {
+    public boolean exec() throws CliException {
         byte[] b = null;
         if (args.length >= 3) {
             b = args[2].getBytes();
diff --git a/src/java/main/org/apache/zookeeper/cli/CliCommand.java b/src/java/main/org/apache/zookeeper/cli/CliCommand.java
index 527a19e..3d0a90b 100644
--- a/src/java/main/org/apache/zookeeper/cli/CliCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/CliCommand.java
@@ -17,11 +17,8 @@
  */
 package org.apache.zookeeper.cli;
 
-import java.io.IOException;
 import java.io.PrintStream;
 import java.util.Map;
-import org.apache.commons.cli.ParseException;
-import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.ZooKeeper;
 
 /**
@@ -108,17 +105,14 @@
      * parse the command arguments
      * @param cmdArgs
      * @return this CliCommand
-     * @throws ParseException 
+     * @throws CliParseException
      */
-    abstract public CliCommand parse(String cmdArgs[]) throws ParseException;
+    abstract public CliCommand parse(String cmdArgs[]) throws CliParseException;
     
     /**
      * 
      * @return
-     * @throws KeeperException
-     * @throws IOException
-     * @throws InterruptedException 
+     * @throws CliException
      */
-    abstract public boolean exec() throws KeeperException,
-            IOException, InterruptedException;
+    abstract public boolean exec() throws CliException;
 }
diff --git a/src/java/main/org/apache/zookeeper/cli/CliException.java b/src/java/main/org/apache/zookeeper/cli/CliException.java
new file mode 100644
index 0000000..401071f
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/cli/CliException.java
@@ -0,0 +1,54 @@
+/**
+ * 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.cli;
+
+public class CliException extends Exception {
+
+    protected int exitCode;
+
+    public CliException(String message) {
+        this(message, 1);
+    }
+
+    public CliException(String message, int exitCode) {
+        super(message);
+        this.exitCode = exitCode;
+    }
+
+    public CliException(Throwable cause) {
+        this(cause, 1);
+    }
+
+    public CliException(Throwable cause, int exitCode) {
+        super(cause);
+        this.exitCode = exitCode;
+    }
+
+    public CliException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public CliException(String message, Throwable cause, int exitCode) {
+        super(message, cause);
+        this.exitCode = exitCode;
+    }
+
+    public int getExitCode() {
+        return exitCode;
+    }
+}
diff --git a/src/java/main/org/apache/zookeeper/cli/CliParseException.java b/src/java/main/org/apache/zookeeper/cli/CliParseException.java
new file mode 100644
index 0000000..69303da
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/cli/CliParseException.java
@@ -0,0 +1,30 @@
+/**
+ * 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.cli;
+
+import org.apache.commons.cli.ParseException;
+
+public class CliParseException extends CliException {
+    public CliParseException(ParseException parseException) {
+        super(parseException);
+    }
+
+    public CliParseException(String message) {
+        super(message);
+    }
+}
diff --git a/src/java/main/org/apache/zookeeper/cli/CliWrapperException.java b/src/java/main/org/apache/zookeeper/cli/CliWrapperException.java
new file mode 100644
index 0000000..302cd98
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/cli/CliWrapperException.java
@@ -0,0 +1,59 @@
+/**
+ * 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.cli;
+
+import org.apache.zookeeper.KeeperException;
+
+public class CliWrapperException extends CliException {
+    public CliWrapperException(Throwable cause) {
+        super(getMessage(cause), cause);
+    }
+    
+    private static String getMessage(Throwable cause) {
+        if (cause instanceof  KeeperException) {
+            KeeperException keeperException = (KeeperException) cause;
+            if (keeperException instanceof KeeperException.NoNodeException) {
+                return "Node does not exist: " + keeperException.getPath();
+            } else if (keeperException instanceof KeeperException.NoChildrenForEphemeralsException) {
+                return "Ephemerals cannot have children: " + keeperException.getPath();
+            } else if (keeperException instanceof KeeperException.NodeExistsException) {
+                return "Node already exists: " + keeperException.getPath();
+            } else if (keeperException instanceof KeeperException.NotEmptyException) {
+                return "Node not empty: " + keeperException.getPath();
+            } else if (keeperException instanceof KeeperException.NotReadOnlyException) {
+                return "Not a read-only call: " + keeperException.getPath();
+            } else if (keeperException instanceof KeeperException.InvalidACLException) {
+                return "Acl is not valid : " + keeperException.getPath();
+            } else if (keeperException instanceof KeeperException.NoAuthException) {
+                return "Authentication is not valid : " + keeperException.getPath();
+            } else if (keeperException instanceof KeeperException.BadArgumentsException) {
+                return "Arguments are not valid : " + keeperException.getPath();
+            } else if (keeperException instanceof KeeperException.BadVersionException) {
+                return "version No is not valid : " + keeperException.getPath();
+            } else if (keeperException instanceof KeeperException.ReconfigInProgress) {
+                return "Another reconfiguration is in progress -- concurrent " +
+                        "reconfigs not supported (yet)";
+            } else if (keeperException instanceof KeeperException.NewConfigNoQuorum) {
+                return "No quorum of new config is connected and " +
+                        "up-to-date with the leader of last commmitted config - try invoking reconfiguration after " +
+                        "new servers are connected and synced";
+            }
+        }
+        return cause.getMessage();
+    }
+}
diff --git a/src/java/main/org/apache/zookeeper/cli/CloseCommand.java b/src/java/main/org/apache/zookeeper/cli/CloseCommand.java
index 1d96639..d1c83d9 100644
--- a/src/java/main/org/apache/zookeeper/cli/CloseCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/CloseCommand.java
@@ -17,10 +17,6 @@
  */
 package org.apache.zookeeper.cli;
 
-import java.io.IOException;
-import org.apache.commons.cli.ParseException;
-import org.apache.zookeeper.KeeperException;
-
 /**
  * close command for cli
  */
@@ -32,13 +28,17 @@
     
     
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, IOException, InterruptedException {
-        zk.close();
+    public boolean exec() throws CliException {
+        try {
+            zk.close();
+        } catch (Exception ex) {
+            throw new CliWrapperException(ex);
+        }
         
         return false;
     }
diff --git a/src/java/main/org/apache/zookeeper/cli/CommandNotFoundException.java b/src/java/main/org/apache/zookeeper/cli/CommandNotFoundException.java
new file mode 100644
index 0000000..9692da2
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/cli/CommandNotFoundException.java
@@ -0,0 +1,25 @@
+/**
+ * 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.cli;
+
+public class CommandNotFoundException extends CliException {
+
+    public CommandNotFoundException(String command) {
+        super("Command not found: " + command, 127);
+    }
+}
diff --git a/src/java/main/org/apache/zookeeper/cli/CreateCommand.java b/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
index cc96939..2c37784 100644
--- a/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
@@ -45,26 +45,29 @@
 
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if(args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
         return this;
     }
 
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliException {
         CreateMode flags = CreateMode.PERSISTENT;
         boolean hasE = cl.hasOption("e");
         boolean hasS = cl.hasOption("s");
         boolean hasC = cl.hasOption("c");
         if (hasC && (hasE || hasS)) {
-            err.println("-c cannot be combined with -s or -e. Containers cannot be ephemeral or sequential.");
-            return false;
+            throw new MalformedCommandException("-c cannot be combined with -s or -e. Containers cannot be ephemeral or sequential.");
         }
 
         if(hasE && hasS) {
@@ -90,10 +93,12 @@
             err.println("Created " + newPath);
         } catch(KeeperException.EphemeralOnLocalSessionException e) {
             err.println("Unable to create ephemeral node on a local session");
-            return false;
+            throw new CliWrapperException(e);
         } catch (KeeperException.InvalidACLException ex) {
             err.println(ex.getMessage());
-            return false;
+            throw new CliWrapperException(ex);
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
         }
         return true;
     }
diff --git a/src/java/main/org/apache/zookeeper/cli/DelQuotaCommand.java b/src/java/main/org/apache/zookeeper/cli/DelQuotaCommand.java
index 2043f47..31a0546 100644
--- a/src/java/main/org/apache/zookeeper/cli/DelQuotaCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/DelQuotaCommand.java
@@ -45,31 +45,38 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, IOException, 
-                                 InterruptedException {
+    public boolean exec() throws CliException {
         //if neither option -n or -b is specified, we delete
         // the quota node for thsi node.
         String path = args[1];
-        if (cl.hasOption("b")) {
-            delQuota(zk, path, true, false);
-        } else if (cl.hasOption("n")) {
-            delQuota(zk, path, false, true);
-        } else if (args.length == 2) {
-            // we dont have an option specified.
-            // just delete whole quota node
-            delQuota(zk, path, true, true);
+        try {
+            if (cl.hasOption("b")) {
+                delQuota(zk, path, true, false);
+            } else if (cl.hasOption("n")) {
+                delQuota(zk, path, false, true);
+            } else if (args.length == 2) {
+                // we dont have an option specified.
+                // just delete whole quota node
+                delQuota(zk, path, true, true);
+            }
+        } catch (KeeperException|InterruptedException|IOException ex) {
+            throw new CliWrapperException(ex);
         }
         return false;
     }
diff --git a/src/java/main/org/apache/zookeeper/cli/DeleteAllCommand.java b/src/java/main/org/apache/zookeeper/cli/DeleteAllCommand.java
index badf5b7..4d1b749 100644
--- a/src/java/main/org/apache/zookeeper/cli/DeleteAllCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/DeleteAllCommand.java
@@ -42,24 +42,32 @@
     }
     
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        CommandLine cl = parser.parse(options, cmdArgs);
+        CommandLine cl;
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException,
-            InterruptedException {
+    public boolean exec() throws CliException {
         printDeprecatedWarning();
         
         String path = args[1];
-        ZKUtil.deleteRecursive(zk, path);
+        try {
+            ZKUtil.deleteRecursive(zk, path);
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
+        }
         return false;
     }
     
diff --git a/src/java/main/org/apache/zookeeper/cli/DeleteCommand.java b/src/java/main/org/apache/zookeeper/cli/DeleteCommand.java
index 4714be5..1a0d02a 100644
--- a/src/java/main/org/apache/zookeeper/cli/DeleteCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/DeleteCommand.java
@@ -38,12 +38,16 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
         
         retainCompatibility(cmdArgs);
@@ -51,7 +55,7 @@
         return this;
     }
 
-    private void retainCompatibility(String[] cmdArgs) throws ParseException {
+    private void retainCompatibility(String[] cmdArgs) throws CliParseException {
         // delete path [version]
         if (args.length > 2) {
             // rewrite to option
@@ -63,13 +67,17 @@
             err.println("'delete path [version]' has been deprecated. "
                     + "Please use 'delete [-v version] path' instead.");
             Parser parser = new PosixParser();
-            cl = parser.parse(options, newCmd);
+            try {
+                cl = parser.parse(options, cmdArgs);
+            } catch (ParseException ex) {
+                throw new CliParseException(ex);
+            }
             args = cl.getArgs();
         }
     }
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliException {
         String path = args[1];
         int version;
         if (cl.hasOption("v")) {
@@ -80,8 +88,8 @@
         
         try {
         zk.delete(path, version);
-        } catch(KeeperException.BadVersionException ex) {
-            err.println(ex.getMessage());
+        } catch(KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
         }
         return false;
     }
diff --git a/src/java/main/org/apache/zookeeper/cli/GetAclCommand.java b/src/java/main/org/apache/zookeeper/cli/GetAclCommand.java
index e646a04..3f9e22b 100644
--- a/src/java/main/org/apache/zookeeper/cli/GetAclCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/GetAclCommand.java
@@ -45,22 +45,32 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliException {
         String path = args[1];
         Stat stat = new Stat();
-        List<ACL> acl = zk.getACL(path, stat);
+        List<ACL> acl;
+        try {
+           acl = zk.getACL(path, stat);
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
+        }
+
         for (ACL a : acl) {
             out.println(a.getId() + ": "
                         + getPermString(a.getPerms()));
diff --git a/src/java/main/org/apache/zookeeper/cli/GetCommand.java b/src/java/main/org/apache/zookeeper/cli/GetCommand.java
index 0ec8270..bbc63e6 100644
--- a/src/java/main/org/apache/zookeeper/cli/GetCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/GetCommand.java
@@ -40,13 +40,17 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
 
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         retainCompatibility(cmdArgs);
@@ -54,7 +58,7 @@
         return this;
     }
 
-    private void retainCompatibility(String[] cmdArgs) throws ParseException {
+    private void retainCompatibility(String[] cmdArgs) throws CliParseException {
         // get path [watch]
         if (args.length > 2) {
             // rewrite to option
@@ -62,17 +66,26 @@
             err.println("'get path [watch]' has been deprecated. "
                     + "Please use 'get [-s] [-w] path' instead.");
             Parser parser = new PosixParser();
-            cl = parser.parse(options, cmdArgs);
+            try {
+                cl = parser.parse(options, cmdArgs);
+            } catch (ParseException ex) {
+                throw new CliParseException(ex);
+            }
             args = cl.getArgs();
         }
     }
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliException {
         boolean watch = cl.hasOption("w");
         String path = args[1];
         Stat stat = new Stat();
-        byte data[] = zk.getData(path, watch, stat);
+        byte data[];
+        try {
+            data = zk.getData(path, watch, stat);
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliException(ex);
+        }
         data = (data == null) ? "null".getBytes() : data;
         out.println(new String(data));
         if (cl.hasOption("s")) {
diff --git a/src/java/main/org/apache/zookeeper/cli/GetConfigCommand.java b/src/java/main/org/apache/zookeeper/cli/GetConfigCommand.java
index 8c6a4f5..dda6281 100644
--- a/src/java/main/org/apache/zookeeper/cli/GetConfigCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/GetConfigCommand.java
@@ -42,23 +42,32 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
 
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 1) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliException {
         boolean watch = cl.hasOption("w");        
         Stat stat = new Stat();
-        byte data[] = zk.getConfig(watch, stat);
+        byte data[];
+        try {
+            data = zk.getConfig(watch, stat);
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
+        }
         data = (data == null) ? "null".getBytes() : data;
         if (cl.hasOption("c")) {
             out.println(ConfigUtils.getClientConfigStr(new String(data)));
diff --git a/src/java/main/org/apache/zookeeper/cli/ListQuotaCommand.java b/src/java/main/org/apache/zookeeper/cli/ListQuotaCommand.java
index 907c466..e3b9083 100644
--- a/src/java/main/org/apache/zookeeper/cli/ListQuotaCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/ListQuotaCommand.java
@@ -36,20 +36,24 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        CommandLine cl = parser.parse(options, cmdArgs);
+        CommandLine cl;
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if(args.length < 2) {
-            throw new ParseException(getUsageStr());
-        }    
+            throw new CliParseException(getUsageStr());
+        }
         
         return this;
     }
     
     @Override
-    public boolean exec() throws KeeperException,
-            InterruptedException {
+    public boolean exec() throws CliException {
         String path = args[1];
         String absolutePath = Quotas.quotaZookeeper + path + "/"
                 + Quotas.limitNode;
@@ -67,6 +71,8 @@
                     + new StatsTrack(new String(data)).toString());
         } catch (KeeperException.NoNodeException ne) {
             err.println("quota for " + path + " does not exist.");
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
         }
         
         return false;
diff --git a/src/java/main/org/apache/zookeeper/cli/Ls2Command.java b/src/java/main/org/apache/zookeeper/cli/Ls2Command.java
index 2b02583..43f5d0e 100644
--- a/src/java/main/org/apache/zookeeper/cli/Ls2Command.java
+++ b/src/java/main/org/apache/zookeeper/cli/Ls2Command.java
@@ -34,25 +34,35 @@
     }
     
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        CommandLine cl = parser.parse(options, cmdArgs);
+        CommandLine cl;
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
-        }    
+            throw new CliParseException(getUsageStr());
+        }
         
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliException {
         err.println("'ls2' has been deprecated. "
                   + "Please use 'ls [-s] path' instead.");
         String path = args[1];
         boolean watch = args.length > 2;
         Stat stat = new Stat();
-        List<String> children = zk.getChildren(path, watch, stat);
+        List<String> children;
+        try {
+            children = zk.getChildren(path, watch, stat);
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
+        }
         out.println(children);
         new StatPrinter(out).print(stat);
         return watch;
diff --git a/src/java/main/org/apache/zookeeper/cli/LsCommand.java b/src/java/main/org/apache/zookeeper/cli/LsCommand.java
index 43099b8..d3ccb0e 100644
--- a/src/java/main/org/apache/zookeeper/cli/LsCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/LsCommand.java
@@ -47,24 +47,25 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
+
         args = cl.getArgs();
         if (cl.hasOption("?")) {
             printHelp();
         }
 
-        if (args.length < 2) {
-            throw new ParseException(getUsageStr());
-        }
-
         retainCompatibility(cmdArgs);
         
         return this;
     }
 
-    private void retainCompatibility(String[] cmdArgs) throws ParseException {
+    private void retainCompatibility(String[] cmdArgs) throws CliParseException {
         // get path [watch]
         if (args.length > 2) {
             // rewrite to option
@@ -72,13 +73,21 @@
             err.println("'ls path [watch]' has been deprecated. "
                     + "Please use 'ls [-w] path' instead.");
             Parser parser = new PosixParser();
-            cl = parser.parse(options, cmdArgs);
+            try {
+                cl = parser.parse(options, cmdArgs);
+            } catch (ParseException ex) {
+                throw new CliParseException(ex);
+            }
             args = cl.getArgs();
         }
     }
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliException {
+        if (args.length < 2) {
+            throw new MalformedCommandException(getUsageStr());
+        }
+
         String path = args[1];
         boolean watch = cl.hasOption("w");
         boolean withStat = cl.hasOption("s");
@@ -96,9 +105,8 @@
             if (withStat) {
                 new StatPrinter(out).print(stat);
             }
-        } catch (KeeperException.NoAuthException ex) {
-            err.println(ex.getMessage());
-            watch = false;
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
         }
         return watch;
     }
diff --git a/src/java/main/org/apache/zookeeper/cli/MalformedCommandException.java b/src/java/main/org/apache/zookeeper/cli/MalformedCommandException.java
new file mode 100644
index 0000000..cc1ed41
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/cli/MalformedCommandException.java
@@ -0,0 +1,24 @@
+/**
+ * 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.cli;
+
+public class MalformedCommandException extends CliException {
+    public MalformedCommandException(String message) {
+        super(message);
+    }
+}
diff --git a/src/java/main/org/apache/zookeeper/cli/ReconfigCommand.java b/src/java/main/org/apache/zookeeper/cli/ReconfigCommand.java
index 282af55..deb7914 100644
--- a/src/java/main/org/apache/zookeeper/cli/ReconfigCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/ReconfigCommand.java
@@ -79,20 +79,24 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         joining = null;
         leaving = null;
         members = null;
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         if (!(cl.hasOption("file") || cl.hasOption("members")) && !cl.hasOption("add") && !cl.hasOption("remove")) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
         if (cl.hasOption("v")) {
             try{ 
                 version = Long.parseLong(cl.getOptionValue("v"), 16);
             } catch (NumberFormatException e){
-                throw new ParseException("-v must be followed by a long (configuration version)");
+                throw new CliParseException("-v must be followed by a long (configuration version)");
             }
         } else {
             version = -1;
@@ -100,12 +104,12 @@
 
         // Simple error checking for conflicting modes
         if ((cl.hasOption("file") || cl.hasOption("members")) && (cl.hasOption("add") || cl.hasOption("remove"))) {
-            throw new ParseException("Can't use -file or -members together with -add or -remove (mixing incremental" +
+            throw new CliParseException("Can't use -file or -members together with -add or -remove (mixing incremental" +
             		" and non-incremental modes is not allowed)");
         }
         if (cl.hasOption("file") && cl.hasOption("members"))
         {
-            throw new ParseException("Can't use -file and -members together (conflicting non-incremental modes)");
+            throw new CliParseException("Can't use -file and -members together (conflicting non-incremental modes)");
         }
 
         // Set the joining/leaving/members values based on the mode we're in
@@ -132,14 +136,14 @@
                 //client doesn't know what leader election alg is used
                 members = QuorumPeerConfig.parseDynamicConfig(dynamicCfg, 0, true, false).toString();
             } catch (Exception e) {
-                throw new ParseException("Error processing " + cl.getOptionValue("file") + e.getMessage());                
+                throw new CliParseException("Error processing " + cl.getOptionValue("file") + e.getMessage());
             } 
         }
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliException {
         try {
             Stat stat = new Stat();
             byte[] curConfig = zk.reconfig(joining,
@@ -149,8 +153,8 @@
             if (cl.hasOption("s")) {
                 new StatPrinter(out).print(stat);
             }
-        } catch (KeeperException ex) {
-            err.println(ex.getMessage());
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
         }
         return false;
     }
diff --git a/src/java/main/org/apache/zookeeper/cli/RemoveWatchesCommand.java b/src/java/main/org/apache/zookeeper/cli/RemoveWatchesCommand.java
index f63cf0f..604cd4b 100644
--- a/src/java/main/org/apache/zookeeper/cli/RemoveWatchesCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/RemoveWatchesCommand.java
@@ -47,18 +47,22 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliWrapperException {
         String path = args[1];
         WatcherType wtype = WatcherType.Any;
         // if no matching option -c or -d or -a is specified, we remove
@@ -75,9 +79,8 @@
 
         try {
             zk.removeAllWatches(path, wtype, local);
-        } catch (KeeperException.NoWatcherException ex) {
-            err.println(ex.getMessage());
-            return false;
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
         }
         return true;
     }
diff --git a/src/java/main/org/apache/zookeeper/cli/SetAclCommand.java b/src/java/main/org/apache/zookeeper/cli/SetAclCommand.java
index f27446c..7fb4f0f 100644
--- a/src/java/main/org/apache/zookeeper/cli/SetAclCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/SetAclCommand.java
@@ -42,20 +42,23 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 3) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException,
-            InterruptedException {
+    public boolean exec() throws CliException {
         String path = args[1];
         String aclStr = args[2];
         List<ACL> acl = AclParser.parse(aclStr);
@@ -70,10 +73,8 @@
             if (cl.hasOption("s")) {
                 new StatPrinter(out).print(stat);
             }
-        } catch (KeeperException.InvalidACLException ex) {
-            err.println(ex.getMessage());
-        } catch (KeeperException.NoAuthException ex) {
-            err.println(ex.getMessage());
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
         }
 
         return false;
diff --git a/src/java/main/org/apache/zookeeper/cli/SetCommand.java b/src/java/main/org/apache/zookeeper/cli/SetCommand.java
index 1c39377..10bb129 100644
--- a/src/java/main/org/apache/zookeeper/cli/SetCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/SetCommand.java
@@ -40,19 +40,23 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 3) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, InterruptedException {
+    public boolean exec() throws CliException {
         String path = args[1];
         byte[] data = args[2].getBytes();
         int version;
@@ -67,8 +71,8 @@
             if (cl.hasOption("s")) {
                 new StatPrinter(out).print(stat);
             }
-        } catch (KeeperException.BadVersionException ex) {
-            err.println(ex.getMessage());
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
         }
         return false;
     }
diff --git a/src/java/main/org/apache/zookeeper/cli/SetQuotaCommand.java b/src/java/main/org/apache/zookeeper/cli/SetQuotaCommand.java
index 873c6d2..cd670a2 100644
--- a/src/java/main/org/apache/zookeeper/cli/SetQuotaCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/SetQuotaCommand.java
@@ -46,33 +46,44 @@
    }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, IOException,
-            InterruptedException {
+    public boolean exec() throws CliException {
         // get the args
         String path = args[1];
 
         if (cl.hasOption("b")) {
             // we are setting the bytes quota
             long bytes = Long.parseLong(cl.getOptionValue("b"));
-            createQuota(zk, path, bytes, -1);
+            try {
+                createQuota(zk, path, bytes, -1);
+            } catch (KeeperException|IOException|InterruptedException ex) {
+                throw new CliWrapperException(ex);
+            }
         } else if (cl.hasOption("n")) {
             // we are setting the num quota
             int numNodes = Integer.parseInt(cl.getOptionValue("n"));
-            createQuota(zk, path, -1L, numNodes);
+            try {
+                createQuota(zk, path, -1L, numNodes);
+            } catch (KeeperException|IOException|InterruptedException ex) {
+                throw new CliWrapperException(ex);
+            }
         } else {
-            err.println(getUsageStr());
+            throw new MalformedCommandException(getUsageStr());
         }
 
         return false;
diff --git a/src/java/main/org/apache/zookeeper/cli/StatCommand.java b/src/java/main/org/apache/zookeeper/cli/StatCommand.java
index e5cfe54..b37071b 100644
--- a/src/java/main/org/apache/zookeeper/cli/StatCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/StatCommand.java
@@ -40,12 +40,16 @@
 
     
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        cl = parser.parse(options, cmdArgs);
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if(args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }    
         
         retainCompatibility(cmdArgs);
@@ -53,7 +57,7 @@
         return this;
     }
 
-    private void retainCompatibility(String[] cmdArgs) throws ParseException {
+    private void retainCompatibility(String[] cmdArgs) throws CliParseException {
         // stat path [watch]
         if (args.length > 2) {
             // rewrite to option
@@ -61,19 +65,27 @@
             err.println("'stat path [watch]' has been deprecated. "
                     + "Please use 'stat [-w] path' instead.");
             Parser parser = new PosixParser();
-            cl = parser.parse(options, cmdArgs);
+            try {
+                cl = parser.parse(options, cmdArgs);
+            } catch (ParseException ex) {
+                throw new CliParseException(ex);
+            }
             args = cl.getArgs();
         }
     }
     
     @Override
-    public boolean exec() throws KeeperException,
-            InterruptedException {
+    public boolean exec() throws CliException {
         String path = args[1];
         boolean watch = cl.hasOption("w");
-        Stat stat = zk.exists(path, watch);
+        Stat stat;
+        try {
+            stat = zk.exists(path, watch);
+        } catch (KeeperException|InterruptedException ex) {
+            throw new CliWrapperException(ex);
+        }
         if (stat == null) {
-            throw new KeeperException.NoNodeException(path);
+            throw new CliWrapperException(new KeeperException.NoNodeException(path));
         }
         new StatPrinter(out).print(stat);
         return watch;
diff --git a/src/java/main/org/apache/zookeeper/cli/SyncCommand.java b/src/java/main/org/apache/zookeeper/cli/SyncCommand.java
index c12d35d..f79db88 100644
--- a/src/java/main/org/apache/zookeeper/cli/SyncCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/SyncCommand.java
@@ -38,19 +38,24 @@
     }
 
     @Override
-    public CliCommand parse(String[] cmdArgs) throws ParseException {
+    public CliCommand parse(String[] cmdArgs) throws CliParseException {
         Parser parser = new PosixParser();
-        CommandLine cl = parser.parse(options, cmdArgs);
+        CommandLine cl;
+        try {
+            cl = parser.parse(options, cmdArgs);
+        } catch (ParseException ex) {
+            throw new CliParseException(ex);
+        }
         args = cl.getArgs();
         if (args.length < 2) {
-            throw new ParseException(getUsageStr());
+            throw new CliParseException(getUsageStr());
         }
 
         return this;
     }
 
     @Override
-    public boolean exec() throws KeeperException, IOException, InterruptedException {
+    public boolean exec() throws CliException {
         String path = args[1];
         zk.sync(path, new AsyncCallback.VoidCallback() {
 
diff --git a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java
index 574eab0..a140dab 100644
--- a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java
+++ b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java
@@ -27,6 +27,9 @@
 
 import org.apache.zookeeper.AsyncCallback.VoidCallback;
 import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.cli.CliException;
+import org.apache.zookeeper.cli.CliWrapperException;
+import org.apache.zookeeper.cli.MalformedCommandException;
 import org.apache.zookeeper.cli.LsCommand;
 import org.apache.zookeeper.data.Stat;
 import org.apache.zookeeper.test.ClientBase;
@@ -41,8 +44,7 @@
 public class ZooKeeperTest extends ClientBase {
 
     @Test
-    public void testDeleteRecursive() throws IOException, InterruptedException,
-            KeeperException {
+    public void testDeleteRecursive() throws IOException, InterruptedException, CliException, KeeperException {
         final ZooKeeper zk = createClient();
         // making sure setdata works on /
         zk.setData("/", "some".getBytes(), -1);
@@ -140,7 +142,7 @@
     
     @Test
     public void testStatWhenPathDoesNotExist() throws IOException,
-    		InterruptedException {
+    		InterruptedException, MalformedCommandException {
     	final ZooKeeper zk = createClient();
     	ZooKeeperMain main = new ZooKeeperMain(zk);
     	String cmdstring = "stat /invalidPath";
@@ -148,8 +150,8 @@
     	try {
     		main.processZKCmd(main.cl);
     		Assert.fail("As Node does not exist, command should fail by throwing No Node Exception.");
-    	} catch (KeeperException e) {
-    		Assert.assertEquals("KeeperErrorCode = NoNode for /invalidPath", e.getMessage());
+    	} catch (CliException e) {
+    		Assert.assertEquals("Node does not exist: /invalidPath", e.getMessage());
     	}
     }
 
@@ -220,38 +222,47 @@
     }
 
     @Test
-    public void testInvalidCommand() throws Exception {
-        final ZooKeeper zk = createClient();
-        ZooKeeperMain zkMain = new ZooKeeperMain(zk);
-        String cmdstring = "cret -s /node1";
-        zkMain.cl.parseCommand(cmdstring);
-        Assert.assertFalse("Doesn't validate the command", zkMain
-                .processZKCmd(zkMain.cl));
+    public void testNonexistantCommand() throws Exception {
+      testInvalidCommand("cret -s /node1", 127);
     }
 
     @Test
     public void testCreateCommandWithoutPath() throws Exception {
+        testInvalidCommand("create", 1);
+    }
+
+    @Test
+    public void testCreateEphemeralCommandWithoutPath() throws Exception {
+        testInvalidCommand("create -e ", 1);
+    }
+
+    @Test
+    public void testCreateSequentialCommandWithoutPath() throws Exception {
+        testInvalidCommand("create -s ", 1);
+    }
+
+    @Test
+    public void testCreateEphemeralSequentialCommandWithoutPath() throws Exception {
+        testInvalidCommand("create -s -e ", 1);
+    }
+
+    private void testInvalidCommand(String cmdString, int exitCode) throws Exception {
         final ZooKeeper zk = createClient();
         ZooKeeperMain zkMain = new ZooKeeperMain(zk);
-        String cmdstring = "create ";
-        zkMain.cl.parseCommand(cmdstring);
-        Assert.assertFalse("Path is not validated.", zkMain
-                .processZKCmd(zkMain.cl));
-        // create ephemeral
-        cmdstring = "create -e ";
-        zkMain.cl.parseCommand(cmdstring);
-        Assert.assertFalse("Path is not validated.", zkMain
-                .processZKCmd(zkMain.cl));
-        // create sequential
-        cmdstring = "create -s ";
-        zkMain.cl.parseCommand(cmdstring);
-        Assert.assertFalse("Path is not validated.", zkMain
-                .processZKCmd(zkMain.cl));
-        // create ephemeral sequential
-        cmdstring = "create -s -e ";
-        zkMain.cl.parseCommand(cmdstring);
-        Assert.assertFalse("Path is not validated.", zkMain
-                .processZKCmd(zkMain.cl));
+        zkMain.cl.parseCommand(cmdString);
+
+        // Verify that the exit code is set properly
+        zkMain.processCmd(zkMain.cl);
+        Assert.assertEquals(exitCode, zkMain.exitCode);
+
+        // Verify that the correct exception is thrown
+        try {
+          zkMain.processZKCmd(zkMain.cl);
+          Assert.fail();
+        } catch (CliException e) {
+          return;
+        }
+        Assert.fail("invalid command should throw CliException");
     }
 
     @Test
@@ -349,8 +360,8 @@
         try {
             Assert.assertFalse(zkMain.processZKCmd(zkMain.cl));
             Assert.fail("Path doesn't exists so, command should fail.");
-        } catch (KeeperException e) {
-            Assert.assertEquals(KeeperException.Code.NONODE, e.code());
+        } catch (CliWrapperException e) {
+            Assert.assertEquals(KeeperException.Code.NONODE, ((KeeperException)e.getCause()).code());
         }
     }
 
@@ -380,11 +391,9 @@
          final ZooKeeper zk = createClient();
             ZooKeeperMain zkMain = new ZooKeeperMain(zk);
             String cmdstring = "create -s -e /node data ip:scheme:gggsd"; //invalid acl's
-            try{
-                 zkMain.executeLine(cmdstring);
-            }catch(KeeperException.InvalidACLException e){
-                fail("For Invalid ACls should not throw exception");
-            }
+
+            // For Invalid ACls should not throw exception
+            zkMain.executeLine(cmdstring);
     }
 
     @Test
@@ -394,12 +403,9 @@
             String cmdstring = "create -s -e /node1 data "; 
             String cmdstring1 = "delete /node1 2";//invalid dataversion no
                  zkMain.executeLine(cmdstring);
-           try{
-               zkMain.executeLine(cmdstring1);
-                     
-            }catch(KeeperException.BadVersionException e){
-                fail("For Invalid dataversion number should not throw exception");
-            }
+
+            // For Invalid dataversion number should not throw exception
+            zkMain.executeLine(cmdstring1);
     }
 
     @Test