void internalBlockUntilConnectedOrTimedOut() throws InterruptedException {
    long waitTimeMs = connectionTimeoutMs;
    while (!state.isConnected() && (waitTimeMs > 0)) {
      final AtomicReference<Watcher> previousWatcher = new AtomicReference<Watcher>(null);
      final CountDownLatch latch = new CountDownLatch(1);
      Watcher tempWatcher =
          new Watcher() {
            @Override
            public void process(WatchedEvent event) {
              Watcher localPreviousWatcher = previousWatcher.get();
              if (localPreviousWatcher != null) {
                localPreviousWatcher.process(event);
              }
              latch.countDown();
            }
          };

      previousWatcher.set(state.substituteParentWatcher(tempWatcher));
      long startTimeMs = System.currentTimeMillis();
      try {
        latch.await(1, TimeUnit.SECONDS);
      } finally {
        state.substituteParentWatcher(previousWatcher.get());
      }
      long elapsed = Math.max(1, System.currentTimeMillis() - startTimeMs);
      waitTimeMs -= elapsed;
    }
  }
  /** Close the client */
  public void close() {
    log.debug("Closing");

    started.set(false);
    try {
      state.close();
    } catch (IOException e) {
      log.error("", e);
    }
  }
  /**
   * Must be called after construction
   *
   * @throws IOException errors
   */
  public void start() throws Exception {
    log.debug("Starting");

    if (!started.compareAndSet(false, true)) {
      IllegalStateException error = new IllegalStateException();
      log.error("Already started", error);
      throw error;
    }

    state.start();
  }
  /**
   * This method blocks until the connection to ZK succeeds. Use with caution. The block will
   * timeout after the connection timeout (as passed to the constructor) has elapsed
   *
   * @return true if the connection succeeded, false if not
   * @throws InterruptedException interrupted while waiting
   */
  public boolean blockUntilConnectedOrTimedOut() throws InterruptedException {
    Preconditions.checkArgument(started.get());

    log.debug("blockUntilConnectedOrTimedOut() start");
    TimeTrace trace = startTracer("blockUntilConnectedOrTimedOut");

    internalBlockUntilConnectedOrTimedOut();

    trace.commit();

    boolean localIsConnected = state.isConnected();
    log.debug("blockUntilConnectedOrTimedOut() end. isConnected: " + localIsConnected);

    return localIsConnected;
  }
 /**
  * Return the managed ZK instance.
  *
  * @return client the client
  * @throws Exception if the connection timeout has elapsed or an exception occurs in a background
  *     process
  */
 public ZooKeeper getZooKeeper() throws Exception {
   return state.getZooKeeper();
 }
 /**
  * Returns true if the client is current connected
  *
  * @return true/false
  */
 public boolean isConnected() {
   return state.isConnected();
 }