protected final void onComplete(ZooKeeper zk, int rc, T ret, boolean errorIfNoNode) {
      tasks.add(new Task() {

        @Override
        public void exec(ZooKeeper alwaysNull) {
          pendingRequests--;
          Code code = Code.get(rc);
          if (code == Code.OK) {
            future.complete(ret);
          } else if (code == Code.NONODE) {
            if (errorIfNoNode) {
              future.completeExceptionally(KeeperException.create(code, path));
            } else {
              future.complete(ret);
            }
          } else if (FAIL_FAST_CODES.contains(code)) {
            future.completeExceptionally(KeeperException.create(code, path));
          } else {
            if (code == Code.SESSIONEXPIRED) {
              LOG.warn("{} to {} session expired, close and reconnect", getId(), connectString);
              try {
                zk.close();
              } catch (InterruptedException e) {
                // Restore interrupt status
                Thread.currentThread().interrupt();
              }
            }
            if (ZKTask.this.delay(retryIntervalMs, maxRetries)) {
              LOG.warn("{} to {} failed for {} of {}, code = {}, retries = {}", getId(),
                connectString, operationType, path, code, ZKTask.this.retries);
              tasks.add(ZKTask.this);
            } else {
              LOG.warn("{} to {} failed for {} of {}, code = {}, retries = {}, give up", getId(),
                connectString, operationType, path, code, ZKTask.this.retries);
              future.completeExceptionally(KeeperException.create(code, path));
            }
          }
        }

        @Override
        public void closed(IOException e) {
          // It may happen that a request is succeeded and the onComplete has been called and pushed
          // us into the task queue, but before we get called a close is called and here we will
          // fail the request, although it is succeeded actually.
          // This is not a perfect solution but anyway, it is better than hang the requests for
          // ever, and also acceptable as if you close the zk client before actually getting the
          // response then a failure is always possible.
          future.completeExceptionally(e);
        }
      });
    }
A very simple read only zookeeper implementation without watcher support.
hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ReadOnlyZKClient.java