package com.liveramp.hank.zookeeper; import java.util.Collections; import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; import org.junit.Test; import com.liveramp.hank.test.ZkTestCase; import com.liveramp.hank.util.Condition; import com.liveramp.hank.util.WaitUntil; import com.liveramp.hank.zookeeper.WatchedMap.CompletionAwaiter; import com.liveramp.hank.zookeeper.WatchedMap.CompletionDetector; import com.liveramp.hank.zookeeper.WatchedMap.ElementLoader; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class TestWatchedMap extends ZkTestCase { private static final class StringElementLoader implements ElementLoader<String> { @Override public String load(ZooKeeperPlus zk, String basePath, String relPath) { try { return new String(zk.getData(ZkPath.append(basePath, relPath), false, new Stat())); } catch (Exception e) { throw new RuntimeException(e); } } } @Test public void testIt() throws Exception { final ZooKeeperPlus zk = getZk(); final String colRoot = ZkPath.append(getRoot(), "collection"); zk.create(colRoot, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); final ElementLoader<String> elementLoader = new ElementLoader<String>() { @Override public String load(ZooKeeperPlus zk, String basePath, String relPath) { try { return new String(zk.getData(ZkPath.append(basePath, relPath), false, new Stat())); } catch (Exception e) { throw new RuntimeException(e); } } }; final WatchedMap<String> c1 = new WatchedMap<String>(zk, colRoot, elementLoader); dumpZk(); WaitUntil.orDie(new Condition() { @Override public boolean test() { return 0 == c1.size(); } }); assertEquals(0, c1.size()); zk.create(ZkPath.append(colRoot, "first"), "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); WaitUntil.orDie(new Condition() { @Override public boolean test() { return 1 == c1.size(); } }); assertEquals(1, c1.size()); } @Test public void testCompletionDetector() throws Exception { final ElementLoader<String> elementLoader = new StringElementLoader(); final AtomicBoolean b = new AtomicBoolean(false); CompletionDetector completionDetector = new CompletionDetector() { @Override public void detectCompletion(ZooKeeperPlus zk, String basePath, final String relPath, final CompletionAwaiter awaiter) throws KeeperException, InterruptedException { b.set(true); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } awaiter.completed(relPath); } }).start(); } }; final WatchedMap<String> m = new WatchedMap<String>(getZk(), getRoot(), elementLoader, completionDetector); // no elements yet, so should be empty assertEquals(0, m.size()); // create an element getZk().create(ZkPath.append(getRoot(), "node"), "blah".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // wait for notification to propagate WaitUntil.orDie(new Condition() { @Override public boolean test() { return b.get(); } }); // the detector should have been invoked... assertTrue(b.get()); // ...but it still shouldn't have come through to the actual map, since // there's a delay in the completion detector. assertEquals(0, m.size()); // after waiting a bit, the completion detector should notify the awaiter WaitUntil.orDie(new Condition() { @Override public boolean test() { return 1 == m.size(); } }); assertEquals(1, m.size()); } @Test public void testDeletion() throws Exception { getZk().create(ZkPath.append(getRoot(), "map"), null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); getZk().create(ZkPath.append(getRoot(), "map/1"), "2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); final WatchedMap<String> m = new WatchedMap<String>(getZk(), ZkPath.append(getRoot(), "map"), new StringElementLoader()); assertEquals(new HashMap<String, String>() {{ put("1", "2"); }}, m); getZk().delete(ZkPath.append(getRoot(), "map/1"), 0); WaitUntil.orDie(new Condition() { @Override public boolean test() { return Collections.EMPTY_MAP.equals(m); } }); assertEquals(Collections.EMPTY_MAP, m); } }