Package org.apache.helix.api.accessor

Source Code of org.apache.helix.api.accessor.ClusterAccessor

package org.apache.helix.api.accessor;

/*
* 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.
*/

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.helix.AccessOption;
import org.apache.helix.BaseDataAccessor;
import org.apache.helix.HelixDataAccessor;
import org.apache.helix.PropertyKey;
import org.apache.helix.api.Cluster;
import org.apache.helix.api.Controller;
import org.apache.helix.api.Participant;
import org.apache.helix.api.Resource;
import org.apache.helix.api.Scope;
import org.apache.helix.api.config.ClusterConfig;
import org.apache.helix.api.config.ContainerConfig;
import org.apache.helix.api.config.ParticipantConfig;
import org.apache.helix.api.config.ResourceConfig;
import org.apache.helix.api.config.UserConfig;
import org.apache.helix.api.id.ClusterId;
import org.apache.helix.api.id.ContextId;
import org.apache.helix.api.id.ControllerId;
import org.apache.helix.api.id.MessageId;
import org.apache.helix.api.id.ParticipantId;
import org.apache.helix.api.id.ResourceId;
import org.apache.helix.api.id.SessionId;
import org.apache.helix.api.id.StateModelDefId;
import org.apache.helix.controller.context.ControllerContext;
import org.apache.helix.controller.context.ControllerContextHolder;
import org.apache.helix.controller.provisioner.ContainerId;
import org.apache.helix.controller.provisioner.ContainerSpec;
import org.apache.helix.controller.provisioner.ContainerState;
import org.apache.helix.controller.provisioner.ProvisionerConfig;
import org.apache.helix.controller.rebalancer.config.RebalancerConfig;
import org.apache.helix.controller.rebalancer.config.RebalancerConfigHolder;
import org.apache.helix.controller.stages.ClusterDataCache;
import org.apache.helix.model.ClusterConfiguration;
import org.apache.helix.model.ClusterConstraints;
import org.apache.helix.model.ClusterConstraints.ConstraintType;
import org.apache.helix.model.CurrentState;
import org.apache.helix.model.ExternalView;
import org.apache.helix.model.IdealState;
import org.apache.helix.model.InstanceConfig;
import org.apache.helix.model.LiveInstance;
import org.apache.helix.model.Message;
import org.apache.helix.model.PauseSignal;
import org.apache.helix.model.ProvisionerConfigHolder;
import org.apache.helix.model.ResourceAssignment;
import org.apache.helix.model.ResourceConfiguration;
import org.apache.helix.model.StateModelDefinition;
import org.apache.helix.util.HelixUtil;
import org.apache.log4j.Logger;

import com.google.common.collect.Maps;

public class ClusterAccessor {
  private static Logger LOG = Logger.getLogger(ClusterAccessor.class);

  private final HelixDataAccessor _accessor;
  private final PropertyKey.Builder _keyBuilder;
  private final ClusterId _clusterId;

  private final ClusterDataCache _cache;

  /**
   * Instantiate a cluster accessor
   * @param clusterId the cluster to access
   * @param accessor HelixDataAccessor for the physical store
   */
  public ClusterAccessor(ClusterId clusterId, HelixDataAccessor accessor) {
    this(clusterId, accessor, new ClusterDataCache());
  }

  /**
   * Instantiate a cluster accessor
   * @param clusterId the cluster to access
   * @param accessor HelixDataAccessor for the physical store
   */
  public ClusterAccessor(ClusterId clusterId, HelixDataAccessor accessor, ClusterDataCache cache) {
    _accessor = accessor;
    _keyBuilder = accessor.keyBuilder();
    _clusterId = clusterId;
    _cache = cache;
  }

  /**
   * create a new cluster, fail if it already exists
   * @return true if created, false if creation failed
   */
  public boolean createCluster(ClusterConfig cluster) {
    ClusterConfiguration configuration = _accessor.getProperty(_keyBuilder.clusterConfig());
    if (configuration != null && isClusterStructureValid()) {
      LOG.error("Cluster already created. Aborting.");
      return false;
    }
    clearClusterStructure();
    initClusterStructure();
    Map<StateModelDefId, StateModelDefinition> stateModelDefs = cluster.getStateModelMap();
    for (StateModelDefinition stateModelDef : stateModelDefs.values()) {
      addStateModelDefinition(stateModelDef);
    }
    Map<ResourceId, ResourceConfig> resources = cluster.getResourceMap();
    for (ResourceConfig resource : resources.values()) {
      addResource(resource);
    }
    Map<ParticipantId, ParticipantConfig> participants = cluster.getParticipantMap();
    for (ParticipantConfig participant : participants.values()) {
      addParticipant(participant);
    }
    _accessor.createProperty(_keyBuilder.constraints(), null);
    for (ClusterConstraints constraints : cluster.getConstraintMap().values()) {
      _accessor.setProperty(_keyBuilder.constraint(constraints.getType().toString()), constraints);
    }
    ClusterConfiguration clusterConfig =
        ClusterConfiguration.from(_clusterId, cluster.getUserConfig());
    if (cluster.autoJoinAllowed()) {
      clusterConfig.setAutoJoinAllowed(cluster.autoJoinAllowed());
    }
    if (cluster.isPaused()) {
      _accessor.createProperty(_keyBuilder.pause(), new PauseSignal("pause"));
    }
    _accessor.setProperty(_keyBuilder.clusterConfig(), clusterConfig);

    return true;
  }

  /**
   * Update the cluster configuration
   * @param clusterDelta change to the cluster configuration
   * @return updated ClusterConfig, or null if there was an error
   */
  public ClusterConfig updateCluster(ClusterConfig.Delta clusterDelta) {
    clusterDelta.merge(_accessor);
    Cluster cluster = readCluster();
    return (cluster != null) ? cluster.getConfig() : null;
  }

  /**
   * drop a cluster
   * @return true if the cluster was dropped, false if there was an error
   */
  public boolean dropCluster() {
    LOG.info("Dropping cluster: " + _clusterId);
    List<String> liveInstanceNames = _accessor.getChildNames(_keyBuilder.liveInstances());
    if (liveInstanceNames.size() > 0) {
      LOG.error("Can't drop cluster: " + _clusterId + " because there are running participant: "
          + liveInstanceNames + ", shutdown participants first.");
      return false;
    }

    LiveInstance leader = _accessor.getProperty(_keyBuilder.controllerLeader());
    if (leader != null) {
      LOG.error("Can't drop cluster: " + _clusterId + ", because leader: " + leader.getId()
          + " are running, shutdown leader first.");
      return false;
    }

    return _accessor.removeProperty(_keyBuilder.cluster());
  }

  /**
   * read entire cluster data
   * @return cluster snapshot or null
   */
  public Cluster readCluster() {
    if (!isClusterStructureValid()) {
      LOG.error("Cluster is not fully set up");
      return null;
    }

    // refresh the cache
    _cache.refresh(_accessor);

    LiveInstance leader = _cache.getLeader();

    /**
     * map of constraint-type to constraints
     */
    Map<String, ClusterConstraints> constraintMap = _cache.getConstraintMap();

    // read all the resources
    Map<ResourceId, Resource> resourceMap = readResources(true);

    // read all the participants
    Map<ParticipantId, Participant> participantMap = readParticipants(true);

    // read the controllers
    Map<ControllerId, Controller> controllerMap = new HashMap<ControllerId, Controller>();
    ControllerId leaderId = null;
    if (leader != null) {
      leaderId = ControllerId.from(leader.getId());
      controllerMap.put(leaderId, new Controller(leaderId, leader, true));
    }

    // read the constraints
    Map<ConstraintType, ClusterConstraints> clusterConstraintMap =
        new HashMap<ConstraintType, ClusterConstraints>();
    for (String constraintType : constraintMap.keySet()) {
      clusterConstraintMap.put(ConstraintType.valueOf(constraintType),
          constraintMap.get(constraintType));
    }

    // read the pause status
    PauseSignal pauseSignal = _cache.getPauseSignal();
    boolean isPaused = pauseSignal != null;

    ClusterConfiguration clusterConfig = _cache.getClusterConfig();
    boolean autoJoinAllowed = false;
    UserConfig userConfig;
    if (clusterConfig != null) {
      userConfig = clusterConfig.getUserConfig();
      autoJoinAllowed = clusterConfig.autoJoinAllowed();
    } else {
      userConfig = new UserConfig(Scope.cluster(_clusterId));
    }

    // read the state model definitions
    Map<StateModelDefId, StateModelDefinition> stateModelMap = readStateModelDefinitions(true);

    // read controller context
    Map<ContextId, ControllerContext> contextMap = readControllerContext(true);

    // create the cluster snapshot object
    return new Cluster(_clusterId, resourceMap, participantMap, controllerMap, leaderId,
        clusterConstraintMap, stateModelMap, contextMap, userConfig, isPaused, autoJoinAllowed,
        _cache);
  }

  /**
   * Get all the state model definitions for this cluster
   * @param useCache Use the ClusterDataCache associated with this class rather than reading again
   * @return map of state model def id to state model definition
   */
  private Map<StateModelDefId, StateModelDefinition> readStateModelDefinitions(boolean useCache) {
    Map<StateModelDefId, StateModelDefinition> stateModelDefs = Maps.newHashMap();
    Collection<StateModelDefinition> stateModelList;
    if (useCache) {
      stateModelList = _cache.getStateModelDefMap().values();
    } else {
      stateModelList = _accessor.getChildValues(_keyBuilder.stateModelDefs());
    }
    for (StateModelDefinition stateModelDef : stateModelList) {
      stateModelDefs.put(stateModelDef.getStateModelDefId(), stateModelDef);
    }
    return stateModelDefs;
  }

  /**
   * Read all resources in the cluster
   * @param useCache Use the ClusterDataCache associated with this class rather than reading again
   * @return map of resource id to resource
   */
  private Map<ResourceId, Resource> readResources(boolean useCache) {
    if (!useCache && !isClusterStructureValid()) {
      LOG.error("Cluster is not fully set up yet!");
      return Collections.emptyMap();
    }

    Map<String, IdealState> idealStateMap;
    Map<String, ResourceConfiguration> resourceConfigMap;
    Map<String, ExternalView> externalViewMap;
    Map<String, ResourceAssignment> resourceAssignmentMap;
    if (useCache) {
      idealStateMap = _cache.getIdealStates();
      resourceConfigMap = _cache.getResourceConfigs();
    } else {
      idealStateMap = _accessor.getChildValuesMap(_keyBuilder.idealStates());
      resourceConfigMap = _accessor.getChildValuesMap(_keyBuilder.resourceConfigs());
    }

    // now read external view and resource assignments if needed
    if (!useCache) {
      externalViewMap = _accessor.getChildValuesMap(_keyBuilder.externalViews());
      resourceAssignmentMap = _accessor.getChildValuesMap(_keyBuilder.resourceAssignments());
      _cache.setAssignmentWritePolicy(true);
    } else {
      externalViewMap = Maps.newHashMap();
      resourceAssignmentMap = Maps.newHashMap();
      _cache.setAssignmentWritePolicy(false);
    }

    // populate all the resources
    Map<ResourceId, Resource> resourceMap = Maps.newHashMap();
    for (String resourceName : idealStateMap.keySet()) {
      ResourceId resourceId = ResourceId.from(resourceName);
      resourceMap.put(
          resourceId,
          createResource(resourceId, resourceConfigMap.get(resourceName),
              idealStateMap.get(resourceName), externalViewMap.get(resourceName),
              resourceAssignmentMap.get(resourceName)));
    }

    return resourceMap;
  }

  /**
   * Read all participants in the cluster
   * @param useCache Use the ClusterDataCache associated with this class rather than reading again
   * @return map of participant id to participant, or empty map
   */
  private Map<ParticipantId, Participant> readParticipants(boolean useCache) {
    if (!useCache && !isClusterStructureValid()) {
      LOG.error("Cluster is not fully set up yet!");
      return Collections.emptyMap();
    }
    Map<String, InstanceConfig> instanceConfigMap;
    Map<String, LiveInstance> liveInstanceMap;
    if (useCache) {
      instanceConfigMap = _cache.getInstanceConfigMap();
      liveInstanceMap = _cache.getLiveInstances();
    } else {
      instanceConfigMap = _accessor.getChildValuesMap(_keyBuilder.instanceConfigs());
      liveInstanceMap = _accessor.getChildValuesMap(_keyBuilder.liveInstances());
    }

    /**
     * map of participant-id to map of message-id to message
     */
    Map<String, Map<String, Message>> messageMap = Maps.newHashMap();
    for (String participantName : liveInstanceMap.keySet()) {
      Map<String, Message> instanceMsgMap;
      if (useCache) {
        instanceMsgMap = _cache.getMessages(participantName);
      } else {
        instanceMsgMap = _accessor.getChildValuesMap(_keyBuilder.messages(participantName));
      }
      messageMap.put(participantName, instanceMsgMap);
    }

    /**
     * map of participant-id to map of resource-id to current-state
     */
    Map<String, Map<String, CurrentState>> currentStateMap = Maps.newHashMap();
    for (String participantName : liveInstanceMap.keySet()) {
      LiveInstance liveInstance = liveInstanceMap.get(participantName);
      SessionId sessionId = liveInstance.getTypedSessionId();
      Map<String, CurrentState> instanceCurStateMap;
      if (useCache) {
        instanceCurStateMap = _cache.getCurrentState(participantName, sessionId.stringify());
      } else {
        instanceCurStateMap =
            _accessor.getChildValuesMap(_keyBuilder.currentStates(participantName,
                sessionId.stringify()));
      }
      currentStateMap.put(participantName, instanceCurStateMap);
    }

    // read all the participants
    Map<ParticipantId, Participant> participantMap = Maps.newHashMap();
    for (String participantName : instanceConfigMap.keySet()) {
      InstanceConfig instanceConfig = instanceConfigMap.get(participantName);
      UserConfig userConfig = instanceConfig.getUserConfig();
      LiveInstance liveInstance = liveInstanceMap.get(participantName);
      Map<String, Message> instanceMsgMap = messageMap.get(participantName);

      ParticipantId participantId = ParticipantId.from(participantName);

      participantMap.put(
          participantId,
          createParticipant(participantId, instanceConfig, userConfig, liveInstance,
              instanceMsgMap, currentStateMap.get(participantName)));
    }

    return participantMap;
  }

  /**
   * Read the persisted controller contexts
   * @param useCache Use the ClusterDataCache associated with this class rather than reading again
   * @return map of context id to controller context
   */
  private Map<ContextId, ControllerContext> readControllerContext(boolean useCache) {
    Map<String, ControllerContextHolder> contextHolders;
    if (useCache) {
      contextHolders = _cache.getContextMap();
    } else {
      contextHolders = _accessor.getChildValuesMap(_keyBuilder.controllerContexts());
    }
    Map<ContextId, ControllerContext> contexts = Maps.newHashMap();
    for (String contextName : contextHolders.keySet()) {
      contexts.put(ContextId.from(contextName), contextHolders.get(contextName).getContext());
    }
    return contexts;
  }

  /**
   * add a resource to cluster
   * @param resource
   * @return true if resource added, false if there was an error
   */
  public boolean addResource(ResourceConfig resource) {
    if (resource == null || resource.getIdealState() == null) {
      LOG.error("Resource not fully defined with an ideal state");
      return false;
    }

    if (!isClusterStructureValid()) {
      LOG.error("Cluster: " + _clusterId + " structure is not valid");
      return false;
    }
    IdealState idealState = resource.getIdealState();
    StateModelDefId stateModelDefId = idealState.getStateModelDefId();
    if (_accessor.getProperty(_keyBuilder.stateModelDef(stateModelDefId.stringify())) == null) {
      LOG.error("State model: " + stateModelDefId + " not found in cluster: " + _clusterId);
      return false;
    }

    ResourceId resourceId = resource.getId();
    if (_accessor.getProperty(_keyBuilder.idealStates(resourceId.stringify())) != null) {
      LOG.error("Skip adding resource: " + resourceId
          + ", because resource ideal state already exists in cluster: " + _clusterId);
      return false;
    }
    if (_accessor.getProperty(_keyBuilder.resourceConfig(resourceId.stringify())) != null) {
      LOG.error("Skip adding resource: " + resourceId
          + ", because resource config already exists in cluster: " + _clusterId);
      return false;
    }

    // Persist the ideal state
    _accessor.setProperty(_keyBuilder.idealStates(resourceId.stringify()), idealState);

    // Add resource user config
    boolean persistConfig = false;
    ResourceConfiguration configuration = new ResourceConfiguration(resourceId);
    if (resource.getUserConfig() != null) {
      configuration.addNamespacedConfig(resource.getUserConfig());
      persistConfig = true;
    }
    RebalancerConfig rebalancerConfig = resource.getRebalancerConfig();
    if (rebalancerConfig != null) {
      // only persist if this is not easily convertible to an ideal state
      configuration.addNamespacedConfig(new RebalancerConfigHolder(rebalancerConfig)
          .toNamespacedConfig());
      persistConfig = true;
    }
    ProvisionerConfig provisionerConfig = resource.getProvisionerConfig();
    if (provisionerConfig != null) {
      configuration.addNamespacedConfig(new ProvisionerConfigHolder(provisionerConfig)
          .toNamespacedConfig());
      persistConfig = true;
    }
    if (persistConfig) {
      _accessor.setProperty(_keyBuilder.resourceConfig(resourceId.stringify()), configuration);
    }
    return true;
  }

  /**
   * drop a resource from cluster
   * @param resourceId
   * @return true if removal succeeded, false otherwise
   */
  public boolean dropResource(ResourceId resourceId) {
    if (_accessor.getProperty(_keyBuilder.idealStates(resourceId.stringify())) == null) {
      LOG.error("Skip removing resource: " + resourceId
          + ", because resource ideal state already removed from cluster: " + _clusterId);
      return false;
    }
    _accessor.removeProperty(_keyBuilder.idealStates(resourceId.stringify()));
    _accessor.removeProperty(_keyBuilder.resourceConfig(resourceId.stringify()));
    return true;
  }

  /**
   * check if cluster structure is valid
   * @return true if valid or false otherwise
   */
  protected boolean isClusterStructureValid() {
    List<String> paths = HelixUtil.getRequiredPathsForCluster(_clusterId.toString());
    BaseDataAccessor<?> baseAccessor = _accessor.getBaseDataAccessor();
    if (baseAccessor != null) {
      boolean[] existsResults = baseAccessor.exists(paths, 0);
      int ind = 0;
      for (boolean exists : existsResults) {
        if (!exists) {
          LOG.warn("Path does not exist:" + paths.get(ind));
          return false;
        }
        ind = ind + 1;
      }
    }
    return true;
  }

  /**
   * Create empty persistent properties to ensure that there is a valid cluster structure
   */
  private void initClusterStructure() {
    BaseDataAccessor<?> baseAccessor = _accessor.getBaseDataAccessor();
    List<String> paths = HelixUtil.getRequiredPathsForCluster(_clusterId.toString());
    for (String path : paths) {
      boolean status = baseAccessor.create(path, null, AccessOption.PERSISTENT);
      if (!status && LOG.isDebugEnabled()) {
        LOG.debug(path + " already exists");
      }
    }
  }

  /**
   * Remove all but the top level cluster node; intended for reconstructing the cluster
   */
  private void clearClusterStructure() {
    BaseDataAccessor<?> baseAccessor = _accessor.getBaseDataAccessor();
    List<String> paths = HelixUtil.getRequiredPathsForCluster(_clusterId.toString());
    baseAccessor.remove(paths, 0);
  }

  /**
   * add a participant to cluster
   * @param participant
   * @return true if participant added, false otherwise
   */
  public boolean addParticipant(ParticipantConfig participant) {
    if (participant == null) {
      LOG.error("Participant not initialized");
      return false;
    }
    if (!isClusterStructureValid()) {
      LOG.error("Cluster: " + _clusterId + " structure is not valid");
      return false;
    }

    ParticipantId participantId = participant.getId();
    InstanceConfig existConfig =
        _accessor.getProperty(_keyBuilder.instanceConfig(participantId.stringify()));
    if (existConfig != null && isParticipantStructureValid(participantId)) {
      LOG.error("Config for participant: " + participantId + " already exists in cluster: "
          + _clusterId);
      return false;
    }

    // clear and rebuild the participant structure
    clearParticipantStructure(participantId);
    initParticipantStructure(participantId);

    // add the config
    _accessor.setProperty(_keyBuilder.instanceConfig(participantId.stringify()),
        participant.getInstanceConfig());
    return true;
  }

  /**
   * drop a participant from cluster
   * @param participantId
   * @return true if participant dropped, false if there was an error
   */
  public boolean dropParticipant(ParticipantId participantId) {
    if (_accessor.getProperty(_keyBuilder.instanceConfig(participantId.stringify())) == null) {
      LOG.error("Config for participant: " + participantId + " does NOT exist in cluster");
    }

    if (_accessor.getProperty(_keyBuilder.instance(participantId.stringify())) == null) {
      LOG.error("Participant: " + participantId + " structure does NOT exist in cluster");
    }

    // delete participant config path
    _accessor.removeProperty(_keyBuilder.instanceConfig(participantId.stringify()));

    // delete participant path
    _accessor.removeProperty(_keyBuilder.instance(participantId.stringify()));
    return true;
  }

  /**
   * Add a state model definition. Updates the existing state model definition if it already exists.
   * @param stateModelDef fully initialized state model definition
   * @return true if the model is persisted, false otherwise
   */
  public boolean addStateModelDefinition(StateModelDefinition stateModelDef) {
    if (!isClusterStructureValid()) {
      LOG.error("Cluster: " + _clusterId + " structure is not valid");
      return false;
    }

    return _accessor
        .createProperty(_keyBuilder.stateModelDef(stateModelDef.getId()), stateModelDef);
  }

  /**
   * Remove a state model definition if it exists
   * @param stateModelDefId state model definition id
   * @return true if removed, false if it did not exist
   */
  public boolean dropStateModelDefinition(StateModelDefId stateModelDefId) {
    return _accessor.removeProperty(_keyBuilder.stateModelDef(stateModelDefId.stringify()));
  }

  /**
   * Read the leader controller if it is live
   * @return Controller snapshot, or null
   */
  public Controller readLeader() {
    LiveInstance leader = _accessor.getProperty(_keyBuilder.controllerLeader());
    if (leader != null) {
      ControllerId leaderId = ControllerId.from(leader.getId());
      return new Controller(leaderId, leader, true);
    }
    return null;
  }

  /**
   * Update a participant configuration
   * @param participantId the participant to update
   * @param participantDelta changes to the participant
   * @return ParticipantConfig, or null if participant is not persisted
   */
  public ParticipantConfig updateParticipant(ParticipantId participantId,
      ParticipantConfig.Delta participantDelta) {
    participantDelta.merge(_accessor);
    Participant participant = readParticipant(participantId);
    return (participant != null) ? participant.getConfig() : null;
  }

  /**
   * create a participant based on physical model
   * @param participantId
   * @param instanceConfig
   * @param userConfig
   * @param liveInstance
   * @param instanceMsgMap map of message-id to message
   * @param instanceCurStateMap map of resource-id to current-state
   * @return participant
   */
  private static Participant createParticipant(ParticipantId participantId,
      InstanceConfig instanceConfig, UserConfig userConfig, LiveInstance liveInstance,
      Map<String, Message> instanceMsgMap, Map<String, CurrentState> instanceCurStateMap) {
    Map<MessageId, Message> msgMap = new HashMap<MessageId, Message>();
    if (instanceMsgMap != null) {
      for (String msgId : instanceMsgMap.keySet()) {
        Message message = instanceMsgMap.get(msgId);
        msgMap.put(MessageId.from(msgId), message);
      }
    }

    Map<ResourceId, CurrentState> curStateMap = new HashMap<ResourceId, CurrentState>();
    if (instanceCurStateMap != null) {

      for (String resourceName : instanceCurStateMap.keySet()) {
        curStateMap.put(ResourceId.from(resourceName), instanceCurStateMap.get(resourceName));
      }
    }

    // set up the container config if it exists
    ContainerConfig containerConfig = null;
    ContainerSpec containerSpec = instanceConfig.getContainerSpec();
    ContainerState containerState = instanceConfig.getContainerState();
    ContainerId containerId = instanceConfig.getContainerId();
    if (containerSpec != null || containerState != null || containerId != null) {
      containerConfig = new ContainerConfig(containerId, containerSpec, containerState);
    }

    // Populate the logical class
    ParticipantConfig participantConfig = ParticipantConfig.from(instanceConfig);
    return new Participant(participantConfig, liveInstance, curStateMap, msgMap, containerConfig);
  }

  /**
   * read participant related data
   * @param participantId
   * @return participant, or null if participant not available
   */
  public Participant readParticipant(ParticipantId participantId) {
    // read physical model
    String participantName = participantId.stringify();
    InstanceConfig instanceConfig =
        _accessor.getProperty(_keyBuilder.instanceConfig(participantName));

    if (instanceConfig == null) {
      LOG.error("Participant " + participantId + " is not present on the cluster");
      return null;
    }

    UserConfig userConfig = instanceConfig.getUserConfig();
    LiveInstance liveInstance = _accessor.getProperty(_keyBuilder.liveInstance(participantName));

    Map<String, Message> instanceMsgMap = Collections.emptyMap();
    Map<String, CurrentState> instanceCurStateMap = Collections.emptyMap();
    if (liveInstance != null) {
      SessionId sessionId = liveInstance.getTypedSessionId();

      instanceMsgMap = _accessor.getChildValuesMap(_keyBuilder.messages(participantName));
      instanceCurStateMap =
          _accessor.getChildValuesMap(_keyBuilder.currentStates(participantName,
              sessionId.stringify()));
    }

    return createParticipant(participantId, instanceConfig, userConfig, liveInstance,
        instanceMsgMap, instanceCurStateMap);
  }

  /**
   * Read a single snapshot of a resource
   * @param resourceId the resource id to read
   * @return Resource or null if not present
   */
  public Resource readResource(ResourceId resourceId) {
    ResourceConfiguration config =
        _accessor.getProperty(_keyBuilder.resourceConfig(resourceId.stringify()));
    IdealState idealState = _accessor.getProperty(_keyBuilder.idealStates(resourceId.stringify()));

    if (idealState == null) {
      LOG.error("Resource " + resourceId + " not present on the cluster");
      return null;
    }

    ExternalView externalView =
        _accessor.getProperty(_keyBuilder.externalView(resourceId.stringify()));
    ResourceAssignment resourceAssignment =
        _accessor.getProperty(_keyBuilder.resourceAssignment(resourceId.stringify()));
    return createResource(resourceId, config, idealState, externalView, resourceAssignment);
  }

  /**
   * Update a resource configuration
   * @param resourceId the resource id to update
   * @param resourceDelta changes to the resource
   * @return ResourceConfig, or null if the resource is not persisted
   */
  public ResourceConfig updateResource(ResourceId resourceId, ResourceConfig.Delta resourceDelta) {
    resourceDelta.merge(_accessor);
    Resource resource = readResource(resourceId);
    return (resource != null) ? resource.getConfig() : null;
  }

  /**
   * Create a resource snapshot instance from the physical model
   * @param resourceId the resource id
   * @param resourceConfiguration physical resource configuration
   * @param idealState ideal state of the resource
   * @param externalView external view of the resource
   * @param resourceAssignment current resource assignment
   * @return Resource
   */
  private static Resource createResource(ResourceId resourceId,
      ResourceConfiguration resourceConfiguration, IdealState idealState,
      ExternalView externalView, ResourceAssignment resourceAssignment) {
    UserConfig userConfig;
    ProvisionerConfig provisionerConfig = null;
    RebalancerConfig rebalancerConfig = null;
    if (resourceConfiguration != null) {
      userConfig = resourceConfiguration.getUserConfig();
      rebalancerConfig = resourceConfiguration.getRebalancerConfig(RebalancerConfig.class);
      provisionerConfig = resourceConfiguration.getProvisionerConfig(ProvisionerConfig.class);
    } else {
      userConfig = new UserConfig(Scope.resource(resourceId));
    }
    ResourceConfig resourceConfig =
        new ResourceConfig.Builder(resourceId).idealState(idealState)
            .rebalancerConfig(rebalancerConfig).provisionerConfig(provisionerConfig)
            .schedulerTaskConfig(Resource.schedulerTaskConfig(idealState)).userConfig(userConfig)
            .build();
    return new Resource(resourceConfig, resourceAssignment, externalView);
  }

  /**
   * Get the cluster ID this accessor is connected to
   * @return ClusterId
   */
  protected ClusterId clusterId() {
    return _clusterId;
  }

  /**
   * Get the accessor for the properties stored for this cluster
   * @return HelixDataAccessor
   */
  protected HelixDataAccessor dataAccessor() {
    return _accessor;
  }

  /**
   * Create empty persistent properties to ensure that there is a valid participant structure
   * @param participantId the identifier under which to initialize the structure
   * @return true if the participant structure exists at the end of this call, false otherwise
   */
  private boolean initParticipantStructure(ParticipantId participantId) {
    if (participantId == null) {
      LOG.error("Participant ID cannot be null when clearing the participant in cluster "
          + _clusterId + "!");
      return false;
    }
    List<String> paths =
        HelixUtil.getRequiredPathsForInstance(_clusterId.toString(), participantId.toString());
    BaseDataAccessor<?> baseAccessor = _accessor.getBaseDataAccessor();
    for (String path : paths) {
      boolean status = baseAccessor.create(path, null, AccessOption.PERSISTENT);
      if (!status) {
        if (LOG.isDebugEnabled()) {
          LOG.debug(path + " already exists");
        }
      }
    }
    return true;
  }

  /**
   * Clear properties for the participant
   * @param participantId the participant for which to clear
   * @return true if all paths removed, false otherwise
   */
  private boolean clearParticipantStructure(ParticipantId participantId) {
    List<String> paths =
        HelixUtil.getRequiredPathsForInstance(_clusterId.toString(), participantId.toString());
    BaseDataAccessor<?> baseAccessor = _accessor.getBaseDataAccessor();
    boolean[] removeResults = baseAccessor.remove(paths, 0);
    boolean result = true;
    for (boolean removeResult : removeResults) {
      result = result && removeResult;
    }
    return result;
  }

  /**
   * check if participant structure is valid
   * @return true if valid or false otherwise
   */
  private boolean isParticipantStructureValid(ParticipantId participantId) {
    List<String> paths =
        HelixUtil.getRequiredPathsForInstance(_clusterId.toString(), participantId.toString());
    BaseDataAccessor<?> baseAccessor = _accessor.getBaseDataAccessor();
    if (baseAccessor != null) {
      boolean[] existsResults = baseAccessor.exists(paths, 0);
      for (boolean exists : existsResults) {
        if (!exists) {
          return false;
        }
      }
    }
    return true;
  }
}
TOP

Related Classes of org.apache.helix.api.accessor.ClusterAccessor

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.