|
@@ -0,0 +1,345 @@
|
|
|
+/**
|
|
|
+ * 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.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.concurrent.CountDownLatch;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+import org.apache.zookeeper.Watcher.Event.EventType;
|
|
|
+import org.apache.zookeeper.ZooDefs.Ids;
|
|
|
+import org.apache.zookeeper.test.ClientBase;
|
|
|
+import org.junit.Assert;
|
|
|
+import org.junit.Test;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Testing remove watches using command line
|
|
|
+ */
|
|
|
+public class RemoveWatchesCmdTest extends ClientBase {
|
|
|
+ private static final Logger LOG = LoggerFactory
|
|
|
+ .getLogger(RemoveWatchesCmdTest.class);
|
|
|
+ private ZooKeeper zk;
|
|
|
+ private ZooKeeperMain zkMain;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void setUp() throws Exception {
|
|
|
+ super.setUp();
|
|
|
+ zk = createClient();
|
|
|
+ zkMain = new ZooKeeperMain(zk);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void tearDown() throws Exception {
|
|
|
+ if (zk != null) {
|
|
|
+ zk.close();
|
|
|
+ }
|
|
|
+ super.tearDown();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test verifies default options. When there is no passed options,
|
|
|
+ * removewatches command will use default options - WatcherType.ANY and
|
|
|
+ * local=false
|
|
|
+ */
|
|
|
+ @Test(timeout = 30000)
|
|
|
+ public void testRemoveWatchesWithNoPassedOptions() throws Exception {
|
|
|
+ List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>();
|
|
|
+ expectedEvents.add(EventType.ChildWatchRemoved);
|
|
|
+ expectedEvents.add(EventType.DataWatchRemoved);
|
|
|
+ MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 2);
|
|
|
+
|
|
|
+ zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE,
|
|
|
+ CreateMode.PERSISTENT);
|
|
|
+ zk.create("/testnode2", "data".getBytes(), Ids.OPEN_ACL_UNSAFE,
|
|
|
+ CreateMode.PERSISTENT);
|
|
|
+
|
|
|
+ LOG.info("Adding childwatcher to /testnode1 and /testnode2");
|
|
|
+ zk.getChildren("/testnode1", myWatcher);
|
|
|
+ zk.getChildren("/testnode2", myWatcher);
|
|
|
+
|
|
|
+ LOG.info("Adding datawatcher to /testnode1 and /testnode2");
|
|
|
+ zk.getData("/testnode1", myWatcher, null);
|
|
|
+ zk.getData("/testnode2", myWatcher, null);
|
|
|
+
|
|
|
+ String cmdstring = "removewatches /testnode1";
|
|
|
+ LOG.info("Remove watchers using shell command : {}", cmdstring);
|
|
|
+ zkMain.cl.parseCommand(cmdstring);
|
|
|
+ Assert.assertTrue("Removewatches cmd fails to remove child watches",
|
|
|
+ zkMain.processZKCmd(zkMain.cl));
|
|
|
+ LOG.info("Waiting for the DataWatchRemoved event");
|
|
|
+ myWatcher.matches();
|
|
|
+
|
|
|
+ // verifying that other path child watches are not affected
|
|
|
+ Assert.assertTrue(
|
|
|
+ "Failed to find child watches for the path testnode2", zk
|
|
|
+ .getChildWatches().contains("/testnode2"));
|
|
|
+ Assert.assertTrue("Failed to find data watches for the path testnode2",
|
|
|
+ zk.getDataWatches().contains("/testnode2"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test verifies deletion of NodeDataChanged watches
|
|
|
+ */
|
|
|
+ @Test(timeout = 30000)
|
|
|
+ public void testRemoveNodeDataChangedWatches() throws Exception {
|
|
|
+ LOG.info("Adding data watcher using getData()");
|
|
|
+ List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>();
|
|
|
+ expectedEvents.add(EventType.DataWatchRemoved);
|
|
|
+ MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 1);
|
|
|
+
|
|
|
+ zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE,
|
|
|
+ CreateMode.PERSISTENT);
|
|
|
+ zk.getData("/testnode1", myWatcher, null);
|
|
|
+
|
|
|
+ String cmdstring = "removewatches /testnode1 -d";
|
|
|
+ LOG.info("Remove watchers using shell command : {}", cmdstring);
|
|
|
+ zkMain.cl.parseCommand(cmdstring);
|
|
|
+ Assert.assertTrue("Removewatches cmd fails to remove data watches",
|
|
|
+ zkMain.processZKCmd(zkMain.cl));
|
|
|
+
|
|
|
+ LOG.info("Waiting for the DataWatchRemoved event");
|
|
|
+ myWatcher.matches();
|
|
|
+
|
|
|
+ // verifying that other path data watches are removed
|
|
|
+ Assert.assertEquals(
|
|
|
+ "Data watches are not removed : " + zk.getDataWatches(), 0, zk
|
|
|
+ .getDataWatches().size());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test verifies deletion of NodeCreated data watches
|
|
|
+ */
|
|
|
+ @Test(timeout = 30000)
|
|
|
+ public void testRemoveNodeCreatedWatches() throws Exception {
|
|
|
+ List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>();
|
|
|
+ expectedEvents.add(EventType.DataWatchRemoved);
|
|
|
+ MyWatcher myWatcher1 = new MyWatcher("/testnode1", expectedEvents, 1);
|
|
|
+ MyWatcher myWatcher2 = new MyWatcher("/testnode1/testnode2",
|
|
|
+ expectedEvents, 1);
|
|
|
+ // Adding pre-created watcher
|
|
|
+ LOG.info("Adding NodeCreated watcher");
|
|
|
+ zk.exists("/testnode1", myWatcher1);
|
|
|
+ zk.exists("/testnode1/testnode2", myWatcher2);
|
|
|
+
|
|
|
+ String cmdstring1 = "removewatches /testnode1 -d";
|
|
|
+ LOG.info("Remove watchers using shell command : {}", cmdstring1);
|
|
|
+ zkMain.cl.parseCommand(cmdstring1);
|
|
|
+ Assert.assertTrue(
|
|
|
+ "Removewatches cmd fails to remove pre-create watches",
|
|
|
+ zkMain.processZKCmd(zkMain.cl));
|
|
|
+ myWatcher1.matches();
|
|
|
+ Assert.assertEquals(
|
|
|
+ "Failed to remove pre-create watches :" + zk.getExistWatches(),
|
|
|
+ 1, zk.getExistWatches().size());
|
|
|
+ Assert.assertTrue(
|
|
|
+ "Failed to remove pre-create watches :" + zk.getExistWatches(),
|
|
|
+ zk.getExistWatches().contains("/testnode1/testnode2"));
|
|
|
+
|
|
|
+ String cmdstring2 = "removewatches /testnode1/testnode2 -d";
|
|
|
+ LOG.info("Remove watchers using shell command : {}", cmdstring2);
|
|
|
+ zkMain.cl.parseCommand(cmdstring2);
|
|
|
+ Assert.assertTrue("Removewatches cmd fails to remove data watches",
|
|
|
+ zkMain.processZKCmd(zkMain.cl));
|
|
|
+
|
|
|
+ myWatcher2.matches();
|
|
|
+ Assert.assertEquals(
|
|
|
+ "Failed to remove pre-create watches : " + zk.getExistWatches(),
|
|
|
+ 0, zk.getExistWatches().size());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test verifies deletion of NodeChildrenChanged watches
|
|
|
+ */
|
|
|
+ @Test(timeout = 30000)
|
|
|
+ public void testRemoveNodeChildrenChangedWatches() throws Exception {
|
|
|
+ List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>();
|
|
|
+ expectedEvents.add(EventType.ChildWatchRemoved);
|
|
|
+ MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 1);
|
|
|
+
|
|
|
+ zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE,
|
|
|
+ CreateMode.PERSISTENT);
|
|
|
+ LOG.info("Adding child changed watcher");
|
|
|
+ zk.getChildren("/testnode1", myWatcher);
|
|
|
+
|
|
|
+ String cmdstring = "removewatches /testnode1 -c";
|
|
|
+ LOG.info("Remove watchers using shell command : {}", cmdstring);
|
|
|
+ zkMain.cl.parseCommand(cmdstring);
|
|
|
+ Assert.assertTrue("Removewatches cmd fails to remove child watches",
|
|
|
+ zkMain.processZKCmd(zkMain.cl));
|
|
|
+ myWatcher.matches();
|
|
|
+ Assert.assertEquals(
|
|
|
+ "Failed to remove child watches : " + zk.getChildWatches(), 0,
|
|
|
+ zk.getChildWatches().size());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test verifies deletion of NodeDeleted watches
|
|
|
+ */
|
|
|
+ @Test(timeout = 30000)
|
|
|
+ public void testRemoveNodeDeletedWatches() throws Exception {
|
|
|
+ LOG.info("Adding NodeDeleted watcher");
|
|
|
+ List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>();
|
|
|
+ expectedEvents.add(EventType.ChildWatchRemoved);
|
|
|
+ expectedEvents.add(EventType.NodeDeleted);
|
|
|
+ MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 1);
|
|
|
+
|
|
|
+ zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE,
|
|
|
+ CreateMode.PERSISTENT);
|
|
|
+ zk.create("/testnode1/testnode2", "data".getBytes(),
|
|
|
+ Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
|
|
|
+ zk.getChildren("/testnode1/testnode2", myWatcher);
|
|
|
+ zk.getChildren("/testnode1", myWatcher);
|
|
|
+
|
|
|
+ String cmdstring = "removewatches /testnode1 -c";
|
|
|
+ LOG.info("Remove watchers using shell command : {}", cmdstring);
|
|
|
+ zkMain.cl.parseCommand(cmdstring);
|
|
|
+ Assert.assertTrue("Removewatches cmd fails to remove child watches",
|
|
|
+ zkMain.processZKCmd(zkMain.cl));
|
|
|
+ LOG.info("Waiting for the ChildWatchRemoved event");
|
|
|
+ myWatcher.matches();
|
|
|
+ Assert.assertEquals(
|
|
|
+ "Failed to remove child watches : " + zk.getChildWatches(), 1,
|
|
|
+ zk.getChildWatches().size());
|
|
|
+
|
|
|
+ Assert.assertTrue(
|
|
|
+ "Failed to remove child watches :" + zk.getChildWatches(), zk
|
|
|
+ .getChildWatches().contains("/testnode1/testnode2"));
|
|
|
+
|
|
|
+ // verify node delete watcher
|
|
|
+ zk.delete("/testnode1/testnode2", -1);
|
|
|
+ myWatcher.matches();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test verifies deletion of any watches
|
|
|
+ */
|
|
|
+ @Test(timeout = 30000)
|
|
|
+ public void testRemoveAnyWatches() throws Exception {
|
|
|
+ verifyRemoveAnyWatches(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test verifies deletion of watches locally when there is no server
|
|
|
+ * connection
|
|
|
+ */
|
|
|
+ @Test(timeout = 30000)
|
|
|
+ public void testRemoveWatchesLocallyWhenNoServerConnection()
|
|
|
+ throws Exception {
|
|
|
+ verifyRemoveAnyWatches(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void verifyRemoveAnyWatches(boolean local) throws Exception {
|
|
|
+ final Map<String, List<EventType>> pathVsEvent = new HashMap<String, List<EventType>>();
|
|
|
+ LOG.info("Adding NodeChildrenChanged, NodeDataChanged watchers");
|
|
|
+ final CountDownLatch watcherLatch = new CountDownLatch(2);
|
|
|
+ Watcher watcher = new Watcher() {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void process(WatchedEvent event) {
|
|
|
+ switch (event.getType()) {
|
|
|
+ case ChildWatchRemoved:
|
|
|
+ case DataWatchRemoved: {
|
|
|
+ addWatchNotifications(pathVsEvent, event);
|
|
|
+ watcherLatch.countDown();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case NodeChildrenChanged:
|
|
|
+ case NodeDataChanged: {
|
|
|
+ addWatchNotifications(pathVsEvent, event);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void addWatchNotifications(
|
|
|
+ final Map<String, List<EventType>> pathVsEvent,
|
|
|
+ WatchedEvent event) {
|
|
|
+ List<EventType> events = pathVsEvent.get(event.getPath());
|
|
|
+ if (null == events) {
|
|
|
+ events = new ArrayList<Watcher.Event.EventType>();
|
|
|
+ pathVsEvent.put(event.getPath(), events);
|
|
|
+ }
|
|
|
+ events.add(event.getType());
|
|
|
+ }
|
|
|
+ };
|
|
|
+ zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE,
|
|
|
+ CreateMode.PERSISTENT);
|
|
|
+ zk.getChildren("/testnode1", watcher);
|
|
|
+ zk.getData("/testnode1", watcher, null);
|
|
|
+ String cmdstring = "removewatches /testnode1 -a";
|
|
|
+ if (local) {
|
|
|
+ LOG.info("Stopping ZK server to verify deletion of watches locally");
|
|
|
+ stopServer();
|
|
|
+ cmdstring = "removewatches /testnode1 -a -l";
|
|
|
+ }
|
|
|
+
|
|
|
+ LOG.info("Remove watchers using shell command : {}", cmdstring);
|
|
|
+ zkMain.cl.parseCommand(cmdstring);
|
|
|
+ Assert.assertTrue(
|
|
|
+ "Removewatches cmd fails to remove child/data watches",
|
|
|
+ zkMain.processZKCmd(zkMain.cl));
|
|
|
+ LOG.info("Waiting for the WatchRemoved events");
|
|
|
+ watcherLatch.await(10, TimeUnit.SECONDS);
|
|
|
+ Assert.assertEquals("Didn't receives WatchRemoved events!", 1,
|
|
|
+ pathVsEvent.size());
|
|
|
+ Assert.assertTrue(
|
|
|
+ "Didn't receives DataWatchRemoved!",
|
|
|
+ pathVsEvent.get("/testnode1").contains(
|
|
|
+ EventType.DataWatchRemoved));
|
|
|
+ Assert.assertTrue("Didn't receives ChildWatchRemoved!", pathVsEvent
|
|
|
+ .get("/testnode1").contains(EventType.ChildWatchRemoved));
|
|
|
+ }
|
|
|
+
|
|
|
+ private class MyWatcher implements Watcher {
|
|
|
+ private final String path;
|
|
|
+ private String eventPath;
|
|
|
+ private final CountDownLatch latch;
|
|
|
+ private final List<EventType> expectedEvents = new ArrayList<EventType>();
|
|
|
+
|
|
|
+ public MyWatcher(String path, List<EventType> expectedEvents, int count) {
|
|
|
+ this.path = path;
|
|
|
+ this.latch = new CountDownLatch(count);
|
|
|
+ this.expectedEvents.addAll(expectedEvents);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void process(WatchedEvent event) {
|
|
|
+ LOG.debug("Event path : {}, eventPath : {}"
|
|
|
+ + new Object[] { path, event.getPath() });
|
|
|
+ this.eventPath = event.getPath();
|
|
|
+ if (expectedEvents.contains(event.getType())) {
|
|
|
+ latch.countDown();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean matches() throws InterruptedException {
|
|
|
+ if (!latch.await(CONNECTION_TIMEOUT / 3, TimeUnit.MILLISECONDS)) {
|
|
|
+ LOG.error("Failed to get watch notifications!");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ LOG.debug("Client path : {} eventPath : {}", new Object[] { path,
|
|
|
+ eventPath });
|
|
|
+ return path.equals(eventPath);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|