private String getItemNodePath(String key) { return getNodePath(key); }
public ZKMap(ZKClient zkClient, String namespace, Serializer<T> serializer) throws ExecutionException, InterruptedException { this.zkClient = namespace == null ? zkClient : ZKClients.namespace(zkClient, namespace); this.serializer = serializer; this.currentView = new AtomicReference<Map<String, T>>(Collections.<String, T>emptyMap()); this.waitingForElements = Maps.newHashMap(); setExternalChangeWatcher(); }
ZKMap<String> map1 = new ZKMap<String>(zkClient, path, Serializers.stringSerializer()); ZKMap<String> map2 = new ZKMap<String>(zkClient, path, Serializers.stringSerializer()); Assert.assertEquals(0, map1.size()); Assert.assertEquals(0, map2.size()); map2.remove("foo"); Assert.assertEquals(0, map1.size()); Assert.assertEquals(0, map2.size()); map1.put("key1", "value1"); map2.put("key2", "value2"); map1.put("key3", "value3"); Assert.assertTrue(mapEventuallyEquals(map1, "key1", "value1", "key2", "value2", "key3", "value3")); Assert.assertTrue(mapEventuallyEquals(map2, "key1", "value1", "key2", "value2", "key3", "value3")); map1.put("key2", "value2_m"); Assert.assertTrue(mapEventuallyEquals(map1, "key1", "value1", "key2", "value2_m", "key3", "value3")); Assert.assertTrue(mapEventuallyEquals(map2, "key1", "value1", "key2", "value2_m", "key3", "value3")); map2.remove("key2"); Assert.assertTrue(mapEventuallyEquals(map1, "key1", "value1", "key3", "value3")); Assert.assertTrue(mapEventuallyEquals(map2, "key1", "value1", "key3", "value3")); Assert.assertNull(map1.get("key2")); Assert.assertNull(map2.get("key2")); map1.remove("key2"); Assert.assertTrue(mapEventuallyEquals(map1, "key1", "value1", "key3", "value3")); Assert.assertTrue(mapEventuallyEquals(map2, "key1", "value1", "key3", "value3")); Assert.assertNull(map1.get("key2"));
private boolean mapEquals(ZKMap<String> map, String... keyvals) throws ExecutionException, InterruptedException { if (map.size() != keyvals.length / 2) { return false; } for (int i = 0; i < keyvals.length; i += 2) { String key = keyvals[i]; String val = keyvals[i+1]; if (!val.equals(map.get(key))) { return false; } if (!val.equals(map.getOrWait(key).get())) { return false; } } return true; } }
@Override public T remove(Object key) { if (!(key instanceof String)) { throw new IllegalArgumentException("Expected key of type java.lang.String but was " + (key == null ? null : key.getClass())); } Map<String, T> current = Maps.newHashMap(currentView.get()); T removed = current.remove(key); currentView.set(ImmutableMap.<String, T>builder().putAll(current).build()); // note: we cannot only issue remove from zk if removed != null because even if removed == null this could mean // the element was removed (and for other race-condition reasons) Futures.getUnchecked(ZKClientExt.delete(zkClient, getItemNodePath((String) key), true)); return removed; }
public T put(String key, T value) { Map<String, T> current = Maps.newHashMap(currentView.get()); T result = current.put(key, value); currentView.set(ImmutableMap.<String, T>builder().putAll(current).build()); // Note: we do delete and add new node with new data VS createOrSet() to avoid attaching watchers to every node String itemNodePath = getItemNodePath(key); Futures.getUnchecked(ZKClientExt.delete(zkClient, itemNodePath, true)); Futures.getUnchecked(zkClient.create(itemNodePath, serializer.serialize(value), CreateMode.PERSISTENT)); return result; }
public void clear() { currentView.set(Collections.<String, T>emptyMap()); // Hint: again, we can try to make removal more efficient by cleaning only when in-mem collection cleaned smth, // but then we may face races... NodeChildren nodeChildren = Futures.getUnchecked(zkClient.getChildren("")); List<ListenableFuture<String>> deleteFutures = Lists.newArrayList(); for (String node : nodeChildren.getChildren()) { deleteFutures.add(ZKClientExt.delete(zkClient, getNodePath(node), true)); } Futures.getUnchecked(Futures.allAsList(deleteFutures)); }