Repository: curator Updated Branches: refs/heads/persistent-watch 38c766310 -> 6cfd38c25
More testing and refactoring. Wasn't checking for deleted children after a refresh. Also, allow for different methods of comparing nodes for change. Project: http://git-wip-us.apache.org/repos/asf/curator/repo Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/6cfd38c2 Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/6cfd38c2 Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/6cfd38c2 Branch: refs/heads/persistent-watch Commit: 6cfd38c25391865503ba4cf35530f1794c777b91 Parents: 38c7663 Author: randgalt <randg...@apache.org> Authored: Sat Dec 31 17:57:18 2016 -0500 Committer: randgalt <randg...@apache.org> Committed: Sat Dec 31 17:57:18 2016 -0500 ---------------------------------------------------------------------- .../framework/recipes/watch/CacheFilters.java | 45 ++++- .../recipes/watch/CachedNodeComparator.java | 24 +++ .../recipes/watch/CachedNodeComparators.java | 91 +++++++++ .../recipes/watch/CuratorCacheBase.java | 5 +- .../recipes/watch/CuratorCacheBuilder.java | 13 +- .../recipes/watch/InternalCuratorCache.java | 41 +++- .../recipes/watch/InternalNodeCache.java | 2 +- .../recipes/watch/BaseTestTreeCache.java | 10 + .../framework/recipes/watch/TestTreeCache.java | 200 +++++++++++++++++-- 9 files changed, 397 insertions(+), 34 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/curator/blob/6cfd38c2/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheFilters.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheFilters.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheFilters.java index dec16b1..534829e 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheFilters.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheFilters.java @@ -24,7 +24,7 @@ public class CacheFilters private static final CacheFilter compressedStatAndData = new StandardCacheFilter(CacheAction.STAT_AND_COMPRESSED_DATA); private static final CacheFilter statOnly = new StandardCacheFilter(CacheAction.STAT_ONLY); private static final CacheFilter pathOnly = new StandardCacheFilter(CacheAction.PATH_ONLY); - private static final CacheFilter full = new CacheFilter() + private static final CacheFilter fullStatAndData = new CacheFilter() { @Override public CacheAction actionForPath(String mainPath, String checkPath) @@ -32,6 +32,30 @@ public class CacheFilters return CacheAction.STAT_AND_DATA; } }; + private static final CacheFilter fullCompressedStatAndData = new CacheFilter() + { + @Override + public CacheAction actionForPath(String mainPath, String checkPath) + { + return CacheAction.STAT_AND_COMPRESSED_DATA; + } + }; + private static final CacheFilter fullStatOnly = new CacheFilter() + { + @Override + public CacheAction actionForPath(String mainPath, String checkPath) + { + return CacheAction.STAT_ONLY; + } + }; + private static final CacheFilter fullPathOnly = new CacheFilter() + { + @Override + public CacheAction actionForPath(String mainPath, String checkPath) + { + return CacheAction.PATH_ONLY; + } + }; public static CacheFilter statAndData() { @@ -53,9 +77,24 @@ public class CacheFilters return pathOnly; } - public static CacheFilter full() + public static CacheFilter fullStatAndData() + { + return fullStatAndData; + } + + public static CacheFilter fullCompressedStatAndData() + { + return fullCompressedStatAndData; + } + + public static CacheFilter fullStatOnly() + { + return fullStatOnly; + } + + public static CacheFilter fullPathOnly() { - return full; + return fullPathOnly; } private CacheFilters() http://git-wip-us.apache.org/repos/asf/curator/blob/6cfd38c2/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CachedNodeComparator.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CachedNodeComparator.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CachedNodeComparator.java new file mode 100644 index 0000000..34d86ad --- /dev/null +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CachedNodeComparator.java @@ -0,0 +1,24 @@ +/** + * 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.curator.framework.recipes.watch; + +public interface CachedNodeComparator +{ + boolean isSame(CachedNode n1, CachedNode n2); +} http://git-wip-us.apache.org/repos/asf/curator/blob/6cfd38c2/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CachedNodeComparators.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CachedNodeComparators.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CachedNodeComparators.java new file mode 100644 index 0000000..b54d443 --- /dev/null +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CachedNodeComparators.java @@ -0,0 +1,91 @@ +/** + * 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.curator.framework.recipes.watch; + +import java.util.Arrays; + +public class CachedNodeComparators +{ + private static final CachedNodeComparator dataOnly = new CachedNodeComparator() + { + @Override + public boolean isSame(CachedNode n1, CachedNode n2) + { + return firstCheck(n1, n2) && Arrays.equals(n1.getData(), n2.getData()); + } + }; + + private static final CachedNodeComparator dataAndType = new CachedNodeComparator() + { + @Override + public boolean isSame(CachedNode n1, CachedNode n2) + { + return firstCheck(n1, n2) && Arrays.equals(n1.getData(), n2.getData()) && sameType(n1.getStat().getEphemeralOwner(), n2.getStat().getEphemeralOwner()); + } + }; + + private static final CachedNodeComparator deep = new CachedNodeComparator() + { + @Override + public boolean isSame(CachedNode n1, CachedNode n2) + { + return n1.equals(n2); + } + }; + + private static boolean sameType(long e1, long e2) + { + boolean e1Is = (e1 > 0); + boolean e2Is = (e2 > 0); + return e1Is == e2Is; + } + + private static boolean firstCheck(CachedNode n1, CachedNode n2) + { + if ( n1 == n2 ) + { + return true; + } + //noinspection RedundantIfStatement + if ( (n1 == null) || (n2 == null) ) + { + return false; + } + return true; + } + + public static CachedNodeComparator dataOnly() + { + return dataOnly; + } + + public static CachedNodeComparator deep() + { + return deep; + } + + public static CachedNodeComparator dataAndType() + { + return dataAndType; + } + + private CachedNodeComparators() + { + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/6cfd38c2/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBase.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBase.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBase.java index 1362679..4cc46e8 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBase.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBase.java @@ -42,7 +42,6 @@ abstract class CuratorCacheBase implements CuratorCache private final AtomicReference<CountDownLatch> initialRefreshLatch = new AtomicReference<>(new CountDownLatch(1)); private final boolean sendRefreshEvents; private final AtomicInteger refreshCount = new AtomicInteger(0); - private Function<String, String> function; protected boolean isStarted() { @@ -107,13 +106,13 @@ abstract class CuratorCacheBase implements CuratorCache @Override public Collection<String> childNamesAtPath(final String basePath) { - function = new Function<String, String>() + Function<String, String> function = new Function<String, String>() { @Override public String apply(String path) { ZKPaths.PathAndNode pathAndNode = ZKPaths.getPathAndNode(path); - return pathAndNode.getPath().equals(basePath) ? pathAndNode.getNode() : null; + return (pathAndNode.getPath().equals(basePath) && !path.equals(basePath)) ? pathAndNode.getNode() : null; // must special case "root" as parent of root is root } }; return Collections2.filter(Collections2.transform(paths(), function), Predicates.notNull()); http://git-wip-us.apache.org/repos/asf/curator/blob/6cfd38c2/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBuilder.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBuilder.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBuilder.java index 31633f0..fa26b07 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBuilder.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CuratorCacheBuilder.java @@ -35,6 +35,7 @@ public class CuratorCacheBuilder private boolean refreshOnStart = true; private CacheFilter cacheFilter = CacheFilters.statAndData(); private boolean sortChildren = true; + private CachedNodeComparator nodeComparator = CachedNodeComparators.dataAndType(); public static CuratorCacheBuilder builder(CuratorFramework client, String path) { @@ -47,9 +48,9 @@ public class CuratorCacheBuilder if ( singleNode ) { Preconditions.checkState(refreshFilter == null, "Single node caches do not use RefreshFilters"); - return new InternalNodeCache(client, path, cacheFilter, cacheBuilder.<String, CachedNode>build(), sendRefreshEvents, refreshOnStart); + return new InternalNodeCache(client, path, nodeComparator, cacheFilter, cacheBuilder.<String, CachedNode>build(), sendRefreshEvents, refreshOnStart); } - return new InternalCuratorCache(client, path, cacheFilter, refreshFilter, cacheBuilder.<String, CachedNode>build(), sendRefreshEvents, refreshOnStart, sortChildren); + return new InternalCuratorCache(client, path, nodeComparator, cacheFilter, refreshFilter, cacheBuilder.<String, CachedNode>build(), sendRefreshEvents, refreshOnStart, sortChildren); } public CuratorCacheBuilder forSingleNode() @@ -72,7 +73,7 @@ public class CuratorCacheBuilder { singleNode = false; refreshFilter = RefreshFilters.tree(); - cacheFilter = CacheFilters.full(); + cacheFilter = CacheFilters.fullStatAndData(); return this; } @@ -130,6 +131,12 @@ public class CuratorCacheBuilder return this; } + public CuratorCacheBuilder nodeComparator(CachedNodeComparator nodeComparator) + { + this.nodeComparator = Objects.requireNonNull(nodeComparator, "nodeComparator cannot be null"); + return this; + } + private CuratorCacheBuilder(CuratorFramework client, String path) { this.client = Objects.requireNonNull(client, "client cannot be null"); http://git-wip-us.apache.org/repos/asf/curator/blob/6cfd38c2/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalCuratorCache.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalCuratorCache.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalCuratorCache.java index 750545b..bff8b2a 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalCuratorCache.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalCuratorCache.java @@ -20,6 +20,7 @@ package org.apache.curator.framework.recipes.watch; import com.google.common.annotations.VisibleForTesting; import com.google.common.cache.Cache; +import com.google.common.collect.Sets; import com.google.common.util.concurrent.SettableFuture; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; @@ -27,8 +28,10 @@ import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; +import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -41,6 +44,7 @@ class InternalCuratorCache extends CuratorCacheBase implements Watcher private final PersistentWatcher watcher; private final CuratorFramework client; private final String basePath; + private final CachedNodeComparator nodeComparator; private final CacheFilter cacheFilter; private final RefreshFilter refreshFilter; private final boolean sortChildren; @@ -54,11 +58,12 @@ class InternalCuratorCache extends CuratorCacheBase implements Watcher } }; - InternalCuratorCache(CuratorFramework client, String path, CacheFilter cacheFilter, final RefreshFilter refreshFilter, Cache<String, CachedNode> cache, boolean sendRefreshEvents, final boolean refreshOnStart, boolean sortChildren) + InternalCuratorCache(CuratorFramework client, String path, CachedNodeComparator nodeComparator, CacheFilter cacheFilter, final RefreshFilter refreshFilter, Cache<String, CachedNode> cache, boolean sendRefreshEvents, final boolean refreshOnStart, boolean sortChildren) { super(cache, sendRefreshEvents); this.client = Objects.requireNonNull(client, "client cannot be null"); - basePath = Objects.requireNonNull(path, "path cannot be null"); + this.basePath = Objects.requireNonNull(path, "path cannot be null"); + this.nodeComparator = Objects.requireNonNull(nodeComparator, "nodeComparator cannot be null"); this.cacheFilter = Objects.requireNonNull(cacheFilter, "cacheFilter cannot be null"); this.refreshFilter = Objects.requireNonNull(refreshFilter, "primingFilter cannot be null"); this.sortChildren = sortChildren; @@ -102,11 +107,7 @@ class InternalCuratorCache extends CuratorCacheBase implements Watcher case NodeDeleted: { - CachedNode removed = cache.asMap().remove(event.getPath()); - if ( removed != null ) - { - notifyListeners(CacheEvent.NODE_DELETED, event.getPath(), removed); - } + remove(event.getPath()); break; } @@ -164,7 +165,7 @@ class InternalCuratorCache extends CuratorCacheBase implements Watcher { notifyListeners(CacheEvent.NODE_CREATED, path, newNode); } - else if ( !newNode.equals(oldNode) ) + else if ( !nodeComparator.isSame(newNode, oldNode) ) { notifyListeners(CacheEvent.NODE_CHANGED, path, newNode); } @@ -172,6 +173,7 @@ class InternalCuratorCache extends CuratorCacheBase implements Watcher else if ( event.getType() == CuratorEventType.CHILDREN ) { List<String> children = event.getChildren(); + checkDeletedChildren(path, children); if ( sortChildren ) { Collections.sort(children); @@ -182,6 +184,10 @@ class InternalCuratorCache extends CuratorCacheBase implements Watcher } } } + else if ( (event.getType() == CuratorEventType.CHILDREN) && (event.getResultCode() == KeeperException.Code.NONODE.intValue()) ) + { + checkDeletedChildren(path, Collections.<String>emptyList()); + } else { // TODO @@ -250,6 +256,16 @@ class InternalCuratorCache extends CuratorCacheBase implements Watcher } } + private void checkDeletedChildren(String path, List<String> children) + { + Collection<String> namesAtPath = childNamesAtPath(path); + Sets.SetView<String> deleted = Sets.difference(Sets.newHashSet(namesAtPath), Sets.newHashSet(children)); + for ( String deletedName : deleted ) + { + remove(ZKPaths.makePath(path, deletedName)); + } + } + private CachedNode putNewNode(String path, CacheAction cacheAction, CachedNode newNode) { CachedNode putNode; @@ -290,4 +306,13 @@ class InternalCuratorCache extends CuratorCacheBase implements Watcher task.set(true); } } + + private void remove(String path) + { + CachedNode removed = cache.asMap().remove(path); + if ( removed != null ) + { + notifyListeners(CacheEvent.NODE_DELETED, path, removed); + } + } } http://git-wip-us.apache.org/repos/asf/curator/blob/6cfd38c2/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalNodeCache.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalNodeCache.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalNodeCache.java index b4a7b16..0968e7a 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalNodeCache.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/InternalNodeCache.java @@ -94,7 +94,7 @@ class InternalNodeCache extends CuratorCacheBase } }; - InternalNodeCache(CuratorFramework client, String path, CacheFilter cacheFilter, Cache<String, CachedNode> cache, boolean sendRefreshEvents, boolean refreshOnStart) + InternalNodeCache(CuratorFramework client, String path, CachedNodeComparator nodeComparator, CacheFilter cacheFilter, Cache<String, CachedNode> cache, boolean sendRefreshEvents, boolean refreshOnStart) { super(cache, sendRefreshEvents); this.client = client.newWatcherRemoveCuratorFramework(); http://git-wip-us.apache.org/repos/asf/curator/blob/6cfd38c2/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/BaseTestTreeCache.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/BaseTestTreeCache.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/BaseTestTreeCache.java index ab2e999..c866a81 100644 --- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/BaseTestTreeCache.java +++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/BaseTestTreeCache.java @@ -18,6 +18,7 @@ */ package org.apache.curator.framework.recipes.watch; +import com.google.common.collect.Sets; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.TestCleanState; @@ -164,4 +165,13 @@ public class BaseTestTreeCache extends BaseClassForTests } return event; } + + void assertChildNodeNames(String path, String... names) + { + if ( names == null ) + { + names = new String[0]; + } + Assert.assertEquals(Sets.newHashSet(cache.childNamesAtPath(path)), Sets.newHashSet(names)); + } } http://git-wip-us.apache.org/repos/asf/curator/blob/6cfd38c2/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/TestTreeCache.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/TestTreeCache.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/TestTreeCache.java index b4c5765..fe3a681 100644 --- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/TestTreeCache.java +++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/TestTreeCache.java @@ -18,8 +18,8 @@ */ package org.apache.curator.framework.recipes.watch; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import org.apache.curator.test.KillServerSession; +import org.apache.zookeeper.CreateMode; import org.testng.Assert; import org.testng.annotations.Test; @@ -89,9 +89,9 @@ public class TestTreeCache extends BaseTestTreeCache assertEvent(CacheEvent.CACHE_REFRESHED); assertNoMoreEvents(); - Assert.assertEquals(Sets.newHashSet(cache.childNamesAtPath("/test")), Sets.newHashSet("1", "2", "3")); - Assert.assertEquals(Sets.newHashSet(cache.childNamesAtPath("/test/1")), Sets.newHashSet()); - Assert.assertEquals(Sets.newHashSet(cache.childNamesAtPath("/test/2")), Sets.newHashSet("sub")); + assertChildNodeNames("/test", "1", "2", "3"); + assertChildNodeNames("/test/1"); + assertChildNodeNames("/test/2", "sub"); Assert.assertNull(cache.get("/test/non_exist")); } @@ -136,9 +136,9 @@ public class TestTreeCache extends BaseTestTreeCache assertEvent(CacheEvent.CACHE_REFRESHED); assertNoMoreEvents(); - Assert.assertEquals(cache.childNamesAtPath("/test"), ImmutableSet.of()); + assertChildNodeNames("/test"); Assert.assertNull(cache.get("/test/1")); - Assert.assertEquals(cache.childNamesAtPath("/test/1").size(), 0); + assertChildNodeNames("/test/1"); Assert.assertNull(cache.get("/test/non_exist")); } @@ -160,12 +160,12 @@ public class TestTreeCache extends BaseTestTreeCache assertEvent(CacheEvent.CACHE_REFRESHED); assertNoMoreEvents(); - Assert.assertEquals(Sets.newHashSet(cache.childNamesAtPath("/test")), Sets.newHashSet("1", "2", "3")); - Assert.assertEquals(Sets.newHashSet(cache.childNamesAtPath("/test/1")), Sets.newHashSet()); - Assert.assertEquals(Sets.newHashSet(cache.childNamesAtPath("/test/2")), Sets.newHashSet()); + assertChildNodeNames("/test", "1", "2", "3"); + assertChildNodeNames("/test/1"); + assertChildNodeNames("/test/2"); Assert.assertNull(cache.get("/test/2/sub")); - Assert.assertEquals(cache.childNamesAtPath("/test/2/sub").size(), 0); - Assert.assertEquals(cache.childNamesAtPath("/test/non_exist").size(), 0); + assertChildNodeNames("/test/2/sub"); + assertChildNodeNames("/test/non_exist"); } @Test @@ -218,8 +218,8 @@ public class TestTreeCache extends BaseTestTreeCache assertNoMoreEvents(); Assert.assertTrue(cache.childNamesAtPath("/").contains("test")); - Assert.assertEquals(cache.childNamesAtPath("/test"), ImmutableSet.of("one")); - Assert.assertEquals(cache.childNamesAtPath("/test/one"), ImmutableSet.of()); + assertChildNodeNames("/test", "one"); + assertChildNodeNames("/test/one"); Assert.assertEquals(new String(cache.get("/test/one").getData()), "hey there"); } @@ -237,8 +237,176 @@ public class TestTreeCache extends BaseTestTreeCache assertNoMoreEvents(); Assert.assertTrue(cache.childNamesAtPath("/").contains("test")); - Assert.assertEquals(cache.childNamesAtPath("/test"), ImmutableSet.of()); + assertChildNodeNames("/test"); Assert.assertNull(cache.get("/test/one")); - Assert.assertEquals(cache.childNamesAtPath("/test/one").size(), 0); + assertChildNodeNames("/test/one"); + } + + @Test + public void testWithNamespace() throws Exception + { + client.create().forPath("/outer"); + client.create().forPath("/outer/foo"); + client.create().forPath("/outer/test"); + client.create().forPath("/outer/test/one", "hey there".getBytes()); + + cache = newTreeCacheWithListeners(client.usingNamespace("outer"), "/test"); + cache.start(); + assertEvent(CacheEvent.NODE_CREATED, "/test"); + assertEvent(CacheEvent.NODE_CREATED, "/test/one"); + assertEvent(CacheEvent.CACHE_REFRESHED); + assertNoMoreEvents(); + + assertChildNodeNames("/test", "one"); + assertChildNodeNames("/test/one"); + Assert.assertEquals(new String(cache.get("/test/one").getData()), "hey there"); + } + + @Test + public void testWithNamespaceAtRoot() throws Exception + { + client.create().forPath("/outer"); + client.create().forPath("/outer/foo"); + client.create().forPath("/outer/test"); + client.create().forPath("/outer/test/one", "hey there".getBytes()); + + cache = newTreeCacheWithListeners(client.usingNamespace("outer"), "/"); + cache.start(); + assertEvent(CacheEvent.NODE_CREATED, "/"); + assertEvent(CacheEvent.NODE_CREATED, "/foo"); + assertEvent(CacheEvent.NODE_CREATED, "/test"); + assertEvent(CacheEvent.NODE_CREATED, "/test/one"); + assertEvent(CacheEvent.CACHE_REFRESHED); + assertNoMoreEvents(); + assertChildNodeNames("/", "foo", "test"); + assertChildNodeNames("/foo"); + assertChildNodeNames("/test", "one"); + assertChildNodeNames("/test/one"); + Assert.assertEquals(new String(cache.get("/test/one").getData()), "hey there"); + } + + @Test + public void testSyncInitialPopulation() throws Exception + { + cache = newTreeCacheWithListeners(client, "/test"); + cache.start(); + assertEvent(CacheEvent.CACHE_REFRESHED); + + client.create().forPath("/test"); + client.create().forPath("/test/one", "hey there".getBytes()); + assertEvent(CacheEvent.NODE_CREATED, "/test"); + assertEvent(CacheEvent.NODE_CREATED, "/test/one"); + assertNoMoreEvents(); + } + + @Test + public void testChildrenInitialized() throws Exception + { + client.create().forPath("/test", "".getBytes()); + client.create().forPath("/test/1", "1".getBytes()); + client.create().forPath("/test/2", "2".getBytes()); + client.create().forPath("/test/3", "3".getBytes()); + + cache = newTreeCacheWithListeners(client, "/test"); + cache.start(); + assertEvent(CacheEvent.NODE_CREATED, "/test"); + assertEvent(CacheEvent.NODE_CREATED, "/test/1"); + assertEvent(CacheEvent.NODE_CREATED, "/test/2"); + assertEvent(CacheEvent.NODE_CREATED, "/test/3"); + assertEvent(CacheEvent.CACHE_REFRESHED); + assertNoMoreEvents(); + } + + @Test + public void testUpdateWhenNotCachingData() throws Exception + { + client.create().forPath("/test"); + + cache = buildWithListeners(CuratorCacheBuilder.builder(client, "/test").forTree().withCacheFilter(CacheFilters.fullStatOnly())); + cache.start(); + assertEvent(CacheEvent.NODE_CREATED, "/test"); + assertEvent(CacheEvent.CACHE_REFRESHED); + + client.create().forPath("/test/foo", "first".getBytes()); + assertEvent(CacheEvent.NODE_CREATED, "/test/foo"); + + client.setData().forPath("/test/foo", "something new".getBytes()); + assertEvent(CacheEvent.NODE_CHANGED, "/test/foo"); + assertNoMoreEvents(); + + Assert.assertNotNull(cache.get("/test/foo")); + // No byte data querying the tree because we're not caching data. + Assert.assertEquals(cache.get("/test/foo").getData().length, 0); + } + + @Test + public void testDeleteThenCreate() throws Exception + { + client.create().forPath("/test"); + client.create().forPath("/test/foo", "one".getBytes()); + + cache = newTreeCacheWithListeners(client, "/test"); + cache.start(); + assertEvent(CacheEvent.NODE_CREATED, "/test"); + assertEvent(CacheEvent.NODE_CREATED, "/test/foo"); + assertEvent(CacheEvent.CACHE_REFRESHED); + + client.delete().forPath("/test/foo"); + assertEvent(CacheEvent.NODE_DELETED, "/test/foo", "one".getBytes()); + client.create().forPath("/test/foo", "two".getBytes()); + assertEvent(CacheEvent.NODE_CREATED, "/test/foo"); + + client.delete().forPath("/test/foo"); + assertEvent(CacheEvent.NODE_DELETED, "/test/foo", "two".getBytes()); + client.create().forPath("/test/foo", "two".getBytes()); + assertEvent(CacheEvent.NODE_CREATED, "/test/foo"); + + assertNoMoreEvents(); + } + + @Test + public void testDeleteThenCreateRoot() throws Exception + { + client.create().forPath("/test"); + client.create().forPath("/test/foo", "one".getBytes()); + + cache = newTreeCacheWithListeners(client, "/test/foo"); + cache.start(); + assertEvent(CacheEvent.NODE_CREATED, "/test/foo"); + assertEvent(CacheEvent.CACHE_REFRESHED); + + client.delete().forPath("/test/foo"); + assertEvent(CacheEvent.NODE_DELETED, "/test/foo"); + client.create().forPath("/test/foo", "two".getBytes()); + assertEvent(CacheEvent.NODE_CREATED, "/test/foo"); + + client.delete().forPath("/test/foo"); + assertEvent(CacheEvent.NODE_DELETED, "/test/foo"); + client.create().forPath("/test/foo", "two".getBytes()); + assertEvent(CacheEvent.NODE_CREATED, "/test/foo"); + + assertNoMoreEvents(); + } + + @Test + public void testKilledSession() throws Exception + { + client.create().forPath("/test"); + + cache = newTreeCacheWithListeners(client, "/test"); + cache.start(); + assertEvent(CacheEvent.NODE_CREATED, "/test"); + assertEvent(CacheEvent.CACHE_REFRESHED); + + client.create().forPath("/test/foo", "foo".getBytes()); + assertEvent(CacheEvent.NODE_CREATED, "/test/foo"); + client.create().withMode(CreateMode.EPHEMERAL).forPath("/test/me", "data".getBytes()); + assertEvent(CacheEvent.NODE_CREATED, "/test/me"); + + KillServerSession.kill(client.getZookeeperClient().getZooKeeper(), server.getConnectString()); + assertEvent(CacheEvent.NODE_DELETED, "/test/me", "data".getBytes()); + assertEvent(CacheEvent.CACHE_REFRESHED); + + assertNoMoreEvents(); } }