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:11:40 UTC

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

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/096b622d/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemConfigImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemConfigImpl.java
new file mode 100755
index 0000000..be83024
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemConfigImpl.java
@@ -0,0 +1,1135 @@
+/*
+ * 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.DistributionConfig;
+import org.apache.geode.distributed.internal.DistributionConfigImpl;
+import org.apache.geode.internal.admin.api.AdminXmlException;
+import org.apache.geode.internal.admin.api.CacheServerConfig;
+import org.apache.geode.internal.admin.api.CacheVmConfig;
+import org.apache.geode.internal.admin.api.DistributedSystemConfig;
+import org.apache.geode.internal.admin.api.DistributionLocator;
+import org.apache.geode.internal.admin.api.DistributionLocatorConfig;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.InternalLogWriter;
+import org.apache.geode.internal.logging.LogConfig;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.logging.LogWriterImpl;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+import static org.apache.geode.distributed.ConfigurationProperties.*;
+
+/**
+ * An implementation of the configuration object for an
+ * <code>AdminDistributedSystem</code>.  After a config has been used
+ * to create an <code>AdminDistributedSystem</code> most of the
+ * configuration attributes cannot be changed.  However, some
+ * operations (such as getting information about GemFire managers and
+ * distribution locators) are "passed through" to the
+ * <code>AdminDistributedSystem</code> associated with this
+ * configuration object.
+ *
+ * @since GemFire 3.5
+ */
+public class DistributedSystemConfigImpl
+    implements DistributedSystemConfig {
+
+  private static final Logger logger = LogService.getLogger();
+
+  private String entityConfigXMLFile = DEFAULT_ENTITY_CONFIG_XML_FILE;
+  private String systemId = DEFAULT_SYSTEM_ID;
+  private String mcastAddress = DEFAULT_MCAST_ADDRESS;
+  private int mcastPort = DEFAULT_MCAST_PORT;
+  private int ackWaitThreshold = DEFAULT_ACK_WAIT_THRESHOLD;
+  private int ackSevereAlertThreshold = DEFAULT_ACK_SEVERE_ALERT_THRESHOLD;
+  private String locators = DEFAULT_LOCATORS;
+  private String bindAddress = DEFAULT_BIND_ADDRESS;
+  private String serverBindAddress = DEFAULT_BIND_ADDRESS;
+  private String remoteCommand = DEFAULT_REMOTE_COMMAND;
+  private boolean disableTcp = DEFAULT_DISABLE_TCP;
+  private boolean enableNetworkPartitionDetection = DEFAULT_ENABLE_NETWORK_PARTITION_DETECTION;
+  private boolean disableAutoReconnect = DEFAULT_DISABLE_AUTO_RECONNECT;
+  private int memberTimeout = DEFAULT_MEMBER_TIMEOUT;
+  private String membershipPortRange = getMembershipPortRangeString(DEFAULT_MEMBERSHIP_PORT_RANGE);
+  private int tcpPort = DEFAULT_TCP_PORT;
+
+  private String logFile = DEFAULT_LOG_FILE;
+  private String logLevel = DEFAULT_LOG_LEVEL;
+  private int logDiskSpaceLimit = DEFAULT_LOG_DISK_SPACE_LIMIT;
+  private int logFileSizeLimit = DEFAULT_LOG_FILE_SIZE_LIMIT;
+  private int refreshInterval = DEFAULT_REFRESH_INTERVAL;
+  private Properties gfSecurityProperties = new Properties();
+
+  /**
+   * Listeners to notify when this DistributedSystemConfig changes
+   */
+  private Set listeners = new HashSet();
+
+  /**
+   * Configs for CacheServers that this system config is aware of
+   */
+  private Set cacheServerConfigs = new HashSet();
+
+  /**
+   * Configs for the managed distribution locators in the distributed
+   * system
+   */
+  private Set locatorConfigs = new HashSet();
+
+  /**
+   * The display name of this distributed system
+   */
+  private String systemName = DEFAULT_NAME;
+
+  /**
+   * The admin distributed system object that is configured by this
+   * config object.
+   *
+   * @since GemFire 4.0 */
+  private AdminDistributedSystemImpl system;
+
+  /**
+   * The GemFire log writer used by the distributed system
+   */
+  private InternalLogWriter logWriter;
+
+  ///////////////////////  Static Methods  ///////////////////////
+
+  /**
+   * Filters out all properties that are unique to the admin
+   * <code>DistributedSystemConfig</code> that are not present in the
+   * internal <code>DistributionConfig</code>.
+   *
+   * @since GemFire 4.0
+   */
+  private static Properties
+  filterOutAdminProperties(Properties props) {
+
+    Properties props2 = new Properties();
+    for (Enumeration names = props.propertyNames();
+         names.hasMoreElements(); ) {
+      String name = (String) names.nextElement();
+      if (!(ENTITY_CONFIG_XML_FILE_NAME.equals(name) ||
+          REFRESH_INTERVAL_NAME.equals(name) ||
+          REMOTE_COMMAND_NAME.equals(name)
+      )
+          ) {
+        String value = props.getProperty(name);
+        if ((name != null) && (value != null)) {
+          props2.setProperty(name, value);
+        }
+      }
+    }
+
+    return props2;
+  }
+
+  ////////////////////////  Constructors  ////////////////////////
+
+  /**
+   * Creates a new <code>DistributedSystemConfigImpl</code> based on
+   * the configuration stored in a <code>DistributedSystem</code>'s
+   * <code>DistributionConfig</code>.
+   */
+  public DistributedSystemConfigImpl(DistributionConfig distConfig,
+      String remoteCommand) {
+    if (distConfig == null) {
+      throw new IllegalArgumentException(LocalizedStrings.DistributedSystemConfigImpl_DISTRIBUTIONCONFIG_MUST_NOT_BE_NULL.toLocalizedString());
+    }
+
+    this.mcastAddress = InetAddressUtil.toString(distConfig.getMcastAddress());
+    this.mcastPort = distConfig.getMcastPort();
+    this.locators = distConfig.getLocators();
+    this.membershipPortRange =
+        getMembershipPortRangeString(distConfig.getMembershipPortRange());
+
+    this.systemName = distConfig.getName();
+
+    this.sslEnabled = distConfig.getClusterSSLEnabled();
+    this.sslCiphers = distConfig.getClusterSSLCiphers();
+    this.sslProtocols = distConfig.getClusterSSLProtocols();
+    this.sslAuthenticationRequired = distConfig.getClusterSSLRequireAuthentication();
+
+    this.logFile = distConfig.getLogFile().getPath();
+    this.logLevel =
+        LogWriterImpl.levelToString(distConfig.getLogLevel());
+    this.logDiskSpaceLimit = distConfig.getLogDiskSpaceLimit();
+    this.logFileSizeLimit = distConfig.getLogFileSizeLimit();
+
+    basicSetBindAddress(distConfig.getBindAddress());
+    this.tcpPort = distConfig.getTcpPort();
+
+    this.disableTcp = distConfig.getDisableTcp();
+
+    this.remoteCommand = remoteCommand;
+    this.serverBindAddress = distConfig.getServerBindAddress();
+    this.enableNetworkPartitionDetection = distConfig.getEnableNetworkPartitionDetection();
+    this.memberTimeout = distConfig.getMemberTimeout();
+    this.refreshInterval = DistributedSystemConfig.DEFAULT_REFRESH_INTERVAL;
+    this.gfSecurityProperties = (Properties) distConfig.getSSLProperties().clone();
+  }
+
+  /**
+   * Zero-argument constructor to be used only by subclasses.
+   *
+   * @since GemFire 4.0
+   */
+  protected DistributedSystemConfigImpl() {
+
+  }
+
+  /**
+   * Creates a new <code>DistributedSystemConifgImpl</code> whose
+   * configuration is specified by the given <code>Properties</code>
+   * object.
+   */
+  protected DistributedSystemConfigImpl(Properties props) {
+    this(props, false);
+  }
+
+  /**
+   * Creates a new <code>DistributedSystemConifgImpl</code> whose configuration
+   * is specified by the given <code>Properties</code> object.
+   * 
+   * @param props
+   *          The configuration properties specified by the caller
+   * @param ignoreGemFirePropsFile
+   *          whether to skip loading distributed system properties from
+   *          gemfire.properties file
+   *          
+   * @since GemFire 6.5
+   */
+  protected DistributedSystemConfigImpl(Properties props,
+      boolean ignoreGemFirePropsFile) {
+    this(new DistributionConfigImpl(
+            filterOutAdminProperties(props), ignoreGemFirePropsFile),
+        DEFAULT_REMOTE_COMMAND);
+    String remoteCommand = props.getProperty(REMOTE_COMMAND_NAME);
+    if (remoteCommand != null) {
+      this.remoteCommand = remoteCommand;
+    }
+
+    String entityConfigXMLFile =
+        props.getProperty(ENTITY_CONFIG_XML_FILE_NAME);
+    if (entityConfigXMLFile != null) {
+      this.entityConfigXMLFile = entityConfigXMLFile;
+    }
+
+    String refreshInterval =
+        props.getProperty(REFRESH_INTERVAL_NAME);
+    if (refreshInterval != null) {
+      try {
+        this.refreshInterval = Integer.parseInt(refreshInterval);
+      } catch (NumberFormatException nfEx) {
+        throw new IllegalArgumentException(
+            LocalizedStrings.DistributedSystemConfigImpl_0_IS_NOT_A_VALID_INTEGER_1.toLocalizedString(new Object[] { refreshInterval, REFRESH_INTERVAL_NAME }));
+      }
+    }
+  }
+
+  //////////////////////  Instance Methods  //////////////////////
+
+  /**
+   * Returns the <code>LogWriterI18n</code> to be used when administering
+   * the distributed system. Returns null if nothing has been provided via
+   * <code>setInternalLogWriter</code>.
+   *
+   * @since GemFire 4.0
+   */
+  public InternalLogWriter getInternalLogWriter() {
+    // LOG: used only for sharing between IDS, AdminDSImpl and AgentImpl -- to prevent multiple banners, etc.
+    synchronized (this) {
+      return this.logWriter;
+    }
+  }
+
+  /**
+   * Sets the <code>LogWriterI18n</code> to be used when administering the
+   * distributed system.
+   */
+  public void setInternalLogWriter(InternalLogWriter logWriter) {
+    // LOG: used only for sharing between IDS, AdminDSImpl and AgentImpl -- to prevent multiple banners, etc.
+    synchronized (this) {
+      this.logWriter = logWriter;
+    }
+  }
+
+  public LogConfig createLogConfig() {
+    return new LogConfig() {
+      @Override
+      public int getLogLevel() {
+        return LogWriterImpl.levelNameToCode(DistributedSystemConfigImpl.this.getLogLevel());
+      }
+
+      @Override
+      public File getLogFile() {
+        return new File(DistributedSystemConfigImpl.this.getLogFile());
+      }
+
+      @Override
+      public int getLogFileSizeLimit() {
+        return DistributedSystemConfigImpl.this.getLogFileSizeLimit();
+      }
+
+      @Override
+      public int getLogDiskSpaceLimit() {
+        return DistributedSystemConfigImpl.this.getLogDiskSpaceLimit();
+      }
+
+      @Override
+      public String getName() {
+        return DistributedSystemConfigImpl.this.getSystemName();
+      }
+
+      @Override
+      public String toLoggerString() {
+        return DistributedSystemConfigImpl.this.toString();
+      }
+    };
+  }
+
+  /**
+   * Marks this config object as "read only".  Attempts to modify a
+   * config object will result in a {@link IllegalStateException}
+   * being thrown.
+   *
+   * @since GemFire 4.0
+   */
+  void setDistributedSystem(AdminDistributedSystemImpl system) {
+    this.system = system;
+  }
+
+  /**
+   * Checks to see if this config object is "read only".  If it is,
+   * then an {@link IllegalStateException} is thrown.
+   *
+   * @since GemFire 4.0
+   */
+  protected void checkReadOnly() {
+    if (this.system != null) {
+      throw new IllegalStateException(
+          LocalizedStrings.DistributedSystemConfigImpl_A_DISTRIBUTEDSYSTEMCONFIG_OBJECT_CANNOT_BE_MODIFIED_AFTER_IT_HAS_BEEN_USED_TO_CREATE_AN_ADMINDISTRIBUTEDSYSTEM
+              .toLocalizedString());
+    }
+  }
+
+  public String getEntityConfigXMLFile() {
+    return this.entityConfigXMLFile;
+  }
+
+  public void setEntityConfigXMLFile(String xmlFile) {
+    checkReadOnly();
+    this.entityConfigXMLFile = xmlFile;
+    configChanged();
+  }
+
+  /**
+   * Parses the XML configuration file that describes managed
+   * entities.
+   *
+   * @throws AdminXmlException If a problem is encountered while parsing the XML file.
+   */
+  private void parseEntityConfigXMLFile() {
+    String fileName = this.entityConfigXMLFile;
+    File xmlFile = new File(fileName);
+    if (!xmlFile.exists()) {
+      if (DEFAULT_ENTITY_CONFIG_XML_FILE.equals(fileName)) {
+        // Default doesn't exist, no big deal
+        return;
+      } else {
+        throw new AdminXmlException(LocalizedStrings.DistributedSystemConfigImpl_ENTITY_CONFIGURATION_XML_FILE_0_DOES_NOT_EXIST.toLocalizedString(fileName));
+      }
+    }
+
+    try {
+      InputStream is = new FileInputStream(xmlFile);
+      try {
+        ManagedEntityConfigXmlParser.parse(is, this);
+      } finally {
+        is.close();
+      }
+    } catch (IOException ex) {
+      throw new AdminXmlException(LocalizedStrings.DistributedSystemConfigImpl_WHILE_PARSING_0.toLocalizedString(fileName), ex);
+    }
+  }
+
+  public String getSystemId() {
+    return this.systemId;
+  }
+
+  public void setSystemId(String systemId) {
+    checkReadOnly();
+    this.systemId = systemId;
+    configChanged();
+  }
+
+  /**
+   * Returns the multicast address for the system
+   */
+  public String getMcastAddress() {
+    return this.mcastAddress;
+  }
+
+  public void setMcastAddress(String mcastAddress) {
+    checkReadOnly();
+    this.mcastAddress = mcastAddress;
+    configChanged();
+  }
+
+  /**
+   * Returns the multicast port for the system
+   */
+  public int getMcastPort() {
+    return this.mcastPort;
+  }
+
+  public void setMcastPort(int mcastPort) {
+    checkReadOnly();
+    this.mcastPort = mcastPort;
+    configChanged();
+  }
+
+  public int getAckWaitThreshold() {
+    return this.ackWaitThreshold;
+  }
+
+  public void setAckWaitThreshold(int seconds) {
+    checkReadOnly();
+    this.ackWaitThreshold = seconds;
+    configChanged();
+  }
+
+  public int getAckSevereAlertThreshold() {
+    return this.ackSevereAlertThreshold;
+  }
+
+  public void setAckSevereAlertThreshold(int seconds) {
+    checkReadOnly();
+    this.ackSevereAlertThreshold = seconds;
+    configChanged();
+  }
+
+  /**
+   * Returns the comma-delimited list of locators for the system
+   */
+  public String getLocators() {
+    return this.locators;
+  }
+
+  public void setLocators(String locators) {
+    checkReadOnly();
+    if (locators == null) {
+      this.locators = "";
+    } else {
+      this.locators = locators;
+    }
+    configChanged();
+  }
+
+  /**
+   * Returns the value for membership-port-range
+   *
+   * @return the value for the Distributed System property membership-port-range
+   */
+  public String getMembershipPortRange() {
+    return this.membershipPortRange;
+  }
+
+  /**
+   * Sets the Distributed System property membership-port-range
+   *
+   * @param membershipPortRangeStr the value for membership-port-range given as two numbers separated
+   *                               by a minus sign.
+   */
+  public void setMembershipPortRange(String membershipPortRangeStr) {
+    /*
+     * FIXME: Setting attributes in DistributedSystemConfig has no effect on
+     * DistributionConfig which is actually used for connection with DS. This is
+     * true for all such attributes. Should be addressed in the Admin Revamp if 
+     * we want these 'set' calls to affect anything. Then we can use the 
+     * validation code in DistributionConfigImpl code.
+     */
+    checkReadOnly();
+    if (membershipPortRangeStr == null) {
+      this.membershipPortRange = getMembershipPortRangeString(DEFAULT_MEMBERSHIP_PORT_RANGE);
+    } else {
+      try {
+        if (validateMembershipRange(membershipPortRangeStr)) {
+          this.membershipPortRange = membershipPortRangeStr;
+        } else {
+          throw new IllegalArgumentException(
+              LocalizedStrings.DistributedSystemConfigImpl_INVALID_VALUE_FOR_MEMBERSHIP_PORT_RANGE
+                  .toLocalizedString(new Object[] { membershipPortRangeStr,
+                      MEMBERSHIP_PORT_RANGE_NAME }));
+        }
+      } catch (Exception e) {
+        if (logger.isDebugEnabled()) {
+          logger.debug(e.getMessage(), e);
+        }
+      }
+    }
+  }
+
+  public void setTcpPort(int port) {
+    checkReadOnly();
+    this.tcpPort = port;
+    configChanged();
+  }
+
+  public int getTcpPort() {
+    return this.tcpPort;
+  }
+
+  /**
+   * Validates the given string - which is expected in the format as two numbers
+   * separated by a minus sign - in to an integer array of length 2 with first
+   * element as lower end & second element as upper end of the range.
+   *
+   * @param membershipPortRange membership-port-range given as two numbers separated by a minus
+   *                            sign.
+   * @return true if the membership-port-range string is valid, false otherwise
+   */
+  private boolean validateMembershipRange(String membershipPortRange) {
+    int[] range = null;
+    if (membershipPortRange != null && membershipPortRange.trim().length() > 0) {
+      String[] splitted = membershipPortRange.split("-");
+      range = new int[2];
+      range[0] = Integer.parseInt(splitted[0].trim());
+      range[1] = Integer.parseInt(splitted[1].trim());
+      //NumberFormatException if any could be thrown
+
+      if (range[0] < 0 || range[0] >= range[1] ||
+          range[1] < 0 || range[1] > 65535) {
+        range = null;
+      }
+    }
+    return range != null;
+  }
+
+  /**
+   * @return the String representation of membershipPortRange with lower & upper
+   * limits of the port range separated by '-' e.g. 1-65535
+   */
+  private static String getMembershipPortRangeString(int[] membershipPortRange) {
+    String membershipPortRangeString = "";
+    if (membershipPortRange != null &&
+        membershipPortRange.length == 2) {
+      membershipPortRangeString = membershipPortRange[0] + "-" +
+          membershipPortRange[1];
+    }
+
+    return membershipPortRangeString;
+  }
+
+  public String getBindAddress() {
+    return this.bindAddress;
+  }
+
+  public void setBindAddress(String bindAddress) {
+    checkReadOnly();
+    basicSetBindAddress(bindAddress);
+    configChanged();
+  }
+
+  public String getServerBindAddress() {
+    return this.serverBindAddress;
+  }
+
+  public void setServerBindAddress(String bindAddress) {
+    checkReadOnly();
+    basicSetServerBindAddress(bindAddress);
+    configChanged();
+  }
+
+  public boolean getDisableTcp() {
+    return this.disableTcp;
+  }
+
+  public void setDisableTcp(boolean flag) {
+    checkReadOnly();
+    disableTcp = flag;
+    configChanged();
+  }
+
+  public void setEnableNetworkPartitionDetection(boolean newValue) {
+    checkReadOnly();
+    this.enableNetworkPartitionDetection = newValue;
+    configChanged();
+  }
+
+  public boolean getEnableNetworkPartitionDetection() {
+    return this.enableNetworkPartitionDetection;
+  }
+
+  public void setDisableAutoReconnect(boolean newValue) {
+    checkReadOnly();
+    this.disableAutoReconnect = newValue;
+    configChanged();
+  }
+
+  public boolean getDisableAutoReconnect() {
+    return this.disableAutoReconnect;
+  }
+
+  public int getMemberTimeout() {
+    return this.memberTimeout;
+  }
+
+  public void setMemberTimeout(int value) {
+    checkReadOnly();
+    this.memberTimeout = value;
+    configChanged();
+  }
+
+  private void basicSetBindAddress(String bindAddress) {
+    if (!validateBindAddress(bindAddress)) {
+      throw new IllegalArgumentException(LocalizedStrings.DistributedSystemConfigImpl_INVALID_BIND_ADDRESS_0.toLocalizedString(bindAddress));
+    }
+    this.bindAddress = bindAddress;
+  }
+
+  private void basicSetServerBindAddress(String bindAddress) {
+    if (!validateBindAddress(bindAddress)) {
+      throw new IllegalArgumentException(LocalizedStrings.DistributedSystemConfigImpl_INVALID_BIND_ADDRESS_0.toLocalizedString(bindAddress));
+    }
+    this.serverBindAddress = bindAddress;
+  }
+
+  /**
+   * Returns the remote command setting to use for remote administration
+   */
+  public String getRemoteCommand() {
+    return this.remoteCommand;
+  }
+
+  /**
+   * Sets the remote command for this config object.  This attribute
+   * may be modified after this config object has been used to create
+   * an admin distributed system.
+   */
+  public void setRemoteCommand(String remoteCommand) {
+    if (!ALLOW_ALL_REMOTE_COMMANDS) {
+      checkRemoteCommand(remoteCommand);
+    }
+    this.remoteCommand = remoteCommand;
+    configChanged();
+  }
+
+  private static final boolean ALLOW_ALL_REMOTE_COMMANDS = Boolean.getBoolean(DistributionConfig.GEMFIRE_PREFIX + "admin.ALLOW_ALL_REMOTE_COMMANDS");
+  private static final String[] LEGAL_REMOTE_COMMANDS = { "rsh", "ssh" };
+  private static final String ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH = "Allowed remote commands include \"rsh {HOST} {CMD}\" or \"ssh {HOST} {CMD}\" with valid rsh or ssh switches. Invalid: ";
+
+  private final void checkRemoteCommand(final String remoteCommand) {
+    if (remoteCommand == null || remoteCommand.isEmpty()) {
+      return;
+    }
+    final String command = remoteCommand.toLowerCase().trim();
+    if (!command.contains("{host}") || !command.contains("{cmd}")) {
+      throw new IllegalArgumentException(ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH + remoteCommand);
+    }
+
+    final StringTokenizer tokenizer = new StringTokenizer(command, " ");
+    final ArrayList<String> array = new ArrayList<String>();
+    for (int i = 0; tokenizer.hasMoreTokens(); i++) {
+      String string = tokenizer.nextToken();
+      if (i == 0) {
+        // first element must be rsh or ssh
+        boolean found = false;
+        for (int j = 0; j < LEGAL_REMOTE_COMMANDS.length; j++) {
+          if (string.contains(LEGAL_REMOTE_COMMANDS[j])) {
+            // verify command is at end of string
+            if (!(string.endsWith(LEGAL_REMOTE_COMMANDS[j]) || string.endsWith(LEGAL_REMOTE_COMMANDS[j] + ".exe"))) {
+              throw new IllegalArgumentException(ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH + remoteCommand);
+            }
+            found = true;
+          }
+        }
+        if (!found) {
+          throw new IllegalArgumentException(ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH + remoteCommand);
+        }
+      } else {
+        final boolean isSwitch = string.startsWith("-");
+        final boolean isHostOrCmd = string.equals("{host}") || string.equals("{cmd}");
+
+        // additional elements must be switches or values-for-switches or {host} or user@{host} or {cmd}
+        if (!isSwitch && !isHostOrCmd) {
+          final String previous = (array == null || array.isEmpty()) ? null : array.get(array.size() - 1);
+          final boolean isValueForSwitch = previous != null && previous.startsWith("-");
+          final boolean isHostWithUser = string.contains("@") && string.endsWith("{host}");
+
+          if (!(isValueForSwitch || isHostWithUser)) {
+            throw new IllegalArgumentException(ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH + remoteCommand);
+          }
+        }
+      }
+      array.add(string);
+    }
+  }
+
+  public String getSystemName() {
+    return this.systemName;
+  }
+
+  public void setSystemName(final String systemName) {
+    checkReadOnly();
+    this.systemName = systemName;
+    configChanged();
+  }
+
+  /**
+   * Returns an array of configurations for statically known
+   * CacheServers
+   *
+   * @since GemFire 4.0
+   */ 
+  public CacheServerConfig[] getCacheServerConfigs() {
+    return (CacheServerConfig[]) this.cacheServerConfigs.toArray(
+        new CacheServerConfig[this.cacheServerConfigs.size()]);
+  }
+
+  public CacheVmConfig[] getCacheVmConfigs() {
+    return (CacheVmConfig[]) this.cacheServerConfigs.toArray(new CacheVmConfig[this.cacheServerConfigs.size()]);
+  }
+
+  /**
+   * Creates the configuration for a CacheServer
+   *
+   * @since GemFire 4.0
+   */
+  public CacheServerConfig createCacheServerConfig() {
+    CacheServerConfig config = new CacheServerConfigImpl();
+    addCacheServerConfig(config);
+    return config;
+  }
+
+  public CacheVmConfig createCacheVmConfig() {
+    return (CacheVmConfig) createCacheServerConfig();
+  }
+
+  /**
+   * Adds the configuration for a CacheServer
+   *
+   * @since GemFire 4.0
+   */
+  private void addCacheServerConfig(CacheServerConfig managerConfig) {
+    checkReadOnly();
+
+    if (managerConfig == null)
+      return;
+    for (Iterator iter = this.cacheServerConfigs.iterator(); iter.hasNext(); ) {
+      CacheServerConfigImpl impl = (CacheServerConfigImpl) iter.next();
+      if (impl.equals(managerConfig)) {
+        return;
+      }
+    }
+    this.cacheServerConfigs.add(managerConfig);
+    configChanged();
+  }
+
+  /**
+   * Removes the configuration for a CacheServer
+   *
+   * @since GemFire 4.0
+   */
+  public void removeCacheServerConfig(CacheServerConfig managerConfig) {
+    removeCacheVmConfig((CacheVmConfig) managerConfig);
+  }
+
+  public void removeCacheVmConfig(CacheVmConfig managerConfig) {
+    checkReadOnly();
+    this.cacheServerConfigs.remove(managerConfig);
+    configChanged();
+  }
+
+  /**
+   * Returns the configurations of all managed distribution locators
+   */
+  public DistributionLocatorConfig[] getDistributionLocatorConfigs() {
+    if (this.system != null) {
+      DistributionLocator[] locators =
+          this.system.getDistributionLocators();
+      DistributionLocatorConfig[] configs =
+          new DistributionLocatorConfig[locators.length];
+      for (int i = 0; i < locators.length; i++) {
+        configs[i] = locators[i].getConfig();
+      }
+      return configs;
+
+    } else {
+      Object[] array =
+          new DistributionLocatorConfig[this.locatorConfigs.size()];
+      return (DistributionLocatorConfig[]) this.locatorConfigs.toArray(array);
+    }
+  }
+
+  /**
+   * Creates the configuration for a DistributionLocator
+   */
+  public DistributionLocatorConfig createDistributionLocatorConfig() {
+    checkReadOnly();
+    DistributionLocatorConfig config = new DistributionLocatorConfigImpl();
+    addDistributionLocatorConfig(config);
+    return config;
+  }
+
+  /**
+   * Adds the configuration for a DistributionLocator
+   */
+  private void addDistributionLocatorConfig(DistributionLocatorConfig config) {
+    checkReadOnly();
+    this.locatorConfigs.add(config);
+    configChanged();
+  }
+
+  /**
+   * Removes the configuration for a DistributionLocator
+   */
+  public void removeDistributionLocatorConfig(DistributionLocatorConfig config) {
+    checkReadOnly();
+    this.locatorConfigs.remove(config);
+    configChanged();
+  }
+
+  /**
+   * Validates the bind address.  The address may be a host name or IP address,
+   * but it must not be empty and must be usable for creating an InetAddress.
+   * Cannot have a leading '/' (which InetAddress.toString() produces).
+   *
+   * @param bindAddress host name or IP address to validate
+   */
+  public static boolean validateBindAddress(String bindAddress) {
+    if (bindAddress == null || bindAddress.length() == 0)
+      return true;
+    if (InetAddressUtil.validateHost(bindAddress) == null)
+      return false;
+    return true;
+  }
+
+  public synchronized void configChanged() {
+    ConfigListener[] clients = null;
+    synchronized (this.listeners) {
+      clients = (ConfigListener[])
+          listeners.toArray(new ConfigListener[this.listeners.size()]);
+    }
+    for (int i = 0; i < clients.length; i++) {
+      try {
+        clients[i].configChanged(this);
+      } catch (Exception e) {
+        logger.warn(e.getMessage(), e);
+      }
+    }
+  }
+
+  /**
+   * Registers listener for notification of changes in this config.
+   */
+  public void addListener(ConfigListener listener) {
+    synchronized (this.listeners) {
+      this.listeners.add(listener);
+    }
+  }
+
+  /**
+   * Removes previously registered listener of this config.
+   */
+  public void removeListener(ConfigListener listener) {
+    synchronized (this.listeners) {
+      this.listeners.remove(listener);
+    }
+  }
+
+  // -------------------------------------------------------------------------
+  //   SSL support...
+  // -------------------------------------------------------------------------
+  private boolean sslEnabled =
+      DistributionConfig.DEFAULT_SSL_ENABLED;
+  private String sslProtocols =
+      DistributionConfig.DEFAULT_SSL_PROTOCOLS;
+  private String sslCiphers =
+      DistributionConfig.DEFAULT_SSL_CIPHERS;
+  private boolean sslAuthenticationRequired =
+      DistributionConfig.DEFAULT_SSL_REQUIRE_AUTHENTICATION;
+  private Properties sslProperties = new Properties();
+
+  public boolean isSSLEnabled() {
+    return this.sslEnabled;
+  }
+
+  public void setSSLEnabled(boolean enabled) {
+    checkReadOnly();
+    this.sslEnabled = enabled;
+    configChanged();
+  }
+
+  public String getSSLProtocols() {
+    return this.sslProtocols;
+  }
+
+  public void setSSLProtocols(String protocols) {
+    checkReadOnly();
+    this.sslProtocols = protocols;
+    configChanged();
+  }
+
+  public String getSSLCiphers() {
+    return this.sslCiphers;
+  }
+
+  public void setSSLCiphers(String ciphers) {
+    checkReadOnly();
+    this.sslCiphers = ciphers;
+    configChanged();
+  }
+
+  public boolean isSSLAuthenticationRequired() {
+    return this.sslAuthenticationRequired;
+  }
+
+  public void setSSLAuthenticationRequired(boolean authRequired) {
+    checkReadOnly();
+    this.sslAuthenticationRequired = authRequired;
+    configChanged();
+  }
+
+  public Properties getSSLProperties() {
+    return this.sslProperties;
+  }
+
+  public void setSSLProperties(Properties sslProperties) {
+    checkReadOnly();
+    this.sslProperties = sslProperties;
+    if (this.sslProperties == null) {
+      this.sslProperties = new Properties();
+    }
+    configChanged();
+  }
+
+  public void addSSLProperty(String key, String value) {
+    checkReadOnly();
+    this.sslProperties.put(key, value);
+    configChanged();
+  }
+
+  public void removeSSLProperty(String key) {
+    checkReadOnly();
+    this.sslProperties.remove(key);
+    configChanged();
+  }
+
+  /**
+   * @return the gfSecurityProperties
+   * @since GemFire 6.6.3
+   */
+  public Properties getGfSecurityProperties() {
+    return gfSecurityProperties;
+  }
+
+  public String getLogFile() {
+    return this.logFile;
+  }
+
+  public void setLogFile(String logFile) {
+    checkReadOnly();
+    this.logFile = logFile;
+    configChanged();
+  }
+
+  public String getLogLevel() {
+    return this.logLevel;
+  }
+
+  public void setLogLevel(String logLevel) {
+    checkReadOnly();
+    this.logLevel = logLevel;
+    configChanged();
+  }
+
+  public int getLogDiskSpaceLimit() {
+    return this.logDiskSpaceLimit;
+  }
+
+  public void setLogDiskSpaceLimit(int limit) {
+    checkReadOnly();
+    this.logDiskSpaceLimit = limit;
+    configChanged();
+  }
+
+  public int getLogFileSizeLimit() {
+    return this.logFileSizeLimit;
+  }
+
+  public void setLogFileSizeLimit(int limit) {
+    checkReadOnly();
+    this.logFileSizeLimit = limit;
+    configChanged();
+  }
+
+  /**
+   * Returns the refreshInterval in seconds
+   */
+  public int getRefreshInterval() {
+    return this.refreshInterval;
+  }
+
+  /**
+   * Sets the refreshInterval in seconds
+   */
+  public void setRefreshInterval(int timeInSecs) {
+    checkReadOnly();
+    this.refreshInterval = timeInSecs;
+    configChanged();
+  }
+
+  /**
+   * Makes sure that the mcast port and locators are correct and
+   * consistent.
+   *
+   * @throws IllegalArgumentException If configuration is not valid
+   */
+  public void validate() {
+    if (this.getMcastPort() < MIN_MCAST_PORT ||
+        this.getMcastPort() > MAX_MCAST_PORT) {
+      throw new IllegalArgumentException(LocalizedStrings.DistributedSystemConfigImpl_MCASTPORT_MUST_BE_AN_INTEGER_INCLUSIVELY_BETWEEN_0_AND_1
+          .toLocalizedString(new Object[] { Integer.valueOf(MIN_MCAST_PORT), Integer.valueOf(MAX_MCAST_PORT) }));
+    }
+
+    // disabled in 5.1 - multicast and locators can be used together
+    //if (!DEFAULT_LOCATORS.equals(this.getLocators()) &&
+    //    this.mcastPort > 0) { 
+    //  throw new IllegalArgumentException(
+    //    "mcastPort must be zero when locators are specified");
+    //}
+
+    LogWriterImpl.levelNameToCode(this.logLevel);
+
+    if (this.logFileSizeLimit < MIN_LOG_FILE_SIZE_LIMIT ||
+        this.logFileSizeLimit > MAX_LOG_FILE_SIZE_LIMIT) {
+      throw new IllegalArgumentException(LocalizedStrings.DistributedSystemConfigImpl_LOGFILESIZELIMIT_MUST_BE_AN_INTEGER_BETWEEN_0_AND_1
+          .toLocalizedString(new Object[] { Integer.valueOf(MIN_LOG_FILE_SIZE_LIMIT), Integer.valueOf(MAX_LOG_FILE_SIZE_LIMIT) }));
+    }
+
+    if (this.logDiskSpaceLimit < MIN_LOG_DISK_SPACE_LIMIT ||
+        this.logDiskSpaceLimit > MAX_LOG_DISK_SPACE_LIMIT) {
+      throw new IllegalArgumentException(LocalizedStrings.DistributedSystemConfigImpl_LOGDISKSPACELIMIT_MUST_BE_AN_INTEGER_BETWEEN_0_AND_1
+          .toLocalizedString(new Object[] { Integer.valueOf(MIN_LOG_DISK_SPACE_LIMIT), Integer.valueOf(MAX_LOG_DISK_SPACE_LIMIT) }));
+    }
+
+    parseEntityConfigXMLFile();
+  }
+
+  /**
+   * Makes a deep copy of this config object.
+   */
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DistributedSystemConfigImpl other =
+        (DistributedSystemConfigImpl) super.clone();
+    other.system = null;
+    other.cacheServerConfigs = new HashSet();
+    other.locatorConfigs = new HashSet();
+
+    DistributionLocatorConfig[] myLocators =
+        this.getDistributionLocatorConfigs();
+    for (int i = 0; i < myLocators.length; i++) {
+      DistributionLocatorConfig locator = myLocators[i];
+      other.addDistributionLocatorConfig((DistributionLocatorConfig) locator.clone());
+    }
+
+    CacheServerConfig[] myCacheServers = this.getCacheServerConfigs();
+    for (int i = 0; i < myCacheServers.length; i++) {
+      CacheServerConfig locator = myCacheServers[i];
+      other.addCacheServerConfig((CacheServerConfig) locator.clone());
+    }
+
+    return other;
+  }
+
+  @Override
+  public String toString() {
+    StringBuffer buf = new StringBuffer(1000);
+    String lf = System.getProperty("line.separator");
+    if (lf == null)
+      lf = ",";
+
+    buf.append("DistributedSystemConfig(");
+    buf.append(lf);
+    buf.append("  system-name=");
+    buf.append(String.valueOf(this.systemName));
+    buf.append(lf);
+    buf.append("  " + MCAST_ADDRESS + "=");
+    buf.append(String.valueOf(this.mcastAddress));
+    buf.append(lf);
+    buf.append("  " + MCAST_PORT + "=");
+    buf.append(String.valueOf(this.mcastPort));
+    buf.append(lf);
+    buf.append("  " + LOCATORS + "=");
+    buf.append(String.valueOf(this.locators));
+    buf.append(lf);
+    buf.append("  " + MEMBERSHIP_PORT_RANGE_NAME + "=");
+    buf.append(getMembershipPortRange());
+    buf.append(lf);
+    buf.append("  " + BIND_ADDRESS + "=");
+    buf.append(String.valueOf(this.bindAddress));
+    buf.append(lf);
+    buf.append("  " + TCP_PORT + "=" + this.tcpPort);
+    buf.append(lf);
+    buf.append("  " + DISABLE_TCP + "=");
+    buf.append(String.valueOf(this.disableTcp));
+    buf.append(lf);
+    buf.append("  " + DISABLE_AUTO_RECONNECT + "=");
+    buf.append(String.valueOf(this.disableAutoReconnect));
+    buf.append(lf);
+    buf.append("  " + REMOTE_COMMAND_NAME + "=");
+    buf.append(String.valueOf(this.remoteCommand));
+    buf.append(lf);
+    buf.append("  " + CLUSTER_SSL_ENABLED + "=");
+    buf.append(String.valueOf(this.sslEnabled));
+    buf.append(lf);
+    buf.append("  " + CLUSTER_SSL_CIPHERS + "=");
+    buf.append(String.valueOf(this.sslCiphers));
+    buf.append(lf);
+    buf.append("  " + CLUSTER_SSL_PROTOCOLS + "=");
+    buf.append(String.valueOf(this.sslProtocols));
+    buf.append(lf);
+    buf.append("  " + CLUSTER_SSL_REQUIRE_AUTHENTICATION + "=");
+    buf.append(String.valueOf(this.sslAuthenticationRequired));
+    buf.append(lf);
+    buf.append("  " + LOG_FILE_NAME + "=");
+    buf.append(String.valueOf(this.logFile));
+    buf.append(lf);
+    buf.append("  " + LOG_LEVEL_NAME + "=");
+    buf.append(String.valueOf(this.logLevel));
+    buf.append(lf);
+    buf.append("  " + LOG_DISK_SPACE_LIMIT_NAME + "=");
+    buf.append(String.valueOf(this.logDiskSpaceLimit));
+    buf.append(lf);
+    buf.append("  " + LOG_FILE_SIZE_LIMIT_NAME + "=");
+    buf.append(String.valueOf(this.logFileSizeLimit));
+    buf.append(lf);
+    buf.append("  " + REFRESH_INTERVAL_NAME + "=");
+    buf.append(String.valueOf(this.refreshInterval));
+    buf.append(")");
+    return buf.toString();
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/096b622d/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..9d3f5e6
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthConfigImpl.java
@@ -0,0 +1,58 @@
+/*
+ * 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/096b622d/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..ac47ddc
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthEvaluator.java
@@ -0,0 +1,172 @@
+/*
+ * 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/096b622d/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..3ce8bcc
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthMonitor.java
@@ -0,0 +1,436 @@
+/*
+ * 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/096b622d/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..5b40d64
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorConfigImpl.java
@@ -0,0 +1,192 @@
+/*
+ * 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/096b622d/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..0e5db21
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorImpl.java
@@ -0,0 +1,336 @@
+/*
+ * 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();
+  }
+
+}
+