Package org.apache.helix.api.config

Source Code of org.apache.helix.api.config.ClusterConfig$Builder

package org.apache.helix.api.config;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.helix.alerts.AlertsHolder;
import org.apache.helix.alerts.StatsHolder;
import org.apache.helix.api.Scope;
import org.apache.helix.api.State;
import org.apache.helix.api.id.ClusterId;
import org.apache.helix.api.id.ConstraintId;
import org.apache.helix.api.id.ParticipantId;
import org.apache.helix.api.id.ResourceId;
import org.apache.helix.api.id.StateModelDefId;
import org.apache.helix.model.Alerts;
import org.apache.helix.model.ClusterConstraints;
import org.apache.helix.model.ClusterConstraints.ConstraintAttribute;
import org.apache.helix.model.ClusterConstraints.ConstraintType;
import org.apache.helix.model.ClusterConstraints.ConstraintValue;
import org.apache.helix.model.ConstraintItem;
import org.apache.helix.model.Message.MessageType;
import org.apache.helix.model.PersistentStats;
import org.apache.helix.model.StateModelDefinition;
import org.apache.helix.model.Transition;
import org.apache.helix.model.builder.ConstraintItemBuilder;
import org.apache.log4j.Logger;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/**
* Configuration properties of a cluster
*/
public class ClusterConfig {
  private static final Logger LOG = Logger.getLogger(ClusterConfig.class);

  private final ClusterId _id;
  private final Map<ResourceId, ResourceConfig> _resourceMap;
  private final Map<ParticipantId, ParticipantConfig> _participantMap;
  private final Map<ConstraintType, ClusterConstraints> _constraintMap;
  private final Map<StateModelDefId, StateModelDefinition> _stateModelMap;
  private final PersistentStats _stats;
  private final Alerts _alerts;
  private final UserConfig _userConfig;
  private final boolean _isPaused;
  private final boolean _autoJoin;

  /**
   * Initialize a cluster configuration. Also see ClusterConfig.Builder
   * @param id cluster id
   * @param resourceMap map of resource id to resource config
   * @param participantMap map of participant id to participant config
   * @param constraintMap map of constraint type to all constraints of that type
   * @param stateModelMap map of state model id to state model definition
   * @param stats statistics to watch on the cluster
   * @param alerts alerts that the cluster can trigger
   * @param userConfig user-defined cluster properties
   * @param isPaused true if paused, false if active
   * @param allowAutoJoin true if participants can join automatically, false otherwise
   */
  private ClusterConfig(ClusterId id, Map<ResourceId, ResourceConfig> resourceMap,
      Map<ParticipantId, ParticipantConfig> participantMap,
      Map<ConstraintType, ClusterConstraints> constraintMap,
      Map<StateModelDefId, StateModelDefinition> stateModelMap, PersistentStats stats,
      Alerts alerts, UserConfig userConfig, boolean isPaused, boolean allowAutoJoin) {
    _id = id;
    _resourceMap = ImmutableMap.copyOf(resourceMap);
    _participantMap = ImmutableMap.copyOf(participantMap);
    _constraintMap = ImmutableMap.copyOf(constraintMap);
    _stateModelMap = ImmutableMap.copyOf(stateModelMap);
    _stats = stats;
    _alerts = alerts;
    _userConfig = userConfig;
    _isPaused = isPaused;
    _autoJoin = allowAutoJoin;
  }

  /**
   * Get cluster id
   * @return cluster id
   */
  public ClusterId getId() {
    return _id;
  }

  /**
   * Get resources in the cluster
   * @return a map of resource id to resource, or empty map if none
   */
  public Map<ResourceId, ResourceConfig> getResourceMap() {
    return _resourceMap;
  }

  /**
   * Get all the constraints on the cluster
   * @return map of constraint type to constraints
   */
  public Map<ConstraintType, ClusterConstraints> getConstraintMap() {
    return _constraintMap;
  }

  /**
   * Get the maximum number of participants that can be in a state
   * @param scope the scope for the bound
   * @param stateModelDefId the state model of the state
   * @param state the constrained state
   * @return The upper bound, which can be "-1" if unspecified, a numerical upper bound, "R" for
   *         number of replicas, or "N" for number of participants
   */
  public String getStateUpperBoundConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
      State state) {
    // set up attributes to match based on the scope
    ClusterConstraints stateConstraints = getConstraintMap().get(ConstraintType.STATE_CONSTRAINT);
    Map<ConstraintAttribute, String> matchAttributes = Maps.newHashMap();
    matchAttributes.put(ConstraintAttribute.STATE, state.toString());
    matchAttributes.put(ConstraintAttribute.STATE_MODEL, stateModelDefId.toString());
    switch (scope.getType()) {
    case CLUSTER:
      // cluster is implicit
      break;
    case RESOURCE:
      matchAttributes.put(ConstraintAttribute.RESOURCE, scope.getScopedId().stringify());
      break;
    default:
      LOG.error("Unsupported scope for state constraint: " + scope);
      return "-1";
    }
    Set<ConstraintItem> matches = stateConstraints.match(matchAttributes);
    int value = -1;
    for (ConstraintItem item : matches) {
      // match: if an R or N is found, always choose that one
      // otherwise, take the minimum of the counts specified in the constraints
      String constraintValue = item.getConstraintValue();
      if (constraintValue != null) {
        if (constraintValue.equals(ConstraintValue.N.toString())
            || constraintValue.equals(ConstraintValue.R.toString())) {
          return constraintValue;
        } else {
          try {
            int current = Integer.parseInt(constraintValue);
            if (value == -1 || current < value) {
              value = current;
            }
          } catch (NumberFormatException e) {
            LOG.error("Invalid state upper bound: " + constraintValue);
          }
        }
      }
    }
    return Integer.toString(value);
  }

  /**
   * Get the limit of simultaneous execution of a transition
   * @param scope the scope under which the transition is constrained
   * @param stateModelDefId the state model of which the transition is a part
   * @param transition the constrained transition
   * @return the limit, or Integer.MAX_VALUE if there is no limit
   */
  public int getTransitionConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
      Transition transition) {
    // set up attributes to match based on the scope
    ClusterConstraints transitionConstraints =
        getConstraintMap().get(ConstraintType.MESSAGE_CONSTRAINT);
    Map<ConstraintAttribute, String> matchAttributes = Maps.newHashMap();
    matchAttributes.put(ConstraintAttribute.STATE_MODEL, stateModelDefId.toString());
    matchAttributes.put(ConstraintAttribute.MESSAGE_TYPE, MessageType.STATE_TRANSITION.toString());
    matchAttributes.put(ConstraintAttribute.TRANSITION, transition.toString());
    switch (scope.getType()) {
    case CLUSTER:
      // cluster is implicit
      break;
    case RESOURCE:
      matchAttributes.put(ConstraintAttribute.RESOURCE, scope.getScopedId().stringify());
      break;
    case PARTICIPANT:
      matchAttributes.put(ConstraintAttribute.INSTANCE, scope.getScopedId().stringify());
      break;
    default:
      LOG.error("Unsupported scope for transition constraints: " + scope);
      return Integer.MAX_VALUE;
    }
    Set<ConstraintItem> matches = transitionConstraints.match(matchAttributes);
    int value = Integer.MAX_VALUE;
    for (ConstraintItem item : matches) {
      String constraintValue = item.getConstraintValue();
      if (constraintValue != null) {
        try {
          int current = Integer.parseInt(constraintValue);
          if (current < value) {
            value = current;
          }
        } catch (NumberFormatException e) {
          LOG.error("Invalid in-flight transition cap: " + constraintValue);
        }
      }
    }
    return value;
  }

  /**
   * Get participants of the cluster
   * @return a map of participant id to participant, or empty map if none
   */
  public Map<ParticipantId, ParticipantConfig> getParticipantMap() {
    return _participantMap;
  }

  /**
   * Get all the state model definitions on the cluster
   * @return map of state model definition id to state model definition
   */
  public Map<StateModelDefId, StateModelDefinition> getStateModelMap() {
    return _stateModelMap;
  }

  /**
   * Get all the statistics persisted on the cluster
   * @return PersistentStats instance
   */
  public PersistentStats getStats() {
    return _stats;
  }

  /**
   * Get all the alerts persisted on the cluster
   * @return Alerts instance
   */
  public Alerts getAlerts() {
    return _alerts;
  }

  /**
   * Get user-specified configuration properties of this cluster
   * @return UserConfig properties
   */
  public UserConfig getUserConfig() {
    return _userConfig;
  }

  /**
   * Check the paused status of the cluster
   * @return true if paused, false otherwise
   */
  public boolean isPaused() {
    return _isPaused;
  }

  /**
   * Check if this cluster allows participants to join automatically
   * @return true if allowed, false if disallowed
   */
  public boolean autoJoinAllowed() {
    return _autoJoin;
  }

  /**
   * Update context for a ClusterConfig
   */
  public static class Delta {
    private enum Fields {
      USER_CONFIG,
      AUTO_JOIN
    }

    private Set<Fields> _updateFields;
    private Map<ConstraintType, Set<ConstraintId>> _removedConstraints;
    private PersistentStats _removedStats;
    private Alerts _removedAlerts;
    private Builder _builder;

    /**
     * Instantiate the delta for a cluster config
     * @param clusterId the cluster to update
     */
    public Delta(ClusterId clusterId) {
      _updateFields = Sets.newHashSet();
      _removedConstraints = Maps.newHashMap();
      for (ConstraintType type : ConstraintType.values()) {
        Set<ConstraintId> constraints = Sets.newHashSet();
        _removedConstraints.put(type, constraints);
      }
      _removedStats = new PersistentStats(PersistentStats.nodeName);
      _removedAlerts = new Alerts(Alerts.nodeName);
      _builder = new Builder(clusterId);
    }

    /**
     * Add a state upper bound constraint
     * @param scope scope under which the constraint is valid
     * @param stateModelDefId identifier of the state model that owns the state
     * @param state the state to constrain
     * @param upperBound maximum number of replicas per partition in the state
     * @return Delta
     */
    public Delta addStateUpperBoundConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
        State state, int upperBound) {
      return addStateUpperBoundConstraint(scope, stateModelDefId, state,
          Integer.toString(upperBound));
    }

    /**
     * Add a state upper bound constraint
     * @param scope scope under which the constraint is valid
     * @param stateModelDefId identifier of the state model that owns the state
     * @param state the state to constrain
     * @param dynamicUpperBound the upper bound of replicas per partition in the state, can be a
     *          number, or the currently supported special bound values:<br />
     *          "R" - Refers to the number of replicas specified during resource
     *          creation. This allows having different replication factor for each
     *          resource without having to create a different state machine. <br />
     *          "N" - Refers to all nodes in the cluster. Useful for resources that need
     *          to exist on all nodes. This way one can add/remove nodes without having
     *          the change the bounds.
     * @return Delta
     */
    public Delta addStateUpperBoundConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
        State state, String dynamicUpperBound) {
      _builder.addStateUpperBoundConstraint(scope, stateModelDefId, state, dynamicUpperBound);
      return this;
    }

    /**
     * Remove state upper bound constraint
     * @param scope scope under which the constraint is valid
     * @param stateModelDefId identifier of the state model that owns the state
     * @param state the state to constrain
     * @return Delta
     */
    public Delta removeStateUpperBoundConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
        State state) {
      _removedConstraints.get(ConstraintType.STATE_CONSTRAINT).add(
          ConstraintId.from(scope, stateModelDefId, state));
      return this;
    }

    /**
     * Add a constraint on the maximum number of in-flight transitions of a certain type
     * @param scope scope of the constraint
     * @param stateModelDefId identifies the state model containing the transition
     * @param transition the transition to constrain
     * @param maxInFlightTransitions number of allowed in-flight transitions in the scope
     * @return Delta
     */
    public Delta addTransitionConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
        Transition transition, int maxInFlightTransitions) {
      _builder.addTransitionConstraint(scope, stateModelDefId, transition, maxInFlightTransitions);
      return this;
    }

    /**
     * Remove a constraint on the maximum number of in-flight transitions of a certain type
     * @param scope scope of the constraint
     * @param stateModelDefId identifies the state model containing the transition
     * @param transition the transition to constrain
     * @return Delta
     */
    public Delta removeTransitionConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
        Transition transition) {
      _removedConstraints.get(ConstraintType.MESSAGE_CONSTRAINT).add(
          ConstraintId.from(scope, stateModelDefId, transition));
      return this;
    }

    /**
     * Add a single constraint item
     * @param type type of the constraint item
     * @param constraintId unique constraint id
     * @param item instantiated ConstraintItem
     * @return Delta
     */
    public Delta addConstraintItem(ConstraintType type, ConstraintId constraintId,
        ConstraintItem item) {
      _builder.addConstraint(type, constraintId, item);
      return this;
    }

    /**
     * Remove a single constraint item
     * @param type type of the constraint item
     * @param constraintId unique constraint id
     * @return Delta
     */
    public Delta removeConstraintItem(ConstraintType type, ConstraintId constraintId) {
      _removedConstraints.get(type).add(constraintId);
      return this;
    }

    /*
     * Set the user configuration
     * @param userConfig user-specified properties
     * @return Delta
     */
    public Delta setUserConfig(UserConfig userConfig) {
      _builder.userConfig(userConfig);
      _updateFields.add(Fields.USER_CONFIG);
      return this;
    }

    /**
     * Allow or disallow participants from automatically being able to join the cluster
     * @param autoJoin true if allowed, false if disallowed
     * @return Delta
     */
    public Delta setAutoJoin(boolean autoJoin) {
      _builder.autoJoin(autoJoin);
      _updateFields.add(Fields.AUTO_JOIN);
      return this;
    }

    /**
     * Add a statistic specification to the cluster. Existing specifications will not be overwritten
     * @param stat string specifying the stat specification
     * @return Delta
     */
    public Delta addStat(String stat) {
      _builder.addStat(stat);
      return this;
    }

    /**
     * Add an alert specification for the cluster. Existing specifications will not be overwritten
     * @param alert string specifying the alert specification
     * @return Delta
     */
    public Delta addAlert(String alert) {
      _builder.addAlert(alert);
      return this;
    }

    /**
     * Remove a statistic specification from the cluster
     * @param stat statistic specification
     * @return Delta
     */
    public Delta removeStat(String stat) {
      Map<String, Map<String, String>> parsedStat = StatsHolder.parseStat(stat);
      Map<String, Map<String, String>> currentStats = _removedStats.getMapFields();
      for (String statName : parsedStat.keySet()) {
        currentStats.put(statName, parsedStat.get(statName));
      }
      return this;
    }

    /**
     * Remove an alert specification for the cluster
     * @param alert alert specification
     * @return Delta
     */
    public Delta removeAlert(String alert) {
      Map<String, Map<String, String>> currAlertMap = _removedAlerts.getMapFields();
      if (!currAlertMap.containsKey(alert)) {
        Map<String, String> parsedAlert = Maps.newHashMap();
        StringBuilder statsName = new StringBuilder();
        AlertsHolder.parseAlert(alert, statsName, parsedAlert);
        removeStat(statsName.toString());
        currAlertMap.put(alert, parsedAlert);
      }
      return this;
    }

    /**
     * Create a ClusterConfig that is the combination of an existing ClusterConfig and this delta
     * @param orig the original ClusterConfig
     * @return updated ClusterConfig
     */
    public ClusterConfig mergeInto(ClusterConfig orig) {
      // copy in original and updated fields
      ClusterConfig deltaConfig = _builder.build();
      Builder builder =
          new Builder(orig.getId()).addResources(orig.getResourceMap().values())
              .addParticipants(orig.getParticipantMap().values())
              .addStateModelDefinitions(orig.getStateModelMap().values())
              .userConfig(orig.getUserConfig()).pausedStatus(orig.isPaused())
              .autoJoin(orig.autoJoinAllowed()).addStats(orig.getStats())
              .addAlerts(orig.getAlerts());
      for (Fields field : _updateFields) {
        switch (field) {
        case USER_CONFIG:
          builder.userConfig(deltaConfig.getUserConfig());
          break;
        case AUTO_JOIN:
          builder.autoJoin(deltaConfig.autoJoinAllowed());
          break;
        }
      }
      // add constraint deltas
      for (ConstraintType type : ConstraintType.values()) {
        ClusterConstraints constraints;
        if (orig.getConstraintMap().containsKey(type)) {
          constraints = orig.getConstraintMap().get(type);
        } else {
          constraints = new ClusterConstraints(type);
        }
        // add new constraints
        if (deltaConfig.getConstraintMap().containsKey(type)) {
          ClusterConstraints deltaConstraints = deltaConfig.getConstraintMap().get(type);
          for (ConstraintId constraintId : deltaConstraints.getConstraintItems().keySet()) {
            ConstraintItem constraintItem = deltaConstraints.getConstraintItem(constraintId);
            constraints.addConstraintItem(constraintId, constraintItem);
          }
        }
        // remove constraints
        for (ConstraintId constraintId : _removedConstraints.get(type)) {
          constraints.removeConstraintItem(constraintId);
        }
        builder.addConstraint(constraints);
      }

      // add stats and alerts
      builder.addStats(deltaConfig.getStats());
      builder.addAlerts(deltaConfig.getAlerts());

      // get the result
      ClusterConfig result = builder.build();

      // remove stats
      PersistentStats stats = result.getStats();
      for (String removedStat : _removedStats.getMapFields().keySet()) {
        if (stats.getMapFields().containsKey(removedStat)) {
          stats.getMapFields().remove(removedStat);
        }
      }

      // remove alerts
      Alerts alerts = result.getAlerts();
      for (String removedAlert : _removedAlerts.getMapFields().keySet()) {
        if (alerts.getMapFields().containsKey(removedAlert)) {
          alerts.getMapFields().remove(removedAlert);
        }
      }
      return result;
    }
  }

  /**
   * Assembles a cluster configuration
   */
  public static class Builder {
    private final ClusterId _id;
    private final Map<ResourceId, ResourceConfig> _resourceMap;
    private final Map<ParticipantId, ParticipantConfig> _participantMap;
    private final Map<ConstraintType, ClusterConstraints> _constraintMap;
    private final Map<StateModelDefId, StateModelDefinition> _stateModelMap;
    private UserConfig _userConfig;
    private PersistentStats _stats;
    private Alerts _alerts;
    private boolean _isPaused;
    private boolean _autoJoin;

    /**
     * Initialize builder for a cluster
     * @param id cluster id
     */
    public Builder(ClusterId id) {
      _id = id;
      _resourceMap = new HashMap<ResourceId, ResourceConfig>();
      _participantMap = new HashMap<ParticipantId, ParticipantConfig>();
      _constraintMap = new HashMap<ConstraintType, ClusterConstraints>();
      _stateModelMap = new HashMap<StateModelDefId, StateModelDefinition>();
      _isPaused = false;
      _autoJoin = false;
      _userConfig = new UserConfig(Scope.cluster(id));
      _stats = new PersistentStats(PersistentStats.nodeName);
      _alerts = new Alerts(Alerts.nodeName);
    }

    /**
     * Add a resource to the cluster
     * @param resource resource configuration
     * @return Builder
     */
    public Builder addResource(ResourceConfig resource) {
      _resourceMap.put(resource.getId(), resource);
      return this;
    }

    /**
     * Add multiple resources to the cluster
     * @param resources resource configurations
     * @return Builder
     */
    public Builder addResources(Collection<ResourceConfig> resources) {
      for (ResourceConfig resource : resources) {
        addResource(resource);
      }
      return this;
    }

    /**
     * Add a participant to the cluster
     * @param participant participant configuration
     * @return Builder
     */
    public Builder addParticipant(ParticipantConfig participant) {
      _participantMap.put(participant.getId(), participant);
      return this;
    }

    /**
     * Add multiple participants to the cluster
     * @param participants participant configurations
     * @return Builder
     */
    public Builder addParticipants(Collection<ParticipantConfig> participants) {
      for (ParticipantConfig participant : participants) {
        addParticipant(participant);
      }
      return this;
    }

    /**
     * Add a constraint to the cluster
     * @param constraint cluster constraint of a specific type
     * @return Builder
     */
    public Builder addConstraint(ClusterConstraints constraint) {
      ClusterConstraints existConstraints = getConstraintsInstance(constraint.getType());
      for (ConstraintId constraintId : constraint.getConstraintItems().keySet()) {
        existConstraints
            .addConstraintItem(constraintId, constraint.getConstraintItem(constraintId));
      }
      return this;
    }

    /**
     * Add a single constraint item
     * @param type type of the constraint
     * @param constraintId unique constraint identifier
     * @param item instantiated ConstraintItem
     * @return Builder
     */
    public Builder addConstraint(ConstraintType type, ConstraintId constraintId, ConstraintItem item) {
      ClusterConstraints existConstraints = getConstraintsInstance(type);
      existConstraints.addConstraintItem(constraintId, item);
      return this;
    }

    /**
     * Add multiple constraints to the cluster
     * @param constraints cluster constraints of multiple distinct types
     * @return Builder
     */
    public Builder addConstraints(Collection<ClusterConstraints> constraints) {
      for (ClusterConstraints constraint : constraints) {
        addConstraint(constraint);
      }
      return this;
    }

    /**
     * Add a constraint on the maximum number of in-flight transitions of a certain type
     * @param scope scope of the constraint
     * @param stateModelDefId identifies the state model containing the transition
     * @param transition the transition to constrain
     * @param maxInFlightTransitions number of allowed in-flight transitions in the scope
     * @return Builder
     */
    public Builder addTransitionConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
        Transition transition, int maxInFlightTransitions) {
      Map<String, String> attributes = Maps.newHashMap();
      attributes.put(ConstraintAttribute.MESSAGE_TYPE.toString(),
          MessageType.STATE_TRANSITION.toString());
      attributes.put(ConstraintAttribute.CONSTRAINT_VALUE.toString(),
          Integer.toString(maxInFlightTransitions));
      attributes.put(ConstraintAttribute.TRANSITION.toString(), transition.toString());
      attributes.put(ConstraintAttribute.STATE_MODEL.toString(), stateModelDefId.stringify());
      switch (scope.getType()) {
      case CLUSTER:
        // cluster is implicit
        break;
      case RESOURCE:
        attributes.put(ConstraintAttribute.RESOURCE.toString(), scope.getScopedId().stringify());
        break;
      case PARTICIPANT:
        attributes.put(ConstraintAttribute.INSTANCE.toString(), scope.getScopedId().stringify());
        break;
      default:
        LOG.error("Unsupported scope for adding a transition constraint: " + scope);
        return this;
      }
      ConstraintItem item = new ConstraintItemBuilder().addConstraintAttributes(attributes).build();
      ClusterConstraints constraints = getConstraintsInstance(ConstraintType.MESSAGE_CONSTRAINT);
      constraints.addConstraintItem(ConstraintId.from(scope, stateModelDefId, transition), item);
      return this;
    }

    /**
     * Add a state upper bound constraint
     * @param scope scope under which the constraint is valid
     * @param stateModelDefId identifier of the state model that owns the state
     * @param state the state to constrain
     * @param upperBound maximum number of replicas per partition in the state
     * @return Builder
     */
    public Builder addStateUpperBoundConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
        State state, int upperBound) {
      return addStateUpperBoundConstraint(scope, stateModelDefId, state,
          Integer.toString(upperBound));
    }

    /**
     * Add a state upper bound constraint
     * @param scope scope under which the constraint is valid
     * @param stateModelDefId identifier of the state model that owns the state
     * @param state the state to constrain
     * @param dynamicUpperBound the upper bound of replicas per partition in the state, can be a
     *          number, or the currently supported special bound values:<br />
     *          "R" - Refers to the number of replicas specified during resource
     *          creation. This allows having different replication factor for each
     *          resource without having to create a different state machine. <br />
     *          "N" - Refers to all nodes in the cluster. Useful for resources that need
     *          to exist on all nodes. This way one can add/remove nodes without having
     *          the change the bounds.
     * @return Builder
     */
    public Builder addStateUpperBoundConstraint(Scope<?> scope, StateModelDefId stateModelDefId,
        State state, String dynamicUpperBound) {
      Map<String, String> attributes = Maps.newHashMap();
      attributes.put(ConstraintAttribute.STATE.toString(), state.toString());
      attributes.put(ConstraintAttribute.STATE_MODEL.toString(), stateModelDefId.stringify());
      attributes.put(ConstraintAttribute.CONSTRAINT_VALUE.toString(), dynamicUpperBound);
      switch (scope.getType()) {
      case CLUSTER:
        // cluster is implicit
        break;
      case RESOURCE:
        attributes.put(ConstraintAttribute.RESOURCE.toString(), scope.getScopedId().stringify());
        break;
      default:
        LOG.error("Unsupported scope for adding a state constraint: " + scope);
        return this;
      }
      ConstraintItem item = new ConstraintItemBuilder().addConstraintAttributes(attributes).build();
      ClusterConstraints constraints = getConstraintsInstance(ConstraintType.STATE_CONSTRAINT);
      constraints.addConstraintItem(ConstraintId.from(scope, stateModelDefId, state), item);
      return this;
    }

    /**
     * Add a state model definition to the cluster
     * @param stateModelDef state model definition of the cluster
     * @return Builder
     */
    public Builder addStateModelDefinition(StateModelDefinition stateModelDef) {
      _stateModelMap.put(stateModelDef.getStateModelDefId(), stateModelDef);
      // add state constraints from the state model definition
      for (State state : stateModelDef.getTypedStatesPriorityList()) {
        if (!stateModelDef.getNumParticipantsPerState(state).equals("-1")) {
          addStateUpperBoundConstraint(Scope.cluster(_id), stateModelDef.getStateModelDefId(),
              state, stateModelDef.getNumParticipantsPerState(state));
        }
      }
      return this;
    }

    /**
     * Add multiple state model definitions
     * @param stateModelDefs collection of state model definitions for the cluster
     * @return Builder
     */
    public Builder addStateModelDefinitions(Collection<StateModelDefinition> stateModelDefs) {
      for (StateModelDefinition stateModelDef : stateModelDefs) {
        addStateModelDefinition(stateModelDef);
      }
      return this;
    }

    /**
     * Add a statistic specification to the cluster. Existing specifications will not be overwritten
     * @param stat String specifying the stat specification
     * @return Builder
     */
    public Builder addStat(String stat) {
      Map<String, Map<String, String>> parsedStat = StatsHolder.parseStat(stat);
      Map<String, Map<String, String>> currentStats = _stats.getMapFields();
      for (String statName : parsedStat.keySet()) {
        if (!currentStats.containsKey(statName)) {
          currentStats.put(statName, parsedStat.get(statName));
        }
      }
      return this;
    }

    /**
     * Add statistic specifications to the cluster. Existing specifications will not be overwritten
     * @param stats PersistentStats specifying the stat specification
     * @return Builder
     */
    public Builder addStats(PersistentStats stats) {
      if (stats == null) {
        return this;
      }
      Map<String, Map<String, String>> parsedStat = stats.getMapFields();
      Map<String, Map<String, String>> currentStats = _stats.getMapFields();
      for (String statName : parsedStat.keySet()) {
        if (!currentStats.containsKey(statName)) {
          currentStats.put(statName, parsedStat.get(statName));
        }
      }
      return this;
    }

    /**
     * Add alert specifications to the cluster. Existing specifications will not be overwritten
     * @param alert string representing alert specifications
     * @return Builder
     */
    public Builder addAlert(String alert) {
      Map<String, Map<String, String>> currAlertMap = _alerts.getMapFields();
      if (!currAlertMap.containsKey(alert)) {
        Map<String, String> parsedAlert = Maps.newHashMap();
        StringBuilder statsName = new StringBuilder();
        AlertsHolder.parseAlert(alert, statsName, parsedAlert);
        addStat(statsName.toString());
        currAlertMap.put(alert, parsedAlert);
      }
      return this;
    }

    /**
     * Add alert specifications to the cluster. Existing specifications will not be overwritten
     * @param alerts Alerts instance
     * @return Builder
     */
    public Builder addAlerts(Alerts alerts) {
      if (alerts == null) {
        return this;
      }
      Map<String, Map<String, String>> alertMap = alerts.getMapFields();
      for (String alert : alertMap.keySet()) {
        addAlert(alert);
      }
      return this;
    }

    /**
     * Set the paused status of the cluster
     * @param isPaused true if paused, false otherwise
     * @return Builder
     */
    public Builder pausedStatus(boolean isPaused) {
      _isPaused = isPaused;
      return this;
    }

    /**
     * Allow or disallow participants from automatically being able to join the cluster
     * @param autoJoin true if allowed, false if disallowed
     * @return Builder
     */
    public Builder autoJoin(boolean autoJoin) {
      _autoJoin = autoJoin;
      return this;
    }

    /**
     * Set the user configuration
     * @param userConfig user-specified properties
     * @return Builder
     */
    public Builder userConfig(UserConfig userConfig) {
      _userConfig = userConfig;
      return this;
    }

    /**
     * Create the cluster configuration
     * @return ClusterConfig
     */
    public ClusterConfig build() {
      return new ClusterConfig(_id, _resourceMap, _participantMap, _constraintMap, _stateModelMap,
          _stats, _alerts, _userConfig, _isPaused, _autoJoin);
    }

    /**
     * Get a valid instance of ClusterConstraints for a type
     * @param type the type
     * @return ClusterConstraints
     */
    private ClusterConstraints getConstraintsInstance(ConstraintType type) {
      ClusterConstraints constraints = _constraintMap.get(type);
      if (constraints == null) {
        constraints = new ClusterConstraints(type);
        _constraintMap.put(type, constraints);
      }
      return constraints;
    }
  }
}
TOP

Related Classes of org.apache.helix.api.config.ClusterConfig$Builder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.