You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by rv...@apache.org on 2015/04/28 23:40:45 UTC

[40/51] [partial] incubator-geode git commit: Init

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributedSystemHealthMonitor.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributedSystemHealthMonitor.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributedSystemHealthMonitor.java
new file mode 100644
index 0000000..ca7f067
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributedSystemHealthMonitor.java
@@ -0,0 +1,428 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.admin.internal;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.logging.log4j.Logger;
+
+import com.gemstone.gemfire.SystemFailure;
+import com.gemstone.gemfire.admin.AdminException;
+import com.gemstone.gemfire.admin.GemFireHealth;
+import com.gemstone.gemfire.admin.GemFireHealthConfig;
+import com.gemstone.gemfire.admin.GemFireMemberStatus;
+import com.gemstone.gemfire.admin.RegionSubRegionSnapshot;
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.cache.RegionAttributes;
+import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
+import com.gemstone.gemfire.internal.Assert;
+import com.gemstone.gemfire.internal.Config;
+import com.gemstone.gemfire.internal.SocketCreator;
+import com.gemstone.gemfire.internal.admin.AdminBridgeServer;
+import com.gemstone.gemfire.internal.admin.CacheInfo;
+import com.gemstone.gemfire.internal.admin.DLockInfo;
+import com.gemstone.gemfire.internal.admin.GemFireVM;
+import com.gemstone.gemfire.internal.admin.GfManagerAgent;
+import com.gemstone.gemfire.internal.admin.HealthListener;
+import com.gemstone.gemfire.internal.admin.Stat;
+import com.gemstone.gemfire.internal.admin.StatAlertDefinition;
+import com.gemstone.gemfire.internal.admin.StatListener;
+import com.gemstone.gemfire.internal.admin.StatResource;
+import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
+import com.gemstone.gemfire.internal.logging.LogService;
+import com.gemstone.gemfire.internal.logging.LoggingThreadGroup;
+import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage;
+
+/**
+ * A thread that monitors the health of the distributed system.  It is
+ * kind of like a {@link
+ * com.gemstone.gemfire.distributed.internal.HealthMonitorImpl}.  In
+ * order to get it to place nice with the rest of the health
+ * monitoring APIs, this class pretends that it is a
+ * <code>GemFireVM</code>.  Kind of hokey, but it beats a bunch of
+ * special-case code.
+ *
+ * @author David Whitlock
+ *
+ * @since 3.5
+ * */
+class DistributedSystemHealthMonitor implements Runnable, GemFireVM {
+
+  private static final Logger logger = LogService.getLogger();
+  
+  /** Evaluates the health of the distributed system */
+  private DistributedSystemHealthEvaluator eval;
+
+  /** Notified when the health of the distributed system changes */
+  private GemFireHealthImpl healthImpl;
+
+  /** The number of seconds between health checks */
+  private int interval;
+
+  /** The thread in which the monitoring occurs */
+  private Thread thread;
+
+  /** Has this monitor been asked to stop? */
+  private volatile boolean stopRequested = false;
+
+  /** The health of the distributed system the last time we checked. */
+  private GemFireHealth.Health prevHealth = GemFireHealth.GOOD_HEALTH;
+
+  /** The most recent <code>OKAY_HEALTH</code> diagnoses of the
+   * GemFire system */
+  private List okayDiagnoses;
+
+  /** The most recent <code>POOR_HEALTH</code> diagnoses of the
+   * GemFire system */
+  private List poorDiagnoses;
+
+  //////////////////////  Constructors  //////////////////////
+
+  /**
+   * Creates a new <code>DistributedSystemHealthMonitor</code> that
+   * evaluates the health of the distributed system against the given
+   * thresholds once every <code>interval</code> seconds.
+   *
+   * @param eval
+   *        Used to evaluate the health of the distributed system
+   * @param healthImpl
+   *        Receives callbacks when the health of the distributed
+   *        system changes
+   * @param interval
+   *        How often the health is checked
+   */
+  DistributedSystemHealthMonitor(DistributedSystemHealthEvaluator eval,
+                                 GemFireHealthImpl healthImpl,
+                                 int interval) {
+    this.eval = eval;
+    this.healthImpl = healthImpl;
+    this.interval = interval;
+    this.okayDiagnoses = new ArrayList();
+    this.poorDiagnoses = new ArrayList();
+
+    ThreadGroup group =
+      LoggingThreadGroup.createThreadGroup(LocalizedStrings.DistributedSystemHealthMonitor_HEALTH_MONITORS.toLocalizedString(), logger);
+    String name = LocalizedStrings.DistributedSystemHealthMonitor_HEALTH_MONITOR_FOR_0.toLocalizedString(eval.getDescription());
+    this.thread = new Thread(group, this, name);
+    this.thread.setDaemon(true);
+  }
+
+  /**
+   * Does the work of monitoring the health of the distributed
+   * system. 
+   */
+  public void run() {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Monitoring health of {} every {} seconds", this.eval.getDescription(), interval);
+    }
+
+    while (!this.stopRequested) {
+      SystemFailure.checkFailure();
+      try {
+        Thread.sleep(interval * 1000);
+        List status = new ArrayList();
+        eval.evaluate(status);
+
+        GemFireHealth.Health overallHealth = GemFireHealth.GOOD_HEALTH;
+        this.okayDiagnoses.clear();
+        this.poorDiagnoses.clear();
+
+        for (Iterator iter = status.iterator(); iter.hasNext(); ) {
+          AbstractHealthEvaluator.HealthStatus health =
+            (AbstractHealthEvaluator.HealthStatus) iter.next();
+          if (overallHealth == GemFireHealth.GOOD_HEALTH) {
+            if ((health.getHealthCode() != GemFireHealth.GOOD_HEALTH)) {
+              overallHealth = health.getHealthCode();
+            }
+
+          } else if (overallHealth == GemFireHealth.OKAY_HEALTH) {
+            if (health.getHealthCode() == GemFireHealth.POOR_HEALTH) {
+              overallHealth = GemFireHealth.POOR_HEALTH;
+            }
+          }
+
+          GemFireHealth.Health healthCode = health.getHealthCode();
+          if (healthCode == GemFireHealth.OKAY_HEALTH) {
+            this.okayDiagnoses.add(health.getDiagnosis());
+
+          } else if (healthCode == GemFireHealth.POOR_HEALTH) {
+            this.poorDiagnoses.add(health.getDiagnosis());
+            break;
+          }
+        }
+        
+        if (overallHealth != prevHealth) {
+          healthImpl.healthChanged(this, overallHealth);
+          this.prevHealth = overallHealth;
+        }
+        
+      } catch (InterruptedException ex) {
+        // We're all done
+        // No need to reset the interrupted flag, since we're going to exit.
+        break;
+      }
+    }
+
+    eval.close();
+    if (logger.isDebugEnabled()) {
+      logger.debug("Stopped checking for distributed system health");
+    }
+  }
+
+  /**
+   * Starts this <code>DistributedSystemHealthMonitor</code>
+   */
+  void start(){
+    this.thread.start();
+  }
+
+  /**
+   * Stops this <code>DistributedSystemHealthMonitor</code>
+   */
+  void stop() {
+    if (this.thread.isAlive()) {
+      this.stopRequested = true;
+      this.thread.interrupt();
+      this.healthImpl.nodeLeft(null, this);
+
+      try {
+        this.thread.join();
+      } 
+      catch (InterruptedException ex) {
+        Thread.currentThread().interrupt();
+        logger.warn(LocalizedMessage.create(LocalizedStrings.DistributedSystemHealthMonitor_INTERRUPTED_WHILE_STOPPING_HEALTH_MONITOR_THREAD), ex);
+      }
+    }
+  }
+
+  //////////////////////  GemFireVM Methods  //////////////////////
+
+  public java.net.InetAddress getHost() {
+    try {
+      return SocketCreator.getLocalHost();
+
+    } catch (Exception ex) {
+      throw new com.gemstone.gemfire.InternalGemFireException(LocalizedStrings.DistributedSystemHealthMonitor_COULD_NOT_GET_LOCALHOST.toLocalizedString());
+    }
+  }
+  
+  public String getName() {
+//    return getId().toString();
+    throw new UnsupportedOperationException("Not a real GemFireVM");
+  }
+
+  public java.io.File getWorkingDirectory() {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public java.io.File getGemFireDir() {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+  
+  public java.util.Date getBirthDate() {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public Properties getLicenseInfo(){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public GemFireMemberStatus getSnapshot() {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public RegionSubRegionSnapshot getRegionSnapshot() {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public StatResource[] getStats(String statisticsTypeName){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public StatResource[] getAllStats(){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+   
+  public DLockInfo[] getDistributedLockInfo(){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public void addStatListener(StatListener observer,
+                              StatResource observedResource,
+                              Stat observedStat){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+  
+  public void removeStatListener(StatListener observer){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }  
+
+  public void addHealthListener(HealthListener observer,
+                                GemFireHealthConfig cfg){
+
+  }
+  
+  public void removeHealthListener(){
+
+  }
+
+  public void resetHealthStatus(){
+    this.prevHealth = GemFireHealth.GOOD_HEALTH;
+  }
+
+  public String[] getHealthDiagnosis(GemFireHealth.Health healthCode){
+    if (healthCode == GemFireHealth.GOOD_HEALTH) {
+      return new String[0];
+
+    } else if (healthCode == GemFireHealth.OKAY_HEALTH) {
+      String[] array = new String[this.okayDiagnoses.size()];
+      this.okayDiagnoses.toArray(array);
+      return array;
+
+    } else {
+      Assert.assertTrue(healthCode == GemFireHealth.POOR_HEALTH);
+      String[] array = new String[this.poorDiagnoses.size()];
+      this.poorDiagnoses.toArray(array);
+      return array;
+    }
+  }
+
+  public Config getConfig(){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public void setConfig(Config cfg){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public GfManagerAgent getManagerAgent(){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+  
+  public String[] getSystemLogs(){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public void setInspectionClasspath(String classpath){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+  
+  public String getInspectionClasspath(){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+  
+  public Region[] getRootRegions(){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public Region getRegion(CacheInfo c, String path) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public Region createVMRootRegion(CacheInfo c, String name,
+                                   RegionAttributes attrs) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public Region createSubregion(CacheInfo c, String parentPath,
+                                String name, RegionAttributes attrs) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public void setCacheInspectionMode(int mode) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public int getCacheInspectionMode(){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public void takeRegionSnapshot(String regionName, int snapshotId){
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public InternalDistributedMember getId() {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public CacheInfo getCacheInfo() {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public String getVersionInfo() {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public CacheInfo setCacheLockTimeout(CacheInfo c, int v) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public CacheInfo setCacheLockLease(CacheInfo c, int v) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public CacheInfo setCacheSearchTimeout(CacheInfo c, int v) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public AdminBridgeServer addBridgeServer(CacheInfo cache)
+    throws AdminException {
+
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public AdminBridgeServer getBridgeInfo(CacheInfo cache, 
+                                         int id)
+    throws AdminException {
+
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public AdminBridgeServer startBridgeServer(CacheInfo cache,
+                                             AdminBridgeServer bridge)
+    throws AdminException {
+
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  public AdminBridgeServer stopBridgeServer(CacheInfo cache,
+                                            AdminBridgeServer bridge)
+    throws AdminException {
+
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  /**
+   * This operation is not supported for this object. Will throw 
+   * UnsupportedOperationException if invoked.
+   */
+  public void setAlertsManager(StatAlertDefinition[] alertDefs, 
+      long refreshInterval, boolean setRemotely) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  /**
+   * This operation is not supported for this object. Will throw 
+   * UnsupportedOperationException if invoked.
+   */
+  public void setRefreshInterval(long refreshInterval) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+
+  /**
+   * This operation is not supported for this object. Will throw 
+   * UnsupportedOperationException if invoked.
+   */
+  public void updateAlertDefinitions(StatAlertDefinition[] alertDefs,
+      int actionCode) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributionLocatorConfigImpl.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributionLocatorConfigImpl.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributionLocatorConfigImpl.java
new file mode 100644
index 0000000..1e3e7e8
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributionLocatorConfigImpl.java
@@ -0,0 +1,183 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.admin.internal;
+
+import com.gemstone.gemfire.admin.DistributionLocator;
+import com.gemstone.gemfire.admin.DistributionLocatorConfig;
+import com.gemstone.gemfire.distributed.internal.InternalLocator;
+import com.gemstone.gemfire.distributed.internal.DistributionConfig;
+import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
+
+import java.net.InetAddress;
+import java.util.Properties;
+
+/**
+ * Provides an implementation of
+ * <code>DistributionLocatorConfig</code>.
+ *
+ * @author David Whitlock
+ * @since 4.0
+ */
+public class DistributionLocatorConfigImpl 
+  extends ManagedEntityConfigImpl 
+  implements DistributionLocatorConfig {
+
+  /** The minimum networking port (0) */
+  public static final int MIN_PORT = 0;
+
+  /** The maximum networking port (65535) */
+  public static final int MAX_PORT = 65535;
+  
+  //////////////////////  Instance Fields  //////////////////////
+
+  /** The port on which this locator listens */
+  private int port;
+
+  /** The address to bind to on a multi-homed host */
+  private String bindAddress;
+  
+  /** The properties used to configure the DistributionLocator's
+      DistributedSystem */
+  private Properties dsProperties;
+
+  /** The DistributionLocator that was created with this config */
+  private DistributionLocator locator;
+
+  //////////////////////  Static Methods  //////////////////////
+
+  /**
+   * Contacts a distribution locator on the given host and port and
+   * creates a <code>DistributionLocatorConfig</code> for it.
+   *
+   * @see InternalLocator#getLocatorInfo
+   *
+   * @return <code>null</code> if the locator cannot be contacted
+   */
+  static DistributionLocatorConfig
+    createConfigFor(String host, int port, InetAddress bindAddress) {
+
+    String[] info = null;
+    if (bindAddress != null) {
+      info = InternalLocator.getLocatorInfo(bindAddress, port);
+    }
+    else {
+      info = InternalLocator.getLocatorInfo(InetAddressUtil.toInetAddress(host), port);
+    }
+    if (info == null) {
+      return null;
+    }
+
+    DistributionLocatorConfigImpl config =
+      new DistributionLocatorConfigImpl();
+    config.setHost(host);
+    config.setPort(port);
+    if (bindAddress != null) {
+      config.setBindAddress(bindAddress.getHostAddress());
+    }
+    config.setWorkingDirectory(info[0]);
+    config.setProductDirectory(info[1]);
+
+    return config;
+  }
+
+  ///////////////////////  Constructors  ///////////////////////
+
+  /**
+   * Creates a new <code>DistributionLocatorConfigImpl</code> with the
+   * default settings.
+   */
+  public DistributionLocatorConfigImpl() {
+    this.port = 0;
+    this.bindAddress = null;
+    this.locator = null;
+    this.dsProperties = new java.util.Properties();
+    this.dsProperties.setProperty(DistributionConfig.MCAST_PORT_NAME, "0");
+  }
+
+  /////////////////////  Instance Methods  /////////////////////
+
+  /**
+   * Sets the locator that was configured with this
+   * <Code>DistributionLocatorConfigImpl</code>. 
+   */
+  void setLocator(DistributionLocator locator) {
+    this.locator = locator;
+  }
+
+  @Override
+  protected boolean isReadOnly() {
+    return this.locator != null && this.locator.isRunning();
+  }
+
+  public int getPort() {
+    return this.port;
+  }
+
+  public void setPort(int port) {
+    checkReadOnly();
+    this.port = port;
+    configChanged();
+  }
+
+  public String getBindAddress() {
+    return this.bindAddress;
+  }
+
+  public void setBindAddress(String bindAddress) {
+    checkReadOnly();
+    this.bindAddress = bindAddress;
+    configChanged();
+  }
+  
+  public void setDistributedSystemProperties(Properties props) {
+    this.dsProperties = props;
+  }
+  
+  public Properties getDistributedSystemProperties() {
+    return this.dsProperties;
+  }
+
+  @Override
+  public void validate() {
+    super.validate();
+
+    if (port < MIN_PORT || port > MAX_PORT) {
+      throw new IllegalArgumentException(LocalizedStrings.DistributionLocatorConfigImpl_PORT_0_MUST_BE_AN_INTEGER_BETWEEN_1_AND_2.toLocalizedString(new Object[] {Integer.valueOf(port), Integer.valueOf(MIN_PORT), Integer.valueOf(MAX_PORT)}));
+    }
+
+    if (this.bindAddress != null &&
+        InetAddressUtil.validateHost(this.bindAddress) == null) {
+      throw new IllegalArgumentException(LocalizedStrings.DistributionLocatorConfigImpl_INVALID_HOST_0.toLocalizedString(this.bindAddress));
+    }
+  }
+
+  /**
+   * Currently, listeners are not supported on the locator config.
+   */
+  @Override
+  protected void configChanged() {
+
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DistributionLocatorConfigImpl clone =
+      (DistributionLocatorConfigImpl) super.clone();
+    clone.locator = null;
+    return clone;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("DistributionLocatorConfig: host=").append(getHost());
+    sb.append(", bindAddress=").append(getBindAddress());
+    sb.append(", port=").append(getPort());
+    return sb.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributionLocatorImpl.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributionLocatorImpl.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributionLocatorImpl.java
new file mode 100755
index 0000000..d970816
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/DistributionLocatorImpl.java
@@ -0,0 +1,322 @@
+/*=========================================================================
+ * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+
+package com.gemstone.gemfire.admin.internal;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.logging.log4j.Logger;
+
+import com.gemstone.gemfire.admin.AdminDistributedSystem;
+import com.gemstone.gemfire.admin.DistributionLocator;
+import com.gemstone.gemfire.admin.DistributionLocatorConfig;
+import com.gemstone.gemfire.admin.ManagedEntityConfig;
+import com.gemstone.gemfire.distributed.internal.DM;
+import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
+import com.gemstone.gemfire.internal.admin.remote.DistributionLocatorId;
+import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
+import com.gemstone.gemfire.internal.logging.LogService;
+import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage;
+
+/**
+ * Default administrative implementation of a DistributionLocator.
+ *
+ * @author    Kirk Lund
+ * @since     3.5
+ */
+public class DistributionLocatorImpl
+  implements DistributionLocator, InternalManagedEntity {
+  
+  private static final Logger logger = LogService.getLogger();
+  
+  /** How many new <code>DistributionLocator</code>s have been created? */
+  private static int newLocators = 0;
+
+  ////////////////////  Instance Fields  ////////////////////
+
+  /** The configuration object for this locator */
+  private final DistributionLocatorConfigImpl config;
+
+  /** The id of this distribution locator */
+  private final String id;
+  
+  /** Used to control the actual DistributionLocator service */
+  private ManagedEntityController controller;
+
+  /** The system that this locator is a part of */
+  private AdminDistributedSystemImpl system;
+
+  // -------------------------------------------------------------------------
+  //   constructor(s)...
+  // -------------------------------------------------------------------------
+  
+  /**
+   * Constructs new instance of <code>DistributionLocatorImpl</code>
+   * that is a member of the given distributed system.
+   */
+  public DistributionLocatorImpl(DistributionLocatorConfig config,
+                                 AdminDistributedSystemImpl system) {
+    this.config = (DistributionLocatorConfigImpl) config;
+    this.config.validate();
+    this.config.setManagedEntity(this);
+    this.id = getNewId();
+    this.controller = system.getEntityController();
+    this.system = system;
+  }
+  
+  // -------------------------------------------------------------------------
+  //   Attribute accessors/mutators...
+  // -------------------------------------------------------------------------
+  
+        public String getId() {
+                return this.id;
+        }
+
+  public String getNewId() {
+    synchronized (DistributionLocatorImpl.class) {
+      return "Locator" + (++newLocators);
+    }
+  }
+
+  /**
+   * Returns the configuration object for this locator.
+   *
+   * @since 4.0
+   */
+  public DistributionLocatorConfig getConfig() {
+    return this.config;
+  }
+
+  public AdminDistributedSystem getDistributedSystem() {
+    return this.system;
+  }
+
+  /**
+   * Unfortunately, it doesn't make much sense to maintain the state
+   * of a locator.  The admin API does not receive notification when
+   * the locator actually starts and stops.  If we try to guess, we'll
+   * just end up with race conditions galore.  So, we can't fix bug
+   * 32455 for locators.
+   */
+  public int setState(int state) {
+    throw new UnsupportedOperationException(LocalizedStrings.DistributionLocatorImpl_CAN_NOT_SET_THE_STATE_OF_A_LOCATOR.toLocalizedString());
+  }
+
+  // -------------------------------------------------------------------------
+  //   Operations...
+  // -------------------------------------------------------------------------
+  
+  /**
+   * Polls to determine whether or not this managed entity has
+   * started.
+   */
+  public boolean waitToStart(long timeout) 
+    throws InterruptedException {
+
+    if (Thread.interrupted()) throw new InterruptedException();
+    
+    long start = System.currentTimeMillis();
+    while (System.currentTimeMillis() - start < timeout) {
+      if (this.isRunning()) {
+        return true;
+
+      } else {
+        Thread.sleep(100);
+      }
+    }
+
+    logger.info(LocalizedMessage.create(
+      LocalizedStrings.DistributionLocatorImpl_DONE_WAITING_FOR_LOCATOR));
+    return this.isRunning();
+  }
+
+  /**
+   * Polls to determine whether or not this managed entity has
+   * stopped.
+   */
+  public boolean waitToStop(long timeout) 
+    throws InterruptedException {
+
+    if (Thread.interrupted()) throw new InterruptedException();
+    
+    long start = System.currentTimeMillis();
+    while (System.currentTimeMillis() - start < timeout) {
+      if (!this.isRunning()) {
+        return true;
+
+      } else {
+        Thread.sleep(100);
+      }
+    }
+
+    return !this.isRunning();
+  }
+
+  public boolean isRunning() {
+    DM dm = ((AdminDistributedSystemImpl)getDistributedSystem()).getDistributionManager();
+    if(dm == null) {
+      try {
+        return this.controller.isRunning(this);
+      }
+      catch (IllegalStateException e) {
+        return false;
+      }
+    }
+    
+    String host = getConfig().getHost();
+    int port = getConfig().getPort();
+    String bindAddress = getConfig().getBindAddress();
+    
+    boolean found = false;
+    Map<InternalDistributedMember, Collection<String>> hostedLocators = dm.getAllHostedLocators();
+    for (Iterator<InternalDistributedMember> memberIter = hostedLocators.keySet().iterator(); memberIter.hasNext();) {
+      for (Iterator<String> locatorIter = hostedLocators.get(memberIter.next()).iterator(); locatorIter.hasNext();) {
+        DistributionLocatorId locator = new DistributionLocatorId(locatorIter.next());
+        found = found || locator.getHost().getHostAddress().equals(host);
+        found = found || locator.getHost().getHostName().equals(host);
+        if (!found && !host.contains(".")) {
+          try {
+            InetAddress inetAddr = InetAddress.getByName(host);
+            found = locator.getHost().getHostName().equals(inetAddr.getHostName());
+            if (!found) {
+              found = locator.getHost().getHostAddress().equals(inetAddr.getHostAddress());
+            }
+          }
+          catch (UnknownHostException e) {
+            // try config host as if it is an IP address instead of host name
+          }
+        }
+        if (locator.getBindAddress() != null && !locator.getBindAddress().isEmpty()
+            && bindAddress != null && !bindAddress.isEmpty()) {
+          found = found && locator.getBindAddress().equals(bindAddress);
+        }
+        found = found && locator.getPort() == port;
+        if (found) {
+          return true;
+        }
+      }
+    }
+    return found;
+  }
+  
+  public void start() {
+    this.config.validate();
+    this.controller.start(this);
+    this.config.setLocator(this);
+    this.system.updateLocatorsString();
+  }
+  
+  public void stop() {
+    this.controller.stop(this);
+    this.config.setLocator(null);
+  }
+  
+  public String getLog() {
+    return this.controller.getLog(this);
+  }
+  
+	/**
+	 * Returns a string representation of the object.
+	 * 
+	 * @return a string representation of the object
+	 */
+  @Override
+	public String toString() {
+		return "DistributionLocator " + getId();
+	}
+
+  ////////////////////////  Command execution  ////////////////////////
+
+  public ManagedEntityConfig getEntityConfig() {
+    return this.getConfig();
+  }
+
+  public String getEntityType() {
+    return "Locator";
+  }
+
+  public String getStartCommand() {
+    StringBuffer sb = new StringBuffer();
+    sb.append(this.controller.getProductExecutable(this, "gemfire"));
+    sb.append(" start-locator -q -dir=");
+    sb.append(this.getConfig().getWorkingDirectory());
+    sb.append(" -port=");
+    sb.append(this.getConfig().getPort());
+    Properties props = config.getDistributedSystemProperties();
+    Enumeration en = props.propertyNames();
+    while (en.hasMoreElements()) {
+      String pn = (String)en.nextElement();
+      sb.append(" -Dgemfire." + pn + "=" + props.getProperty(pn));
+    }
+
+    String bindAddress = this.getConfig().getBindAddress();
+    if (bindAddress != null && bindAddress.length() > 0) {
+      sb.append(" -address=");
+      sb.append(this.getConfig().getBindAddress());
+    }
+    sb.append(" ");
+
+    String sslArgs =
+      this.controller.buildSSLArguments(this.system.getConfig());
+    if (sslArgs != null) {
+      sb.append(sslArgs);
+    }
+
+    return sb.toString().trim();
+  }
+
+  public String getStopCommand() {
+    StringBuffer sb = new StringBuffer();
+    sb.append(this.controller.getProductExecutable(this, "gemfire"));
+    sb.append(" stop-locator -q -dir=");
+    sb.append(this.getConfig().getWorkingDirectory());
+    sb.append(" -port=");
+    sb.append(this.getConfig().getPort());
+
+    String bindAddress = this.getConfig().getBindAddress();
+    if (bindAddress != null && bindAddress.length() > 0) {
+      sb.append(" -address=");
+      sb.append(this.getConfig().getBindAddress());
+    }
+    sb.append(" ");
+
+    String sslArgs =
+      this.controller.buildSSLArguments(this.system.getConfig());
+    if (sslArgs != null) {
+      sb.append(sslArgs);
+    }
+
+    return sb.toString().trim();
+  }
+
+  public String getIsRunningCommand() {
+    StringBuffer sb = new StringBuffer();
+    sb.append(this.controller.getProductExecutable(this, "gemfire"));
+    sb.append(" status-locator -dir=");
+    sb.append(this.getConfig().getWorkingDirectory());
+
+    return sb.toString().trim();
+  }
+
+  public String getLogCommand() {
+    StringBuffer sb = new StringBuffer();
+    sb.append(this.controller.getProductExecutable(this, "gemfire"));
+    sb.append(" tail-locator-log -dir=");
+    sb.append(this.getConfig().getWorkingDirectory());
+
+    return sb.toString().trim();
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/EnabledManagedEntityController.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/EnabledManagedEntityController.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/EnabledManagedEntityController.java
new file mode 100755
index 0000000..8555f96
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/EnabledManagedEntityController.java
@@ -0,0 +1,403 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.admin.internal;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.logging.log4j.Logger;
+
+import com.gemstone.gemfire.admin.AdminDistributedSystem;
+import com.gemstone.gemfire.admin.DistributedSystemConfig;
+import com.gemstone.gemfire.admin.ManagedEntity;
+import com.gemstone.gemfire.admin.ManagedEntityConfig;
+import com.gemstone.gemfire.distributed.internal.DistributionConfig;
+import com.gemstone.gemfire.internal.ProcessOutputReader;
+import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
+import com.gemstone.gemfire.internal.logging.LogService;
+import com.gemstone.gemfire.internal.logging.LoggingThreadGroup;
+import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage;
+
+/**
+ * Implements the actual administration (starting, stopping, etc.) of
+ * GemFire {@link ManagedEntity}s.  It {@link Runtime#exec(java.lang.String) executes}
+ * commands to administer the entities based on information provided
+ * by the {@link InternalManagedEntity} object.  Note that it does not
+ * use <code>SystemAdmin</code> to manage "local" entities; it always
+ * execs the scripts.
+ *
+ * <P>
+ *
+ * This class is a refactoring of <code>Systemcontroller</code>,
+ * <code>RemoteCommand</code>, and <code>LocatorRemoteCommand</code>.
+ *
+ * @author David Whitlock
+ * @author Kirk Lund (original SystemController)
+ * @since 4.0
+ */
+class EnabledManagedEntityController implements ManagedEntityController {
+  private static final Logger logger = LogService.getLogger();
+
+//  /** A lock to ensure that only entity is managed at a time.  See bug
+//   * 31374. */
+//  private static Object startStopLock = new Object();
+
+  /** Known strings found in output indicating error. */
+  private static final String[] ERROR_OUTPUTS = new String[] {
+    "No such file or directory",
+    "The system cannot find the file specified.",
+    "Access is denied.",
+    "cannot open",
+    "ERROR"
+  };
+
+  /** Token in command prefix to be replaced with actual HOST */
+  private static final String HOST = "{HOST}";
+
+  /** Token in command prefix to be replaced with actual execution CMD */
+  private static final String CMD = "{CMD}";
+
+  //////////////////////  Instance Fields  //////////////////////
+
+  /** The thread group in which threads launched by this system
+   * controller reside. */
+  private final ThreadGroup threadGroup;
+
+  /** System to which the managed entities belong */
+  private final AdminDistributedSystem system;
+
+  ///////////////////////  Constructors  ///////////////////////
+
+  /**
+   * Creates a new <code>ManagedEntityController</code> for entities
+   * in the given distributed system.
+   */
+  EnabledManagedEntityController(AdminDistributedSystem system) {
+    this.system = system;
+    this.threadGroup =
+      LoggingThreadGroup.createThreadGroup("ManagedEntityController threads", logger);
+  }
+
+  /////////////////////  Instance Methods  /////////////////////
+
+  /**
+   * Returns <code>true</code> if the <code>output</code> string
+   * contains a known error message.
+   */
+  private boolean outputIsError(String output) {
+    if (output == null) return false;
+    boolean error = false;
+    for (int i = 0; i < ERROR_OUTPUTS.length; i++) {
+      error = output.indexOf(ERROR_OUTPUTS[i]) > -1;
+      if (error) return error;
+    }
+    return error;
+  }
+
+  /**
+   * Executes a command using {@link Runtime#exec(java.lang.String)}.
+   *
+   * @param command
+   *        The full command to remotely execute
+   *
+   * @return Output from the command that was executed or
+   *         <code>null</code> if the executing the command failed.
+   */
+  protected String execute(String command,
+                         InternalManagedEntity entity) {
+    /* TODO: this is getting ugly... clients of this method really need to
+       have the ability to do their own parsing/checking of 'output' */
+    if (command == null || command.length() == 0) {
+      throw new IllegalArgumentException(LocalizedStrings.ManagedEntityController_EXECUTION_COMMAND_IS_EMPTY.toLocalizedString());
+    }
+
+    File workingDir =
+      new File(entity.getEntityConfig().getWorkingDirectory());
+    logger.info(LocalizedMessage.create(LocalizedStrings.ManagedEntityController_EXECUTING_REMOTE_COMMAND_0_IN_DIRECTORY_1, new Object[] {command, workingDir}));
+    Process p = null;
+    try {
+      p = Runtime.getRuntime().exec(command, null /* env */,
+                                    workingDir);
+
+    } catch (java.io.IOException e) {
+      logger.fatal(LocalizedMessage.create(LocalizedStrings.ManagedEntityController_WHILE_EXECUTING_0, command), e);
+      return null;
+    }
+
+    final ProcessOutputReader pos = new ProcessOutputReader(p);
+    int retCode = pos.getExitCode();
+    final String output = pos.getOutput();
+    logger.info(LocalizedMessage.create(LocalizedStrings.ManagedEntityController_RESULT_OF_EXECUTING_0_IS_1, new Object[] {command, Integer.valueOf(retCode)}));
+    logger.info(LocalizedMessage.create(LocalizedStrings.ManagedEntityController_OUTPUT_OF_0_IS_1, new Object[] {command, output}));
+
+    if (retCode != 0 || outputIsError(output)) {
+      logger.warn(LocalizedMessage.create(LocalizedStrings.ManagedEntityController_REMOTE_EXECUTION_OF_0_FAILED, command));
+      return null;
+    }
+
+    return output;
+  }
+
+  /** Returns true if the path ends with a path separator. */
+  private boolean endsWithSeparator(String path) {
+    return path.endsWith("/") || path.endsWith("\\");
+  }
+
+  /** Translates the path between Windows and UNIX. */
+  private String getOSPath(String path) {
+    if (pathIsWindows(path)) {
+      return path.replace('/', '\\');
+    } else {
+      return path.replace('\\', '/');
+    }
+  }
+
+//  /** Returns true if the path is on Windows. */
+//  private boolean pathIsWindows(File path) {
+//    return pathIsWindows(path.toString());
+//  }
+
+  /** Returns true if the path is on Windows. */
+  private boolean pathIsWindows(String path) {
+    if (path != null && path.length() > 1) {
+      return (Character.isLetter(path.charAt(0)) && path.charAt(1) == ':') ||
+        (path.startsWith("//") || path.startsWith("\\\\"));
+    }
+    return false;
+  }
+
+  /**
+   * If the managed entity resides on a remote host, then
+   * <code>command</code> is munged to take the remote command into account.
+   *
+   * @throws IllegalStateException
+   *        If a remote command is required, but one has not been
+   *        specified.
+   */
+  private String arrangeRemoteCommand(InternalManagedEntity entity,
+                                      String cmd) {
+
+    String host = entity.getEntityConfig().getHost();
+    if (InetAddressUtil.isLocalHost(host)) {
+      // No arranging necessary
+      return cmd;
+    }
+
+    String prefix = entity.getEntityConfig().getRemoteCommand();
+    if (prefix == null || prefix.length() <= 0) {
+      prefix = entity.getDistributedSystem().getRemoteCommand();
+    }
+
+    if (prefix == null || prefix.length() <= 0) {
+      throw new IllegalStateException(LocalizedStrings.ManagedEntityController_A_REMOTE_COMMAND_MUST_BE_SPECIFIED_TO_OPERATE_ON_A_MANAGED_ENTITY_ON_HOST_0
+          .toLocalizedString(host));
+    }
+
+    int hostIdx = prefix.indexOf(HOST);
+    int cmdIdx = prefix.indexOf(CMD);
+    if (hostIdx == -1 && cmdIdx == -1) {
+      return prefix + " " + host + " " + cmd;
+    }
+
+    if (hostIdx >= 0) {
+      String start = prefix.substring(0, hostIdx);
+      String end = null;
+      if (hostIdx + HOST.length() >= prefix.length()) {
+        end = "";
+      } else {
+        end = prefix.substring(hostIdx + HOST.length());
+      }
+      prefix = start + host + end;
+      cmdIdx = prefix.indexOf(CMD); //recalculate;
+    }
+
+    if (cmdIdx >= 0) {
+      String start = prefix.substring(0, cmdIdx);
+      String end = null;
+      if (cmdIdx + CMD.length() >= prefix.length()) {
+        end = "";
+      } else {
+        end = prefix.substring(cmdIdx + CMD.length());
+      }
+      prefix = start + cmd + end;
+    }
+    return prefix;
+  }
+
+  /**
+   * Returns the full path to the executable in
+   * <code>$GEMFIRE/bin</code> taking into account the {@linkplain
+   * ManagedEntityConfig#getProductDirectory product directory} and the
+   * platform's file separator.
+   *
+   * <P>
+   *
+   * Note: we should probably do a better job of determine whether or
+   * not the machine on which the entity runs is Windows or Linux.
+   *
+   * @param executable
+   *        The name of the executable that resides in
+   *        <code>$GEMFIRE/bin</code>.
+   */
+  public String getProductExecutable(InternalManagedEntity entity,
+                                     String executable) {
+    String productDirectory =
+      entity.getEntityConfig().getProductDirectory();
+    String path = null;
+    File productDir = new File(productDirectory);
+//    if (productDir != null) (cannot be null)
+    {
+      path = productDir.getPath();
+      if (!endsWithSeparator(path)) {
+        path += File.separator;
+      }
+      path += "bin" + File.separator;
+    }
+//    else {
+//      path = "";
+//    }
+
+    String bat = "";
+    if (pathIsWindows(path)) {
+      bat = ".bat";
+    }
+    return getOSPath(path) + executable + bat;
+  }
+
+  /**
+   * Builds optional SSL command-line arguments.  Returns null if SSL is not
+   * enabled for the distributed system.
+   */
+  public String buildSSLArguments(DistributedSystemConfig config) {
+    Properties sslProps = buildSSLProperties(config, true);
+    if (sslProps == null) return null;
+
+    StringBuffer sb = new StringBuffer();
+    for (Iterator iter = sslProps.keySet().iterator(); iter.hasNext();) {
+      String key = (String) iter.next();
+      String value = sslProps.getProperty(key);
+      sb.append(" -J-D" + key + "=" + value);
+    }
+
+    return sb.toString();
+  }
+
+  /**
+   * Builds optional SSL properties for DistributionLocator. Returns null if SSL
+   * is not enabled for the distributed system.
+   *
+   * @param forCommandLine
+   *                true indicates that
+   *                {@link DistributionConfig#GEMFIRE_PREFIX} should be
+   *                prepended so the argument will become -Dgemfire.xxxx
+   */
+  private Properties buildSSLProperties(DistributedSystemConfig config,
+                                        boolean forCommandLine) {
+    if (!config.isSSLEnabled()) return null;
+
+    String prefix = "";
+    if (forCommandLine) prefix = DistributionConfig.GEMFIRE_PREFIX;
+
+    Properties sslProps = (Properties) config.getSSLProperties().clone();
+    // add ssl-enabled, etc...
+    sslProps.setProperty(prefix +
+                         DistributionConfig.MCAST_PORT_NAME,
+                         "0");
+    sslProps.setProperty(prefix +
+                         DistributionConfig.SSL_ENABLED_NAME,
+                         String.valueOf(config.isSSLEnabled()));
+    sslProps.setProperty(prefix +
+                         DistributionConfig.SSL_CIPHERS_NAME,
+                         config.getSSLCiphers());
+    sslProps.setProperty(prefix +
+                         DistributionConfig.SSL_PROTOCOLS_NAME,
+                         config.getSSLProtocols());
+    sslProps.setProperty(prefix +
+                         DistributionConfig.SSL_REQUIRE_AUTHENTICATION_NAME,
+                         String.valueOf(config.isSSLAuthenticationRequired()));
+    return sslProps;
+  }
+
+
+  /**
+   * Starts a managed entity.
+   */
+  public void start(final InternalManagedEntity entity) {
+    final String command =
+      arrangeRemoteCommand(entity, entity.getStartCommand());
+    Thread start = new Thread(this.threadGroup, new Runnable() {
+        public void run() {
+          execute(command, entity);
+        }
+      }, "Start " + entity.getEntityType());
+    start.start();
+  }
+
+  /**
+   * Stops a managed entity.
+   */
+  public void stop(final InternalManagedEntity entity) {
+    final String command =
+      arrangeRemoteCommand(entity, entity.getStopCommand());
+    Thread stop = new Thread(this.threadGroup, new Runnable() {
+        public void run() {
+          execute(command, entity);
+        }
+      }, "Stop " + entity.getEntityType());
+    stop.start();
+  }
+
+  /**
+   * Returns whether or not a managed entity is running
+   */
+  public boolean isRunning(InternalManagedEntity entity) {
+    final String command =
+      arrangeRemoteCommand(entity, entity.getIsRunningCommand());
+    String output = execute(command, entity);
+
+    if (output == null ||
+        (output.indexOf("stop" /* "ing" "ped" */) != -1) ||
+        (output.indexOf("killed") != -1) ||
+        (output.indexOf("starting") != -1)) {
+      return false;
+
+    } else if (output.indexOf("running") != -1) {
+      return true;
+
+    } else {
+      throw new IllegalStateException(LocalizedStrings.ManagedEntityController_COULD_NOT_DETERMINE_IF_MANAGED_ENTITY_WAS_RUNNING_0
+          .toLocalizedString(output));
+    }
+  }
+
+  /**
+   * Returns the contents of a locator's log file.  Other APIs are
+   * used to get the log file of managed entities that are also system
+   * members.
+   */
+  public String getLog(DistributionLocatorImpl locator) {
+    String command =
+      arrangeRemoteCommand(locator, locator.getLogCommand());
+    return execute(command, locator);
+  }
+
+  /**
+   * Returns the contents of the given directory using the given
+   * managed entity to determine the host and remote command.
+   */
+  private String listDirectory(InternalManagedEntity entity,
+                               String dir) {
+    ManagedEntityConfig config = entity.getEntityConfig();
+    String listFile =
+        pathIsWindows(config.getProductDirectory()) ? "dir " : "ls ";
+    String command =
+      arrangeRemoteCommand(entity, listFile + dir);
+    return execute(command, entity);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FinishBackupRequest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FinishBackupRequest.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FinishBackupRequest.java
new file mode 100644
index 0000000..c464a75
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FinishBackupRequest.java
@@ -0,0 +1,159 @@
+/*=========================================================================
+ * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.admin.internal;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.logging.log4j.Logger;
+
+import com.gemstone.gemfire.CancelException;
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.cache.persistence.PersistentID;
+import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.distributed.internal.DM;
+import com.gemstone.gemfire.distributed.internal.DistributionManager;
+import com.gemstone.gemfire.distributed.internal.DistributionMessage;
+import com.gemstone.gemfire.distributed.internal.ReplyException;
+import com.gemstone.gemfire.internal.admin.remote.AdminFailureResponse;
+import com.gemstone.gemfire.internal.admin.remote.AdminMultipleReplyProcessor;
+import com.gemstone.gemfire.internal.admin.remote.AdminResponse;
+import com.gemstone.gemfire.internal.admin.remote.CliLegacyMessage;
+import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
+import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
+import com.gemstone.gemfire.internal.logging.LogService;
+import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage;
+
+/**
+ * A request send from an admin VM to all of the peers to indicate
+ * that that should complete the backup operation.
+ * 
+ * @author dsmith
+ *
+ */
+public class FinishBackupRequest  extends CliLegacyMessage {
+  private static final Logger logger = LogService.getLogger();
+  
+  private File targetDir;
+  private File baselineDir;
+  
+  public FinishBackupRequest() {
+    super();
+  }
+
+  public FinishBackupRequest(File targetDir,File baselineDir) {
+    this.targetDir = targetDir;
+    this.baselineDir = baselineDir;
+  }
+  
+  public static Map<DistributedMember, Set<PersistentID>> send(DM dm, Set recipients, File targetDir, File baselineDir) {
+    FinishBackupRequest request = new FinishBackupRequest(targetDir,baselineDir);
+    request.setRecipients(recipients);
+
+    FinishBackupReplyProcessor replyProcessor = new FinishBackupReplyProcessor(dm, recipients);
+    request.msgId = replyProcessor.getProcessorId();
+    dm.putOutgoing(request);
+    try {
+      replyProcessor.waitForReplies();
+    } catch (ReplyException e) {
+      if(!(e.getCause() instanceof CancelException)) {
+        throw e;
+      }
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+    AdminResponse response = request.createResponse((DistributionManager)dm);
+    response.setSender(dm.getDistributionManagerId());
+    replyProcessor.process(response);
+    return replyProcessor.results;
+  }
+  
+  @Override
+  protected AdminResponse createResponse(DistributionManager dm) {
+    GemFireCacheImpl cache = GemFireCacheImpl.getInstance();
+    HashSet<PersistentID> persistentIds;
+    if(cache == null) {
+      persistentIds = new HashSet<PersistentID>();
+    } else {
+      try {
+        persistentIds = cache.getBackupManager().finishBackup(targetDir, baselineDir);
+      } catch (IOException e) {
+        logger.error(LocalizedMessage.create(LocalizedStrings.CliLegacyMessage_ERROR, this.getClass()), e);
+        return AdminFailureResponse.create(dm, getSender(), e);
+      }
+    }
+    
+    return new FinishBackupResponse(this.getSender(), persistentIds);
+  }
+
+  public int getDSFID() {
+    return FINISH_BACKUP_REQUEST;
+  }
+  
+  @Override
+  public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+    super.fromData(in);
+    targetDir = DataSerializer.readFile(in);
+    baselineDir = DataSerializer.readFile(in);
+  }
+
+  @Override
+  public void toData(DataOutput out) throws IOException {
+    super.toData(out);
+    DataSerializer.writeFile(targetDir, out);
+    DataSerializer.writeFile(baselineDir, out);
+  }
+
+  private static class FinishBackupReplyProcessor extends AdminMultipleReplyProcessor {
+    Map<DistributedMember, Set<PersistentID>> results = Collections.synchronizedMap(new HashMap<DistributedMember, Set<PersistentID>>());
+    public FinishBackupReplyProcessor(DM dm, Collection initMembers) {
+      super(dm, initMembers);
+    }
+    
+    @Override
+    protected boolean stopBecauseOfExceptions() {
+      return false;
+    }
+
+    
+    
+    @Override
+    protected int getAckWaitThreshold() {
+      //Disable the 15 second warning if the backup is taking a long time
+      return 0;
+    }
+
+    @Override
+    public long getAckSevereAlertThresholdMS() {
+      //Don't log severe alerts for backups either
+      return Long.MAX_VALUE;
+    }
+
+    @Override
+    protected void process(DistributionMessage msg, boolean warn) {
+      if(msg instanceof FinishBackupResponse) {
+        final HashSet<PersistentID> persistentIds = ((FinishBackupResponse) msg).getPersistentIds();
+        if(persistentIds != null && !persistentIds.isEmpty()) {
+          results.put(msg.getSender(), persistentIds);
+        }
+      }
+      super.process(msg, warn);
+    }
+    
+    
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FinishBackupResponse.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FinishBackupResponse.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FinishBackupResponse.java
new file mode 100644
index 0000000..998c0c3
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FinishBackupResponse.java
@@ -0,0 +1,70 @@
+/*=========================================================================
+ * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.admin.internal;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.HashSet;
+
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.cache.persistence.PersistentID;
+import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
+import com.gemstone.gemfire.internal.admin.remote.AdminResponse;
+
+/**
+ * The reply for a {@link FinishBackupRequest}. The
+ * reply contains the persistent ids of the disk stores
+ * that were backed up on this member.
+ * 
+ * @author dsmith
+ *
+ */
+public class FinishBackupResponse extends AdminResponse {
+  
+  private HashSet<PersistentID> persistentIds;
+  
+  public FinishBackupResponse() {
+    super();
+  }
+
+  public FinishBackupResponse(InternalDistributedMember sender, HashSet<PersistentID> persistentIds) {
+    this.setRecipient(sender);
+    this.persistentIds = persistentIds;
+  }
+  
+  public HashSet<PersistentID> getPersistentIds() {
+    return persistentIds;
+  }
+
+  @Override
+  public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+    super.fromData(in);
+    persistentIds = DataSerializer.readHashSet(in);
+  }
+
+  @Override
+  public void toData(DataOutput out) throws IOException {
+    super.toData(out);
+    DataSerializer.writeHashSet(persistentIds, out);
+  }
+
+  @Override
+  protected Object clone() throws CloneNotSupportedException {
+    return super.clone();
+  }
+
+  public int getDSFID() {
+    return FINISH_BACKUP_RESPONSE;
+  }
+  
+  @Override
+  public String toString() {
+    return getClass().getName() + ": " + persistentIds;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FlushToDiskRequest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FlushToDiskRequest.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FlushToDiskRequest.java
new file mode 100644
index 0000000..3336aa2
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FlushToDiskRequest.java
@@ -0,0 +1,89 @@
+/*=========================================================================
+ * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.admin.internal;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.gemstone.gemfire.CancelException;
+import com.gemstone.gemfire.cache.persistence.PersistentID;
+import com.gemstone.gemfire.distributed.internal.DM;
+import com.gemstone.gemfire.distributed.internal.DistributionManager;
+import com.gemstone.gemfire.distributed.internal.ReplyException;
+import com.gemstone.gemfire.internal.admin.remote.AdminMultipleReplyProcessor;
+import com.gemstone.gemfire.internal.admin.remote.AdminResponse;
+import com.gemstone.gemfire.internal.admin.remote.CliLegacyMessage;
+import com.gemstone.gemfire.internal.cache.DiskStoreImpl;
+import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
+
+/**
+ * A request to from an admin VM to all non admin members
+ * to start a backup. In the prepare phase of the backup,
+ * the members will suspend bucket destroys to make sure
+ * buckets aren't missed during the backup.
+ * 
+ * @author dsmith
+ *
+ */
+public class FlushToDiskRequest  extends CliLegacyMessage {
+  
+  public FlushToDiskRequest() {
+    
+  }
+  
+  public static void send(DM dm, Set recipients) {
+    FlushToDiskRequest request = new FlushToDiskRequest();
+    request.setRecipients(recipients);
+
+    FlushToDiskProcessor replyProcessor = new FlushToDiskProcessor(dm, recipients);
+    request.msgId = replyProcessor.getProcessorId();
+    dm.putOutgoing(request);
+    try {
+      replyProcessor.waitForReplies();
+    } catch (ReplyException e) {
+      if(!(e.getCause() instanceof CancelException)) {
+        throw e;
+      }
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+    AdminResponse response = request.createResponse((DistributionManager)dm);
+    response.setSender(dm.getDistributionManagerId());
+    replyProcessor.process(response);
+  }
+  
+  @Override
+  protected AdminResponse createResponse(DistributionManager dm) {
+    GemFireCacheImpl cache = GemFireCacheImpl.getInstance();
+    HashSet<PersistentID> persistentIds;
+    if(cache != null) {
+      Collection<DiskStoreImpl> diskStores = cache.listDiskStoresIncludingRegionOwned();
+      for(DiskStoreImpl store : diskStores) {
+        store.flush();
+      }
+    }
+    
+    return new FlushToDiskResponse(this.getSender());
+  }
+
+  public int getDSFID() {
+    return FLUSH_TO_DISK_REQUEST;
+  }
+  
+  private static class FlushToDiskProcessor extends AdminMultipleReplyProcessor {
+    public FlushToDiskProcessor(DM dm, Collection initMembers) {
+      super(dm, initMembers);
+    }
+    
+    @Override
+    protected boolean stopBecauseOfExceptions() {
+      return false;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FlushToDiskResponse.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FlushToDiskResponse.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FlushToDiskResponse.java
new file mode 100644
index 0000000..b08b133
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/FlushToDiskResponse.java
@@ -0,0 +1,37 @@
+/*=========================================================================
+ * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.admin.internal;
+
+import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
+import com.gemstone.gemfire.internal.admin.remote.AdminResponse;
+
+/**
+ * The response to the {@link FlushToDiskRequest}
+ * 
+ * @author dsmith
+ *
+ */
+public class FlushToDiskResponse extends AdminResponse {
+
+  public FlushToDiskResponse() {
+    super();
+  }
+
+  public FlushToDiskResponse(InternalDistributedMember sender) {
+    this.setRecipient(sender);
+  }
+  
+  public int getDSFID() {
+    return FLUSH_TO_DISK_RESPONSE;
+  }
+  
+  @Override
+  public String toString() {
+    return getClass().getName();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/GemFireHealthConfigImpl.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/GemFireHealthConfigImpl.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/GemFireHealthConfigImpl.java
new file mode 100644
index 0000000..61e5022
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/GemFireHealthConfigImpl.java
@@ -0,0 +1,75 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.admin.internal;
+
+import com.gemstone.gemfire.admin.*;
+import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
+
+// @todo davidw Delegate to a "parent" config for properties that are not overridden.
+// This will be made easier with a special <code>HealthConfigAttribute</code> class.
+/**
+ * The implementation of <code>GemFireHealthConfig</code>
+ *
+ *
+ * @author David Whitlock
+ *
+ * @since 3.5
+ */
+public class GemFireHealthConfigImpl
+  extends CacheHealthConfigImpl
+  implements GemFireHealthConfig {
+
+  private static final long serialVersionUID = -6797673296902808018L;
+
+  /** The name of the host to which this configuration applies. */
+  private String hostName;
+
+  /** The number of seconds to wait between evaluating the health of
+   * GemFire. */
+  private int interval = DEFAULT_HEALTH_EVALUATION_INTERVAL;
+
+  ////////////////////////  Constructors  ////////////////////////
+
+  /**
+   * Creates a new <code>GemFireHealthConfigImpl</code> that applies
+   * to the host with the given name.
+   *
+   * @param hostName
+   *        The name of the host to which this configuration applies.
+   *        If <code>null</code>, then this is the "default"
+   *        configuration. 
+   */
+  public GemFireHealthConfigImpl(String hostName) {
+    this.hostName = hostName;
+  }
+
+  ///////////////////////  Instance Methods  ///////////////////////
+
+  public String getHostName() {
+    return this.hostName;
+  }
+
+  public void setHealthEvaluationInterval(int interval) {
+    this.interval = interval;
+  }
+
+  public int getHealthEvaluationInterval() {
+    return this.interval;
+  }
+
+  @Override
+  public String toString() {
+    if (this.hostName == null) {
+      return LocalizedStrings.GemFireHealthConfigImpl_DEFAULT_GEMFIRE_HEALTH_CONFIGURATION.toLocalizedString();
+
+    } else {
+      return LocalizedStrings.GemFireHealthConfigImpl_GEMFIRE_HEALTH_CONFIGURATION_FOR_HOST_0.toLocalizedString(this.hostName);
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/19459053/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/GemFireHealthEvaluator.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/GemFireHealthEvaluator.java b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/GemFireHealthEvaluator.java
new file mode 100644
index 0000000..f5647a9
--- /dev/null
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/admin/internal/GemFireHealthEvaluator.java
@@ -0,0 +1,179 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.admin.internal;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.logging.log4j.Logger;
+
+import com.gemstone.gemfire.admin.GemFireHealth;
+import com.gemstone.gemfire.admin.GemFireHealthConfig;
+import com.gemstone.gemfire.distributed.internal.DistributionManager;
+import com.gemstone.gemfire.internal.Assert;
+import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
+import com.gemstone.gemfire.internal.logging.LogService;
+
+/**
+ * Evaluates the health of various GemFire components in the VM
+ * according to a {@link GemFireHealthConfig}.
+ *
+ * <P>
+ *
+ * Note that evaluators never reside in the administration VM, they
+ * only in member VMs.  They are not <code>Serializable</code> and
+ * aren't meant to be.
+ *
+ * @see MemberHealthEvaluator
+ * @see CacheHealthEvaluator
+ *
+ * @author David Whitlock
+ *
+ * @since 3.5
+ */
+public class GemFireHealthEvaluator {
+
+  private static final Logger logger = LogService.getLogger();
+  
+  /** Determines how the health of GemFire is determined */
+  private GemFireHealthConfig config;
+
+  /** Evaluates the health of this member of the distributed system */
+  private MemberHealthEvaluator memberHealth;
+
+  /** Evaluates the health of the Cache hosted in this VM */
+  private CacheHealthEvaluator cacheHealth;
+
+  /** The most recent <code>OKAY_HEALTH</code> diagnoses of the
+   * GemFire system */
+  private List okayDiagnoses;
+
+  /** The most recent <code>POOR_HEALTH</code> diagnoses of the
+   * GemFire system */
+  private List poorDiagnoses;
+
+  ///////////////////////  Constructors  ///////////////////////
+
+  /**
+   * Creates a new <code>GemFireHealthEvaluator</code>
+   *
+   * @param config
+   *        The configuration that determines whether or GemFire is
+   *        healthy 
+   * @param dm
+   *        The distribution manager 
+   */
+  public GemFireHealthEvaluator(GemFireHealthConfig config,
+                                DistributionManager dm) {
+    if (config == null) {
+      throw new NullPointerException(LocalizedStrings.GemFireHealthEvaluator_NULL_GEMFIREHEALTHCONFIG.toLocalizedString());
+    }
+
+    this.config = config;
+    this.memberHealth = new MemberHealthEvaluator(config, dm);
+    this.cacheHealth = new CacheHealthEvaluator(config, dm);
+    this.okayDiagnoses = new ArrayList();
+    this.poorDiagnoses = new ArrayList();
+  }
+
+  //////////////////////  Instance Methods  //////////////////////
+
+  /**
+   * Evaluates the health of the GemFire components in this VM.
+   *
+   * @return The aggregate health code (such as {@link
+   *         GemFireHealth#OKAY_HEALTH}) of the GemFire components.
+   */
+  public GemFireHealth.Health evaluate() {
+    List status = new ArrayList();
+    this.memberHealth.evaluate(status);
+    this.cacheHealth.evaluate(status);
+
+    GemFireHealth.Health overallHealth = GemFireHealth.GOOD_HEALTH;
+    this.okayDiagnoses.clear();
+    this.poorDiagnoses.clear();
+
+    for (Iterator iter = status.iterator(); iter.hasNext(); ) {
+      AbstractHealthEvaluator.HealthStatus health =
+        (AbstractHealthEvaluator.HealthStatus) iter.next();
+      if (overallHealth == GemFireHealth.GOOD_HEALTH) {
+        if ((health.getHealthCode() != GemFireHealth.GOOD_HEALTH)) {
+          overallHealth = health.getHealthCode();
+        }
+
+      } else if (overallHealth == GemFireHealth.OKAY_HEALTH) {
+        if (health.getHealthCode() == GemFireHealth.POOR_HEALTH) {
+          overallHealth = GemFireHealth.POOR_HEALTH;
+        }
+      }
+
+      GemFireHealth.Health healthCode = health.getHealthCode();
+      if (healthCode == GemFireHealth.OKAY_HEALTH) {
+        this.okayDiagnoses.add(health.getDiagnosis());
+
+      } else if (healthCode == GemFireHealth.POOR_HEALTH) {
+        this.poorDiagnoses.add(health.getDiagnosis());
+      }
+    }
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Evaluated health to be {}", overallHealth);
+    }
+    return overallHealth;
+  }
+
+  /**
+   * Returns detailed information explaining the current health status.
+   * Each array element is a different cause for the current status.
+   * An empty array will be returned if the current status is {@link
+   * GemFireHealth#GOOD_HEALTH}. 
+   */
+  public String[] getDiagnosis(GemFireHealth.Health healthCode) {
+    if (healthCode == GemFireHealth.GOOD_HEALTH) {
+      return new String[0];
+
+    } else if (healthCode == GemFireHealth.OKAY_HEALTH) {
+      String[] array = new String[this.okayDiagnoses.size()];
+      this.okayDiagnoses.toArray(array);
+      return array;
+
+    } else {
+      Assert.assertTrue(healthCode == GemFireHealth.POOR_HEALTH);
+      String[] array = new String[this.poorDiagnoses.size()];
+      this.poorDiagnoses.toArray(array);
+      return array;
+    }
+  }
+
+  /**
+   * Resets the state of this evaluator
+   */
+  public void reset() {
+    this.okayDiagnoses.clear();
+    this.poorDiagnoses.clear();
+  }
+
+  /**
+   * Returns the heath evaluation interval, in seconds.
+   *
+   * @see GemFireHealthConfig#getHealthEvaluationInterval
+   */
+  public int getEvaluationInterval() {
+    return this.config.getHealthEvaluationInterval();
+  }
+
+  /**
+   * Closes this evaluator and releases all of its resources
+   */
+  public void close() {
+    this.memberHealth.close();
+    this.cacheHealth.close();
+  }
+
+}