public boolean add(T value) { try { // NOTE: we updating the currentView only so that this collection reflects the changes right away and hence its // behavior is more user-friendly Collection<T> current = currentView.get(); currentView.set(ImmutableList.<T>builder().addAll(current).add(value).build()); return ZKClientExt.createOrSet(zkClient, getItemNodePath(), serializer.serialize(value), CreateMode.PERSISTENT_SEQUENTIAL).get().getPath() != null; } catch (Exception e) { throw Throwables.propagate(e); } }
public void clear() { currentView.set(Collections.<T>emptyList()); // 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)); }
public ZKCollection(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<Collection<T>>(Collections.<T>emptyList()); setExternalChangeWatcher(); }
ZKCollection<String> collection1 = new ZKCollection<String>(zkClient, path, Serializers.stringSerializer()); ZKCollection<String> collection2 = new ZKCollection<String>(zkClient, path, Serializers.stringSerializer()); Assert.assertEquals(0, collection1.size()); Assert.assertEquals(0, collection2.size()); collection1.remove("foo"); Assert.assertEquals(0, collection1.size()); Assert.assertEquals(0, collection2.size()); collection1.add("value1"); collection2.add("value2"); collection1.add("value3"); Assert.assertTrue(collectionEventuallyEquals(collection1, "value1", "value2", "value3")); Assert.assertTrue(collectionEventuallyEquals(collection2, "value1", "value2", "value3")); collection1.remove("value2"); Assert.assertTrue(collectionEventuallyEquals(collection1, "value1", "value3")); Assert.assertTrue(collectionEventuallyEquals(collection2, "value1", "value3")); collection1.remove("value2"); Assert.assertTrue(collectionEventuallyEquals(collection1, "value1", "value3")); Assert.assertTrue(collectionEventuallyEquals(collection2, "value1", "value3")); collection2.add("value4"); Assert.assertTrue(collectionEventuallyEquals(collection1, "value1", "value3", "value4")); Assert.assertTrue(collectionEventuallyEquals(collection2, "value1", "value3", "value4")); collection1.add("value4"); Assert.assertTrue(collectionEventuallyEquals(collection1, "value1", "value3", "value4", "value4"));
@Override public boolean remove(Object o) { List<T> current = Lists.newArrayList(currentView.get()); boolean removed = current.remove(o); if (removed) { currentView.set(ImmutableList.<T>builder().addAll(current).build()); } // Hint: we can try to make removal more efficient if we keep map<nodeName->object> internally, or at least try to // remove only when in-mem collection removed smth, but then we may face races... NodeChildren children = Futures.getUnchecked(ZKClientExt.getChildrenOrNull(zkClient, "")); if (children == null) { return false; } List<String> nodes = children.getChildren(); for (String node : nodes) { byte[] data = Futures.getUnchecked(zkClient.getData(getNodePath(node))).getData(); if (o.equals(serializer.deserialize(data))) { return Futures.getUnchecked(ZKClientExt.delete(zkClient, getNodePath(node), true)) != null; } } return removed; }