|
@@ -53,12 +53,14 @@ import org.apache.zookeeper.client.ZooKeeperSaslClient;
|
|
|
import org.apache.zookeeper.common.PathUtils;
|
|
|
import org.apache.zookeeper.data.ACL;
|
|
|
import org.apache.zookeeper.data.Stat;
|
|
|
+import org.apache.zookeeper.proto.AddWatchRequest;
|
|
|
import org.apache.zookeeper.proto.CheckWatchesRequest;
|
|
|
import org.apache.zookeeper.proto.Create2Response;
|
|
|
import org.apache.zookeeper.proto.CreateRequest;
|
|
|
import org.apache.zookeeper.proto.CreateResponse;
|
|
|
import org.apache.zookeeper.proto.CreateTTLRequest;
|
|
|
import org.apache.zookeeper.proto.DeleteRequest;
|
|
|
+import org.apache.zookeeper.proto.ErrorResponse;
|
|
|
import org.apache.zookeeper.proto.ExistsRequest;
|
|
|
import org.apache.zookeeper.proto.GetACLRequest;
|
|
|
import org.apache.zookeeper.proto.GetACLResponse;
|
|
@@ -83,6 +85,7 @@ import org.apache.zookeeper.proto.SyncRequest;
|
|
|
import org.apache.zookeeper.proto.SyncResponse;
|
|
|
import org.apache.zookeeper.server.DataTree;
|
|
|
import org.apache.zookeeper.server.EphemeralType;
|
|
|
+import org.apache.zookeeper.server.watch.PathParentIterator;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
@@ -254,6 +257,18 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
return rc;
|
|
|
}
|
|
|
}
|
|
|
+ protected List<String> getPersistentWatches() {
|
|
|
+ synchronized (watchManager.persistentWatches) {
|
|
|
+ List<String> rc = new ArrayList<String>(watchManager.persistentWatches.keySet());
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ protected List<String> getPersistentRecursiveWatches() {
|
|
|
+ synchronized (watchManager.persistentRecursiveWatches) {
|
|
|
+ List<String> rc = new ArrayList<String>(watchManager.persistentRecursiveWatches.keySet());
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* Manage watchers and handle events generated by the ClientCnxn object.
|
|
@@ -267,6 +282,8 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
private final Map<String, Set<Watcher>> dataWatches = new HashMap<String, Set<Watcher>>();
|
|
|
private final Map<String, Set<Watcher>> existWatches = new HashMap<String, Set<Watcher>>();
|
|
|
private final Map<String, Set<Watcher>> childWatches = new HashMap<String, Set<Watcher>>();
|
|
|
+ private final Map<String, Set<Watcher>> persistentWatches = new HashMap<String, Set<Watcher>>();
|
|
|
+ private final Map<String, Set<Watcher>> persistentRecursiveWatches = new HashMap<String, Set<Watcher>>();
|
|
|
private boolean disableAutoWatchReset;
|
|
|
|
|
|
ZKWatchManager(boolean disableAutoWatchReset) {
|
|
@@ -296,6 +313,8 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
removedWatchers.put(EventType.ChildWatchRemoved, childWatchersToRem);
|
|
|
HashSet<Watcher> dataWatchersToRem = new HashSet<>();
|
|
|
removedWatchers.put(EventType.DataWatchRemoved, dataWatchersToRem);
|
|
|
+ HashSet<Watcher> persistentWatchersToRem = new HashSet<>();
|
|
|
+ removedWatchers.put(EventType.PersistentWatchRemoved, persistentWatchersToRem);
|
|
|
boolean removedWatcher = false;
|
|
|
switch (watcherType) {
|
|
|
case Children: {
|
|
@@ -324,10 +343,23 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
boolean removedDataWatcher = removeWatches(dataWatches, watcher, clientPath, local, rc, dataWatchersToRem);
|
|
|
removedWatcher |= removedDataWatcher;
|
|
|
}
|
|
|
+
|
|
|
synchronized (existWatches) {
|
|
|
boolean removedDataWatcher = removeWatches(existWatches, watcher, clientPath, local, rc, dataWatchersToRem);
|
|
|
removedWatcher |= removedDataWatcher;
|
|
|
}
|
|
|
+
|
|
|
+ synchronized (persistentWatches) {
|
|
|
+ boolean removedPersistentWatcher = removeWatches(persistentWatches,
|
|
|
+ watcher, clientPath, local, rc, persistentWatchersToRem);
|
|
|
+ removedWatcher |= removedPersistentWatcher;
|
|
|
+ }
|
|
|
+
|
|
|
+ synchronized (persistentRecursiveWatches) {
|
|
|
+ boolean removedPersistentRecursiveWatcher = removeWatches(persistentRecursiveWatches,
|
|
|
+ watcher, clientPath, local, rc, persistentWatchersToRem);
|
|
|
+ removedWatcher |= removedPersistentRecursiveWatcher;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
// Watcher function doesn't exists for the specified params
|
|
@@ -373,6 +405,18 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
synchronized (childWatches) {
|
|
|
containsWatcher = contains(path, watcher, childWatches);
|
|
|
}
|
|
|
+
|
|
|
+ synchronized (persistentWatches) {
|
|
|
+ boolean contains_temp = contains(path, watcher,
|
|
|
+ persistentWatches);
|
|
|
+ containsWatcher |= contains_temp;
|
|
|
+ }
|
|
|
+
|
|
|
+ synchronized (persistentRecursiveWatches) {
|
|
|
+ boolean contains_temp = contains(path, watcher,
|
|
|
+ persistentRecursiveWatches);
|
|
|
+ containsWatcher |= contains_temp;
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
case Data: {
|
|
@@ -384,6 +428,18 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
boolean contains_temp = contains(path, watcher, existWatches);
|
|
|
containsWatcher |= contains_temp;
|
|
|
}
|
|
|
+
|
|
|
+ synchronized (persistentWatches) {
|
|
|
+ boolean contains_temp = contains(path, watcher,
|
|
|
+ persistentWatches);
|
|
|
+ containsWatcher |= contains_temp;
|
|
|
+ }
|
|
|
+
|
|
|
+ synchronized (persistentRecursiveWatches) {
|
|
|
+ boolean contains_temp = contains(path, watcher,
|
|
|
+ persistentRecursiveWatches);
|
|
|
+ containsWatcher |= contains_temp;
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
case Any: {
|
|
@@ -395,10 +451,23 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
boolean contains_temp = contains(path, watcher, dataWatches);
|
|
|
containsWatcher |= contains_temp;
|
|
|
}
|
|
|
+
|
|
|
synchronized (existWatches) {
|
|
|
boolean contains_temp = contains(path, watcher, existWatches);
|
|
|
containsWatcher |= contains_temp;
|
|
|
}
|
|
|
+
|
|
|
+ synchronized (persistentWatches) {
|
|
|
+ boolean contains_temp = contains(path, watcher,
|
|
|
+ persistentWatches);
|
|
|
+ containsWatcher |= contains_temp;
|
|
|
+ }
|
|
|
+
|
|
|
+ synchronized (persistentRecursiveWatches) {
|
|
|
+ boolean contains_temp = contains(path, watcher,
|
|
|
+ persistentRecursiveWatches);
|
|
|
+ containsWatcher |= contains_temp;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
// Watcher function doesn't exists for the specified params
|
|
@@ -490,6 +559,18 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ synchronized (persistentWatches) {
|
|
|
+ for (Set<Watcher> ws: persistentWatches.values()) {
|
|
|
+ result.addAll(ws);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ synchronized (persistentRecursiveWatches) {
|
|
|
+ for (Set<Watcher> ws: persistentRecursiveWatches.values()) {
|
|
|
+ result.addAll(ws);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return result;
|
|
|
case NodeDataChanged:
|
|
|
case NodeCreated:
|
|
@@ -499,11 +580,13 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
synchronized (existWatches) {
|
|
|
addTo(existWatches.remove(clientPath), result);
|
|
|
}
|
|
|
+ addPersistentWatches(clientPath, result);
|
|
|
break;
|
|
|
case NodeChildrenChanged:
|
|
|
synchronized (childWatches) {
|
|
|
addTo(childWatches.remove(clientPath), result);
|
|
|
}
|
|
|
+ addPersistentWatches(clientPath, result);
|
|
|
break;
|
|
|
case NodeDeleted:
|
|
|
synchronized (dataWatches) {
|
|
@@ -520,6 +603,7 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
synchronized (childWatches) {
|
|
|
addTo(childWatches.remove(clientPath), result);
|
|
|
}
|
|
|
+ addPersistentWatches(clientPath, result);
|
|
|
break;
|
|
|
default:
|
|
|
String errorMsg = String.format(
|
|
@@ -534,6 +618,16 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ private void addPersistentWatches(String clientPath, Set<Watcher> result) {
|
|
|
+ synchronized (persistentWatches) {
|
|
|
+ addTo(persistentWatches.get(clientPath), result);
|
|
|
+ }
|
|
|
+ synchronized (persistentRecursiveWatches) {
|
|
|
+ for (String path : PathParentIterator.forAll(clientPath).asIterable()) {
|
|
|
+ addTo(persistentRecursiveWatches.get(path), result);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -627,6 +721,31 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ class AddWatchRegistration extends WatchRegistration {
|
|
|
+ private final AddWatchMode mode;
|
|
|
+
|
|
|
+ public AddWatchRegistration(Watcher watcher, String clientPath, AddWatchMode mode) {
|
|
|
+ super(watcher, clientPath);
|
|
|
+ this.mode = mode;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected Map<String, Set<Watcher>> getWatches(int rc) {
|
|
|
+ switch (mode) {
|
|
|
+ case PERSISTENT:
|
|
|
+ return watchManager.persistentWatches;
|
|
|
+ case PERSISTENT_RECURSIVE:
|
|
|
+ return watchManager.persistentRecursiveWatches;
|
|
|
+ }
|
|
|
+ throw new IllegalArgumentException("Mode not supported: " + mode);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected boolean shouldAddWatch(int rc) {
|
|
|
+ return rc == 0 || rc == KeeperException.Code.NONODE.intValue();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@InterfaceAudience.Public
|
|
|
public enum States {
|
|
|
CONNECTING,
|
|
@@ -3035,6 +3154,91 @@ public class ZooKeeper implements AutoCloseable {
|
|
|
removeWatches(ZooDefs.OpCode.removeWatches, path, null, watcherType, local, cb, ctx);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Add a watch to the given znode using the given mode. Note: not all
|
|
|
+ * watch types can be set with this method. Only the modes available
|
|
|
+ * in {@link AddWatchMode} can be set with this method.
|
|
|
+ *
|
|
|
+ * @param basePath the path that the watcher applies to
|
|
|
+ * @param watcher the watcher
|
|
|
+ * @param mode type of watcher to add
|
|
|
+ * @throws InterruptedException If the server transaction is interrupted.
|
|
|
+ * @throws KeeperException If the server signals an error with a non-zero
|
|
|
+ * error code.
|
|
|
+ * @since 3.6.0
|
|
|
+ */
|
|
|
+ public void addWatch(String basePath, Watcher watcher, AddWatchMode mode)
|
|
|
+ throws KeeperException, InterruptedException {
|
|
|
+ PathUtils.validatePath(basePath);
|
|
|
+ String serverPath = prependChroot(basePath);
|
|
|
+
|
|
|
+ RequestHeader h = new RequestHeader();
|
|
|
+ h.setType(ZooDefs.OpCode.addWatch);
|
|
|
+ AddWatchRequest request = new AddWatchRequest(serverPath, mode.getMode());
|
|
|
+ ReplyHeader r = cnxn.submitRequest(h, request, new ErrorResponse(),
|
|
|
+ new AddWatchRegistration(watcher, basePath, mode));
|
|
|
+ if (r.getErr() != 0) {
|
|
|
+ throw KeeperException.create(KeeperException.Code.get(r.getErr()),
|
|
|
+ basePath);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Add a watch to the given znode using the given mode. Note: not all
|
|
|
+ * watch types can be set with this method. Only the modes available
|
|
|
+ * in {@link AddWatchMode} can be set with this method. In this version of the method,
|
|
|
+ * the default watcher is used
|
|
|
+ *
|
|
|
+ * @param basePath the path that the watcher applies to
|
|
|
+ * @param mode type of watcher to add
|
|
|
+ * @throws InterruptedException If the server transaction is interrupted.
|
|
|
+ * @throws KeeperException If the server signals an error with a non-zero
|
|
|
+ * error code.
|
|
|
+ * @since 3.6.0
|
|
|
+ */
|
|
|
+ public void addWatch(String basePath, AddWatchMode mode)
|
|
|
+ throws KeeperException, InterruptedException {
|
|
|
+ addWatch(basePath, watchManager.defaultWatcher, mode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Async version of {@link #addWatch(String, Watcher, AddWatchMode)} (see it for details)
|
|
|
+ *
|
|
|
+ * @param basePath the path that the watcher applies to
|
|
|
+ * @param watcher the watcher
|
|
|
+ * @param mode type of watcher to add
|
|
|
+ * @param cb a handler for the callback
|
|
|
+ * @param ctx context to be provided to the callback
|
|
|
+ * @throws IllegalArgumentException if an invalid path is specified
|
|
|
+ * @since 3.6.0
|
|
|
+ */
|
|
|
+ public void addWatch(String basePath, Watcher watcher, AddWatchMode mode,
|
|
|
+ VoidCallback cb, Object ctx) {
|
|
|
+ PathUtils.validatePath(basePath);
|
|
|
+ String serverPath = prependChroot(basePath);
|
|
|
+
|
|
|
+ RequestHeader h = new RequestHeader();
|
|
|
+ h.setType(ZooDefs.OpCode.addWatch);
|
|
|
+ AddWatchRequest request = new AddWatchRequest(serverPath, mode.getMode());
|
|
|
+ cnxn.queuePacket(h, new ReplyHeader(), request, new ErrorResponse(), cb,
|
|
|
+ basePath, serverPath, ctx, new AddWatchRegistration(watcher, basePath, mode));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Async version of {@link #addWatch(String, AddWatchMode)} (see it for details)
|
|
|
+ *
|
|
|
+ * @param basePath the path that the watcher applies to
|
|
|
+ * @param mode type of watcher to add
|
|
|
+ * @param cb a handler for the callback
|
|
|
+ * @param ctx context to be provided to the callback
|
|
|
+ * @throws IllegalArgumentException if an invalid path is specified
|
|
|
+ * @since 3.6.0
|
|
|
+ */
|
|
|
+ public void addWatch(String basePath, AddWatchMode mode,
|
|
|
+ VoidCallback cb, Object ctx) {
|
|
|
+ addWatch(basePath, watchManager.defaultWatcher, mode, cb, ctx);
|
|
|
+ }
|
|
|
+
|
|
|
private void validateWatcher(Watcher watcher) {
|
|
|
if (watcher == null) {
|
|
|
throw new IllegalArgumentException("Invalid Watcher, shouldn't be null!");
|