zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ph...@apache.org
Subject svn commit: r1759905 - in /zookeeper/branches/branch-3.5: CHANGES.txt src/java/main/org/apache/zookeeper/ZKUtil.java src/java/main/org/apache/zookeeper/cli/LsCommand.java src/java/test/org/apache/zookeeper/ZooKeeperTest.java
Date Thu, 08 Sep 2016 20:52:35 GMT
Author: phunt
Date: Thu Sep  8 20:52:35 2016
New Revision: 1759905

URL: http://svn.apache.org/viewvc?rev=1759905&view=rev
Log:
ZOOKEEPER-1962: Add a CLI command to recursively list a znode and children (Gautam Gopalakrishnan,
Hongchao Deng, Enis Soztutar via phunt)

Modified:
    zookeeper/branches/branch-3.5/CHANGES.txt
    zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/ZKUtil.java
    zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/cli/LsCommand.java
    zookeeper/branches/branch-3.5/src/java/test/org/apache/zookeeper/ZooKeeperTest.java

Modified: zookeeper/branches/branch-3.5/CHANGES.txt
URL: http://svn.apache.org/viewvc/zookeeper/branches/branch-3.5/CHANGES.txt?rev=1759905&r1=1759904&r2=1759905&view=diff
==============================================================================
--- zookeeper/branches/branch-3.5/CHANGES.txt (original)
+++ zookeeper/branches/branch-3.5/CHANGES.txt Thu Sep  8 20:52:35 2016
@@ -1,5 +1,10 @@
 Unreleased
 
+NEW FEATURES:
+
+  ZOOKEEPER-1962: Add a CLI command to recursively list a znode and
+  children (Gautam Gopalakrishnan, Hongchao Deng, Enis Soztutar via phunt)
+
 BUGFIXES:
 
   ZOOKEEPER-1898: ZooKeeper Java cli shell always returns "0" as exit code

Modified: zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/ZKUtil.java
URL: http://svn.apache.org/viewvc/zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/ZKUtil.java?rev=1759905&r1=1759904&r2=1759905&view=diff
==============================================================================
--- zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/ZKUtil.java (original)
+++ zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/ZKUtil.java Thu Sep 
8 20:52:35 2016
@@ -18,34 +18,37 @@
 package org.apache.zookeeper;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Deque;
 import java.util.LinkedList;
 import java.util.List;
 
+import org.apache.zookeeper.AsyncCallback.StringCallback;
 import org.apache.zookeeper.AsyncCallback.VoidCallback;
+import org.apache.zookeeper.KeeperException.Code;
 import org.apache.zookeeper.common.PathUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-         
+
 public class ZKUtil {
     private static final Logger LOG = LoggerFactory.getLogger(ZKUtil.class);
     /**
-     * Recursively delete the node with the given path. 
+     * Recursively delete the node with the given path.
      * <p>
      * Important: All versions, of all nodes, under the given node are deleted.
      * <p>
-     * If there is an error with deleting one of the sub-nodes in the tree, 
+     * If there is an error with deleting one of the sub-nodes in the tree,
      * this operation would abort and would be the responsibility of the app to handle the
same.
-     * 
+     *
      * See {@link #delete(String, int)} for more details.
-     * 
+     *
      * @throws IllegalArgumentException if an invalid path is specified
      */
     public static void deleteRecursive(ZooKeeper zk, final String pathRoot)
         throws InterruptedException, KeeperException
     {
         PathUtils.validatePath(pathRoot);
-      
+
         List<String> tree = listSubTreeBFS(zk, pathRoot);
         LOG.debug("Deleting " + tree);
         LOG.debug("Deleting " + tree.size() + " subnodes ");
@@ -54,15 +57,15 @@ public class ZKUtil {
             zk.delete(tree.get(i), -1); //Delete all versions of the node with -1.
         }
     }
-    
+
 
     /**
      * Recursively delete the node with the given path. (async version).
-     * 
+     *
      * <p>
      * Important: All versions, of all nodes, under the given node are deleted.
      * <p>
-     * If there is an error with deleting one of the sub-nodes in the tree, 
+     * If there is an error with deleting one of the sub-nodes in the tree,
      * this operation would abort and would be the responsibility of the app to handle the
same.
      * <p>
      * @param zk the zookeeper handle
@@ -76,7 +79,7 @@ public class ZKUtil {
         throws InterruptedException, KeeperException
     {
         PathUtils.validatePath(pathRoot);
-      
+
         List<String> tree = listSubTreeBFS(zk, pathRoot);
         LOG.debug("Deleting " + tree);
         LOG.debug("Deleting " + tree.size() + " subnodes ");
@@ -85,22 +88,22 @@ public class ZKUtil {
             zk.delete(tree.get(i), -1, cb, ctx); //Delete all versions of the node with -1.
         }
     }
-    
+
     /**
-     * BFS Traversal of the system under pathRoot, with the entries in the list, in the 
+     * BFS Traversal of the system under pathRoot, with the entries in the list, in the
      * same order as that of the traversal.
      * <p>
      * <b>Important:</b> This is <i>not an atomic snapshot</i> of
the tree ever, but the
      *  state as it exists across multiple RPCs from zkClient to the ensemble.
-     * For practical purposes, it is suggested to bring the clients to the ensemble 
-     * down (i.e. prevent writes to pathRoot) to 'simulate' a snapshot behavior.   
-     * 
+     * For practical purposes, it is suggested to bring the clients to the ensemble
+     * down (i.e. prevent writes to pathRoot) to 'simulate' a snapshot behavior.
+     *
      * @param zk the zookeeper handle
      * @param pathRoot The znode path, for which the entire subtree needs to be listed.
-     * @throws InterruptedException 
-     * @throws KeeperException 
+     * @throws InterruptedException
+     * @throws KeeperException
      */
-    public static List<String> listSubTreeBFS(ZooKeeper zk, final String pathRoot)
throws 
+    public static List<String> listSubTreeBFS(ZooKeeper zk, final String pathRoot)
throws
         KeeperException, InterruptedException {
         Deque<String> queue = new LinkedList<String>();
         List<String> tree = new ArrayList<String>();
@@ -120,4 +123,49 @@ public class ZKUtil {
         }
         return tree;
     }
+
+    /**
+     * Visits the subtree with root as given path and calls the passed callback with each
znode
+     * found during the search. It performs a depth-first, pre-order traversal of the tree.
+     * <p>
+     * <b>Important:</b> This is <i>not an atomic snapshot</i> of
the tree ever, but the
+     * state as it exists across multiple RPCs from zkClient to the ensemble.
+     * For practical purposes, it is suggested to bring the clients to the ensemble
+     * down (i.e. prevent writes to pathRoot) to 'simulate' a snapshot behavior.
+     */
+    public static void visitSubTreeDFS(ZooKeeper zk, final String path, boolean watch,
+        StringCallback cb) throws KeeperException, InterruptedException {
+        PathUtils.validatePath(path);
+
+        zk.getData(path, watch, null);
+        cb.processResult(Code.OK.intValue(), path, null, path);
+        visitSubTreeDFSHelper(zk, path, watch, cb);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void visitSubTreeDFSHelper(ZooKeeper zk, final String path,
+        boolean watch, StringCallback cb)
+            throws KeeperException, InterruptedException {
+        // we've already validated, therefore if the path is of length 1 it's the root
+        final boolean isRoot = path.length() == 1;
+        try {
+            List<String> children = zk.getChildren(path, watch, null);
+            Collections.sort(children);
+
+            for (String child : children) {
+                String childPath = (isRoot ? path : path + "/") + child;
+                cb.processResult(Code.OK.intValue(), childPath, null, child);
+            }
+
+            for (String child : children) {
+                String childPath = (isRoot ? path : path + "/") + child;
+                visitSubTreeDFSHelper(zk, childPath, watch, cb);
+            }
+        }
+        catch (KeeperException.NoNodeException e) {
+            // Handle race condition where a node is listed
+            // but gets deleted before it can be queried
+            return; // ignore
+        }
+    }
 }
\ No newline at end of file

Modified: zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/cli/LsCommand.java
URL: http://svn.apache.org/viewvc/zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/cli/LsCommand.java?rev=1759905&r1=1759904&r2=1759905&view=diff
==============================================================================
--- zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/cli/LsCommand.java (original)
+++ zookeeper/branches/branch-3.5/src/java/main/org/apache/zookeeper/cli/LsCommand.java Thu
Sep  8 20:52:35 2016
@@ -19,7 +19,9 @@ package org.apache.zookeeper.cli;
 import java.util.Collections;
 import java.util.List;
 import org.apache.commons.cli.*;
+import org.apache.zookeeper.AsyncCallback.StringCallback;
 import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZKUtil;
 import org.apache.zookeeper.data.Stat;
 
 /**
@@ -35,10 +37,11 @@ public class LsCommand extends CliComman
         options.addOption("?", false, "help");
         options.addOption("s", false, "stat");
         options.addOption("w", false, "watch");
+        options.addOption("R", false, "recurse");
     }
 
     public LsCommand() {
-        super("ls", "[-s] [-w] path");
+        super("ls", "[-s] [-w] [-R] path");
     }
 
     private void printHelp() {
@@ -61,7 +64,7 @@ public class LsCommand extends CliComman
         }
 
         retainCompatibility(cmdArgs);
-        
+
         return this;
     }
 
@@ -91,19 +94,19 @@ public class LsCommand extends CliComman
         String path = args[1];
         boolean watch = cl.hasOption("w");
         boolean withStat = cl.hasOption("s");
+        boolean recursive = cl.hasOption("R");
         try {
-            Stat stat = new Stat();
-            List<String> children;
-            if (withStat) {
-                // with stat
-                children = zk.getChildren(path, watch, stat);
+            if (recursive) {
+                ZKUtil.visitSubTreeDFS(zk, path, watch, new StringCallback() {
+                    @Override
+                    public void processResult(int rc, String path, Object ctx, String name)
{
+                        out.println(path);
+                    }
+                });
             } else {
-                // without stat
-                children = zk.getChildren(path, watch);
-            }
-            out.println(printChildren(children));
-            if (withStat) {
-                new StatPrinter(out).print(stat);
+                Stat stat = withStat ? new Stat() : null;
+                List<String> children = zk.getChildren(path, watch, stat);
+                printChildren(children, stat);
             }
         } catch (KeeperException|InterruptedException ex) {
             throw new CliWrapperException(ex);
@@ -111,20 +114,22 @@ public class LsCommand extends CliComman
         return watch;
     }
 
-    private String printChildren(List<String> children) {
+    private void printChildren(List<String> children, Stat stat) {
         Collections.sort(children);
-        StringBuilder sb = new StringBuilder();
-        sb.append("[");
+        out.append("[");
         boolean first = true;
         for (String child : children) {
             if (!first) {
-                sb.append(", ");
+                out.append(", ");
             } else {
                 first = false;
             }
-            sb.append(child);
+            out.append(child);
+        }
+        out.append("]");
+        if (stat != null) {
+            new StatPrinter(out).print(stat);
         }
-        sb.append("]");
-        return sb.toString();
+        out.append("\n");
     }
 }

Modified: zookeeper/branches/branch-3.5/src/java/test/org/apache/zookeeper/ZooKeeperTest.java
URL: http://svn.apache.org/viewvc/zookeeper/branches/branch-3.5/src/java/test/org/apache/zookeeper/ZooKeeperTest.java?rev=1759905&r1=1759904&r2=1759905&view=diff
==============================================================================
--- zookeeper/branches/branch-3.5/src/java/test/org/apache/zookeeper/ZooKeeperTest.java (original)
+++ zookeeper/branches/branch-3.5/src/java/test/org/apache/zookeeper/ZooKeeperTest.java Thu
Sep  8 20:52:35 2016
@@ -22,23 +22,22 @@ import static org.junit.Assert.*;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
 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.cli.*;
+import org.apache.zookeeper.common.StringUtils;
 import org.apache.zookeeper.data.Stat;
 import org.apache.zookeeper.test.ClientBase;
 import org.junit.Assert;
 import org.junit.Test;
 
 /**
- * 
- * Testing Zookeeper public methods
+ *
+ * Testing ZooKeeper public methods
  *
  */
 public class ZooKeeperTest extends ClientBase {
@@ -139,7 +138,7 @@ public class ZooKeeperTest extends Clien
         }
         Assert.assertEquals(4, ((AtomicInteger) ctx).get());
     }
-    
+
     @Test
     public void testStatWhenPathDoesNotExist() throws IOException,
     		InterruptedException, MalformedCommandException {
@@ -405,7 +404,7 @@ public class ZooKeeperTest extends Clien
     public void testDeleteWithInvalidVersionNo() throws Exception {
          final ZooKeeper zk = createClient();
             ZooKeeperMain zkMain = new ZooKeeperMain(zk);
-            String cmdstring = "create -s -e /node1 data "; 
+            String cmdstring = "create -s -e /node1 data ";
             String cmdstring1 = "delete /node1 2";//invalid dataversion no
                  zkMain.executeLine(cmdstring);
 
@@ -434,6 +433,19 @@ public class ZooKeeperTest extends Clien
         }
     }
 
+    private static void runCommandExpect(CliCommand command, List<String> expectedResults)
+            throws Exception {
+        // call command and put result in byteStream
+        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(byteStream);
+        command.setOut(out);
+        command.exec();
+
+        String result = byteStream.toString();
+        assertTrue(result, result.contains(
+                StringUtils.joinStrings(expectedResults, "\n")));
+    }
+
     @Test
     public void testSortedLs() throws Exception {
         final ZooKeeper zk = createClient();
@@ -445,17 +457,89 @@ public class ZooKeeperTest extends Clien
         zkMain.executeLine("create /test1");
         zkMain.executeLine("create /zk1");
 
-        // call ls and put result in byteStream
-        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
-        PrintStream out = new PrintStream(byteStream);
-        String lsCmd = "ls /";
-        LsCommand entity = new LsCommand();
-        entity.setZk(zk);
-        entity.setOut(out);
-        entity.parse(lsCmd.split(" ")).exec();
+        LsCommand cmd = new LsCommand();
+        cmd.setZk(zk);
+        cmd.parse("ls /".split(" "));
+        List<String> expected = new ArrayList<String>();
+        expected.add("[aa1, aa2, aa3, test1, zk1, zookeeper]\n");
+        runCommandExpect(cmd, expected);
+    }
 
-        String result = byteStream.toString();
-        assertTrue(result, result.contains("[aa1, aa2, aa3, test1, zk1, zookeeper]"));
+    @Test
+    public void testLsrCommand() throws Exception {
+        final ZooKeeper zk = createClient();
+        ZooKeeperMain zkMain = new ZooKeeperMain(zk);
+
+        zkMain.executeLine("create /a");
+        zkMain.executeLine("create /a/b");
+        zkMain.executeLine("create /a/c");
+        zkMain.executeLine("create /a/b/d");
+        zkMain.executeLine("create /a/c/e");
+        zkMain.executeLine("create /a/f");
+
+        LsCommand cmd = new LsCommand();
+        cmd.setZk(zk);
+        cmd.parse("ls -R /a".split(" "));
+
+        List<String> expected = new ArrayList<String>();
+        expected.add("/a");
+        expected.add("/a/b");
+        expected.add("/a/c");
+        expected.add("/a/f");
+        expected.add("/a/b/d");
+        expected.add("/a/c/e");
+        runCommandExpect(cmd, expected);
     }
 
+    @Test
+    public void testLsrRootCommand() throws Exception {
+        final ZooKeeper zk = createClient();
+        ZooKeeperMain zkMain = new ZooKeeperMain(zk);
+
+        LsCommand cmd = new LsCommand();
+        cmd.setZk(zk);
+        cmd.parse("ls -R /".split(" "));
+
+        List<String> expected = new ArrayList<String>();
+        expected.add("/");
+        expected.add("/zookeeper");
+        runCommandExpect(cmd, expected);
+    }
+
+    @Test
+    public void testLsrLeafCommand() throws Exception {
+        final ZooKeeper zk = createClient();
+        ZooKeeperMain zkMain = new ZooKeeperMain(zk);
+
+        zkMain.executeLine("create /b");
+        zkMain.executeLine("create /b/c");
+
+        LsCommand cmd = new LsCommand();
+        cmd.setZk(zk);
+        cmd.parse("ls -R /b/c".split(" "));
+
+        List<String> expected = new ArrayList<String>();
+        expected.add("/b/c");
+        runCommandExpect(cmd, expected);
+    }
+
+    @Test
+    public void testLsrNonexistantZnodeCommand() throws Exception {
+        final ZooKeeper zk = createClient();
+        ZooKeeperMain zkMain = new ZooKeeperMain(zk);
+
+        zkMain.executeLine("create /b");
+        zkMain.executeLine("create /b/c");
+
+        LsCommand cmd = new LsCommand();
+        cmd.setZk(zk);
+        cmd.parse("ls -R /b/c/d".split(" "));
+
+        try {
+            runCommandExpect(cmd, new ArrayList<String>());
+            Assert.fail("Path doesn't exists so, command should fail.");
+        } catch (CliWrapperException e) {
+            Assert.assertEquals(KeeperException.Code.NONODE, ((KeeperException)e.getCause()).code());
+        }
+    }
 }



Mime
View raw message