From commits-return-7267-archive-asf-public=cust-asf.ponee.io@zookeeper.apache.org Wed Oct 24 11:31:03 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 7358E1807AA for ; Wed, 24 Oct 2018 11:30:59 +0200 (CEST) Received: (qmail 69697 invoked by uid 500); 24 Oct 2018 09:30:57 -0000 Mailing-List: contact commits-help@zookeeper.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@zookeeper.apache.org Delivered-To: mailing list commits@zookeeper.apache.org Received: (qmail 69143 invoked by uid 99); 24 Oct 2018 09:30:57 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 24 Oct 2018 09:30:57 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 5BECDE11C8; Wed, 24 Oct 2018 09:30:56 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: andor@apache.org To: commits@zookeeper.apache.org Date: Wed, 24 Oct 2018 09:31:07 -0000 Message-Id: <9154df58924046cb9cf220713e6dd0c3@git.apache.org> In-Reply-To: <25d338a3641d4672b9e83437148d061f@git.apache.org> References: <25d338a3641d4672b9e83437148d061f@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [12/51] [partial] zookeeper git commit: ZOOKEEPER-3032: MAVEN MIGRATION - branch-3.5 - zookeeper-server http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/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 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 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 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 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 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 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 wList = new ArrayList(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 wList = new ArrayList(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 wList = new ArrayList(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 wList = new ArrayList(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> pathVsWatcher, + Watcher watcher, String path, boolean local, int rc, + Set 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 eventsAfterWatchRemoval = new ArrayList(); + 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 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 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/43d71c2e/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 validIds = new ArrayList(); + 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 aclList = new ArrayList(); + 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 invalidIds = new ArrayList(); + invalidIds.add("user@KERB.REALM/server.com"); + invalidIds.add("user@KERB.REALM1@KERB.REALM2"); + + int i = 0; + for(String invalidId: invalidIds) { + List aclList = new ArrayList(); + 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/43d71c2e/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/43d71c2e/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 getChildWatches() { + return super.getChildWatches(); + } + + + @Override + public List getDataWatches() { + return super.getDataWatches(); + } + + + @Override + public List 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/43d71c2e/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 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/43d71c2e/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 + *

+ * 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.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 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/43d71c2e/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); + } + + }; + +}