/**
   * Return the stat of the node of the given path. Return null if no such a node exists.
   *
   * <p>If the watch is non-null and the call is successful (no exception is thrown), a watch will
   * be left on the node with the given path. The watch will be triggered by a successful operation
   * that creates/delete the node or sets the data on the node.
   *
   * @param path the node path
   * @param watcher explicit watcher
   * @return the stat of the node of the given path; return null if no such a node exists.
   * @throws KeeperException If the server signals an error
   * @throws InterruptedException If the server transaction is interrupted.
   * @throws IllegalArgumentException if an invalid path is specified
   */
  public Stat exists(final String path, Watcher watcher)
      throws KeeperException, InterruptedException {
    final String clientPath = path;
    PathUtils.validatePath(clientPath);

    // the watch contains the un-chroot path
    WatchRegistration wcb = null;
    if (watcher != null) {
      wcb = new ExistsWatchRegistration(watcher, clientPath);
    }

    final String serverPath = prependChroot(clientPath);

    RequestHeader h = new RequestHeader();
    h.setType(ZooDefs.OpCode.exists);
    ExistsRequest request = new ExistsRequest();
    request.setPath(serverPath);
    request.setWatch(watcher != null);
    SetDataResponse response = new SetDataResponse();
    ReplyHeader r = cnxn.submitRequest(h, request, response, wcb);
    if (r.getErr() != 0) {
      if (r.getErr() == KeeperException.Code.NONODE.intValue()) {
        return null;
      }
      throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath);
    }

    return response.getStat().getCzxid() == -1 ? null : response.getStat();
  }
  /**
   * Set the data for the node of the given path if such a node exists and the given version matches
   * the version of the node (if the given version is -1, it matches any node's versions). Return
   * the stat of the node.
   *
   * <p>This operation, if successful, will trigger all the watches on the node of the given path
   * left by getData calls.
   *
   * <p>A KeeperException with error code KeeperException.NoNode will be thrown if no node with the
   * given path exists.
   *
   * <p>A KeeperException with error code KeeperException.BadVersion will be thrown if the given
   * version does not match the node's version.
   *
   * <p>The maximum allowable size of the data array is 1 MB (1,048,576 bytes). Arrays larger than
   * this will cause a KeeperExecption to be thrown.
   *
   * @param path the path of the node
   * @param data the data to set
   * @param version the expected matching version
   * @return the state of the node
   * @throws InterruptedException If the server transaction is interrupted.
   * @throws KeeperException If the server signals an error with a non-zero error code.
   * @throws IllegalArgumentException if an invalid path is specified
   */
  public Stat setData(final String path, byte data[], int version)
      throws KeeperException, InterruptedException {
    final String clientPath = path;
    PathUtils.validatePath(clientPath);

    final String serverPath = prependChroot(clientPath);

    RequestHeader h = new RequestHeader();
    h.setType(ZooDefs.OpCode.setData);
    SetDataRequest request = new SetDataRequest();
    request.setPath(serverPath);
    request.setData(data);
    request.setVersion(version);
    SetDataResponse response = new SetDataResponse();
    ReplyHeader r = cnxn.submitRequest(h, request, response, null);
    if (r.getErr() != 0) {
      throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath);
    }
    return response.getStat();
  }