[ https://issues.apache.org/jira/browse/CURATOR-33?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14072562#comment-14072562 ]
ASF GitHub Bot commented on CURATOR-33: --------------------------------------- Github user cammckenzie commented on a diff in the pull request: https://github.com/apache/curator/pull/17#discussion_r15323064 --- Diff: curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/TestTreeCache.java --- @@ -0,0 +1,421 @@ +/** + * 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.cache; + +import com.google.common.collect.ImmutableSortedSet; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.api.UnhandledErrorListener; +import org.apache.curator.retry.RetryOneTime; +import org.apache.curator.test.BaseClassForTests; +import org.apache.curator.test.KillSession; +import org.apache.curator.test.Timing; +import org.apache.curator.utils.CloseableUtils; +import org.apache.zookeeper.CreateMode; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +public class TestTreeCache extends BaseClassForTests +{ + private final Timing timing = new Timing(); + private CuratorFramework client; + private TreeCache cache; + private List<Throwable> exceptions; + private BlockingQueue<TreeCacheEvent> events; + private TreeCacheListener eventListener; + + /** + * A TreeCache that records exceptions. + */ + private TreeCache newTreeCache(String path, boolean cacheData) + { + return new TreeCache(client, path, cacheData) + { + @Override + protected void handleException(Throwable e) + { + exceptions.add(e); + } + }; + } + + @Override + @BeforeMethod + public void setup() throws Exception + { + super.setup(); + + exceptions = new ArrayList<Throwable>(); + events = new LinkedBlockingQueue<TreeCacheEvent>(); + eventListener = new TreeCacheListener() + { + @Override + public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception + { + events.add(event); + } + }; + + client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); + client.start(); + client.getUnhandledErrorListenable().addListener(new UnhandledErrorListener() + { + @Override + public void unhandledError(String message, Throwable e) + { + exceptions.add(e); + } + } + ); + cache = newTreeCache("/test", true); + cache.getListenable().addListener(eventListener); + } + + @Override + @AfterMethod + public void teardown() throws Exception + { + try + { + try + { + for ( Throwable exception : exceptions ) + { + Assert.fail("Exception was thrown", exception); + } + } + finally + { + CloseableUtils.closeQuietly(cache); + CloseableUtils.closeQuietly(client); + } + } + finally + { + super.teardown(); + } + } + + @Test + public void testStartup() throws Exception + { + client.create().forPath("/test"); + client.create().forPath("/test/1", "one".getBytes()); + client.create().forPath("/test/2", "two".getBytes()); + client.create().forPath("/test/3", "three".getBytes()); + client.create().forPath("/test/2/sub", "two-sub".getBytes()); + + cache.start(); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/1", "one".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/2", "two".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/3", "three".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/2/sub", "two-sub".getBytes()); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + assertNoMoreEvents(); + + Assert.assertEquals(cache.getCurrentChildren("/test"), ImmutableSortedSet.of("1", "2", "3")); + Assert.assertEquals(cache.getCurrentChildren("/test/1"), Collections.emptySet()); + Assert.assertEquals(cache.getCurrentChildren("/test/2"), ImmutableSortedSet.of("sub")); + Assert.assertNull(cache.getCurrentChildren("/test/non_exist")); + } + + @Test + public void testStartEmpty() throws Exception + { + cache.start(); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + + client.create().forPath("/test"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertNoMoreEvents(); + } + + @Test + public void testAsyncInitialPopulation() throws Exception + { + client.create().forPath("/test"); + client.create().forPath("/test/one", "hey there".getBytes()); + cache.start(); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + assertNoMoreEvents(); + } + + @Test + public void testSyncInitialPopulation() throws Exception + { + cache.start(); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + client.create().forPath("/test"); + client.create().forPath("/test/one", "hey there".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/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.start(); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/1"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/2"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/3"); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + assertNoMoreEvents(); + } + + @Test + public void testUpdateWhenNotCachingData() throws Exception + { + cache = newTreeCache("/test", false); + cache.getListenable().addListener(eventListener); + + client.create().forPath("/test"); + cache.start(); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + + client.create().forPath("/test/foo", "first".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); + + client.setData().forPath("/test/foo", "something new".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_UPDATED, "/test/foo"); + assertNoMoreEvents(); + } + + @Test + public void testDeleteThenCreate() throws Exception + { + client.create().forPath("/test"); + client.create().forPath("/test/foo", "one".getBytes()); + cache.start(); + + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + + client.delete().forPath("/test/foo"); + assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/foo"); + client.create().forPath("/test/foo", "two".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); + + assertNoMoreEvents(); + } + + // see https://github.com/Netflix/curator/issues/27 - was caused by not comparing old->new data + @Test + public void testIssue27() throws Exception + { + client.create().forPath("/test"); + client.create().forPath("/test/a"); + client.create().forPath("/test/b"); + client.create().forPath("/test/c"); + + client.getChildren().forPath("/test"); + + cache.start(); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/a"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/b"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/c"); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + + client.delete().forPath("/test/a"); + client.create().forPath("/test/a"); + assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/a"); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/a"); + + assertNoMoreEvents(); + } + + @Test + public void testKilledSession() throws Exception + { + client.create().forPath("/test"); + cache.start(); + + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + + client.create().forPath("/test/foo", "foo".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); + client.create().withMode(CreateMode.EPHEMERAL).forPath("/test/me", "data".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/me"); + + KillSession.kill(client.getZookeeperClient().getZooKeeper(), server.getConnectString()); + assertEvent(TreeCacheEvent.Type.CONNECTION_SUSPENDED); + assertEvent(TreeCacheEvent.Type.CONNECTION_LOST); + assertEvent(TreeCacheEvent.Type.CONNECTION_RECONNECTED); + assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/me"); + + assertNoMoreEvents(); + } + + @Test + public void testBasics() throws Exception + { + client.create().forPath("/test"); + cache.start(); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); + assertEvent(TreeCacheEvent.Type.INITIALIZED); + Assert.assertEquals(cache.getCurrentChildren("/test"), Collections.emptySortedSet()); + + client.create().forPath("/test/one", "hey there".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); + Assert.assertEquals(cache.getCurrentChildren("/test"), ImmutableSortedSet.of("one")); + Assert.assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there"); + + client.setData().forPath("/test/one", "sup!".getBytes()); + assertEvent(TreeCacheEvent.Type.NODE_UPDATED, "/test/one"); + Assert.assertEquals(cache.getCurrentChildren("/test"), ImmutableSortedSet.of("one")); + Assert.assertEquals(new String(cache.getCurrentData("/test/one").getData()), "sup!"); + + client.delete().forPath("/test/one"); + assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/one"); + Assert.assertEquals(cache.getCurrentChildren("/test"), Collections.emptySortedSet()); --- End diff -- Collections.emptySortedSet() is a new addition to Java 1.8. Curator is still building against Java 1.6. > Recursive Node Cache > -------------------- > > Key: CURATOR-33 > URL: https://issues.apache.org/jira/browse/CURATOR-33 > Project: Apache Curator > Issue Type: Improvement > Components: Recipes > Reporter: John Vines > Fix For: awaiting-response > > Attachments: CURATOR-33.2.patch, CURATOR-33.patch > > > Currently the PathChildrenCache will trigger listen events for all children > at the given node. However, it would be useful to have a cache that would > trigger listen events for the entire hierarchy below the given node. -- This message was sent by Atlassian JIRA (v6.2#6252)