You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2016/10/26 22:49:34 UTC

[68/93] incubator-geode git commit: GEODE-288: move admin package to internal

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthConfigImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthConfigImpl.java
new file mode 100644
index 0000000..d38d5cb
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthConfigImpl.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import org.apache.geode.internal.admin.api.DistributedSystemHealthConfig;
+
+/**
+ * The implementation of <code>DistributedSystemHealthConfig</code>. Note that because it never
+ * leaves the management VM, it is not <code>Serializable</code> and is not part of the
+ * {@link GemFireHealthConfigImpl} class hierarchy.
+ *
+ *
+ * @since GemFire 3.5
+ */
+public class DistributedSystemHealthConfigImpl implements DistributedSystemHealthConfig {
+
+  /**
+   * The maximum number of application members that can unexceptedly leave a healthy the distributed
+   * system.
+   */
+  private long maxDepartedApplications = DEFAULT_MAX_DEPARTED_APPLICATIONS;
+
+  ////////////////////// Constructors //////////////////////
+
+  /**
+   * Creates a new <code>DistributedSystemHealthConfigImpl</code> with the default configuration.
+   */
+  protected DistributedSystemHealthConfigImpl() {
+
+  }
+
+  ///////////////////// Instance Methods /////////////////////
+
+  public long getMaxDepartedApplications() {
+    return this.maxDepartedApplications;
+  }
+
+  public void setMaxDepartedApplications(long maxDepartedApplications) {
+    this.maxDepartedApplications = maxDepartedApplications;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthEvaluator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthEvaluator.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthEvaluator.java
new file mode 100644
index 0000000..5087933
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthEvaluator.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import org.apache.geode.internal.admin.api.DistributedSystemHealthConfig;
+import org.apache.geode.distributed.internal.DM;
+import org.apache.geode.distributed.internal.DistributionConfig;
+import org.apache.geode.distributed.internal.DistributionManager;
+import org.apache.geode.distributed.internal.MembershipListener;
+import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Contains the logic for evaluating the health of an entire GemFire distributed system according to
+ * the thresholds provided in a {@link DistributedSystemHealthConfig}.
+ *
+ * <P>
+ *
+ * Note that unlike other evaluators, the <code>DistributedSystemHealthEvaluator</code> resides in
+ * the "administrator" VM and not in the member VMs. This is because there only needs to be one
+ * <code>DistributedSystemHealthEvaluator</code> per distributed system.
+ *
+ *
+ * @since GemFire 3.5
+ */
+class DistributedSystemHealthEvaluator extends AbstractHealthEvaluator
+    implements MembershipListener {
+
+  /** The config from which we get the evaluation criteria */
+  private DistributedSystemHealthConfig config;
+
+  /**
+   * The distribution manager with which this MembershipListener is registered
+   */
+  private DM dm;
+
+  /** The description of the distributed system being evaluated */
+  private String description;
+
+  /**
+   * The number of application members that have unexpectedly left since the previous evaluation
+   */
+  private int crashedApplications;
+
+  /////////////////////// Constructors ///////////////////////
+
+  /**
+   * Creates a new <code>DistributedSystemHealthEvaluator</code>
+   */
+  DistributedSystemHealthEvaluator(DistributedSystemHealthConfig config, DM dm) {
+    super(null, dm);
+
+    this.config = config;
+    this.dm = dm;
+    this.dm.addMembershipListener(this);
+
+    StringBuffer sb = new StringBuffer();
+    sb.append("Distributed System ");
+
+    String desc = null;
+    if (dm instanceof DistributionManager) {
+      desc = ((DistributionManager) dm).getDistributionConfigDescription();
+    }
+
+    if (desc != null) {
+      sb.append(desc);
+
+    } else {
+      DistributionConfig dsc = dm.getSystem().getConfig();
+      String locators = dsc.getLocators();
+      if (locators == null || locators.equals("")) {
+        sb.append("using multicast ");
+        sb.append(dsc.getMcastAddress());
+        sb.append(":");
+        sb.append(dsc.getMcastPort());
+
+      } else {
+        sb.append("using locators ");
+        sb.append(locators);
+      }
+    }
+
+    this.description = sb.toString();
+  }
+
+  //////////////////// Instance Methods ////////////////////
+
+  @Override
+  protected String getDescription() {
+    return this.description;
+  }
+
+  /**
+   * Checks to make sure that the number of application members of the distributed system that have
+   * left unexpected since the last evaluation is less than the
+   * {@linkplain DistributedSystemHealthConfig#getMaxDepartedApplications threshold}. If not, the
+   * status is "poor" health.
+   */
+  void checkDepartedApplications(List status) {
+    synchronized (this) {
+      long threshold = this.config.getMaxDepartedApplications();
+      if (this.crashedApplications > threshold) {
+        String s =
+            LocalizedStrings.DistributedSystemHealth_THE_NUMBER_OF_APPLICATIONS_THAT_HAVE_LEFT_THE_DISTRIBUTED_SYSTEM_0_EXCEEDS_THE_THRESHOLD_1
+                .toLocalizedString(
+                    new Object[] {Long.valueOf(this.crashedApplications), Long.valueOf(threshold)});
+        status.add(poorHealth(s));
+      }
+      this.crashedApplications = 0;
+    }
+  }
+
+  @Override
+  protected void check(List status) {
+    checkDepartedApplications(status);
+  }
+
+  @Override
+  void close() {
+    this.dm.removeMembershipListener(this);
+  }
+
+  public void memberJoined(InternalDistributedMember id) {
+
+  }
+
+  /**
+   * Keeps track of which members depart unexpectedly
+   */
+  public void memberDeparted(InternalDistributedMember id, boolean crashed) {
+    if (!crashed)
+      return;
+    synchronized (this) {
+      int kind = id.getVmKind();
+      switch (kind) {
+        case DistributionManager.LOCATOR_DM_TYPE:
+        case DistributionManager.NORMAL_DM_TYPE:
+          this.crashedApplications++;
+          break;
+        default:
+          break;
+      }
+    } // synchronized
+  }
+
+  public void quorumLost(Set<InternalDistributedMember> failures,
+      List<InternalDistributedMember> remaining) {}
+
+  public void memberSuspect(InternalDistributedMember id, InternalDistributedMember whoSuspected,
+      String reason) {}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthMonitor.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthMonitor.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthMonitor.java
new file mode 100644
index 0000000..5a6e660
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthMonitor.java
@@ -0,0 +1,461 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.logging.log4j.Logger;
+
+import org.apache.geode.SystemFailure;
+import org.apache.geode.internal.admin.api.AdminException;
+import org.apache.geode.internal.admin.api.GemFireHealth;
+import org.apache.geode.internal.admin.api.GemFireHealthConfig;
+import org.apache.geode.internal.admin.api.GemFireMemberStatus;
+import org.apache.geode.internal.admin.api.RegionSubRegionSnapshot;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionAttributes;
+import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
+import org.apache.geode.internal.Assert;
+import org.apache.geode.internal.Config;
+import org.apache.geode.internal.net.SocketCreator;
+import org.apache.geode.internal.admin.AdminBridgeServer;
+import org.apache.geode.internal.admin.CacheInfo;
+import org.apache.geode.internal.admin.DLockInfo;
+import org.apache.geode.internal.admin.GemFireVM;
+import org.apache.geode.internal.admin.GfManagerAgent;
+import org.apache.geode.internal.admin.HealthListener;
+import org.apache.geode.internal.admin.Stat;
+import org.apache.geode.internal.admin.StatAlertDefinition;
+import org.apache.geode.internal.admin.StatListener;
+import org.apache.geode.internal.admin.StatResource;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.logging.LoggingThreadGroup;
+import org.apache.geode.internal.logging.log4j.LocalizedMessage;
+
+/**
+ * A thread that monitors the health of the distributed system. It is kind of like a
+ * {@link org.apache.geode.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.
+ *
+ *
+ * @since GemFire 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 org.apache.geode.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 addCacheServer(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/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorConfigImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorConfigImpl.java
new file mode 100644
index 0000000..9dcd16f
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorConfigImpl.java
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import org.apache.geode.internal.admin.api.DistributionLocator;
+import org.apache.geode.internal.admin.api.DistributionLocatorConfig;
+import org.apache.geode.distributed.internal.tcpserver.*;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+
+import java.net.InetAddress;
+import java.util.Properties;
+
+import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
+
+/**
+ * Provides an implementation of <code>DistributionLocatorConfig</code>.
+ *
+ * @since GemFire 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 TcpClient#getLocatorInfo
+   *
+   * @return <code>null</code> if the locator cannot be contacted
+   */
+  static DistributionLocatorConfig createConfigFor(String host, int port, InetAddress bindAddress) {
+    TcpClient client = new TcpClient();
+    String[] info = null;
+    if (bindAddress != null) {
+      info = client.getInfo(bindAddress, port);
+    } else {
+      info = client.getInfo(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(MCAST_PORT, "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/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorImpl.java
new file mode 100755
index 0000000..ccfbcac
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorImpl.java
@@ -0,0 +1,329 @@
+/*
+ * 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.
+ */
+
+package org.apache.geode.internal.admin.api.impl;
+
+import org.apache.geode.internal.admin.api.AdminDistributedSystem;
+import org.apache.geode.internal.admin.api.DistributionLocator;
+import org.apache.geode.internal.admin.api.DistributionLocatorConfig;
+import org.apache.geode.internal.admin.api.ManagedEntityConfig;
+import org.apache.geode.distributed.internal.DM;
+import org.apache.geode.distributed.internal.DistributionConfig;
+import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
+import org.apache.geode.internal.admin.remote.DistributionLocatorId;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.logging.log4j.LocalizedMessage;
+import org.apache.logging.log4j.Logger;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.*;
+
+/**
+ * Default administrative implementation of a DistributionLocator.
+ *
+ * @since GemFire 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 GemFire 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(" -D" + DistributionConfig.GEMFIRE_PREFIX + "" + 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/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/EnabledManagedEntityController.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/EnabledManagedEntityController.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/EnabledManagedEntityController.java
new file mode 100755
index 0000000..554d160
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/EnabledManagedEntityController.java
@@ -0,0 +1,385 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import org.apache.geode.internal.admin.api.AdminDistributedSystem;
+import org.apache.geode.internal.admin.api.DistributedSystemConfig;
+import org.apache.geode.internal.admin.api.ManagedEntity;
+import org.apache.geode.internal.admin.api.ManagedEntityConfig;
+import org.apache.geode.distributed.internal.DistributionConfig;
+import org.apache.geode.internal.ProcessOutputReader;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.logging.LoggingThreadGroup;
+import org.apache.geode.internal.logging.log4j.LocalizedMessage;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Properties;
+
+import static org.apache.geode.distributed.ConfigurationProperties.*;
+
+/**
+ * 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>.
+ *
+ * @since GemFire 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 + MCAST_PORT, "0");
+    sslProps.setProperty(prefix + CLUSTER_SSL_ENABLED, String.valueOf(config.isSSLEnabled()));
+    sslProps.setProperty(prefix + CLUSTER_SSL_CIPHERS, config.getSSLCiphers());
+    sslProps.setProperty(prefix + CLUSTER_SSL_PROTOCOLS, config.getSSLProtocols());
+    sslProps.setProperty(prefix + CLUSTER_SSL_REQUIRE_AUTHENTICATION,
+        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/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupRequest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupRequest.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupRequest.java
new file mode 100644
index 0000000..dd0d0dc
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupRequest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+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 org.apache.geode.CancelException;
+import org.apache.geode.DataSerializer;
+import org.apache.geode.cache.persistence.PersistentID;
+import org.apache.geode.distributed.DistributedMember;
+import org.apache.geode.distributed.internal.DM;
+import org.apache.geode.distributed.internal.DistributionManager;
+import org.apache.geode.distributed.internal.DistributionMessage;
+import org.apache.geode.distributed.internal.ReplyException;
+import org.apache.geode.internal.admin.remote.AdminFailureResponse;
+import org.apache.geode.internal.admin.remote.AdminMultipleReplyProcessor;
+import org.apache.geode.internal.admin.remote.AdminResponse;
+import org.apache.geode.internal.admin.remote.CliLegacyMessage;
+import org.apache.geode.internal.cache.GemFireCacheImpl;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.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.
+ * 
+ *
+ */
+public class FinishBackupRequest extends CliLegacyMessage {
+  private static final Logger logger = LogService.getLogger();
+
+  private File targetDir;
+  private File baselineDir;
+  private boolean abort;
+
+  public FinishBackupRequest() {
+    super();
+  }
+
+  public FinishBackupRequest(File targetDir, File baselineDir, boolean abort) {
+    this.targetDir = targetDir;
+    this.baselineDir = baselineDir;
+    this.abort = abort;
+  }
+
+  public static Map<DistributedMember, Set<PersistentID>> send(DM dm, Set recipients,
+      File targetDir, File baselineDir, boolean abort) {
+    FinishBackupRequest request = new FinishBackupRequest(targetDir, baselineDir, abort);
+    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 || cache.getBackupManager() == null) {
+      persistentIds = new HashSet<PersistentID>();
+    } else {
+      try {
+        persistentIds = cache.getBackupManager().finishBackup(targetDir, baselineDir, abort);
+      } 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);
+    abort = DataSerializer.readBoolean(in);
+  }
+
+  @Override
+  public void toData(DataOutput out) throws IOException {
+    super.toData(out);
+    DataSerializer.writeFile(targetDir, out);
+    DataSerializer.writeFile(baselineDir, out);
+    DataSerializer.writeBoolean(abort, 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/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupResponse.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupResponse.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupResponse.java
new file mode 100644
index 0000000..ad68f97
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupResponse.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.HashSet;
+
+import org.apache.geode.DataSerializer;
+import org.apache.geode.cache.persistence.PersistentID;
+import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
+import org.apache.geode.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.
+ * 
+ *
+ */
+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/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskRequest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskRequest.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskRequest.java
new file mode 100644
index 0000000..c780d1d
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskRequest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.geode.CancelException;
+import org.apache.geode.cache.persistence.PersistentID;
+import org.apache.geode.distributed.internal.DM;
+import org.apache.geode.distributed.internal.DistributionManager;
+import org.apache.geode.distributed.internal.ReplyException;
+import org.apache.geode.internal.admin.remote.AdminMultipleReplyProcessor;
+import org.apache.geode.internal.admin.remote.AdminResponse;
+import org.apache.geode.internal.admin.remote.CliLegacyMessage;
+import org.apache.geode.internal.cache.DiskStoreImpl;
+import org.apache.geode.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.
+ * 
+ *
+ */
+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/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskResponse.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskResponse.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskResponse.java
new file mode 100644
index 0000000..869d56b
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskResponse.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
+import org.apache.geode.internal.admin.remote.AdminResponse;
+
+/**
+ * The response to the {@link FlushToDiskRequest}
+ * 
+ *
+ */
+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/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthConfigImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthConfigImpl.java
new file mode 100644
index 0000000..16b1d79
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthConfigImpl.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import org.apache.geode.internal.admin.api.GemFireHealthConfig;
+import org.apache.geode.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>
+ *
+ *
+ *
+ * @since GemFire 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/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthEvaluator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthEvaluator.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthEvaluator.java
new file mode 100644
index 0000000..d35a94c
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthEvaluator.java
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+package org.apache.geode.internal.admin.api.impl;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.logging.log4j.Logger;
+
+import org.apache.geode.internal.admin.api.GemFireHealth;
+import org.apache.geode.internal.admin.api.GemFireHealthConfig;
+import org.apache.geode.distributed.internal.DistributionManager;
+import org.apache.geode.internal.Assert;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.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
+ *
+ *
+ * @since GemFire 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();
+  }
+
+}