Repository: curator Updated Branches: refs/heads/persistent-watch 2148b6d1a -> 02de71b19
Added composite cache selector Project: http://git-wip-us.apache.org/repos/asf/curator/repo Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/02de71b1 Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/02de71b1 Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/02de71b1 Branch: refs/heads/persistent-watch Commit: 02de71b1906f2a4988a90411e9f81842cdc6091e Parents: 2148b6d Author: randgalt <randg...@apache.org> Authored: Mon Jan 2 21:36:27 2017 -0500 Committer: randgalt <randg...@apache.org> Committed: Mon Jan 2 21:36:27 2017 -0500 ---------------------------------------------------------------------- .../framework/recipes/watch/CacheSelectors.java | 107 +++++++++++++++++++ .../recipes/watch/BaseTestTreeCache.java | 2 +- .../framework/recipes/watch/TestTreeCache.java | 50 +++++++++ 3 files changed, 158 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/curator/blob/02de71b1/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheSelectors.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheSelectors.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheSelectors.java index ab22f7a..db1a8a8 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheSelectors.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/CacheSelectors.java @@ -20,6 +20,13 @@ package org.apache.curator.framework.recipes.watch; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.utils.ZKPaths; +import org.apache.zookeeper.common.PathUtils; +import org.apache.zookeeper.server.PathIterator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; /** * Pre-built caching selectors @@ -31,6 +38,20 @@ public class CacheSelectors private static final CacheSelector statOnly = new StandardCacheSelector(CacheAction.STAT_ONLY); private static final CacheSelector pathOnly = new StandardCacheSelector(CacheAction.PATH_ONLY); + private static class CompositeEntry + { + final String path; + final CacheSelector selector; + final int length; + + private CompositeEntry(String path, CacheSelector selector, int length) + { + this.path = path; + this.selector = selector; + this.length = length; + } + } + private static class StandardCacheSelector implements CacheSelector { private final CacheAction cacheAction; @@ -211,6 +232,92 @@ public class CacheSelectors return new MaxDepthCacheSelector(maxDepth, cacheAction); } + /** + * <p> + * Creates a composite cache selector that applies different selectors for different parent + * paths. The given selectorMap maps a parent path to a cache selector. + * </p> + * + * <p> + * e.g. a map such as: <code>/a/b -> selectorA, /a/c -> selectorB</code> will apply "selectorA" + * to paths that are children of "/a/b", "a/b/x", "a/b/x/y", etc. and "selectorB" to paths that are children of + * "a/c", etc. + * </p> + * + * <p> + * If no matches are found in the map, {@link #statAndData()} is used. NOTE: the longest + * paths (counted by segments) take precedence. i.e. the mapping of "/a/b/c" will match + * before "/a/b". + * </p> + * + * @param selectorMap the selector map + * @return the composite + */ + public static CacheSelector composite(Map<String, CacheSelector> selectorMap) + { + return composite(selectorMap, statAndData); + } + + /** + * Same as {@link #composite(Map)} but uses the given default selector instead of {@link #statAndData()}. + * + * @param selectorMap the selector map + * @param defaultSelector default selector if no match is found in the map + * @return the composite + */ + public static CacheSelector composite(Map<String, CacheSelector> selectorMap, final CacheSelector defaultSelector) + { + final List<CompositeEntry> entries = new ArrayList<>(); + for ( Map.Entry<String, CacheSelector> entry : selectorMap.entrySet() ) + { + String path = entry.getKey(); + PathUtils.validatePath(path); + CacheSelector selector = entry.getValue(); + int length = path.split("/").length; + entries.add(new CompositeEntry(path, selector, length)); + } + Collections.sort(entries, new Comparator<CompositeEntry>() + { + @Override + public int compare(CompositeEntry e1, CompositeEntry e2) + { + return e2.length - e1.length; // reverse length order + } + }); + + return new CacheSelector() + { + @Override + public boolean traverseChildren(String basePath, String fullPath) + { + return getSelector(fullPath).traverseChildren(basePath, fullPath); + } + + @Override + public CacheAction actionForPath(String basePath, String fullPath) + { + return getSelector(fullPath).actionForPath(basePath, fullPath); + } + + private CacheSelector getSelector(String fullPath) + { + String parent = ZKPaths.getPathAndNode(fullPath).getPath(); + for ( CompositeEntry entry : entries ) + { + PathIterator pathIterator = new PathIterator(fullPath); + while ( pathIterator.hasNext() ) + { + if ( pathIterator.next().equals(entry.path) ) + { + return entry.selector; + } + } + } + return defaultSelector; + } + }; + } + private CacheSelectors() { } http://git-wip-us.apache.org/repos/asf/curator/blob/02de71b1/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 95610f4..12ed7fa 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 @@ -38,9 +38,9 @@ public class BaseTestTreeCache extends BaseClassForTests { protected CuratorFramework client; protected CuratorCache cache; + protected final Timing timing = new Timing(); private final AtomicBoolean hadBackgroundException = new AtomicBoolean(false); private final BlockingQueue<EventAndNode> events = new LinkedBlockingQueue<>(); - private final Timing timing = new Timing(); /** * Automatically records all events into an easily testable event stream. http://git-wip-us.apache.org/repos/asf/curator/blob/02de71b1/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 a04fb67..e82538d 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 @@ -23,6 +23,8 @@ import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.CreateMode; import org.testng.Assert; import org.testng.annotations.Test; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.Semaphore; public class TestTreeCache extends BaseTestTreeCache @@ -543,4 +545,52 @@ public class TestTreeCache extends BaseTestTreeCache assertEvent(CacheEvent.NODE_CREATED, "/test"); assertNoMoreEvents(); } + + @Test + public void testCompositeSelector() throws Exception + { + client.create().creatingParentsIfNeeded().forPath("/root/a1/b1/c1"); + client.create().creatingParentsIfNeeded().forPath("/root/a1/b1/c2"); + client.create().creatingParentsIfNeeded().forPath("/root/a1/b2/c1"); + + client.create().creatingParentsIfNeeded().forPath("/root/a2/b1/c1"); + client.create().creatingParentsIfNeeded().forPath("/root/a2/b1/c2"); + client.create().creatingParentsIfNeeded().forPath("/root/a2/b2/c1"); + + client.create().creatingParentsIfNeeded().forPath("/root/a3/b1/c1"); + client.create().creatingParentsIfNeeded().forPath("/root/a3/b1/c2"); + client.create().creatingParentsIfNeeded().forPath("/root/a3/b2/c1"); + + CacheSelector depth0 = CacheSelectors.maxDepth(0); + CacheSelector depth1 = CacheSelectors.maxDepth(1); + CacheSelector standard = CacheSelectors.statAndData(); + Map<String, CacheSelector> map = new HashMap<>(); + map.put("/root/a1", depth0); + map.put("/root/a2", depth1); + map.put("/root/a3", standard); + map.put("/root/a3/b2", depth0); + CacheSelector composite = CacheSelectors.composite(map); + cache = CuratorCacheBuilder.builder(client, "/root").withCacheSelector(composite).build(); + Assert.assertTrue(timing.awaitLatch(cache.start())); + + Assert.assertTrue(cache.exists("/root")); + + Assert.assertFalse(cache.exists("/root/a1")); + Assert.assertFalse(cache.exists("/root/a1/b1")); + Assert.assertFalse(cache.exists("/root/a1/b2")); + + Assert.assertTrue(cache.exists("/root/a2")); + Assert.assertFalse(cache.exists("/root/a2/b1")); + Assert.assertFalse(cache.exists("/root/a2/b2")); + Assert.assertFalse(cache.exists("/root/a2/b1/c1")); + Assert.assertFalse(cache.exists("/root/a2/b1/c2")); + Assert.assertFalse(cache.exists("/root/a2/b2/c1")); + + Assert.assertTrue(cache.exists("/root/a3")); + Assert.assertTrue(cache.exists("/root/a3/b1")); + Assert.assertFalse(cache.exists("/root/a3/b2")); + Assert.assertTrue(cache.exists("/root/a3/b1/c1")); + Assert.assertTrue(cache.exists("/root/a3/b1/c2")); + Assert.assertFalse(cache.exists("/root/a3/b2/c1")); + } }