public void add(TimeValue timeout, final TimeoutClusterStateListener listener) {
   if (lifecycle.stoppedOrClosed()) {
     listener.onClose();
     return;
   }
   NotifyTimeout notifyTimeout = new NotifyTimeout(listener, timeout);
   Timeout timerTimeout =
       timerService.newTimeout(notifyTimeout, timeout, TimerService.ExecutionType.THREADED);
   onGoingTimeouts.add(new Tuple<Timeout, NotifyTimeout>(timerTimeout, notifyTimeout));
   clusterStateListeners.add(listener);
   // call the post added notification on the same event thread
   updateTasksExecutor.execute(
       new Runnable() {
         @Override
         public void run() {
           listener.postAdded();
         }
       });
 }
 @Override
 protected ClusterHealthResponse masterOperation(ClusterHealthRequest request, ClusterState state)
     throws ElasticSearchException {
   int waitFor = 4;
   if (request.waitForStatus() == null) {
     waitFor--;
   }
   if (request.waitForRelocatingShards() == -1) {
     waitFor--;
   }
   if (request.waitForActiveShards() == -1) {
     waitFor--;
   }
   if (request.waitForNodes().isEmpty()) {
     waitFor--;
   }
   if (waitFor == 0) {
     // no need to wait for anything
     return clusterHealth(request);
   }
   long endTime = System.currentTimeMillis() + request.timeout().millis();
   while (true) {
     int waitForCounter = 0;
     ClusterHealthResponse response = clusterHealth(request);
     if (request.waitForStatus() != null
         && response.status().value() <= request.waitForStatus().value()) {
       waitForCounter++;
     }
     if (request.waitForRelocatingShards() != -1
         && response.relocatingShards() <= request.waitForRelocatingShards()) {
       waitForCounter++;
     }
     if (request.waitForActiveShards() != -1
         && response.activeShards() >= request.waitForActiveShards()) {
       waitForCounter++;
     }
     if (!request.waitForNodes().isEmpty()) {
       if (request.waitForNodes().startsWith(">=")) {
         int expected = Integer.parseInt(request.waitForNodes().substring(2));
         if (response.numberOfNodes() >= expected) {
           waitForCounter++;
         }
       } else if (request.waitForNodes().startsWith("ge(")) {
         int expected =
             Integer.parseInt(
                 request.waitForNodes().substring(3, request.waitForNodes().length() - 1));
         if (response.numberOfNodes() >= expected) {
           waitForCounter++;
         }
       } else if (request.waitForNodes().startsWith("<=")) {
         int expected = Integer.parseInt(request.waitForNodes().substring(2));
         if (response.numberOfNodes() <= expected) {
           waitForCounter++;
         }
       } else if (request.waitForNodes().startsWith("le(")) {
         int expected =
             Integer.parseInt(
                 request.waitForNodes().substring(3, request.waitForNodes().length() - 1));
         if (response.numberOfNodes() <= expected) {
           waitForCounter++;
         }
       } else if (request.waitForNodes().startsWith(">")) {
         int expected = Integer.parseInt(request.waitForNodes().substring(1));
         if (response.numberOfNodes() > expected) {
           waitForCounter++;
         }
       } else if (request.waitForNodes().startsWith("gt(")) {
         int expected =
             Integer.parseInt(
                 request.waitForNodes().substring(3, request.waitForNodes().length() - 1));
         if (response.numberOfNodes() > expected) {
           waitForCounter++;
         }
       } else if (request.waitForNodes().startsWith("<")) {
         int expected = Integer.parseInt(request.waitForNodes().substring(1));
         if (response.numberOfNodes() < expected) {
           waitForCounter++;
         }
       } else if (request.waitForNodes().startsWith("lt(")) {
         int expected =
             Integer.parseInt(
                 request.waitForNodes().substring(3, request.waitForNodes().length() - 1));
         if (response.numberOfNodes() < expected) {
           waitForCounter++;
         }
       } else {
         int expected = Integer.parseInt(request.waitForNodes());
         if (response.numberOfNodes() == expected) {
           waitForCounter++;
         }
       }
     }
     if (waitForCounter == waitFor) {
       return response;
     }
     if (timerService.estimatedTimeInMillis() > endTime) {
       response.timedOut = true;
       return response;
     }
     try {
       Thread.sleep(200);
     } catch (InterruptedException e) {
       response.timedOut = true;
       // we got interrupted, bail
       return response;
     }
   }
 }