@Override
  public boolean verify() throws Exception {
    ClusterAccessor clusterAccessor = new ClusterAccessor(ClusterId.from(_clusterName), _accessor);
    Cluster cluster = clusterAccessor.readCluster();

    List<ParticipantId> liveInstances = new ArrayList<ParticipantId>();
    liveInstances.addAll(cluster.getLiveParticipantMap().keySet());
    boolean success = verifyLiveNodes(liveInstances);
    if (!success) {
      LOG.info(
          "liveNodes not match, expect: " + _expectSortedLiveNodes + ", actual: " + liveInstances);
      return false;
    }

    BestPossibleStateOutput bestPossbileStates = calculateBestPossibleState(cluster);
    Map<String, ExternalView> externalViews =
        _accessor.getChildValuesMap(_keyBuilder.externalViews());

    // TODO all ideal-states should be included in external-views

    for (String resourceName : externalViews.keySet()) {
      ExternalView externalView = externalViews.get(resourceName);
      ResourceAssignment assignment =
          bestPossbileStates.getResourceAssignment(ResourceId.from(resourceName));
      final Map<PartitionId, Map<String, String>> bestPossibleState = Maps.newHashMap();
      for (PartitionId partitionId : assignment.getMappedPartitionIds()) {
        Map<String, String> rawStateMap =
            ResourceAssignment.stringMapFromReplicaMap(assignment.getReplicaMap(partitionId));
        bestPossibleState.put(partitionId, rawStateMap);
      }
      success = verifyExternalView(externalView, bestPossibleState);
      if (!success) {
        LOG.info("external-view for resource: " + resourceName + " not match");
        return false;
      }
    }

    return true;
  }
  /**
   * TODO: This code is duplicate in multiple places. Can we do it in to one place in the beginning
   * and compute the stateConstraint instance once and re use at other places. Each IdealState must
   * have a constraint object associated with it
   *
   * @param stateModelDefinition
   * @param rebalancerConfig if rebalancerConfig == null, we can't evaluate R thus no constraints
   * @param cluster
   * @return
   */
  private Map<State, Bounds> computeStateConstraints(
      StateModelDefinition stateModelDefinition, IdealState idealState, Cluster cluster) {
    Map<State, Bounds> stateConstraints = new HashMap<State, Bounds>();

    List<State> statePriorityList = stateModelDefinition.getTypedStatesPriorityList();
    for (State state : statePriorityList) {
      String numInstancesPerState = stateModelDefinition.getNumParticipantsPerState(state);
      int max = -1;
      if ("N".equals(numInstancesPerState)) {
        max = cluster.getLiveParticipantMap().size();
      } else if ("R".equals(numInstancesPerState)) {
        // idealState is null when resource has been dropped,
        // R can't be evaluated and ignore state constraints
        if (idealState != null) {
          String replicas = idealState.getReplicas();
          if (replicas.equals(StateModelToken.ANY_LIVEINSTANCE.toString())) {
            max = cluster.getLiveParticipantMap().size();
          } else {
            max = Integer.parseInt(replicas);
          }
        }
      } else {
        try {
          max = Integer.parseInt(numInstancesPerState);
        } catch (Exception e) {
          // use -1
        }
      }

      if (max > -1) {
        // if state has no constraint, will not put in map
        stateConstraints.put(state, new Bounds(0, max));
      }
    }

    return stateConstraints;
  }
  @Override
  public void process(ClusterEvent event) throws Exception {
    Cluster cluster = event.getAttribute("Cluster");
    Map<StateModelDefId, StateModelDefinition> stateModelDefMap = cluster.getStateModelMap();
    Map<ResourceId, ResourceConfig> resourceMap =
        event.getAttribute(AttributeName.RESOURCES.toString());
    ResourceCurrentState currentStateOutput =
        event.getAttribute(AttributeName.CURRENT_STATE.toString());
    BestPossibleStateOutput bestPossibleStateOutput =
        event.getAttribute(AttributeName.BEST_POSSIBLE_STATE.toString());
    MessageOutput messageGenOutput = event.getAttribute(AttributeName.MESSAGES_ALL.toString());
    if (cluster == null
        || resourceMap == null
        || currentStateOutput == null
        || messageGenOutput == null
        || bestPossibleStateOutput == null) {
      throw new StageException(
          "Missing attributes in event:"
              + event
              + ". Requires Cluster|RESOURCES|CURRENT_STATE|BEST_POSSIBLE_STATE|MESSAGES_ALL");
    }

    MessageOutput output = new MessageOutput();

    for (ResourceId resourceId : resourceMap.keySet()) {
      ResourceConfig resource = resourceMap.get(resourceId);
      StateModelDefinition stateModelDef =
          stateModelDefMap.get(resource.getIdealState().getStateModelDefId());

      if (stateModelDef == null) {
        LOG.info(
            "resource: "
                + resourceId
                + " doesn't have state-model-def; e.g. we add a resource config but not add the resource in ideal-states");
        continue;
      }

      // TODO have a logical model for transition
      Map<String, Integer> stateTransitionPriorities = getStateTransitionPriorityMap(stateModelDef);
      Resource configResource = cluster.getResource(resourceId);

      // if configResource == null, the resource has been dropped
      Map<State, Bounds> stateConstraints =
          computeStateConstraints(
              stateModelDef,
              configResource == null ? null : configResource.getIdealState(),
              cluster);

      // TODO fix it
      for (PartitionId partitionId :
          bestPossibleStateOutput.getResourceAssignment(resourceId).getMappedPartitionIds()) {
        List<Message> messages = messageGenOutput.getMessages(resourceId, partitionId);
        List<Message> selectedMessages =
            selectMessages(
                cluster.getLiveParticipantMap(),
                currentStateOutput.getCurrentStateMap(resourceId, partitionId),
                currentStateOutput.getPendingStateMap(resourceId, partitionId),
                messages,
                stateConstraints,
                stateTransitionPriorities,
                stateModelDef.getTypedInitialState());
        output.setMessages(resourceId, partitionId, selectedMessages);
      }
    }
    event.addAttribute(AttributeName.MESSAGES_SELECTED.toString(), output);
  }