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"));
+    }
 }

Reply via email to