You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zookeeper.apache.org by an...@apache.org on 2018/10/19 12:40:10 UTC
[12/51] [partial] zookeeper git commit: ZOOKEEPER-3032: MAVEN
MIGRATION - zookeeper-server
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/cb9f303b/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesTest.java
new file mode 100644
index 0000000..931bb6f
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesTest.java
@@ -0,0 +1,1258 @@
+/**
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.zookeeper.KeeperException.Code;
+import org.apache.zookeeper.KeeperException.NoWatcherException;
+import org.apache.zookeeper.Watcher.Event.EventType;
+import org.apache.zookeeper.Watcher.WatcherType;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.client.ZKClientConfig;
+import org.apache.zookeeper.server.ServerCnxn;
+import org.apache.zookeeper.test.ClientBase;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Verifies removing watches using ZooKeeper client apis
+ */
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(ZKParameterized.RunnerFactory.class)
+public class RemoveWatchesTest extends ClientBase {
+ private static final Logger LOG = LoggerFactory
+ .getLogger(RemoveWatchesTest.class);
+ private ZooKeeper zk1 = null;
+ private ZooKeeper zk2 = null;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ zk1 = createClient();
+ zk2 = createClient();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (zk1 != null)
+ zk1.close();
+ if (zk2 != null)
+ zk2.close();
+ super.tearDown();
+ }
+
+ private final boolean useAsync;
+
+ public RemoveWatchesTest(boolean useAsync) {
+ this.useAsync = useAsync;
+ }
+
+ @Parameters
+ public static Collection<Object[]> configs() {
+ return Arrays.asList(new Object[][] { { false }, { true }, });
+ }
+
+ private void removeWatches(ZooKeeper zk, String path, Watcher watcher,
+ WatcherType watcherType, boolean local, KeeperException.Code rc)
+ throws InterruptedException, KeeperException {
+ LOG.info(
+ "Sending removeWatches req using zk {} path: {} type: {} watcher: {} ",
+ new Object[] { zk, path, watcherType, watcher });
+ if (useAsync) {
+ MyCallback c1 = new MyCallback(rc.intValue(), path);
+ zk.removeWatches(path, watcher, watcherType, local, c1, null);
+ Assert.assertTrue("Didn't succeeds removeWatch operation",
+ c1.matches());
+ if (KeeperException.Code.OK.intValue() != c1.rc) {
+ KeeperException ke = KeeperException
+ .create(KeeperException.Code.get(c1.rc));
+ throw ke;
+ }
+ } else {
+ zk.removeWatches(path, watcher, watcherType, local);
+ }
+ }
+
+ private void removeAllWatches(ZooKeeper zk, String path,
+ WatcherType watcherType, boolean local, KeeperException.Code rc)
+ throws InterruptedException, KeeperException {
+ LOG.info("Sending removeWatches req using zk {} path: {} type: {} ",
+ new Object[] { zk, path, watcherType });
+ if (useAsync) {
+ MyCallback c1 = new MyCallback(rc.intValue(), path);
+ zk.removeAllWatches(path, watcherType, local, c1, null);
+ Assert.assertTrue("Didn't succeeds removeWatch operation",
+ c1.matches());
+ if (KeeperException.Code.OK.intValue() != c1.rc) {
+ KeeperException ke = KeeperException
+ .create(KeeperException.Code.get(c1.rc));
+ throw ke;
+ }
+ } else {
+ zk.removeAllWatches(path, watcherType, local);
+ }
+ }
+
+ /**
+ * Test verifies removal of single watcher when there is server connection
+ */
+ @Test(timeout = 90000)
+ public void testRemoveSingleWatcher() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+ zk1.create("/node2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+ MyWatcher w1 = new MyWatcher("/node1", 1);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ MyWatcher w2 = new MyWatcher("/node2", 1);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node2", w2));
+ removeWatches(zk2, "/node1", w1, WatcherType.Data, false, Code.OK);
+ Assert.assertEquals("Didn't find data watcher", 1,
+ zk2.getDataWatches().size());
+ Assert.assertEquals("Didn't find data watcher", "/node2",
+ zk2.getDataWatches().get(0));
+ removeWatches(zk2, "/node2", w2, WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove data watcher", w2.matches());
+ // closing session should remove ephemeral nodes and trigger data
+ // watches if any
+ if (zk1 != null) {
+ zk1.close();
+ zk1 = null;
+ }
+
+ List<EventType> events = w1.getEventsAfterWatchRemoval();
+ Assert.assertFalse(
+ "Shouldn't get NodeDeletedEvent after watch removal",
+ events.contains(EventType.NodeDeleted));
+ Assert.assertEquals(
+ "Shouldn't get NodeDeletedEvent after watch removal", 0,
+ events.size());
+ }
+
+ /**
+ * Test verifies removal of multiple data watchers when there is server
+ * connection
+ */
+ @Test(timeout = 90000)
+ public void testMultipleDataWatchers() throws IOException,
+ InterruptedException, KeeperException {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+ MyWatcher w1 = new MyWatcher("/node1", 1);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ MyWatcher w2 = new MyWatcher("/node1", 1);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w2));
+ removeWatches(zk2, "/node1", w2, WatcherType.Data, false, Code.OK);
+ Assert.assertEquals("Didn't find data watcher", 1,
+ zk2.getDataWatches().size());
+ Assert.assertEquals("Didn't find data watcher", "/node1",
+ zk2.getDataWatches().get(0));
+ removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove data watcher", w2.matches());
+ // closing session should remove ephemeral nodes and trigger data
+ // watches if any
+ if (zk1 != null) {
+ zk1.close();
+ zk1 = null;
+ }
+
+ List<EventType> events = w2.getEventsAfterWatchRemoval();
+ Assert.assertEquals(
+ "Shouldn't get NodeDeletedEvent after watch removal", 0,
+ events.size());
+ }
+
+ /**
+ * Test verifies removal of multiple child watchers when there is server
+ * connection
+ */
+ @Test(timeout = 90000)
+ public void testMultipleChildWatchers() throws IOException,
+ InterruptedException, KeeperException {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ MyWatcher w1 = new MyWatcher("/node1", 1);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ zk2.getChildren("/node1", w1);
+ MyWatcher w2 = new MyWatcher("/node1", 1);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ zk2.getChildren("/node1", w2);
+ removeWatches(zk2, "/node1", w2, WatcherType.Children, false, Code.OK);
+ Assert.assertTrue("Didn't remove child watcher", w2.matches());
+ Assert.assertEquals("Didn't find child watcher", 1, zk2
+ .getChildWatches().size());
+ removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove child watcher", w1.matches());
+ // create child to see NodeChildren notification
+ zk1.create("/node1/node2", null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ // waiting for child watchers to be notified
+ int count = 30;
+ while (count > 0) {
+ if (w1.getEventsAfterWatchRemoval().size() > 0) {
+ break;
+ }
+ count--;
+ Thread.sleep(100);
+ }
+ // watcher2
+ List<EventType> events = w2.getEventsAfterWatchRemoval();
+ Assert.assertEquals("Shouldn't get NodeChildrenChanged event", 0,
+ events.size());
+ }
+
+ /**
+ * Test verifies null watcher with WatcherType.Any - remove all the watchers
+ * data, child, exists
+ */
+ @Test(timeout = 90000)
+ public void testRemoveAllWatchers() throws IOException,
+ InterruptedException, KeeperException {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ MyWatcher w1 = new MyWatcher("/node1", 2);
+ MyWatcher w2 = new MyWatcher("/node1", 2);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w2));
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ zk2.getChildren("/node1", w1);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ zk2.getChildren("/node1", w2);
+ removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK);
+ removeWatches(zk2, "/node1", w2, WatcherType.Any, false, Code.OK);
+ zk1.create("/node1/child", null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.EPHEMERAL);
+ Assert.assertTrue("Didn't remove data watcher", w1.matches());
+ Assert.assertTrue("Didn't remove child watcher", w2.matches());
+ }
+
+ /**
+ * Test verifies null watcher with WatcherType.Data - remove all data
+ * watchers. Child watchers shouldn't be removed
+ */
+ @Test(timeout = 90000)
+ public void testRemoveAllDataWatchers() throws IOException,
+ InterruptedException, KeeperException {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ MyWatcher w1 = new MyWatcher("/node1", 1);
+ MyWatcher w2 = new MyWatcher("/node1", 1);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w2));
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ zk2.getChildren("/node1", w1);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ zk2.getChildren("/node1", w2);
+ removeWatches(zk2, "/node1", w1, WatcherType.Data, false, Code.OK);
+ removeWatches(zk2, "/node1", w2, WatcherType.Data, false, Code.OK);
+ zk1.create("/node1/child", null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.EPHEMERAL);
+ Assert.assertTrue("Didn't remove data watcher", w1.matches());
+ Assert.assertTrue("Didn't remove data watcher", w2.matches());
+ // waiting for child watchers to be notified
+ int count = 10;
+ while (count > 0) {
+ if (w1.getEventsAfterWatchRemoval().size() > 0
+ && w2.getEventsAfterWatchRemoval().size() > 0) {
+ break;
+ }
+ count--;
+ Thread.sleep(1000);
+ }
+ // watcher1
+ List<EventType> events = w1.getEventsAfterWatchRemoval();
+ Assert.assertEquals("Didn't get NodeChildrenChanged event", 1,
+ events.size());
+ Assert.assertTrue("Didn't get NodeChildrenChanged event",
+ events.contains(EventType.NodeChildrenChanged));
+ // watcher2
+ events = w2.getEventsAfterWatchRemoval();
+ Assert.assertEquals("Didn't get NodeChildrenChanged event", 1,
+ events.size());
+ Assert.assertTrue("Didn't get NodeChildrenChanged event",
+ events.contains(EventType.NodeChildrenChanged));
+ }
+
+ /**
+ * Test verifies null watcher with WatcherType.Children - remove all child
+ * watchers. Data watchers shouldn't be removed
+ */
+ @Test(timeout = 90000)
+ public void testRemoveAllChildWatchers() throws IOException,
+ InterruptedException, KeeperException {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ MyWatcher w1 = new MyWatcher("/node1", 1);
+ MyWatcher w2 = new MyWatcher("/node1", 1);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w2));
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ zk2.getChildren("/node1", w1);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ zk2.getChildren("/node1", w2);
+ removeWatches(zk2, "/node1", w1, WatcherType.Children, false, Code.OK);
+ removeWatches(zk2, "/node1", w2, WatcherType.Children, false, Code.OK);
+ zk1.setData("/node1", "test".getBytes(), -1);
+ Assert.assertTrue("Didn't remove child watcher", w1.matches());
+ Assert.assertTrue("Didn't remove child watcher", w2.matches());
+ // waiting for child watchers to be notified
+ int count = 10;
+ while (count > 0) {
+ if (w1.getEventsAfterWatchRemoval().size() > 0
+ && w2.getEventsAfterWatchRemoval().size() > 0) {
+ break;
+ }
+ count--;
+ Thread.sleep(1000);
+ }
+ // watcher1
+ List<EventType> events = w1.getEventsAfterWatchRemoval();
+ Assert.assertEquals("Didn't get NodeDataChanged event", 1,
+ events.size());
+ Assert.assertTrue("Didn't get NodeDataChanged event",
+ events.contains(EventType.NodeDataChanged));
+ // watcher2
+ events = w2.getEventsAfterWatchRemoval();
+ Assert.assertEquals("Didn't get NodeDataChanged event", 1,
+ events.size());
+ Assert.assertTrue("Didn't get NodeDataChanged event",
+ events.contains(EventType.NodeDataChanged));
+ }
+
+ /**
+ * Test verifies given watcher doesn't exists!
+ */
+ @Test(timeout = 90000)
+ public void testNoWatcherException() throws IOException,
+ InterruptedException, KeeperException {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ MyWatcher w1 = new MyWatcher("/node1", 2);
+ MyWatcher w2 = new MyWatcher("/node1", 2);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNull("Didn't set data watches", zk2.exists("/node2", w2));
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ zk2.getChildren("/node1", w1);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ zk2.getChildren("/node1", w2);
+
+ // New Watcher which will be used for removal
+ MyWatcher w3 = new MyWatcher("/node1", 2);
+
+ try {
+ removeWatches(zk2, "/node1", w3, WatcherType.Any, false,
+ Code.NOWATCHER);
+ Assert.fail("Should throw exception as given watcher doesn't exists");
+ } catch (KeeperException.NoWatcherException nwe) {
+ // expected
+ }
+ try {
+ removeWatches(zk2, "/node1", w3, WatcherType.Children, false,
+ Code.NOWATCHER);
+ Assert.fail("Should throw exception as given watcher doesn't exists");
+ } catch (KeeperException.NoWatcherException nwe) {
+ // expected
+ }
+ try {
+ removeWatches(zk2, "/node1", w3, WatcherType.Data, false,
+ Code.NOWATCHER);
+ Assert.fail("Should throw exception as given watcher doesn't exists");
+ } catch (KeeperException.NoWatcherException nwe) {
+ // expected
+ }
+ try {
+ removeWatches(zk2, "/nonexists", w3, WatcherType.Data, false,
+ Code.NOWATCHER);
+ Assert.fail("Should throw exception as given watcher doesn't exists");
+ } catch (KeeperException.NoWatcherException nwe) {
+ // expected
+ }
+ }
+
+ /**
+ * Test verifies WatcherType.Any - removes only the configured data watcher
+ * function
+ */
+ @Test(timeout = 90000)
+ public void testRemoveAnyDataWatcher() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ MyWatcher w1 = new MyWatcher("/node1", 1);
+ MyWatcher w2 = new MyWatcher("/node1", 2);
+ // Add multiple data watches
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w2));
+ // Add child watch
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ zk2.getChildren("/node1", w2);
+ removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove data watcher", w1.matches());
+ Assert.assertEquals("Didn't find child watcher", 1, zk2
+ .getChildWatches().size());
+ Assert.assertEquals("Didn't find data watcher", 1, zk2
+ .getDataWatches().size());
+ removeWatches(zk2, "/node1", w2, WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove child watcher", w2.matches());
+ }
+
+ /**
+ * Test verifies WatcherType.Any - removes only the configured child watcher
+ * function
+ */
+ @Test(timeout = 90000)
+ public void testRemoveAnyChildWatcher() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ MyWatcher w1 = new MyWatcher("/node1", 2);
+ MyWatcher w2 = new MyWatcher("/node1", 1);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ // Add multiple child watches
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ zk2.getChildren("/node1", w2);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ zk2.getChildren("/node1", w1);
+ removeWatches(zk2, "/node1", w2, WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove child watcher", w2.matches());
+ Assert.assertEquals("Didn't find child watcher", 1, zk2
+ .getChildWatches().size());
+ Assert.assertEquals("Didn't find data watcher", 1, zk2
+ .getDataWatches().size());
+ removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove watchers", w1.matches());
+ }
+
+ /**
+ * Test verifies when there is no server connection. Remove watches when
+ * local=true, otw should retain it
+ */
+ @Test(timeout = 90000)
+ public void testRemoveWatcherWhenNoConnection() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ MyWatcher w1 = new MyWatcher("/node1", 2);
+ MyWatcher w2 = new MyWatcher("/node1", 1);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ // Add multiple child watches
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ zk2.getChildren("/node1", w1);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ zk2.getChildren("/node1", w2);
+ stopServer();
+ removeWatches(zk2, "/node1", w2, WatcherType.Any, true, Code.OK);
+ Assert.assertTrue("Didn't remove child watcher", w2.matches());
+ Assert.assertFalse("Shouldn't remove data watcher", w1.matches());
+ try {
+ removeWatches(zk2, "/node1", w1, WatcherType.Any, false,
+ Code.CONNECTIONLOSS);
+ Assert.fail("Should throw exception as last watch removal requires server connection");
+ } catch (KeeperException.ConnectionLossException nwe) {
+ // expected
+ }
+ Assert.assertFalse("Shouldn't remove data watcher", w1.matches());
+
+ // when local=true, here if connection not available, simply removes
+ // from local session
+ removeWatches(zk2, "/node1", w1, WatcherType.Any, true, Code.OK);
+ Assert.assertTrue("Didn't remove data watcher", w1.matches());
+ }
+
+ /**
+ * Test verifies many pre-node watchers. Also, verifies internal
+ * datastructure 'watchManager.existWatches'
+ */
+ @Test(timeout = 90000)
+ public void testManyPreNodeWatchers() throws Exception {
+ int count = 50;
+ List<MyWatcher> wList = new ArrayList<MyWatcher>(count);
+ MyWatcher w;
+ String path = "/node";
+ // Exists watcher
+ for (int i = 0; i < count; i++) {
+ final String nodePath = path + i;
+ w = new MyWatcher(nodePath, 1);
+ wList.add(w);
+ LOG.info("Adding pre node watcher {} on path {}", new Object[] { w,
+ nodePath });
+ zk1.exists(nodePath, w);
+ }
+ Assert.assertEquals("Failed to add watchers!", count, zk1
+ .getExistWatches().size());
+ for (int i = 0; i < count; i++) {
+ final MyWatcher watcher = wList.get(i);
+ removeWatches(zk1, path + i, watcher, WatcherType.Data, false,
+ Code.OK);
+ Assert.assertTrue("Didn't remove data watcher", watcher.matches());
+ }
+ Assert.assertEquals("Didn't remove watch references!", 0, zk1
+ .getExistWatches().size());
+ }
+
+ /**
+ * Test verifies many child watchers. Also, verifies internal datastructure
+ * 'watchManager.childWatches'
+ */
+ @Test(timeout = 90000)
+ public void testManyChildWatchers() throws Exception {
+ int count = 50;
+ List<MyWatcher> wList = new ArrayList<MyWatcher>(count);
+ MyWatcher w;
+ String path = "/node";
+
+ // Child watcher
+ for (int i = 0; i < count; i++) {
+ String nodePath = path + i;
+ zk1.create(nodePath, null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ nodePath += "/";
+ }
+ for (int i = 0; i < count; i++) {
+ String nodePath = path + i;
+ w = new MyWatcher(path + i, 1);
+ wList.add(w);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w,
+ nodePath });
+ zk1.getChildren(nodePath, w);
+ nodePath += "/";
+ }
+ Assert.assertEquals("Failed to add watchers!", count, zk1
+ .getChildWatches().size());
+ for (int i = 0; i < count; i++) {
+ final MyWatcher watcher = wList.get(i);
+ removeWatches(zk1, path + i, watcher, WatcherType.Children, false,
+ Code.OK);
+ Assert.assertTrue("Didn't remove child watcher", watcher.matches());
+ }
+ Assert.assertEquals("Didn't remove watch references!", 0, zk1
+ .getChildWatches().size());
+ }
+
+ /**
+ * Test verifies many data watchers. Also, verifies internal datastructure
+ * 'watchManager.dataWatches'
+ */
+ @Test(timeout = 90000)
+ public void testManyDataWatchers() throws Exception {
+ int count = 50;
+ List<MyWatcher> wList = new ArrayList<MyWatcher>(count);
+ MyWatcher w;
+ String path = "/node";
+
+ // Data watcher
+ for (int i = 0; i < count; i++) {
+ String nodePath = path + i;
+ w = new MyWatcher(path + i, 1);
+ wList.add(w);
+ zk1.create(nodePath, null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w,
+ nodePath });
+ zk1.getData(nodePath, w, null);
+ nodePath += "/";
+ }
+ Assert.assertEquals("Failed to add watchers!", count, zk1
+ .getDataWatches().size());
+ for (int i = 0; i < count; i++) {
+ final MyWatcher watcher = wList.get(i);
+ removeWatches(zk1, path + i, watcher, WatcherType.Data, false,
+ Code.OK);
+ Assert.assertTrue("Didn't remove data watcher", watcher.matches());
+ }
+ Assert.assertEquals("Didn't remove watch references!", 0, zk1
+ .getDataWatches().size());
+ }
+
+ /**
+ * Test verifies removal of many watchers locally when no connection and
+ * WatcherType#Any. Also, verifies internal watchManager datastructures
+ */
+ @Test(timeout = 90000)
+ public void testManyWatchersWhenNoConnection() throws Exception {
+ int count = 3;
+ List<MyWatcher> wList = new ArrayList<MyWatcher>(count);
+ MyWatcher w;
+ String path = "/node";
+
+ // Child watcher
+ for (int i = 0; i < count; i++) {
+ String nodePath = path + i;
+ zk1.create(nodePath, null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ nodePath += "/";
+ }
+ for (int i = 0; i < count; i++) {
+ String nodePath = path + i;
+ w = new MyWatcher(path + i, 2);
+ wList.add(w);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w,
+ nodePath });
+ zk1.getChildren(nodePath, w);
+ nodePath += "/";
+ }
+ Assert.assertEquals("Failed to add watchers!", count, zk1
+ .getChildWatches().size());
+
+ // Data watcher
+ for (int i = 0; i < count; i++) {
+ String nodePath = path + i;
+ w = wList.get(i);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w,
+ nodePath });
+ zk1.getData(nodePath, w, null);
+ nodePath += "/";
+ }
+ Assert.assertEquals("Failed to add watchers!", count, zk1
+ .getDataWatches().size());
+ stopServer();
+ for (int i = 0; i < count; i++) {
+ final MyWatcher watcher = wList.get(i);
+ removeWatches(zk1, path + i, watcher, WatcherType.Any, true,
+ Code.OK);
+ Assert.assertTrue("Didn't remove watcher", watcher.matches());
+ }
+ Assert.assertEquals("Didn't remove watch references!", 0, zk1
+ .getChildWatches().size());
+ Assert.assertEquals("Didn't remove watch references!", 0, zk1
+ .getDataWatches().size());
+ }
+
+ /**
+ * Test verifies removing watcher having namespace
+ */
+ @Test(timeout = 90000)
+ public void testChRootRemoveWatcher() throws Exception {
+ // creating the subtree for chRoot clients.
+ String chRoot = "/appsX";
+ zk1.create("/appsX", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ if (zk1 != null) {
+ zk1.close();
+ }
+ if (zk2 != null) {
+ zk2.close();
+ }
+ // Creating chRoot client.
+ zk1 = createClient(this.hostPort + chRoot);
+ zk2 = createClient(this.hostPort + chRoot);
+
+ LOG.info("Creating child znode /node1 using chRoot client");
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ MyWatcher w1 = new MyWatcher("/node1", 2);
+ MyWatcher w2 = new MyWatcher("/node1", 1);
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ // Add multiple child watches
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ zk2.getChildren("/node1", w2);
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ zk2.getChildren("/node1", w1);
+ removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove child watcher", w1.matches());
+ Assert.assertEquals("Didn't find child watcher", 1, zk2
+ .getChildWatches().size());
+ removeWatches(zk2, "/node1", w2, WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove child watcher", w2.matches());
+ }
+
+ /**
+ * Verify that if a given watcher doesn't exist, the server properly
+ * returns an error code for it.
+ *
+ * In our Java client implementation, we check that a given watch exists at
+ * two points:
+ *
+ * 1) before submitting the RemoveWatches request
+ * 2) after a successful server response, when the watcher needs to be
+ * removed
+ *
+ * Since this can be racy (i.e. a watch can fire while a RemoveWatches
+ * request is in-flight), we need to verify that the watch was actually
+ * removed (i.e. from ZKDatabase and DataTree) and return NOWATCHER if
+ * needed.
+ *
+ * Also, other implementations might not do a client side check before
+ * submitting a RemoveWatches request. If we don't do a server side check,
+ * we would just return ZOK even if no watch was removed.
+ *
+ */
+ @Test(timeout = 90000)
+ public void testNoWatcherServerException()
+ throws InterruptedException, IOException, TimeoutException {
+ CountdownWatcher watcher = new CountdownWatcher();
+ MyZooKeeper zk = new MyZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher);
+ boolean nw = false;
+
+ watcher.waitForConnected(CONNECTION_TIMEOUT);
+
+ try {
+ zk.removeWatches("/nowatchhere", watcher, WatcherType.Data, false);
+ } catch (KeeperException nwe) {
+ if (nwe.code().intValue() == Code.NOWATCHER.intValue()) {
+ nw = true;
+ }
+ }
+
+ Assert.assertTrue("Server didn't return NOWATCHER",
+ zk.getRemoveWatchesRC() == Code.NOWATCHER.intValue());
+ Assert.assertTrue("NoWatcherException didn't happen", nw);
+ }
+
+ /**
+ * Test verifies given watcher doesn't exists!
+ */
+ @Test(timeout = 90000)
+ public void testRemoveAllNoWatcherException() throws IOException,
+ InterruptedException, KeeperException {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ try {
+ removeAllWatches(zk2, "/node1", WatcherType.Any, false,
+ Code.NOWATCHER);
+ Assert.fail("Should throw exception as given watcher doesn't exists");
+ } catch (KeeperException.NoWatcherException nwe) {
+ // expected
+ }
+ }
+
+ /**
+ * Test verifies null watcher
+ */
+ @Test(timeout = 30000)
+ public void testNullWatcherReference() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ try {
+ if (useAsync) {
+ zk1.removeWatches("/node1", null, WatcherType.Data, false,
+ null, null);
+ } else {
+ zk1.removeWatches("/node1", null, WatcherType.Data, false);
+ }
+ Assert.fail("Must throw IllegalArgumentException as watcher is null!");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+ }
+
+ /**
+ * Test verifies WatcherType.Data - removes only the configured data watcher
+ * function
+ */
+ @Test(timeout = 90000)
+ public void testRemoveWhenMultipleDataWatchesOnAPath() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ final CountDownLatch dataWatchCount = new CountDownLatch(1);
+ final CountDownLatch rmWatchCount = new CountDownLatch(1);
+ Watcher w1 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ if (event.getType() == EventType.DataWatchRemoved) {
+ rmWatchCount.countDown();
+ }
+ }
+ };
+ Watcher w2 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ if (event.getType() == EventType.NodeDataChanged) {
+ dataWatchCount.countDown();
+ }
+ }
+ };
+ // Add multiple data watches
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w2));
+
+ removeWatches(zk2, "/node1", w1, WatcherType.Data, false, Code.OK);
+ Assert.assertTrue("Didn't remove data watcher",
+ rmWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ zk1.setData("/node1", "test".getBytes(), -1);
+ LOG.info("Waiting for data watchers to be notified");
+ Assert.assertTrue("Didn't get data watch notification!",
+ dataWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ /**
+ * Test verifies WatcherType.Children - removes only the configured child
+ * watcher function
+ */
+ @Test(timeout = 90000)
+ public void testRemoveWhenMultipleChildWatchesOnAPath() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ final CountDownLatch childWatchCount = new CountDownLatch(1);
+ final CountDownLatch rmWatchCount = new CountDownLatch(1);
+ Watcher w1 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ if (event.getType() == EventType.ChildWatchRemoved) {
+ rmWatchCount.countDown();
+ }
+ }
+ };
+ Watcher w2 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ if (event.getType() == EventType.NodeChildrenChanged) {
+ childWatchCount.countDown();
+ }
+ }
+ };
+ // Add multiple child watches
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertEquals("Didn't set child watches", 0,
+ zk2.getChildren("/node1", w1).size());
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertEquals("Didn't set child watches", 0,
+ zk2.getChildren("/node1", w2).size());
+
+ removeWatches(zk2, "/node1", w1, WatcherType.Children, false, Code.OK);
+ Assert.assertTrue("Didn't remove child watcher",
+ rmWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ zk1.create("/node1/node2", null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ LOG.info("Waiting for child watchers to be notified");
+ Assert.assertTrue("Didn't get child watch notification!",
+ childWatchCount
+ .await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ /**
+ * Test verifies WatcherType.Data - removes only the configured data watcher
+ * function
+ */
+ @Test(timeout = 90000)
+ public void testRemoveAllDataWatchesOnAPath() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ final CountDownLatch dWatchCount = new CountDownLatch(2);
+ final CountDownLatch rmWatchCount = new CountDownLatch(2);
+ Watcher w1 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ switch (event.getType()) {
+ case DataWatchRemoved:
+ rmWatchCount.countDown();
+ break;
+ case NodeDataChanged:
+ dWatchCount.countDown();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ Watcher w2 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ switch (event.getType()) {
+ case DataWatchRemoved:
+ rmWatchCount.countDown();
+ break;
+ case NodeDataChanged:
+ dWatchCount.countDown();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ // Add multiple data watches
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w2));
+
+ Assert.assertTrue("Server session is not a watcher",
+ isServerSessionWatcher(zk2.getSessionId(), "/node1",
+ WatcherType.Data));
+ removeAllWatches(zk2, "/node1", WatcherType.Data, false, Code.OK);
+ Assert.assertTrue("Didn't remove data watcher",
+ rmWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ Assert.assertFalse("Server session is still a watcher after removal",
+ isServerSessionWatcher(zk2.getSessionId(), "/node1",
+ WatcherType.Data));
+ }
+
+ /**
+ * Test verifies WatcherType.Children - removes only the configured child
+ * watcher function
+ */
+ @Test(timeout = 90000)
+ public void testRemoveAllChildWatchesOnAPath() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ final CountDownLatch cWatchCount = new CountDownLatch(2);
+ final CountDownLatch rmWatchCount = new CountDownLatch(2);
+ Watcher w1 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ switch (event.getType()) {
+ case ChildWatchRemoved:
+ rmWatchCount.countDown();
+ break;
+ case NodeChildrenChanged:
+ cWatchCount.countDown();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ Watcher w2 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ switch (event.getType()) {
+ case ChildWatchRemoved:
+ rmWatchCount.countDown();
+ break;
+ case NodeChildrenChanged:
+ cWatchCount.countDown();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ // Add multiple child watches
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertEquals("Didn't set child watches", 0,
+ zk2.getChildren("/node1", w1).size());
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertEquals("Didn't set child watches", 0,
+ zk2.getChildren("/node1", w2).size());
+
+ Assert.assertTrue("Server session is not a watcher",
+ isServerSessionWatcher(zk2.getSessionId(), "/node1",
+ WatcherType.Children));
+ removeAllWatches(zk2, "/node1", WatcherType.Children, false, Code.OK);
+ Assert.assertTrue("Didn't remove child watcher",
+ rmWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ Assert.assertFalse("Server session is still a watcher after removal",
+ isServerSessionWatcher(zk2.getSessionId(), "/node1",
+ WatcherType.Children));
+ }
+
+ /**
+ * Test verifies WatcherType.Any - removes all the configured child,data
+ * watcher functions
+ */
+ @Test(timeout = 90000)
+ public void testRemoveAllWatchesOnAPath() throws Exception {
+ zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ final CountDownLatch watchCount = new CountDownLatch(2);
+ final CountDownLatch rmWatchCount = new CountDownLatch(4);
+ Watcher w1 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ switch (event.getType()) {
+ case ChildWatchRemoved:
+ case DataWatchRemoved:
+ rmWatchCount.countDown();
+ break;
+ case NodeChildrenChanged:
+ case NodeDataChanged:
+ watchCount.countDown();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ Watcher w2 = new Watcher() {
+ @Override
+ public void process(WatchedEvent event) {
+ switch (event.getType()) {
+ case ChildWatchRemoved:
+ case DataWatchRemoved:
+ rmWatchCount.countDown();
+ break;
+ case NodeChildrenChanged:
+ case NodeDataChanged:
+ watchCount.countDown();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ // Add multiple child watches
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertEquals("Didn't set child watches", 0,
+ zk2.getChildren("/node1", w1).size());
+ LOG.info("Adding child watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertEquals("Didn't set child watches", 0,
+ zk2.getChildren("/node1", w2).size());
+
+ // Add multiple data watches
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w1,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w1));
+ LOG.info("Adding data watcher {} on path {}", new Object[] { w2,
+ "/node1" });
+ Assert.assertNotNull("Didn't set data watches",
+ zk2.exists("/node1", w2));
+
+ Assert.assertTrue("Server session is not a watcher",
+ isServerSessionWatcher(zk2.getSessionId(), "/node1",
+ WatcherType.Data));
+ removeAllWatches(zk2, "/node1", WatcherType.Any, false, Code.OK);
+ Assert.assertTrue("Didn't remove data watcher",
+ rmWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS));
+ Assert.assertFalse("Server session is still a watcher after removal",
+ isServerSessionWatcher(zk2.getSessionId(), "/node1",
+ WatcherType.Data));
+ Assert.assertEquals("Received watch notification after removal!", 2,
+ watchCount.getCount());
+ }
+
+ /* a mocked ZK class that doesn't do client-side verification
+ * before/after calling removeWatches */
+ private class MyZooKeeper extends ZooKeeper {
+ class MyWatchManager extends ZKWatchManager {
+ public MyWatchManager(boolean disableAutoWatchReset) {
+ super(disableAutoWatchReset);
+ }
+
+ public int lastrc;
+
+ /* Pretend that any watcher exists */
+ void containsWatcher(String path, Watcher watcher,
+ WatcherType watcherType) throws NoWatcherException {
+ }
+
+ /* save the return error code by the server */
+ protected boolean removeWatches(
+ Map<String, Set<Watcher>> pathVsWatcher,
+ Watcher watcher, String path, boolean local, int rc,
+ Set<Watcher> removedWatchers) throws KeeperException {
+ lastrc = rc;
+ return false;
+ }
+ }
+
+ public MyZooKeeper(String hp, int timeout, Watcher watcher)
+ throws IOException {
+ super(hp, timeout, watcher, false);
+ }
+
+ private MyWatchManager myWatchManager;
+
+ protected ZKWatchManager defaultWatchManager() {
+ myWatchManager = new MyWatchManager(getClientConfig().getBoolean(ZKClientConfig.DISABLE_AUTO_WATCH_RESET));
+ return myWatchManager;
+ }
+
+ public int getRemoveWatchesRC() {
+ return myWatchManager.lastrc;
+ }
+ }
+
+ private class MyWatcher implements Watcher {
+ private final String path;
+ private String eventPath;
+ private CountDownLatch latch;
+ private List<EventType> eventsAfterWatchRemoval = new ArrayList<EventType>();
+ MyWatcher(String path, int count) {
+ this.path = path;
+ latch = new CountDownLatch(count);
+ }
+
+ public void process(WatchedEvent event) {
+ LOG.debug("Event path : {}, eventPath : {}"
+ + new Object[] { path, event.getPath() });
+ this.eventPath = event.getPath();
+ // notifies watcher removal
+ if (latch.getCount() == 0) {
+ if (event.getType() != EventType.None) {
+ eventsAfterWatchRemoval.add(event.getType());
+ }
+ }
+ if (event.getType() == EventType.ChildWatchRemoved
+ || event.getType() == EventType.DataWatchRemoved) {
+ latch.countDown();
+ }
+ }
+
+ /**
+ * Returns true if the watcher was triggered. Try to avoid using this
+ * method with assertFalse statements. A false return depends on a timed
+ * out wait on a latch, which makes tests run long.
+ *
+ * @return true if the watcher was triggered, false otherwise
+ * @throws InterruptedException if interrupted while waiting on latch
+ */
+ public boolean matches() throws InterruptedException {
+ if (!latch.await(CONNECTION_TIMEOUT/5, TimeUnit.MILLISECONDS)) {
+ LOG.error("Failed waiting to remove the watches");
+ return false;
+ }
+ LOG.debug("Client path : {} eventPath : {}", new Object[] { path,
+ eventPath });
+ return path.equals(eventPath);
+ }
+
+ public List<EventType> getEventsAfterWatchRemoval() {
+ return eventsAfterWatchRemoval;
+ }
+ }
+
+ private class MyCallback implements AsyncCallback.VoidCallback {
+ private final String path;
+ private final int rc;
+ private String eventPath;
+ int eventRc;
+ private CountDownLatch latch = new CountDownLatch(1);
+
+ public MyCallback(int rc, String path) {
+ this.rc = rc;
+ this.path = path;
+ }
+
+ @Override
+ public void processResult(int rc, String eventPath, Object ctx) {
+ System.out.println("latch:" + path + " " + eventPath);
+ this.eventPath = eventPath;
+ this.eventRc = rc;
+ this.latch.countDown();
+ }
+
+ /**
+ * Returns true if the callback was triggered. Try to avoid using this
+ * method with assertFalse statements. A false return depends on a timed
+ * out wait on a latch, which makes tests run long.
+ *
+ * @return true if the watcher was triggered, false otherwise
+ * @throws InterruptedException if interrupted while waiting on latch
+ */
+ public boolean matches() throws InterruptedException {
+ if (!latch.await(CONNECTION_TIMEOUT/5, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ return path.equals(eventPath) && rc == eventRc;
+ }
+ }
+
+ /**
+ * Checks if a session is registered with the server as a watcher.
+ *
+ * @param long sessionId the session ID to check
+ * @param path the path to check for watchers
+ * @param type the type of watcher
+ * @return true if the client session is a watcher on path for the type
+ */
+ private boolean isServerSessionWatcher(long sessionId, String path,
+ WatcherType type) {
+ Set<ServerCnxn> cnxns = new HashSet<>();
+ CollectionUtils.addAll(cnxns, serverFactory.getConnections().iterator());
+ for (ServerCnxn cnxn : cnxns) {
+ if (cnxn.getSessionId() == sessionId) {
+ return getServer(serverFactory).getZKDatabase().getDataTree()
+ .containsWatcher(path, type, cnxn);
+ }
+ }
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/cb9f303b/zookeeper-server/src/test/java/org/apache/zookeeper/SaslAuthTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/SaslAuthTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/SaslAuthTest.java
new file mode 100644
index 0000000..088fe1f
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/SaslAuthTest.java
@@ -0,0 +1,252 @@
+/**
+ * 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 static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.zookeeper.ClientCnxn.EventThread;
+import org.apache.zookeeper.ClientCnxn.SendThread;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.apache.zookeeper.test.ClientBase;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class SaslAuthTest extends ClientBase {
+ @BeforeClass
+ public static void init() {
+ System.setProperty("zookeeper.authProvider.1",
+ "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
+ try {
+ File tmpDir = createTmpDir();
+ File saslConfFile = new File(tmpDir, "jaas.conf");
+ String jaasContent = getJaasFileContent();
+ FileWriter fwriter = new FileWriter(saslConfFile);
+ fwriter.write(jaasContent);
+ fwriter.close();
+ System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath());
+ } catch (IOException e) {
+ // could not create tmp directory to hold JAAS conf file : test will
+ // fail now.
+ }
+ }
+
+ private static String getJaasFileContent() {
+ StringBuilder jaasContent=new StringBuilder();
+ String newLine = System.getProperty("line.separator");
+ jaasContent.append("Server {");
+ jaasContent.append(newLine);
+ jaasContent.append("org.apache.zookeeper.server.auth.DigestLoginModule required");
+ jaasContent.append(newLine);
+ jaasContent.append("user_super=\"test\";");
+ jaasContent.append(newLine);
+ jaasContent.append("};");
+ jaasContent.append(newLine);
+ jaasContent.append("Client {");
+ jaasContent.append(newLine);
+ jaasContent.append("org.apache.zookeeper.server.auth.DigestLoginModule required");
+ jaasContent.append(newLine);
+ jaasContent.append("username=\"super\"");
+ jaasContent.append(newLine);
+ jaasContent.append("password=\"test\";");
+ jaasContent.append(newLine);
+ jaasContent.append("};");
+ jaasContent.append(newLine);
+ return jaasContent.toString();
+ }
+
+ @AfterClass
+ public static void clean() {
+ System.clearProperty("zookeeper.authProvider.1");
+ System.clearProperty("java.security.auth.login.config");
+ }
+
+ private final CountDownLatch authFailed = new CountDownLatch(1);
+
+ @Override
+ protected TestableZooKeeper createClient(String hp)
+ throws IOException, InterruptedException
+ {
+ MyWatcher watcher = new MyWatcher();
+ return createClient(watcher, hp);
+ }
+
+ private class MyWatcher extends CountdownWatcher {
+ @Override
+ public synchronized void process(WatchedEvent event) {
+ if (event.getState() == KeeperState.AuthFailed) {
+ authFailed.countDown();
+ }
+ else {
+ super.process(event);
+ }
+ }
+ }
+
+ @Test
+ public void testAuth() throws Exception {
+ ZooKeeper zk = createClient();
+ try {
+ zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
+ Thread.sleep(1000);
+ } finally {
+ zk.close();
+ }
+ }
+
+ @Test
+ public void testValidSaslIds() throws Exception {
+ ZooKeeper zk = createClient();
+
+ List<String> validIds = new ArrayList<String>();
+ validIds.add("user");
+ validIds.add("service/host.name.com");
+ validIds.add("user@KERB.REALM");
+ validIds.add("service/host.name.com@KERB.REALM");
+
+ int i = 0;
+ for(String validId: validIds) {
+ List<ACL> aclList = new ArrayList<ACL>();
+ ACL acl = new ACL(0,new Id("sasl",validId));
+ aclList.add(acl);
+ zk.create("/valid"+i,null,aclList,CreateMode.PERSISTENT);
+ i++;
+ }
+ }
+
+ @Test
+ public void testInvalidSaslIds() throws Exception {
+ ZooKeeper zk = createClient();
+
+ List<String> invalidIds = new ArrayList<String>();
+ invalidIds.add("user@KERB.REALM/server.com");
+ invalidIds.add("user@KERB.REALM1@KERB.REALM2");
+
+ int i = 0;
+ for(String invalidId: invalidIds) {
+ List<ACL> aclList = new ArrayList<ACL>();
+ try {
+ ACL acl = new ACL(0,new Id("sasl",invalidId));
+ aclList.add(acl);
+ zk.create("/invalid"+i,null,aclList,CreateMode.PERSISTENT);
+ Assert.fail("SASLAuthenticationProvider.isValid() failed to catch invalid Id.");
+ }
+ catch (KeeperException.InvalidACLException e) {
+ // ok.
+ }
+ finally {
+ i++;
+ }
+ }
+ }
+
+ @Test
+ public void testZKOperationsAfterClientSaslAuthFailure() throws Exception {
+ CountdownWatcher watcher = new CountdownWatcher();
+ ZooKeeper zk = new ZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher);
+ watcher.waitForConnected(CONNECTION_TIMEOUT);
+ try {
+ setSaslFailureFlag(zk);
+
+ // try node creation for around 15 second,
+ int totalTry = 10;
+ int tryCount = 0;
+
+ boolean success = false;
+ while (!success && tryCount++ <= totalTry) {
+ try {
+ zk.create("/saslAuthFail", "data".getBytes(), Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT_SEQUENTIAL);
+ success = true;
+ } catch (KeeperException.ConnectionLossException e) {
+ Thread.sleep(1000);
+ // do nothing
+ }
+ }
+ assertTrue("ZNode creation is failing continuously after Sasl auth failure.", success);
+
+ } finally {
+ zk.close();
+ }
+ }
+
+ // set saslLoginFailed to true to simulate the LoginException
+ private void setSaslFailureFlag(ZooKeeper zk) throws Exception {
+ Field cnxnField = zk.getClass().getDeclaredField("cnxn");
+ cnxnField.setAccessible(true);
+ ClientCnxn clientCnxn = (ClientCnxn) cnxnField.get(zk);
+ Field sendThreadField = clientCnxn.getClass().getDeclaredField("sendThread");
+ sendThreadField.setAccessible(true);
+ SendThread sendThread = (SendThread) sendThreadField.get(clientCnxn);
+ Field saslLoginFailedField = sendThread.getClass().getDeclaredField("saslLoginFailed");
+ saslLoginFailedField.setAccessible(true);
+ saslLoginFailedField.setBoolean(sendThread, true);
+ }
+
+ @Test
+ public void testThreadsShutdownOnAuthFailed() throws Exception {
+ MyWatcher watcher = new MyWatcher();
+ ZooKeeper zk = null;
+ try {
+ zk = new ZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher);
+ watcher.waitForConnected(CONNECTION_TIMEOUT);
+ try {
+ zk.addAuthInfo("FOO", "BAR".getBytes());
+ zk.getData("/path1", false, null);
+ Assert.fail("Should get auth state error");
+ } catch (KeeperException.AuthFailedException e) {
+ if (!authFailed.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ Assert.fail("Should have called my watcher");
+ }
+ }
+ Field cnxnField = zk.getClass().getDeclaredField("cnxn");
+ cnxnField.setAccessible(true);
+ ClientCnxn clientCnxn = (ClientCnxn) cnxnField.get(zk);
+ Field sendThreadField = clientCnxn.getClass().getDeclaredField("sendThread");
+ sendThreadField.setAccessible(true);
+ SendThread sendThread = (SendThread) sendThreadField.get(clientCnxn);
+ Field eventThreadField = clientCnxn.getClass().getDeclaredField("eventThread");
+ eventThreadField.setAccessible(true);
+ EventThread eventThread = (EventThread) eventThreadField.get(clientCnxn);
+ sendThread.join(CONNECTION_TIMEOUT);
+ eventThread.join(CONNECTION_TIMEOUT);
+ Assert.assertFalse("SendThread did not shutdown after authFail", sendThread.isAlive());
+ Assert.assertFalse("EventThread did not shutdown after authFail",
+ eventThread.isAlive());
+ } finally {
+ if (zk != null) {
+ zk.close();
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/cb9f303b/zookeeper-server/src/test/java/org/apache/zookeeper/ServerConfigTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/ServerConfigTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/ServerConfigTest.java
new file mode 100644
index 0000000..27faa74
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/ServerConfigTest.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper;
+
+import org.apache.zookeeper.server.ServerConfig;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+public class ServerConfigTest {
+
+ private ServerConfig serverConfig;
+
+ @Before
+ public void setUp() {
+ serverConfig = new ServerConfig();
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testFewArguments() {
+ String[] args = {"2181"};
+ serverConfig.parse(args);
+ }
+
+ @Test
+ public void testValidArguments() {
+ String[] args = {"2181", "/data/dir", "60000", "10000"};
+ serverConfig.parse(args);
+
+ assertEquals(2181, serverConfig.getClientPortAddress().getPort());
+ assertTrue(checkEquality("/data/dir", serverConfig.getDataDir()));
+ assertEquals(60000, serverConfig.getTickTime());
+ assertEquals(10000, serverConfig.getMaxClientCnxns());
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testTooManyArguments() {
+ String[] args = {"2181", "/data/dir", "60000", "10000", "9999"};
+ serverConfig.parse(args);
+ }
+
+ boolean checkEquality(String a, String b) {
+ assertNotNull(a);
+ assertNotNull(b);
+ return a.equals(b);
+ }
+
+ boolean checkEquality(String a, File b) {
+ assertNotNull(a);
+ assertNotNull(b);
+ return new File(a).equals(b);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/cb9f303b/zookeeper-server/src/test/java/org/apache/zookeeper/TestableZooKeeper.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/TestableZooKeeper.java b/zookeeper-server/src/test/java/org/apache/zookeeper/TestableZooKeeper.java
new file mode 100644
index 0000000..bb1bd12
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/TestableZooKeeper.java
@@ -0,0 +1,128 @@
+/**
+ * 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.net.SocketAddress;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.jute.Record;
+import org.apache.zookeeper.admin.ZooKeeperAdmin;
+import org.apache.zookeeper.proto.ReplyHeader;
+import org.apache.zookeeper.proto.RequestHeader;
+
+public class TestableZooKeeper extends ZooKeeperAdmin {
+
+ public TestableZooKeeper(String host, int sessionTimeout,
+ Watcher watcher) throws IOException {
+ super(host, sessionTimeout, watcher);
+ }
+
+ @Override
+ public List<String> getChildWatches() {
+ return super.getChildWatches();
+ }
+
+
+ @Override
+ public List<String> getDataWatches() {
+ return super.getDataWatches();
+ }
+
+
+ @Override
+ public List<String> getExistWatches() {
+ return super.getExistWatches();
+ }
+
+ /**
+ * Cause this ZooKeeper object to disconnect from the server. It will then
+ * later attempt to reconnect.
+ */
+ public void testableConnloss() throws IOException {
+ synchronized(cnxn) {
+ cnxn.sendThread.testableCloseSocket();
+ }
+ }
+
+ /**
+ * Cause this ZooKeeper object to stop receiving from the ZooKeeperServer
+ * for the given number of milliseconds.
+ * @param ms the number of milliseconds to pause.
+ * @return true if the connection is paused, otherwise false
+ */
+ public boolean pauseCnxn(final long ms) {
+ final CountDownLatch initiatedPause = new CountDownLatch(1);
+ new Thread() {
+ public void run() {
+ synchronized(cnxn) {
+ try {
+ try {
+ cnxn.sendThread.testableCloseSocket();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ initiatedPause.countDown();
+ }
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }.start();
+
+ try {
+ return initiatedPause.await(ms, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public SocketAddress testableLocalSocketAddress() {
+ return super.testableLocalSocketAddress();
+ }
+
+ public SocketAddress testableRemoteSocketAddress() {
+ return super.testableRemoteSocketAddress();
+ }
+
+ /**
+ * @return the last zxid as seen by the client session
+ */
+ public long testableLastZxid() {
+ return cnxn.getLastZxid();
+ }
+
+ public ReplyHeader submitRequest(RequestHeader h, Record request,
+ Record response, WatchRegistration watchRegistration) throws InterruptedException {
+ return cnxn.submitRequest(h, request, response, watchRegistration);
+ }
+
+ /** Testing only!!! Really!!!! This is only here to test when the client
+ * disconnects from the server w/o sending a session disconnect (ie
+ * ending the session cleanly). The server will eventually notice the
+ * client is no longer pinging and will timeout the session.
+ */
+ public void disconnect() {
+ cnxn.disconnect();
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/cb9f303b/zookeeper-server/src/test/java/org/apache/zookeeper/VerGenTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/VerGenTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/VerGenTest.java
new file mode 100644
index 0000000..1d99e45
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/VerGenTest.java
@@ -0,0 +1,79 @@
+/**
+ * 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.File;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.zookeeper.test.ClientBase;
+import org.apache.zookeeper.version.util.VerGen;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+
+/**
+ * Test VerGen, used during the build.
+ *
+ */
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(ZKParameterized.RunnerFactory.class)
+public class VerGenTest extends ZKTestCase {
+ @Parameters
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {
+ {"1.2.3", new Object[] {1, 2, 3, null}},
+ {"1.2.3-dev", new Object[] {1, 2, 3, "dev"}},
+ {"1.2.3-SNAPSHOT", new Object[] {1, 2, 3, "SNAPSHOT"}},
+ {"1.2.3-SNAPSHOT", new Object[] {1, 2, 3, "SNAPSHOT"}},
+ {"1.2.3-foo-bar+123", new Object[] {1, 2, 3, "foo-bar+123"}},
+ {"1.2.3.4.5-SNAPSHOT", new Object[] {1, 2, 3, "SNAPSHOT"}},
+ {"1.2.3.4.5-foo-bar+123", new Object[] {1, 2, 3, "foo-bar+123"}}
+ });
+ }
+
+ private String input;
+
+ private Object[] expected;
+
+ public VerGenTest(String input, Object[] expected) {
+ this.input = input;
+ this.expected = expected;
+ }
+
+ @Test
+ public void testParser() {
+ VerGen.Version v = VerGen.parseVersionString(input);
+ Assert.assertEquals(expected[0], v.maj);
+ Assert.assertEquals(expected[1], v.min);
+ Assert.assertEquals(expected[2], v.micro);
+ Assert.assertEquals(expected[3], v.qualifier);
+ }
+
+ @Test
+ public void testGenFile() throws Exception {
+ VerGen.Version v = VerGen.parseVersionString(input);
+ File outputDir = ClientBase.createTmpDir();
+ VerGen.generateFile(outputDir, v, "1", "Nov1");
+ ClientBase.recursiveDelete(outputDir);
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/cb9f303b/zookeeper-server/src/test/java/org/apache/zookeeper/ZKParameterized.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/ZKParameterized.java b/zookeeper-server/src/test/java/org/apache/zookeeper/ZKParameterized.java
new file mode 100644
index 0000000..1a049fb
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/ZKParameterized.java
@@ -0,0 +1,58 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters;
+import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory;
+import org.junit.runners.parameterized.TestWithParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class ZKParameterized {
+ private static final Logger LOG = LoggerFactory.getLogger(ZKParameterized.class);
+ public static class RunnerFactory extends BlockJUnit4ClassRunnerWithParametersFactory {
+ @Override
+ public org.junit.runner.Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
+ return new ZKParameterized.Runner(test);
+ }
+ }
+
+ public static class Runner extends BlockJUnit4ClassRunnerWithParameters {
+ public Runner(TestWithParameters test) throws InitializationError {
+ super(test);
+ }
+
+
+ @Override
+ protected List<FrameworkMethod> computeTestMethods() {
+ return JUnit4ZKTestRunner.computeTestMethodsForClass(getTestClass().getJavaClass(), super.computeTestMethods());
+ }
+
+
+ @Override
+ protected Statement methodInvoker(FrameworkMethod method, Object test) {
+ return new JUnit4ZKTestRunner.LoggedInvokeMethod(method, test);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/cb9f303b/zookeeper-server/src/test/java/org/apache/zookeeper/ZKTestCase.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/ZKTestCase.java b/zookeeper-server/src/test/java/org/apache/zookeeper/ZKTestCase.java
new file mode 100644
index 0000000..54a6be5
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/ZKTestCase.java
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.junit.Rule;
+import org.junit.rules.MethodRule;
+import org.junit.rules.TestWatchman;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.FrameworkMethod;
+
+/**
+ * Base class for a non-parameterized ZK test.
+ *
+ * Basic utilities shared by all tests. Also logging of various events during
+ * the test execution (start/stop/success/failure/etc...)
+ */
+@SuppressWarnings("deprecation")
+@RunWith(JUnit4ZKTestRunner.class)
+public class ZKTestCase {
+ private static final Logger LOG = LoggerFactory.getLogger(ZKTestCase.class);
+
+ private String testName;
+
+ protected String getTestName() {
+ return testName;
+ }
+
+ @Rule
+ public MethodRule watchman = new TestWatchman() {
+ @Override
+ public void starting(FrameworkMethod method) {
+ // By default, disable starting a JettyAdminServer in tests to avoid
+ // accidentally attempting to start multiple admin servers on the
+ // same port.
+ System.setProperty("zookeeper.admin.enableServer", "false");
+ // ZOOKEEPER-2693 disables all 4lw by default.
+ // Here we enable the 4lw which ZooKeeper tests depends.
+ System.setProperty("zookeeper.4lw.commands.whitelist", "*");
+ testName = method.getName();
+ LOG.info("STARTING " + testName);
+ }
+
+ @Override
+ public void finished(FrameworkMethod method) {
+ LOG.info("FINISHED " + testName);
+ }
+
+ @Override
+ public void succeeded(FrameworkMethod method) {
+ LOG.info("SUCCEEDED " + testName);
+ }
+
+ @Override
+ public void failed(Throwable e, FrameworkMethod method) {
+ LOG.info("FAILED " + testName, e);
+ }
+
+ };
+
+}