All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.admin.internal.GemFireHealthImpl Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed 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. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.admin.internal;

import com.gemstone.gemfire.CancelException;
import com.gemstone.gemfire.admin.*;
import com.gemstone.gemfire.internal.Assert;
import com.gemstone.gemfire.internal.admin.*;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

/**
 * Provides the implementation of the GemFireHealth
 * administration API.  This class is responsible for {@linkplain
 * GemFireVM#addHealthListener sending} the {@link
 * GemFireHealthConfig}s to the remote member VM in which the health
 * is calcualted.
 *
 * @author David Whitlock
 *
 * @since 3.5
 */
public class GemFireHealthImpl
  implements GemFireHealth, JoinLeaveListener, HealthListener {

  /** The distributed system whose health is being monitored */
  private final GfManagerAgent agent;

  /** The default configuration for checking GemFire health */
  protected GemFireHealthConfig defaultConfig;

  /** Maps the name of a host to its GemFireHealthConfig.
   * Note that the mappings are created lazily. */
  private final Map hostConfigs;

  /** Maps the name of a host to all of the members
   * (GemFireVMs) that run on that host. */
  private final Map hostMembers;

  /** The members that are known to be in {@link #OKAY_HEALTH}. */
  private Collection okayHealth;

  /** The members that are known to be in {@link #POOR_HEALTH}. */
  private Collection poorHealth;

  /** The overall health of GemFire */
  private GemFireHealth.Health overallHealth;

  /** Is this GemFireHealthImpl closed? */
  private boolean isClosed;

  /** The configuration specifying how the health of the distributed
   * system should be computed.  */
  protected volatile DistributedSystemHealthConfig dsHealthConfig;

  /** Monitors the health of the entire distributed system */
  private DistributedSystemHealthMonitor dsHealthMonitor = null;

  /** The distributed system whose health is monitored by this
   * GemFireHealth. */
  private final AdminDistributedSystem system;

  
  ///////////////////////  Constructors  ///////////////////////

  /**
   * Creates a new GemFireHealthImpl that monitors the
   * health of member of the given distributed system.
   */
  protected GemFireHealthImpl(GfManagerAgent agent,
                              AdminDistributedSystem system) {
//     agent.getDM().getLogger().info("Creating GemFireHealthImpl",
//                                    new Exception("Stack trace"));

    this.agent = agent;
    this.system = system;

    this.hostConfigs = new HashMap();
    this.hostMembers = new HashMap();
    this.okayHealth = new HashSet();
    this.poorHealth = new HashSet();
    this.overallHealth = GOOD_HEALTH;
    this.isClosed = false;

    GemFireVM[] apps = this.agent.listApplications();
    for (int i = 0; i < apps.length; i++) {
      GemFireVM member = apps[i];
      this.noteNewMember(member);
    }

    agent.addJoinLeaveListener(this);
    setDefaultGemFireHealthConfig(createGemFireHealthConfig(null));
    setDistributedSystemHealthConfig(createDistributedSystemHealthConfig());
  }

  @Override
  public String toString() {
    StringBuffer sb = new StringBuffer();
    sb.append("closed=" + isClosed);
    sb.append("; hostMembers=" + hostMembers);
    sb.append("; okayHealth=" + okayHealth);
    sb.append("; poorHealth=" + poorHealth);
    sb.append("; overallHealth=" + overallHealth);
    sb.append("; diagnosis=" + getDiagnosis());
    return sb.toString();
  }
  //////////////////////  Instance Methods  //////////////////////

  /**
   * Returns the DistributedSystem whose health this
   * GemFireHealth monitors.
   */
  public AdminDistributedSystem getDistributedSystem() {
    return this.system;
  }

  /**
   * A "template factory" method for creating a
   * DistributedSystemHealthConfig. It can be overridden
   * by subclasses to produce instances of different
   * DistributedSystemHealthConfig implementations.
   */
  protected DistributedSystemHealthConfig
    createDistributedSystemHealthConfig() {

    return new DistributedSystemHealthConfigImpl();
  }

  /**
   * A "template factory" method for creating a
   * GemFireHealthConfig.  It can be overridden by
   * subclasses to produce instances of different
   * GemFireHealthConfig implementations.
   *
   * @param hostName
   *        The host whose health we are configuring
   */
  protected GemFireHealthConfig
    createGemFireHealthConfig(String hostName) {

    return new GemFireHealthConfigImpl(hostName);
  }

  /**
   * Throws an {@link IllegalStateException} if this
   * GemFireHealthImpl is closed.
   */
  private void checkClosed() {
    if (this.isClosed) {
      throw new IllegalStateException(LocalizedStrings.GemFireHealthImpl_CANNOT_ACCESS_A_CLOSED_GEMFIREHEALTH_INSTANCE.toLocalizedString());
    }
  }

  /**
   * Returns the overall health of GemFire.  Note that this method
   * does not contact any of the member VMs.  Instead, it relies on
   * the members to alert it of changes in its health via a {@link
   * HealthListener}.
   */
  public GemFireHealth.Health getHealth() {
    checkClosed();
    return this.overallHealth;
  }

  /**
   * Resets the overall health to be {@link #GOOD_HEALTH}.  It also
   * resets the health in the member VMs.
   *
   * @see GemFireVM#resetHealthStatus
   */
  public void resetHealth() {
    checkClosed();

    this.overallHealth = GOOD_HEALTH;
    this.okayHealth.clear();
    this.poorHealth.clear();

    synchronized (this) {
      for (Iterator iter = hostMembers.values().iterator();
           iter.hasNext(); ) {
        List members = (List) iter.next();
        for (Iterator iter2 = members.iterator(); iter2.hasNext(); ) {
          GemFireVM member = (GemFireVM) iter2.next();
          member.resetHealthStatus();
        }
      }
    }
  }

  /**
   * Aggregates the diagnoses from all members of the distributed
   * system. 
   */
  public String getDiagnosis() {
    checkClosed();

    StringBuilder sb = new StringBuilder();

    synchronized (this) {
      for (Iterator iter = hostMembers.values().iterator();
           iter.hasNext(); ) {
        List members = (List) iter.next();
        for (Iterator iter2 = members.iterator(); iter2.hasNext(); ) {
          GemFireVM member = (GemFireVM) iter2.next();
          String[] diagnoses =
            member.getHealthDiagnosis(this.overallHealth);
          for (int i = 0; i < diagnoses.length; i++) {
            sb.append(diagnoses[i]).append("\n");;
          }
        }
      }
    }

    return sb.toString();
  }

  /**
   * Starts a new {@link DistributedSystemHealthMonitor}
   */
  public void setDistributedSystemHealthConfig(DistributedSystemHealthConfig
                                               config) {
    synchronized (this.hostConfigs) {
      // If too many threads are changing the health config, then we
      // will might get an OutOfMemoryError trying to start a new
      // health monitor thread.

      if (this.dsHealthMonitor != null) {
        this.dsHealthMonitor.stop();
      }

      this.dsHealthConfig = config;

      DistributedSystemHealthEvaluator eval =
        new DistributedSystemHealthEvaluator(config, this.agent.getDM());
      int interval =
        this.getDefaultGemFireHealthConfig().getHealthEvaluationInterval();
      this.dsHealthMonitor =
        new DistributedSystemHealthMonitor(eval, this, interval,
                                           this.agent.getDM().getLoggerI18n());
      this.dsHealthMonitor.start();
    }
  }

  public DistributedSystemHealthConfig
    getDistributedSystemHealthConfig() {

    checkClosed();
    return this.dsHealthConfig;
  }

  public GemFireHealthConfig getDefaultGemFireHealthConfig() {
    checkClosed();
    return this.defaultConfig;
  }

  public void setDefaultGemFireHealthConfig(GemFireHealthConfig config) {
    checkClosed();

    if (config.getHostName() != null) {
      throw new IllegalArgumentException(LocalizedStrings.GemFireHealthImpl_THE_GEMFIREHEALTHCONFIG_FOR_FOR_0_CANNOT_SERVE_AS_THE_DEFAULT_HEALTH_CONFIG.toLocalizedString(config.getHostName()));
    }

    this.defaultConfig = config;

    synchronized (this) {
      for (Iterator iter = this.hostMembers.entrySet().iterator();
           iter.hasNext(); ) {
        Map.Entry entry = (Map.Entry) iter.next();
        InetAddress hostIpAddress = (InetAddress) entry.getKey();
        List members = (List) entry.getValue();

        GemFireHealthConfig hostConfig =
          (GemFireHealthConfig) hostConfigs.get(hostIpAddress);
        if (hostConfig == null) {
          hostConfig = config;
        }

        for (Iterator iter2 = members.iterator(); iter2.hasNext(); ) {
          GemFireVM member = (GemFireVM) iter2.next();
          Assert.assertTrue(member.getHost().equals(hostIpAddress));
          member.addHealthListener(this, hostConfig);
        }
      }
    }

    // We only need to do this if the health monitoring interval has
    // change.  This is probably not the most efficient way of doing
    // things.
    if (this.dsHealthConfig != null) {
      setDistributedSystemHealthConfig(this.dsHealthConfig);
    }
  }

  /**
   * Returns the GemFireHealthConfig object for the given host name.
   * 
   * @param hostName
   *          host name for which the GemFire Health Config is needed
   * 
   * @throws IllegalArgumentException
   *           if host with given name could not be found
   */
  public synchronized GemFireHealthConfig
    getGemFireHealthConfig(String hostName){

    checkClosed();

    InetAddress hostIpAddress = null;
    try {
      hostIpAddress = InetAddress.getByName(hostName);
    } catch (UnknownHostException e) {
      throw new IllegalArgumentException(
          LocalizedStrings.GemFireHealthImpl_COULD_NOT_FIND_A_HOST_WITH_NAME_0
              .toLocalizedString(hostName), e);
    }
    
    GemFireHealthConfig config =
      (GemFireHealthConfig) this.hostConfigs.get(hostIpAddress);
    if (config == null) {
      config = createGemFireHealthConfig(hostName);
      this.hostConfigs.put(hostIpAddress, config);
    }

    return config;
  }

  /**
   * Sets the GemFireHealthConfig object for the given host name.
   * 
   * @param hostName
   *          host name for which the GemFire Health Config is needed
   * @param config
   *          GemFireHealthConfig object to set
   * 
   * @throws IllegalArgumentException
   *           if (1) given host name & the host name in the given config do not
   *           match OR (2) host with given name could not be found OR (3) there
   *           are no GemFire components running on the given host
   */
  public void setGemFireHealthConfig(String hostName,
                                     GemFireHealthConfig config) {
    checkClosed();

    synchronized (this) {
      String configHost = config.getHostName();
      if (configHost == null || !configHost.equals(hostName)) {
        StringBuffer sb = new StringBuffer();
        sb.append("The GemFireHealthConfig configures ");
        if (configHost == null) {
          sb.append("the default host ");

        } else {
          sb.append("host \"");
          sb.append(config.getHostName());
          sb.append("\" ");
        }
        sb.append("not \"" + hostName + "\"");
        throw new IllegalArgumentException(sb.toString());
      }
      InetAddress hostIpAddress = null;
      try {
        hostIpAddress = InetAddress.getByName(hostName);
      } catch (UnknownHostException e) {
        throw new IllegalArgumentException(
            LocalizedStrings.GemFireHealthImpl_COULD_NOT_FIND_A_HOST_WITH_NAME_0
                .toLocalizedString(hostName), e);
      }
      
      List members = (List) this.hostMembers.get(hostIpAddress);
      if (members == null || members.isEmpty()) {
        throw new IllegalArgumentException(
            LocalizedStrings.GemFireHealthImpl_THERE_ARE_NO_GEMFIRE_COMPONENTS_ON_HOST_0
                .toLocalizedString(hostName));
      }

      for (Iterator iter = members.iterator(); iter.hasNext(); ) {
        GemFireVM member = (GemFireVM) iter.next();
        member.addHealthListener(this, config);
      }
    }
  }

  /**
   * Tells the members of the distributed system that we are no longer
   * interested in monitoring their health.
   *
   * @see GemFireVM#removeHealthListener
   */
  public void close(){
    this.agent.removeJoinLeaveListener(this);
    
    synchronized (this) {
      if (this.isClosed) {
        return;
      }

      this.isClosed = true;

      if (this.dsHealthMonitor != null) {
        this.dsHealthMonitor.stop();
        this.dsHealthMonitor = null;
      }

      try {
        for (Iterator iter = hostMembers.values().iterator();
             iter.hasNext(); ) {
          List members = (List) iter.next();
          for (Iterator iter2 = members.iterator(); iter2.hasNext(); ) {
            GemFireVM member = (GemFireVM) iter2.next();
            member.removeHealthListener();
          }
        }
      } catch (CancelException e) {
        // if the DS is disconnected, stop trying to distribute to other members
      }

      hostConfigs.clear();
      hostMembers.clear();
      okayHealth.clear();
      poorHealth.clear();
    }
  }

  public boolean isClosed() {
    return this.isClosed;
  }

  /**
   * Makes note of the newly-joined member
   */
  private void noteNewMember(GemFireVM member) {
    InetAddress hostIpAddress = member.getHost();
    List members = (List) this.hostMembers.get(hostIpAddress);
    if (members == null) {
      members = new ArrayList();
      this.hostMembers.put(hostIpAddress, members);
    }
    members.add(member);

  }

  public synchronized void nodeJoined(GfManagerAgent source,
                                      GemFireVM joined) {
    noteNewMember(joined);

    InetAddress hostIpAddress = joined.getHost();

    GemFireHealthConfig config =
      (GemFireHealthConfig) this.hostConfigs.get(hostIpAddress);
    if (config == null) {
      config = this.getDefaultGemFireHealthConfig();
    }
    joined.addHealthListener(this, config);
  }

  /**
   * Makes note of the newly-left member
   */
  public synchronized void nodeLeft(GfManagerAgent source,
                                    GemFireVM left) {
    InetAddress hostIpAddress = left.getHost();
    List members = (List) this.hostMembers.get(hostIpAddress);
    if (members != null) {
      members.remove(left);
      if (members.isEmpty()) {
        // No more members on the host
        this.hostConfigs.remove(hostIpAddress);
        this.hostMembers.remove(hostIpAddress);
      }
    }

    this.okayHealth.remove(left);
    this.poorHealth.remove(left);

    reevaluateHealth();
  }

  /**
   * Does the same thing as {@link #nodeLeft}
   */
  public void nodeCrashed(GfManagerAgent source, GemFireVM crashed) {
    nodeLeft(source, crashed);
  }

  /**
   * Re-evaluates the overall health of GemFire
   */
  private void reevaluateHealth() {
    if (!this.poorHealth.isEmpty()) {
      this.overallHealth = POOR_HEALTH;

    } else if (!this.okayHealth.isEmpty()) {
      this.overallHealth = OKAY_HEALTH;

    } else {
      this.overallHealth = GOOD_HEALTH;
    }
  }

  public void healthChanged(GemFireVM member, GemFireHealth.Health status) {
    if (status == GOOD_HEALTH) {
      this.okayHealth.remove(member);
      this.poorHealth.remove(member);

    } else if (status == OKAY_HEALTH) {
      this.okayHealth.add(member);
      this.poorHealth.remove(member);

    } else if (status == POOR_HEALTH) {
      this.okayHealth.remove(member);
      this.poorHealth.add(member);

    } else {
      Assert.assertTrue(false, "Unknown health code: " + status);
    }

    reevaluateHealth();
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy