Author: henry
Date: Thu Apr 15 07:23:44 2010
New Revision: 934312

URL: http://svn.apache.org/viewvc?rev=934312&view=rev
Log:
ZOOKEEPER-729. Java client API to recursively delete a subtree. (Kay Kay via 
henry)

Added:
    hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/ZooKeeperTest.java
Modified:
    hadoop/zookeeper/trunk/CHANGES.txt
    hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java
    hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeperMain.java

Modified: hadoop/zookeeper/trunk/CHANGES.txt
URL: 
http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/CHANGES.txt?rev=934312&r1=934311&r2=934312&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/CHANGES.txt (original)
+++ hadoop/zookeeper/trunk/CHANGES.txt Thu Apr 15 07:23:44 2010
@@ -23,7 +23,8 @@ IMPROVEMENTS:
   (phunt via mahadev)
 
 NEW FEATURES:
-
+  ZOOKEEPER-729. Java client API to recursively delete a subtree. (Kay
+  Kay via henry)
 
 Release 3.3.0 - 2010-03-24
 

Modified: 
hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java
URL: 
http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java?rev=934312&r1=934311&r2=934312&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java 
(original)
+++ hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java 
Thu Apr 15 07:23:44 2010
@@ -21,8 +21,10 @@ package org.apache.zookeeper;
 import java.io.IOException;
 import java.net.SocketAddress;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -730,6 +732,93 @@ public class ZooKeeper {
         }
     }
 
+    
+    /**
+     * 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, 
+     * 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 void deleteRecursive(final String pathRoot)
+        throws InterruptedException, KeeperException
+    {
+        PathUtils.validatePath(pathRoot);
+      
+        List<String> tree = this.listSubTreeBFS(pathRoot);
+        LOG.debug("Deleting " + tree);
+        LOG.debug("Deleting " + tree.size() + " subnodes ");
+        for (int i = tree.size() - 1; i >= 0 ; --i) {
+            //Delete the leaves first and eventually get rid of the root
+            this.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, 
+     * this operation would abort and would be the responsibility of the app 
to handle the same.
+     * <p>
+     * 
+     * @throws IllegalArgumentException if an invalid path is specified
+     */
+    public void deleteRecursive(final String pathRoot, VoidCallback cb,
+        Object ctx)
+        throws InterruptedException, KeeperException
+    {
+        PathUtils.validatePath(pathRoot);
+      
+        List<String> tree = this.listSubTreeBFS(pathRoot);
+        LOG.debug("Deleting " + tree);
+        LOG.debug("Deleting " + tree.size() + " subnodes ");
+        for (int i = tree.size() - 1; i >= 0 ; --i) {
+            //Delete the leaves first and eventually get rid of the root
+            this.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 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.   
+     * 
+     * @param pathRoot The znode path, for which the entire subtree needs to 
be listed.
+     * @throws InterruptedException 
+     * @throws KeeperException 
+     */
+    public List<String> listSubTreeBFS(final String pathRoot) throws 
KeeperException, InterruptedException {
+        Deque<String> queue = new LinkedList<String>();
+        List<String> tree = new ArrayList<String>();
+        queue.add(pathRoot);
+        tree.add(pathRoot);
+        while (true) {
+            String node = queue.pollFirst();
+            if (node == null) {
+                break;
+            }
+            List<String> children = this.getChildren(node, false);
+            for (final String child : children) {
+                final String childPath = node + "/" + child;
+                queue.add(childPath);
+                tree.add(childPath);
+            }
+        }
+        return tree;
+    }
+    
+
+    
     /**
      * The Asynchronous version of delete. The request doesn't actually until
      * the asynchronous callback is called.

Modified: 
hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeperMain.java
URL: 
http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeperMain.java?rev=934312&r1=934311&r2=934312&view=diff
==============================================================================
--- 
hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeperMain.java 
(original)
+++ 
hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeperMain.java 
Thu Apr 15 07:23:44 2010
@@ -65,6 +65,7 @@ public class ZooKeeperMain {
         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]");
@@ -681,6 +682,9 @@ public class ZooKeeperMain {
         } 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];
+            zk.deleteRecursive(path);
         } else if (cmd.equals("set") && args.length >= 3) {
             path = args[1];
             stat = zk.setData(path, args[2].getBytes(),

Added: 
hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/ZooKeeperTest.java
URL: 
http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/ZooKeeperTest.java?rev=934312&view=auto
==============================================================================
--- 
hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/ZooKeeperTest.java 
(added)
+++ 
hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/ZooKeeperTest.java 
Thu Apr 15 07:23:44 2010
@@ -0,0 +1,125 @@
+/**
+ * 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.IOException;
+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.test.ClientBase;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * 
+ * Testing Zookeeper public methods
+ *
+ */
+public class ZooKeeperTest extends ClientBase {
+
+    @Test
+    public void testDeleteRecursive() throws IOException, InterruptedException,
+            KeeperException {
+        final ZooKeeper zk = createClient();
+        // making sure setdata works on /
+        zk.setData("/", "some".getBytes(), -1);
+        zk.create("/a", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/b", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/b/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/b/v/1", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/c", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/c/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        List<String> children = zk.getChildren("/a", false);
+
+        Assert.assertEquals("2 children - b & c should be present ", children
+                .size(), 2);
+        Assert.assertTrue(children.contains("b"));
+        Assert.assertTrue(children.contains("c"));
+
+        zk.deleteRecursive("/a");
+        Assert.assertNull(zk.exists("/a", null));
+    }
+
+    @Test
+    public void testDeleteRecursiveAsync() throws IOException,
+            InterruptedException, KeeperException {
+        final ZooKeeper zk = createClient();
+        // making sure setdata works on /
+        zk.setData("/", "some".getBytes(), -1);
+        zk.create("/a", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/b", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/b/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/b/v/1", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/c", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        zk.create("/a/c/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+
+        for (int i = 0; i < 50; ++i) {
+            zk.create("/a/c/" + i, "some".getBytes(), Ids.OPEN_ACL_UNSAFE,
+                    CreateMode.PERSISTENT);
+        }
+        List<String> children = zk.getChildren("/a", false);
+
+        Assert.assertEquals("2 children - b & c should be present ", children
+                .size(), 2);
+        Assert.assertTrue(children.contains("b"));
+        Assert.assertTrue(children.contains("c"));
+
+        VoidCallback cb = new VoidCallback() {
+
+            @Override
+            public void processResult(int rc, String path, Object ctx) {
+                synchronized (ctx) {
+                    ((AtomicInteger) ctx).set(4);
+                    ctx.notify();
+                }
+            }
+
+        };
+        final AtomicInteger ctx = new AtomicInteger(3);
+        zk.deleteRecursive("/a", cb, ctx);
+        synchronized (ctx) {
+            ctx.wait();
+        }
+        Assert.assertEquals(4, ((AtomicInteger) ctx).get());
+    }
+}


Reply via email to