You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kh...@apache.org on 2018/08/08 16:51:55 UTC

[geode] branch develop updated: Geode 3530 launcher lifecycle server commands (#2267)

This is an automated email from the ASF dual-hosted git repository.

khowe pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 57581af  Geode 3530 launcher lifecycle server commands (#2267)
57581af is described below

commit 57581af961203ed373456ce84a5c8e7bdb4e0ee6
Author: Helena Bales <hb...@pivotal.io>
AuthorDate: Wed Aug 8 09:51:50 2018 -0700

    Geode 3530 launcher lifecycle server commands (#2267)
    
    
    * GEODE-3530: Modernize LauncherLifecycleCommandsDUnit
    
    * Extract server start and locator start/stop/status commands into their own
    classes
    * Remove the CliCommandTestBase
    * make locator names match test method names
    * add a method for writing classes to a jar file
    * add a function to use up all the server's memory for testing
    * add max heap property for out of memory test
    
    Make the behavior of the server out of memory test more reliable by
    adding a maxheap size (otherwise the size is decided by the JVM)
    
    Signed-off-by: Ken Howe<kh...@pivotal.io>
---
 .../LauncherLifecycleCommandsDUnitTest.java        | 843 ---------------------
 .../cli/commands/RunOutOfMemoryFunction.java       |  34 +
 .../cli/commands/StartLocatorCommandDUnitTest.java |   8 +-
 .../cli/commands/StartServerCommandDUnitTest.java  | 473 ++++++++++++
 .../commands/StatusLocatorCommandDunitTest.java    |  89 ---
 .../cli/commands/StopLocatorCommandDUnitTest.java  | 262 +++++++
 .../apache/geode/test/compiler/ClassBuilder.java   |  25 +-
 .../org/apache/geode/test/compiler/TestObject.java |  21 +
 .../geode/test/compiler/ClassBuilderTest.java      |  62 ++
 9 files changed, 879 insertions(+), 938 deletions(-)

diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/LauncherLifecycleCommandsDUnitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/LauncherLifecycleCommandsDUnitTest.java
deleted file mode 100644
index b5a8ea9..0000000
--- a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/LauncherLifecycleCommandsDUnitTest.java
+++ /dev/null
@@ -1,843 +0,0 @@
-/*
- * 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.management.internal.cli.commands;
-
-import static org.apache.geode.distributed.ConfigurationProperties.DURABLE_CLIENT_ID;
-import static org.apache.geode.distributed.ConfigurationProperties.START_LOCATOR;
-import static org.apache.geode.test.dunit.Assert.assertEquals;
-import static org.apache.geode.test.dunit.Assert.assertFalse;
-import static org.apache.geode.test.dunit.Assert.assertNotNull;
-import static org.apache.geode.test.dunit.Assert.assertTrue;
-import static org.apache.geode.test.dunit.Wait.waitForCriterion;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.net.InetAddress;
-import java.nio.charset.Charset;
-import java.text.DateFormat;
-import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.TimeUnit;
-
-import javax.management.MBeanServerConnection;
-import javax.management.ObjectName;
-import javax.management.Query;
-import javax.management.QueryExp;
-import javax.management.remote.JMXConnector;
-import javax.management.remote.JMXConnectorFactory;
-import javax.management.remote.JMXServiceURL;
-
-import org.junit.FixMethodOrder;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-import org.apache.geode.cache.Region;
-import org.apache.geode.cache.client.ClientCache;
-import org.apache.geode.cache.client.ClientCacheFactory;
-import org.apache.geode.cache.client.ClientRegionFactory;
-import org.apache.geode.cache.client.ClientRegionShortcut;
-import org.apache.geode.cache.client.Pool;
-import org.apache.geode.cache.client.PoolFactory;
-import org.apache.geode.cache.client.PoolManager;
-import org.apache.geode.distributed.AbstractLauncher.ServiceState;
-import org.apache.geode.distributed.AbstractLauncher.Status;
-import org.apache.geode.distributed.LocatorLauncher;
-import org.apache.geode.distributed.LocatorLauncher.Builder;
-import org.apache.geode.distributed.LocatorLauncher.Command;
-import org.apache.geode.distributed.LocatorLauncher.LocatorState;
-import org.apache.geode.distributed.ServerLauncher;
-import org.apache.geode.distributed.ServerLauncher.ServerState;
-import org.apache.geode.distributed.internal.DistributionConfig;
-import org.apache.geode.internal.AvailablePortHelper;
-import org.apache.geode.internal.lang.ObjectUtils;
-import org.apache.geode.internal.lang.StringUtils;
-import org.apache.geode.internal.lang.SystemUtils;
-import org.apache.geode.internal.process.ProcessUtils;
-import org.apache.geode.internal.util.IOUtils;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
-import org.apache.geode.test.dunit.WaitCriterion;
-import org.apache.geode.test.junit.categories.FlakyTest;
-import org.apache.geode.test.junit.rules.RequiresGeodeHome;
-
-/**
- * The LauncherLifecycleCommandsDUnitTest class is a test suite of integration tests testing the
- * contract and functionality of the GemFire launcher lifecycle commands inside Gfsh.
- *
- * @see javax.management.MBeanServerConnection
- * @see javax.management.remote.JMXConnector
- * @see org.apache.geode.distributed.AbstractLauncher
- * @see org.apache.geode.distributed.LocatorLauncher
- * @see org.apache.geode.distributed.ServerLauncher
- * @see org.apache.geode.internal.AvailablePortHelper
- * @see org.apache.geode.management.internal.cli.shell.Gfsh
- * @see org.apache.geode.management.internal.cli.commands.CliCommandTestBase
- * @see org.apache.geode.management.internal.cli.util.CommandStringBuilder
- * @since GemFire 7.0
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@SuppressWarnings("serial")
-public class LauncherLifecycleCommandsDUnitTest extends CliCommandTestBase {
-
-  @Rule
-  public RequiresGeodeHome requiresGeodeHome = new RequiresGeodeHome();
-
-  protected static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyyMMddHHmmssSSS");
-
-  private final Queue<Integer> processIds = new ConcurrentLinkedDeque<>();
-
-  protected static String getMemberId(final int jmxManagerPort, final String memberName)
-      throws Exception {
-    return getMemberId(InetAddress.getLocalHost().getHostName(), jmxManagerPort, memberName);
-  }
-
-  protected static String getMemberId(final String jmxManagerHost, final int jmxManagerPort,
-      final String memberName) throws Exception {
-    JMXConnector connector = null;
-
-    try {
-      connector = JMXConnectorFactory.connect(new JMXServiceURL(String.format(
-          "service:jmx:rmi://%1$s/jndi/rmi://%1$s:%2$d/jmxrmi", jmxManagerHost, jmxManagerPort)));
-
-      MBeanServerConnection connection = connector.getMBeanServerConnection();
-
-      ObjectName objectNamePattern = ObjectName.getInstance("GemFire:type=Member,*");
-
-      QueryExp query = Query.eq(Query.attr("Name"), Query.value(memberName));
-
-      Set<ObjectName> objectNames = connection.queryNames(objectNamePattern, query);
-
-      assertNotNull(objectNames);
-      assertFalse(objectNames.isEmpty());
-      assertEquals(1, objectNames.size());
-
-      // final ObjectName objectName = ObjectName.getInstance("GemFire:type=Member,Name=" +
-      // memberName);
-      ObjectName objectName = objectNames.iterator().next();
-
-      // System.err.printf("ObjectName for Member with Name (%1$s) is %2$s%n", memberName,
-      // objectName);
-
-      return ObjectUtils.toString(connection.getAttribute(objectName, "Id"));
-    } finally {
-      IOUtils.close(connector);
-    }
-  }
-
-  @Override
-  public final void postTearDown() throws Exception {
-    Integer pid;
-
-    while ((pid = processIds.poll()) != null) {
-      if (ProcessUtils.isProcessAlive(pid)) {
-        try {
-          String killCommand = String.format("%1$s %2$d",
-              SystemUtils.isWindows() ? "taskkill /F /PID" : "kill -9", pid);
-          Runtime.getRuntime().exec(killCommand);
-        } catch (Throwable ignore) {
-        }
-      }
-    }
-  }
-
-  @SuppressWarnings("unused")
-  protected void assertStatus(final LocatorState expectedStatus, final LocatorState actualStatus) {
-    assertEquals(expectedStatus.getStatus(), actualStatus.getStatus());
-    assertEquals(expectedStatus.getTimestamp(), actualStatus.getTimestamp());
-    assertEquals(expectedStatus.getServiceLocation(), actualStatus.getServiceLocation());
-    assertTrue(ObjectUtils.equalsIgnoreNull(expectedStatus.getPid(), actualStatus.getPid()));
-    assertEquals(expectedStatus.getUptime(), actualStatus.getUptime());
-    assertEquals(expectedStatus.getWorkingDirectory(), actualStatus.getWorkingDirectory());
-    assertEquals(expectedStatus.getJvmArguments(), actualStatus.getJvmArguments());
-    assertEquals(expectedStatus.getClasspath(), actualStatus.getClasspath());
-    assertEquals(expectedStatus.getGemFireVersion(), actualStatus.getGemFireVersion());
-    assertEquals(expectedStatus.getJavaVersion(), actualStatus.getJavaVersion());
-  }
-
-  protected Integer readPid(final File workingDirectory) {
-    assertTrue(String.format("The working directory (%1$s) must exist!", workingDirectory),
-        workingDirectory != null && workingDirectory.isDirectory());
-
-    File[] files = workingDirectory.listFiles(pathname -> (pathname != null && pathname.isFile()
-        && pathname.getAbsolutePath().endsWith(".pid")));
-
-    assertNotNull(files);
-    assertTrue(files.length > 0);
-
-    File pidFile = files[0];
-
-    BufferedReader fileReader = null;
-
-    try {
-      fileReader = new BufferedReader(new FileReader(pidFile), 1024);
-      return Integer.parseInt(fileReader.readLine().trim());
-    } catch (Exception ignore) {
-      return null;
-    } finally {
-      IOUtils.close(fileReader);
-    }
-  }
-
-  protected String serviceStateStatusStringNormalized(final ServiceState serviceState) {
-    return serviceStateStatusStringNormalized(serviceState.toString());
-  }
-
-  protected String serviceStateStatusStringNormalized(final String serviceStateStatus) {
-    assertNotNull(serviceStateStatus);
-    assertTrue("serviceStateStatus is missing 'Uptime': " + serviceStateStatus,
-        serviceStateStatus.contains("Uptime"));
-    assertTrue("serviceStateStatus is missing 'JVM Arguments': " + serviceStateStatus,
-        serviceStateStatus.contains("JVM Arguments"));
-
-    return serviceStateStatus.substring(0, serviceStateStatus.indexOf("Uptime"))
-        .concat(serviceStateStatus.substring(serviceStateStatus.indexOf("JVM Arguments")));
-  }
-
-  protected Status stopLocator(final File workingDirectory) {
-    return stopLocator(IOUtils.tryGetCanonicalPathElseGetAbsolutePath(workingDirectory));
-  }
-
-  protected Status stopLocator(final String workingDirectory) {
-    final Integer pid = readPid(new File(workingDirectory));
-    return waitForGemFireProcessToStop(
-        new Builder().setCommand(Command.STOP).setWorkingDirectory(workingDirectory).build().stop(),
-        pid);
-  }
-
-  protected Status stopServer(final String workingDirectory) {
-    final Integer pid = readPid(new File(workingDirectory));
-    return waitForGemFireProcessToStop(
-        new ServerLauncher.Builder().setCommand(ServerLauncher.Command.STOP)
-            .setWorkingDirectory(workingDirectory).build().stop(),
-        pid);
-  }
-
-  protected String toString(final Result result) {
-    assert result != null : "The Result object from the command execution cannot be null!";
-
-    StringBuilder buffer = new StringBuilder(StringUtils.LINE_SEPARATOR);
-
-    while (result.hasNextLine()) {
-      buffer.append(result.nextLine());
-      buffer.append(StringUtils.LINE_SEPARATOR);
-    }
-
-    return buffer.toString();
-  }
-
-  protected Status waitForGemFireProcessToStop(final ServiceState serviceState,
-      final Integer pid) {
-    if (!Status.STOPPED.equals(serviceState.getStatus())) {
-
-      if (pid != null) {
-        WaitCriterion waitCriteria = new WaitCriterion() {
-          @Override
-          public boolean done() {
-            return !ProcessUtils.isProcessAlive(pid);
-          }
-
-          @Override
-          public String description() {
-            return String.format("Waiting for GemFire Process with PID (%1$d) to stop.", pid);
-          }
-        };
-
-        waitForCriterion(waitCriteria, TimeUnit.SECONDS.toMillis(15),
-            TimeUnit.SECONDS.toMillis(5), false);
-
-        if (!waitCriteria.done()) {
-          processIds.offer(pid);
-        }
-      }
-    }
-
-    return serviceState.getStatus();
-  }
-
-  /**
-   * Test to verify GEODE-2138
-   *
-   */
-  @Test
-  public void testVersionTitleForStartServerAndLocator() throws IOException {
-    final int locatorPort = AvailablePortHelper.getRandomAvailableTCPPort();
-
-    String pathname = (getClass().getSimpleName() + "_" + getTestMethodName());
-    String pathnameLoc = pathname + "_loc";
-    String pathnameSer = pathname + "_ser";
-
-    File workingDirectoryLoc = temporaryFolder.newFolder(pathnameLoc);
-    File workingDirectorySer = temporaryFolder.newFolder(pathnameSer);
-
-    assertTrue(workingDirectoryLoc.isDirectory() || workingDirectoryLoc.mkdir());
-    assertTrue(workingDirectorySer.isDirectory() || workingDirectorySer.mkdir());
-
-    try {
-      // verify the start locator output does not contain string "gemfire"
-      CommandStringBuilder locCommand = new CommandStringBuilder(CliStrings.START_LOCATOR);
-      locCommand.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, pathnameLoc);
-      locCommand.addOption(CliStrings.START_LOCATOR__CONNECT, Boolean.FALSE.toString());
-      locCommand.addOption(CliStrings.START_LOCATOR__DIR, workingDirectoryLoc.getCanonicalPath());
-      locCommand.addOption(CliStrings.START_LOCATOR__PORT, String.valueOf(locatorPort));
-
-      CommandResult locResult = executeCommand(locCommand.toString());
-      assertNotNull(locResult);
-      assertEquals(Result.Status.OK, locResult.getStatus());
-      String locatorOutput = toString(locResult);
-      assertNotNull(locatorOutput);
-      assertTrue("Locator output was: " + locatorOutput, !locatorOutput.contains("Gemfire"));
-
-      // verify the start server output does not contain string "gemfire"
-      CommandStringBuilder serCommand = new CommandStringBuilder(CliStrings.START_SERVER);
-      serCommand.addOption(CliStrings.START_SERVER__NAME, pathnameSer);
-      serCommand.addOption(CliStrings.START_SERVER__DIR, workingDirectorySer.getCanonicalPath());
-
-      CommandResult serResult = executeCommand(serCommand.toString());
-      assertNotNull(serResult);
-      assertEquals(Result.Status.OK, serResult.getStatus());
-      String serverOutput = toString(serResult);
-
-      assertTrue("Server start output was: " + serverOutput, !serverOutput.contains("Gemfire"));
-    } finally {
-      stopLocator(workingDirectoryLoc);
-      stopServer(IOUtils.tryGetCanonicalPathElseGetAbsolutePath(workingDirectorySer));
-    }
-  }
-
-  @Test
-  public void test003StartServerFailsFastOnMissingCacheXmlFile() throws IOException {
-    String cacheXmlPathname = "/path/to/missing/cache.xml";
-
-    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_SERVER);
-    String pathName = getClass().getSimpleName().concat("_").concat(getTestMethodName());
-    final File workingDirectory = temporaryFolder.newFolder(pathName);
-
-    command.addOption(CliStrings.START_SERVER__NAME, pathName);
-    command.addOption(CliStrings.START_SERVER__DIR, workingDirectory.getCanonicalPath());
-    command.addOption(CliStrings.START_SERVER__CACHE_XML_FILE, cacheXmlPathname);
-
-    CommandResult result = executeCommand(command.toString());
-
-    assertNotNull(result);
-    assertEquals(Result.Status.ERROR, result.getStatus());
-
-    String resultString = toString(result);
-
-    assertTrue(resultString, resultString
-        .contains(MessageFormat.format(CliStrings.CACHE_XML_NOT_FOUND_MESSAGE, cacheXmlPathname)));
-  }
-
-  @Test
-  public void test004StartServerFailsFastOnMissingGemFirePropertiesFile() throws IOException {
-    String gemfirePropertiesFile = "/path/to/missing/gemfire.properties";
-
-    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_SERVER);
-
-    String pathName = getClass().getSimpleName().concat("_").concat(getTestMethodName());
-    final File workingDirectory = temporaryFolder.newFolder(pathName);
-
-    command.addOption(CliStrings.START_SERVER__NAME, pathName);
-    command.addOption(CliStrings.START_SERVER__DIR, workingDirectory.getCanonicalPath());
-    command.addOption(CliStrings.START_SERVER__PROPERTIES, gemfirePropertiesFile);
-
-    CommandResult result = executeCommand(command.toString());
-
-    assertNotNull(result);
-    assertEquals(Result.Status.ERROR, result.getStatus());
-
-    String resultString = toString(result);
-
-    assertTrue(resultString,
-        resultString
-            .contains(MessageFormat.format(CliStrings.GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE,
-                StringUtils.EMPTY, gemfirePropertiesFile)));
-  }
-
-  @Test
-  public void testStartServerFailsFastOnMissingPassword() throws IOException {
-
-    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_SERVER);
-
-    String pathName = getClass().getSimpleName().concat("_").concat(getTestMethodName());
-    final File workingDirectory = temporaryFolder.newFolder(pathName);
-
-    command.addOption(CliStrings.START_SERVER__NAME, pathName);
-    command.addOption(CliStrings.START_SERVER__DIR, workingDirectory.getCanonicalPath());
-    command.addOption(CliStrings.START_SERVER__USERNAME, "test");
-
-    CommandResult result = executeCommand(command.toString());
-
-    assertNotNull(result);
-    assertEquals(Result.Status.ERROR, result.getStatus());
-
-    String resultString = toString(result);
-
-    assertTrue(resultString, resultString.contains("password must be specified"));
-  }
-
-  @Test
-  public void test005StartServerFailsFastOnMissingGemFireSecurityPropertiesFile()
-      throws IOException {
-    String gemfireSecuritiesPropertiesFile = "/path/to/missing/gemfire-securities.properties";
-
-    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_SERVER);
-
-    String pathName = getClass().getSimpleName().concat("_").concat(getTestMethodName());
-    final File workingDirectory = temporaryFolder.newFolder(pathName);
-
-    command.addOption(CliStrings.START_SERVER__NAME, pathName);
-    command.addOption(CliStrings.START_SERVER__DIR, workingDirectory.getCanonicalPath());
-    command.addOption(CliStrings.START_SERVER__SECURITY_PROPERTIES,
-        gemfireSecuritiesPropertiesFile);
-
-    CommandResult result = executeCommand(command.toString());
-
-    assertNotNull(result);
-    assertEquals(Result.Status.ERROR, result.getStatus());
-
-    String resultString = toString(result);
-
-    assertTrue(resultString,
-        resultString
-            .contains(MessageFormat.format(CliStrings.GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE,
-                "Security ", gemfireSecuritiesPropertiesFile)));
-  }
-
-  @Test
-  public void test009StatusLocatorUsingMemberId() throws Exception {
-    final int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2);
-
-    final int jmxManagerPort = ports[0];
-    final int locatorPort = ports[1];
-
-    String pathname = (getClass().getSimpleName() + "_" + getTestMethodName());
-    File workingDirectory = temporaryFolder.newFolder(pathname);
-
-    assertTrue(workingDirectory.isDirectory() || workingDirectory.mkdir());
-
-    try {
-      CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_LOCATOR);
-
-      command.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, pathname);
-      command.addOption(CliStrings.START_LOCATOR__CONNECT, Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__DIR, workingDirectory.getCanonicalPath());
-      command.addOption(CliStrings.START_LOCATOR__PORT, String.valueOf(locatorPort));
-      command.addOption(CliStrings.START_LOCATOR__ENABLE__SHARED__CONFIGURATION,
-          Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__FORCE, Boolean.TRUE.toString());
-      command.addOption(CliStrings.START_LOCATOR__J,
-          "-D" + DistributionConfig.GEMFIRE_PREFIX + "http-service-port=0");
-      command.addOption(CliStrings.START_LOCATOR__J,
-          "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager-port=" + jmxManagerPort);
-
-      CommandResult result = executeCommand(command.toString());
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      LocatorLauncher locatorLauncher = new LocatorLauncher.Builder()
-          .setCommand(LocatorLauncher.Command.STATUS).setBindAddress(null).setPort(locatorPort)
-          .setWorkingDirectory(workingDirectory.getPath()).build();
-
-      assertNotNull(locatorLauncher);
-
-      LocatorState expectedLocatorState =
-          locatorLauncher.waitOnStatusResponse(60, 10, TimeUnit.SECONDS);
-
-      assertNotNull(expectedLocatorState);
-      assertEquals(Status.ONLINE, expectedLocatorState.getStatus());
-
-      result = executeCommand(
-          String.format("%1$s --locator=localhost[%2$d]", CliStrings.CONNECT, locatorPort));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      result = executeCommand(String.format("%1$s --name=%2$s", CliStrings.STATUS_LOCATOR,
-          getMemberId(jmxManagerPort, pathname)));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-      assertTrue(serviceStateStatusStringNormalized(toString(result))
-          .contains(serviceStateStatusStringNormalized(expectedLocatorState)));
-    } finally {
-      stopLocator(workingDirectory);
-    }
-  }
-
-  @Test
-  public void test010StopLocatorUsingMemberNameIDWhenGfshIsNotConnected() {
-    CommandResult result =
-        executeCommand(CliStrings.STOP_LOCATOR + " --name=" + getTestMethodName());
-
-    assertNotNull(result);
-    assertEquals(Result.Status.ERROR, result.getStatus());
-    assertEquals(
-        CliStrings.format(CliStrings.STOP_SERVICE__GFSH_NOT_CONNECTED_ERROR_MESSAGE, "Locator"),
-        StringUtils.trim(toString(result)));
-  }
-
-  @Test
-  public void test011StopLocatorUsingMemberName() throws IOException {
-    final int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2);
-
-    final int jmxManagerPort = ports[0];
-    final int locatorPort = ports[1];
-
-    String pathname = (getClass().getSimpleName() + "_" + getTestMethodName());
-    File workingDirectory = temporaryFolder.newFolder(pathname);
-
-    try {
-      assertTrue(workingDirectory.isDirectory() || workingDirectory.mkdir());
-
-      CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_LOCATOR);
-
-      command.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, pathname);
-      command.addOption(CliStrings.START_LOCATOR__CONNECT, Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__DIR, workingDirectory.getCanonicalPath());
-      command.addOption(CliStrings.START_LOCATOR__PORT, String.valueOf(locatorPort));
-      command.addOption(CliStrings.START_LOCATOR__ENABLE__SHARED__CONFIGURATION,
-          Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__FORCE, Boolean.TRUE.toString());
-      command.addOption(CliStrings.START_LOCATOR__J,
-          "-D" + DistributionConfig.GEMFIRE_PREFIX + "http-service-port=0");
-      command.addOption(CliStrings.START_LOCATOR__J,
-          "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager-port=" + jmxManagerPort);
-
-      CommandResult result = executeCommand(command.toString());
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      final LocatorLauncher locatorLauncher =
-          new Builder().setCommand(Command.STOP).setBindAddress(null).setPort(locatorPort)
-              .setWorkingDirectory(workingDirectory.getPath()).build();
-
-      assertNotNull(locatorLauncher);
-
-      LocatorState locatorStatus = locatorLauncher.waitOnStatusResponse(60, 10, TimeUnit.SECONDS);
-
-      assertNotNull(locatorStatus);
-      assertEquals(Status.ONLINE, locatorStatus.getStatus());
-
-      result = executeCommand(
-          String.format("%1$s --locator=localhost[%2$d]", CliStrings.CONNECT, locatorPort));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      result = executeCommand(
-          String.format("%1$s --name=invalidLocatorMemberName", CliStrings.STOP_LOCATOR));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.ERROR, result.getStatus());
-      assertEquals(
-          CliStrings.format(CliStrings.STOP_LOCATOR__NO_LOCATOR_FOUND_FOR_MEMBER_ERROR_MESSAGE,
-              "invalidLocatorMemberName"),
-          StringUtils.trim(toString(result)));
-
-      locatorStatus = locatorLauncher.status();
-
-      assertNotNull(locatorStatus);
-      assertEquals(Status.ONLINE, locatorStatus.getStatus());
-
-      result = executeCommand(String.format("%1$s --name=%2$s", CliStrings.STOP_LOCATOR, pathname));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      // TODO figure out what output to assert and validate on now that 'stop locator' uses Gfsh's
-      // logger
-      // and standard err/out...
-      // assertIndexDetailsEquals(CliStrings.format(CliStrings.STOP_LOCATOR__SHUTDOWN_MEMBER_MESSAGE,
-      // pathname),
-      // StringUtils.trim(toString(result)));
-
-      WaitCriterion waitCriteria = new WaitCriterion() {
-        @Override
-        public boolean done() {
-          final LocatorState locatorStatus = locatorLauncher.status();
-          return (locatorStatus != null && Status.NOT_RESPONDING.equals(locatorStatus.getStatus()));
-        }
-
-        @Override
-        public String description() {
-          return "wait for the Locator to stop; the Locator will no longer respond after it stops";
-        }
-      };
-
-      waitForCriterion(waitCriteria, 15 * 1000, 5000, true);
-
-      locatorStatus = locatorLauncher.status();
-
-      assertNotNull(locatorStatus);
-      assertEquals(Status.NOT_RESPONDING, locatorStatus.getStatus());
-    } finally {
-    }
-
-  }
-
-  // @see Trac Bug # 46760
-  @Test
-  public void test012StopLocatorUsingMemberId() throws Exception {
-    final int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2);
-
-    final int jmxManagerPort = ports[0];
-    final int locatorPort = ports[1];
-
-    String pathname = (getClass().getSimpleName() + "_" + getTestMethodName());
-    File workingDirectory = temporaryFolder.newFolder(pathname);
-
-    try {
-      assertTrue(workingDirectory.isDirectory() || workingDirectory.mkdir());
-
-      CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_LOCATOR);
-
-      command.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, pathname);
-      command.addOption(CliStrings.START_LOCATOR__CONNECT, Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__DIR, workingDirectory.getCanonicalPath());
-      command.addOption(CliStrings.START_LOCATOR__PORT, String.valueOf(locatorPort));
-      command.addOption(CliStrings.START_LOCATOR__ENABLE__SHARED__CONFIGURATION,
-          Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__FORCE, Boolean.TRUE.toString());
-      command.addOption(CliStrings.START_LOCATOR__J,
-          "-D" + DistributionConfig.GEMFIRE_PREFIX + "http-service-port=0");
-      command.addOption(CliStrings.START_LOCATOR__J,
-          "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager-port=" + jmxManagerPort);
-
-      CommandResult result = executeCommand(command.toString());
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      final LocatorLauncher locatorLauncher =
-          new Builder().setCommand(Command.STOP).setBindAddress(null).setPort(locatorPort)
-              .setWorkingDirectory(workingDirectory.getPath()).build();
-
-      assertNotNull(locatorLauncher);
-
-      LocatorState locatorState = locatorLauncher.waitOnStatusResponse(60, 10, TimeUnit.SECONDS);
-
-      assertNotNull(locatorState);
-      assertEquals(Status.ONLINE, locatorState.getStatus());
-
-      result = executeCommand(
-          String.format("%1$s --locator=localhost[%2$d]", CliStrings.CONNECT, locatorPort));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      String memberId = getMemberId(jmxManagerPort, pathname);
-
-      result = executeCommand(String.format("%1$s --name=%2$s", CliStrings.STOP_LOCATOR, memberId));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      // TODO figure out what output to assert and validate on now that 'stop locator' uses Gfsh's
-      // logger
-      // and standard err/out...
-      // assertIndexDetailsEquals(CliStrings.format(CliStrings.STOP_LOCATOR__SHUTDOWN_MEMBER_MESSAGE,
-      // memberId),
-      // StringUtils.trim(toString(result)));
-
-      WaitCriterion waitCriteria = new WaitCriterion() {
-        @Override
-        public boolean done() {
-          LocatorState locatorState = locatorLauncher.status();
-          return (locatorState != null && Status.NOT_RESPONDING.equals(locatorState.getStatus()));
-        }
-
-        @Override
-        public String description() {
-          return "wait for the Locator to stop; the Locator will no longer respond after it stops";
-        }
-      };
-
-      waitForCriterion(waitCriteria, 15 * 1000, 5000, true);
-
-      locatorState = locatorLauncher.status();
-
-      assertNotNull(locatorState);
-      assertEquals(Status.NOT_RESPONDING, locatorState.getStatus());
-    } finally {
-    }
-  }
-
-  @Test
-  @Category(FlakyTest.class) // GEODE-1866
-  public void test014GemFireServerJvmProcessTerminatesOnOutOfMemoryError() throws Exception {
-    int ports[] = AvailablePortHelper.getRandomAvailableTCPPorts(2);
-    final int serverPort = ports[0];
-    final int locatorPort = ports[1];
-
-    String pathname = getClass().getSimpleName().concat("_").concat(getTestMethodName());
-    File workingDirectory = temporaryFolder.newFolder(pathname);
-
-    assertTrue(workingDirectory.isDirectory() || workingDirectory.mkdir());
-
-    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_SERVER);
-
-    command.addOption(CliStrings.START_SERVER__NAME,
-        pathname + TIMESTAMP.format(Calendar.getInstance().getTime()));
-    command.addOption(CliStrings.START_SERVER__SERVER_PORT, String.valueOf(serverPort));
-    command.addOption(CliStrings.START_SERVER__USE_CLUSTER_CONFIGURATION, Boolean.FALSE.toString());
-    command.addOption(CliStrings.START_SERVER__MAXHEAP, "12M");
-    command.addOption(CliStrings.START_SERVER__LOG_LEVEL, "config");
-    command.addOption(CliStrings.START_SERVER__DIR, workingDirectory.getCanonicalPath());
-    command.addOption(CliStrings.START_SERVER__CACHE_XML_FILE,
-        IOUtils.tryGetCanonicalPathElseGetAbsolutePath(writeAndGetCacheXmlFile(workingDirectory)));
-    command.addOption(CliStrings.START_SERVER__INCLUDE_SYSTEM_CLASSPATH);
-    command.addOption(CliStrings.START_SERVER__J, "-D" + DistributionConfig.GEMFIRE_PREFIX + ""
-        + START_LOCATOR + "=localhost[" + locatorPort + "]");
-
-
-    CommandResult result = executeCommand(command.toString());
-    System.out.println("result=" + result);
-
-    assertNotNull(result);
-    assertEquals(Result.Status.OK, result.getStatus());
-
-    ServerLauncher serverLauncher =
-        new ServerLauncher.Builder().setCommand(ServerLauncher.Command.STATUS)
-            .setWorkingDirectory(IOUtils.tryGetCanonicalPathElseGetAbsolutePath(workingDirectory))
-            .build();
-
-    assertNotNull(serverLauncher);
-
-    ServerState serverState = serverLauncher.status();
-
-    assertNotNull(serverState);
-    assertEquals(Status.ONLINE, serverState.getStatus());
-
-    // Verify our GemFire Server JVM process is running!
-    assertTrue(serverState.isVmWithProcessIdRunning());
-
-    ClientCache clientCache = setupClientCache(pathname + String.valueOf(serverPort), serverPort);
-
-    assertNotNull(clientCache);
-
-    try {
-      Region<Long, String> exampleRegion = clientCache.getRegion("/Example");
-      // run the GemFire Server "out-of-town" with an OutOfMemoryError!
-      for (long index = 0; index < Long.MAX_VALUE; index++) {
-        exampleRegion.put(index, String.valueOf(index));
-      }
-    } catch (Exception ignore) {
-      System.err.printf("%1$s: %2$s%n", ignore.getClass().getName(), ignore.getMessage());
-    } finally {
-      clientCache.close();
-
-      final int serverPid = serverState.getPid();
-
-      WaitCriterion waitCriteria = new WaitCriterion() {
-        @Override
-        public boolean done() {
-          return !ProcessUtils.isProcessAlive(serverPid);
-        }
-
-        @Override
-        public String description() {
-          return "Wait for the GemFire Server JVM process that ran out-of-memory to exit.";
-        }
-      };
-
-      waitForCriterion(waitCriteria, TimeUnit.SECONDS.toMillis(30), TimeUnit.SECONDS.toMillis(10),
-          true);
-
-      // Verify our GemFire Server JVM process is was terminated!
-      assertFalse(serverState.isVmWithProcessIdRunning());
-
-      serverState = serverLauncher.status();
-
-      assertNotNull(serverState);
-      assertEquals(Status.NOT_RESPONDING, serverState.getStatus());
-    }
-  }
-
-  private File writeAndGetCacheXmlFile(final File workingDirectory) throws IOException {
-    File cacheXml = new File(workingDirectory, "cache.xml");
-    StringBuilder buffer = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
-
-    buffer.append(StringUtils.LINE_SEPARATOR);
-    buffer.append(
-        "<!DOCTYPE cache PUBLIC  \"-//GemStone Systems, Inc.//GemFire Declarative Caching 7.0//EN\"");
-    buffer.append(StringUtils.LINE_SEPARATOR);
-    buffer.append("  \"http://www.gemstone.com/dtd/cache7_0.dtd\">");
-    buffer.append(StringUtils.LINE_SEPARATOR);
-    buffer.append("<cache>");
-    buffer.append(StringUtils.LINE_SEPARATOR);
-    buffer.append("  <region name=\"Example\" refid=\"REPLICATE\"/>");
-    buffer.append(StringUtils.LINE_SEPARATOR);
-    buffer.append("</cache>");
-
-    BufferedWriter fileWriter = null;
-
-    try {
-      fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(cacheXml, false),
-          Charset.forName("UTF-8").newEncoder()));
-      fileWriter.write(buffer.toString());
-      fileWriter.flush();
-    } finally {
-      IOUtils.close(fileWriter);
-    }
-
-    return cacheXml;
-  }
-
-  private ClientCache setupClientCache(final String durableClientId, final int serverPort) {
-    ClientCache clientCache =
-        new ClientCacheFactory().set(DURABLE_CLIENT_ID, durableClientId).create();
-
-    PoolFactory poolFactory = PoolManager.createFactory();
-
-    poolFactory.setMaxConnections(10);
-    poolFactory.setMinConnections(1);
-    poolFactory.setReadTimeout(5000);
-    poolFactory.addServer("localhost", serverPort);
-
-    Pool pool = poolFactory.create("serverConnectionPool");
-
-    assertNotNull("The 'serverConnectionPool' was not properly configured and initialized!", pool);
-
-    ClientRegionFactory<Long, String> regionFactory =
-        clientCache.createClientRegionFactory(ClientRegionShortcut.PROXY);
-
-    regionFactory.setPoolName(pool.getName());
-    regionFactory.setKeyConstraint(Long.class);
-    regionFactory.setValueConstraint(String.class);
-
-    Region<Long, String> exampleProxy = regionFactory.create("Example");
-
-    assertNotNull("The 'Example' Client Region was not properly configured and initialized",
-        exampleProxy);
-
-    return clientCache;
-  }
-
-}
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/RunOutOfMemoryFunction.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/RunOutOfMemoryFunction.java
new file mode 100644
index 0000000..c030788
--- /dev/null
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/RunOutOfMemoryFunction.java
@@ -0,0 +1,34 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionContext;
+
+public class RunOutOfMemoryFunction implements Function {
+
+  @Override
+  public void execute(FunctionContext context) {
+    byte[] bytes = new byte[Integer.MAX_VALUE / 2];
+    byte[] bytes1 = new byte[Integer.MAX_VALUE / 2];
+    byte[] bytes2 = new byte[Integer.MAX_VALUE / 2];
+
+    Region testRegion = context.getCache().getRegion("/testRegion");
+    testRegion.put("byteArray", bytes);
+    testRegion.put("byteArray1", bytes1);
+    testRegion.put("byteArray2", bytes2);
+  }
+}
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommandDUnitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommandDUnitTest.java
index 3c2c7f6..0e0bf18 100644
--- a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommandDUnitTest.java
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommandDUnitTest.java
@@ -35,7 +35,6 @@ import java.text.MessageFormat;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -226,14 +225,13 @@ public class StartLocatorCommandDUnitTest {
     assertThat(result.getMessageFromContent()).containsPattern(expectedMessage);
   }
 
-  @Ignore // Ignore until test until tests in CI use a different user
   @Test
   public void testInMissingRelativeDirectoryWithoutCreatePermissions() {
     // path to a missing dir that cannot be created due to insufficient permissions
     final String missingDirPath = "/missing/path/to/start/in";
     final String expectedMessage = "Could not create directory " + missingDirPath
         + ". Please verify directory path or user permissions.";
-    final String memberName = "testWithMissingRelativeDirectory-locator";
+    final String memberName = "testInMissingRelativeDirectoryWithoutCreatePermissions-locator";
 
     CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
         .addOption(START_LOCATOR__MEMBER_NAME, memberName)
@@ -250,7 +248,7 @@ public class StartLocatorCommandDUnitTest {
   public void testInMissingRelativeDirectoryThatCanBeCreated() {
     // path to a missing dir that can be created
     final String missingDirPath = System.getProperty("user.dir") + "/missing/path/to/start/in";
-    final String memberName = "testWithMissingRelativeDirectory-locator";
+    final String memberName = "testInMissingRelativeDirectoryThatCanBeCreated-locator";
     final String expectedMessage = "Locator in " + missingDirPath;
 
     CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
@@ -307,7 +305,7 @@ public class StartLocatorCommandDUnitTest {
       assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
       assertThat(result.getMessageFromContent()).contains(expectedMessage);
 
-      // Verify GEODE-2138
+      // Verify GEODE-2138 (Geode commands do not contain GemFire in output)
       assertThat(result.getMessageFromContent()).doesNotContain("Gemfire")
           .doesNotContain("GemFire");
       assertThat(result.getMessageFromContent()).containsPattern(expectedVersionPattern);
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartServerCommandDUnitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartServerCommandDUnitTest.java
new file mode 100644
index 0000000..dd96e99
--- /dev/null
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartServerCommandDUnitTest.java
@@ -0,0 +1,473 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.CACHE_XML_NOT_FOUND_MESSAGE;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.CREATE_REGION;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.CREATE_REGION__REGION;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.CREATE_REGION__REGIONSHORTCUT;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.GROUP;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__CACHE_XML_FILE;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__DIR;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__FORCE;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__LOCATORS;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__MAXHEAP;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__NAME;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__OFF_HEAP_MEMORY_SIZE;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__PROPERTIES;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__SECURITY_PROPERTIES;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__SERVER_PORT;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_SERVER__USERNAME;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import org.apache.geode.cache.execute.FunctionException;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.distributed.AbstractLauncher;
+import org.apache.geode.distributed.ServerLauncher;
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.internal.process.ProcessType;
+import org.apache.geode.internal.process.ProcessUtils;
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import org.apache.geode.test.compiler.ClassBuilder;
+import org.apache.geode.test.dunit.rules.ClusterStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.junit.rules.GfshCommandRule;
+
+public class StartServerCommandDUnitTest {
+  private static MemberVM locator;
+  private static String locatorConnectionString;
+  private File workingDir;
+
+  @ClassRule
+  public static final ClusterStartupRule cluster = new ClusterStartupRule();
+
+  @ClassRule
+  public static final GfshCommandRule gfsh = new GfshCommandRule();
+
+  @Rule
+  public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  @Before
+  public void before() throws IOException {
+    workingDir = temporaryFolder.newFolder();
+  }
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    // locator used to clean up members started during tests
+    locator = cluster.startLocatorVM(0, 0);
+
+    locatorConnectionString = "localhost[" + locator.getPort() + "]";
+
+    gfsh.connectAndVerify(locator);
+  }
+
+  @AfterClass
+  public static void afterClass() throws Exception {
+    gfsh.connectAndVerify(locator);
+    gfsh.execute("shutdown --include-locators");
+  }
+
+  @Test
+  public void testWithMissingCacheXml() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String missingCacheXmlPath = "/missing/cache.xml";
+    final String memberName = "testWithMissingCacheXml-server";
+    final String expectedError =
+        CliStrings.format(CACHE_XML_NOT_FOUND_MESSAGE, missingCacheXmlPath);
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .addOption(START_SERVER__CACHE_XML_FILE, missingCacheXmlPath)
+        .addOption(START_SERVER__DIR, workingDir.getCanonicalPath())
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithMissingGemFirePropertiesFile() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String missingGemfirePropertiesPath = "/missing/gemfire.properties";
+    final String memberName = "testWithMissingGemFirePropertiesFile-server";
+    final String expectedError =
+        CliStrings.format(GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE, "", missingGemfirePropertiesPath);
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .addOption(START_SERVER__PROPERTIES, missingGemfirePropertiesPath)
+        .addOption(START_SERVER__DIR, workingDir.getCanonicalPath())
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithMissingPassword() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String memberName = "testWithMissingPassword-server";
+    final String expectedError = "password must be specified.";
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .addOption(START_SERVER__USERNAME, "usernameValue")
+        .addOption(START_SERVER__DIR, workingDir.getCanonicalPath())
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithMissingSecurityPropertiesFile() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String missingSecurityPropertiesPath = "/missing/security.properties";
+    final String memberName = "testWithMissingSecurityPropertiesFile-server";
+    final String expectedError = CliStrings.format(GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE,
+        "Security ", missingSecurityPropertiesPath);
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .addOption(START_SERVER__DIR, workingDir.getCanonicalPath())
+        .addOption(START_SERVER__SECURITY_PROPERTIES, missingSecurityPropertiesPath)
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithUnavailablePort() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String memberName = "testWithUnavailablePort-server";
+    final String expectedError =
+        "java.lang.RuntimeException: An IO error occurred while starting a Server";
+    final String expectedCause = "Caused by: java.net.BindException: "
+        + "Network is unreachable; port (" + serverPort + ") is not available on localhost.";
+
+    try (Socket interferingProcess = new Socket()) {
+      interferingProcess.bind(new InetSocketAddress(serverPort)); // make the target port
+                                                                  // unavailable
+
+      String command = new CommandStringBuilder(START_SERVER)
+          .addOption(START_SERVER__NAME, memberName)
+          .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+          .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+          .addOption(START_SERVER__DIR, workingDir.getCanonicalPath())
+          .getCommandString();
+
+      CommandResult result = gfsh.executeCommand(command);
+
+      assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+      assertThat(result.getMessageFromContent()).contains(expectedError).contains(expectedCause);
+    }
+  }
+
+  @Test
+  public void testWithAvailablePort() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String memberName = "testWithAvailablePort-server";
+    final String expectedMessage = "Server in " + workingDir.getCanonicalPath();
+    final String expectedMessage2 = "as " + memberName + " is currently online.";
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .addOption(START_SERVER__DIR, workingDir.getCanonicalPath())
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMessageFromContent()).contains(expectedMessage).contains(expectedMessage2);
+  }
+
+  @Test
+  public void testWithMissingStartDirectoryThatCannotBeCreated() {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String missingDirPath = "/missing/dir/to/start/in";
+    final String memberName = "testWithMissingStartDirectoryThatCannotBeCreated-server";
+    final String expectedError = "Could not create directory " + missingDirPath
+        + ". Please verify directory path or user permissions.";
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .addOption(START_SERVER__DIR, missingDirPath)
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithMissingStartDirectoryThatCanBeCreated() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String missingDirPath = workingDir.getCanonicalPath() + "/missing/dir/to/start/in";
+    final String memberName = "testWithMissingStartDirectoryThatCanBeCreated-server";
+    final String expectedMessage = "Server in " + missingDirPath;
+
+    try {
+      String command = new CommandStringBuilder(START_SERVER)
+          .addOption(START_SERVER__NAME, memberName)
+          .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+          .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+          .addOption(START_SERVER__DIR, missingDirPath)
+          .getCommandString();
+
+      CommandResult result = gfsh.executeCommand(command);
+
+      assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+      assertThat(result.getMessageFromContent()).contains(expectedMessage);
+    } finally {
+      File toDelete = new File(missingDirPath);
+      deleteServerFiles(toDelete);
+    }
+  }
+
+  @Test
+  public void testWithConflictingPIDFile() throws IOException {
+    final String fileName = ProcessType.SERVER.getPidFileName();
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String memberName = "testWithConflictingPIDFile-server";
+
+    // create PID file
+    File pidFile = new File(workingDir.getAbsolutePath(), fileName);
+    assertThat(pidFile.createNewFile()).isTrue();
+
+    // write PID to PID file
+    try (FileWriter fileWriter = new FileWriter(pidFile, false)) {
+      fileWriter.write(getPidOrOne().toString() + "\n");
+      fileWriter.flush();
+    }
+    assertThat(pidFile.isFile()).isTrue();
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .addOption(START_SERVER__DIR, pidFile.getParentFile().getCanonicalPath())
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    final String expectedError = "A PID file already exists and a Server may be running in "
+        + pidFile.getParentFile().getCanonicalPath();
+    final String expectedCause = "Caused by: "
+        + "org.apache.geode.internal.process.FileAlreadyExistsException: Pid file already exists: "
+        + pidFile.getCanonicalPath();
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError).contains(expectedCause);
+
+  }
+
+  @Test
+  public void testWithForceOverwriteConflictingPIDFile() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String memberName = "testWithForceOverwriteConflictingPIDFile-server";
+    final String fileName = ProcessType.SERVER.getPidFileName();
+
+    // create PID file
+    File pidFile = new File(workingDir.getAbsolutePath(), fileName);
+    assertThat(pidFile.createNewFile()).isTrue();
+
+    // write PID to PID file
+    try (FileWriter fileWriter = new FileWriter(pidFile, false)) {
+      fileWriter.write(getPidOrOne().toString() + "\n");
+      fileWriter.flush();
+    }
+    assertThat(pidFile.isFile()).isTrue();
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .addOption(START_SERVER__DIR, pidFile.getParentFile().getCanonicalPath())
+        .addOption(START_SERVER__FORCE, "true")
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    final String expectedMessage = "Server in " + pidFile.getParentFile().getCanonicalPath();
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMessageFromContent()).contains(expectedMessage);
+  }
+
+  @Test
+  public void testWithConnectionToLocator() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String memberName = "testWithConnectionToLocator-server";
+    final String expectedVersionPattern = "Geode Version: \\d+\\.\\d+\\.\\d+";
+    final String expectedMessage = "Server in " + workingDir.getCanonicalPath();
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__DIR, workingDir.getCanonicalPath())
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMessageFromContent()).contains(expectedMessage);
+
+    // verify GEODE-2138 (Geode commands do not contain GemFire in output)
+    assertThat(result.getMessageFromContent()).doesNotContain("Gemfire")
+        .doesNotContain("GemFire");
+    assertThat(result.getMessageFromContent()).containsPattern(expectedVersionPattern);
+  }
+
+  @Test
+  public void testServerJVMTerminatesOnOutOfMemoryError() throws IOException {
+    final Integer serverPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String memberName = "testServerJVMTerminatesOnOutOfMemoryError-server";
+    final String groupName = "serverGroup";
+    final String regionName = "testRegion";
+
+    MemberVM server = cluster.startServerVM(1, locator.getPort());
+
+    String command = new CommandStringBuilder(START_SERVER)
+        .addOption(START_SERVER__NAME, memberName)
+        .addOption("group", groupName)
+        .addOption(START_SERVER__LOCATORS, locatorConnectionString)
+        .addOption(START_SERVER__OFF_HEAP_MEMORY_SIZE, "0M")
+        .addOption(START_SERVER__MAXHEAP, "300M")
+        .addOption(START_SERVER__SERVER_PORT, serverPort.toString())
+        .addOption(START_SERVER__DIR, workingDir.getCanonicalPath())
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+
+    command = new CommandStringBuilder(CREATE_REGION)
+        .addOption(CREATE_REGION__REGIONSHORTCUT, "PARTITION")
+        .addOption(GROUP, groupName)
+        .addOption(CREATE_REGION__REGION, regionName)
+        .getCommandString();
+
+    gfsh.executeAndAssertThat(command).statusIsSuccess();
+
+    ServerLauncher serverLauncher = new ServerLauncher.Builder()
+        .setCommand(ServerLauncher.Command.STATUS)
+        .setWorkingDirectory(workingDir.getCanonicalPath())
+        .build();
+    assertThat(serverLauncher).isNotNull();
+
+    ServerLauncher.ServerState serverState = serverLauncher.status();
+    assertThat(serverState.getStatus()).isEqualTo(AbstractLauncher.Status.ONLINE);
+    assertThat(serverState.isVmWithProcessIdRunning()).isTrue();
+
+    Integer serverPid = serverState.getPid();
+    assertThat(ProcessUtils.isProcessAlive(serverPid)).isTrue();
+
+    final String jarName = "RunOutOfMemory.jar";
+    File jar = temporaryFolder.newFile(jarName);
+    new ClassBuilder().writeJarFromClass(new RunOutOfMemoryFunction().getClass(), jar);
+    gfsh.executeAndAssertThat("deploy --groups=" + groupName + " --jar=" + jar).statusIsSuccess();
+
+    locator.invoke((() -> {
+      try {
+        FunctionService.onMember(groupName).execute(new RunOutOfMemoryFunction()).getResult();
+      } catch (FunctionException e) {
+        e.printStackTrace();
+      }
+    }));
+
+    gfsh.executeAndAssertThat("list members").statusIsSuccess().doesNotContainOutput(memberName)
+        .containsOutput(server.getName());
+
+    server.stop(Boolean.TRUE);
+
+    assertThat(ProcessUtils.isProcessAlive(serverPid)).isFalse();
+  }
+
+  private void deleteServerFiles(File toDelete) {
+    File[] nestedToDelete = toDelete.listFiles();
+
+    if (nestedToDelete != null && nestedToDelete.length > 0) {
+      for (File file : nestedToDelete) {
+        deleteServerFiles(file);
+      }
+    }
+
+    toDelete.delete();
+  }
+
+  /**
+   * Attempts to determine the PID of the running process from the ManagementFactory's runtime MBean
+   *
+   * @return 1 if unable to determine the pid
+   * @return the PID if possible
+   */
+  private Integer getPidOrOne() {
+    Integer pid = 1;
+    String[] name = ManagementFactory.getRuntimeMXBean().getName().split("@");
+    if (name.length > 1) {
+      try {
+        pid = Integer.parseInt(name[0]);
+      } catch (NumberFormatException nex) {
+        // Ignored
+      }
+    }
+
+    return pid;
+  }
+
+}
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StatusLocatorCommandDunitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StatusLocatorCommandDunitTest.java
index 8249f28..30004f8 100644
--- a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StatusLocatorCommandDunitTest.java
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StatusLocatorCommandDunitTest.java
@@ -15,9 +15,6 @@
 package org.apache.geode.management.internal.cli.commands;
 
 import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.apache.geode.internal.i18n.LocalizedStrings.Launcher_Builder_UNKNOWN_HOST_ERROR_MESSAGE;
-import static org.apache.geode.internal.i18n.LocalizedStrings.Launcher_Builder_WORKING_DIRECTORY_NOT_FOUND_ERROR_MESSAGE;
-import static org.apache.geode.management.internal.cli.i18n.CliStrings.LOCATOR_TERM_NAME;
 import static org.apache.geode.management.internal.cli.i18n.CliStrings.STATUS_LOCATOR;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.SoftAssertions.assertSoftly;
@@ -35,7 +32,6 @@ import org.junit.rules.TemporaryFolder;
 import org.apache.geode.distributed.LocatorLauncher;
 import org.apache.geode.distributed.LocatorLauncher.LocatorState;
 import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.i18n.CliStrings;
 import org.apache.geode.management.internal.cli.result.CommandResult;
 import org.apache.geode.test.junit.rules.GfshCommandRule;
 import org.apache.geode.test.junit.rules.GfshCommandRule.PortType;
@@ -69,21 +65,6 @@ public class StatusLocatorCommandDunitTest {
   }
 
   @Test
-  public void testWithDisconnectedGfsh() throws Exception {
-    final String expectedResult =
-        CliStrings.format(CliStrings.STATUS_SERVICE__GFSH_NOT_CONNECTED_ERROR_MESSAGE,
-            LOCATOR_TERM_NAME);
-
-    if (gfsh.isConnected()) {
-      gfsh.disconnect();
-    }
-
-    gfsh.executeAndAssertThat(STATUS_LOCATOR + " --name=" + locatorName)
-        .statusIsError()
-        .containsOutput(expectedResult);
-  }
-
-  @Test
   public void testWithMemberAddress() throws Exception {
     gfsh.connectAndVerify(locator.getPort(), PortType.locator);
 
@@ -97,20 +78,6 @@ public class StatusLocatorCommandDunitTest {
   }
 
   @Test
-  public void testWithInvalidMemberAddress() throws Exception {
-    final String expectedError =
-        Launcher_Builder_UNKNOWN_HOST_ERROR_MESSAGE.toString(LOCATOR_TERM_NAME);
-
-    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
-
-    CommandResult result =
-        gfsh.executeCommand(STATUS_LOCATOR + " --host=invalidHostName --port=" + locator.getPort());
-
-    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
-    assertThat(result.getMessageFromContent()).contains(expectedError);
-  }
-
-  @Test
   public void testWithMemberName() throws Exception {
     gfsh.connectAndVerify(locator.getPort(), PortType.locator);
 
@@ -123,19 +90,6 @@ public class StatusLocatorCommandDunitTest {
   }
 
   @Test
-  public void testWithInvalidMemberName() throws Exception {
-    final String expectedError = CliStrings.format(
-        CliStrings.STATUS_LOCATOR__NO_LOCATOR_FOUND_FOR_MEMBER_ERROR_MESSAGE, "invalidLocatorName");
-
-    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
-
-    CommandResult result = gfsh.executeCommand(STATUS_LOCATOR + " --name=invalidLocatorName");
-
-    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
-    assertThat(result.getMessageFromContent()).contains(expectedError);
-  }
-
-  @Test
   public void testWithMemberID() throws Exception {
     gfsh.connectAndVerify(locator.getPort(), PortType.locator);
 
@@ -148,21 +102,6 @@ public class StatusLocatorCommandDunitTest {
   }
 
   @Test
-  public void testWithInvalidMemberID() throws Exception {
-    final String expectedError =
-        CliStrings.format(CliStrings.STATUS_LOCATOR__NO_LOCATOR_FOUND_FOR_MEMBER_ERROR_MESSAGE,
-            locator.getMemberId() + "1");
-
-    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
-
-    CommandResult result =
-        gfsh.executeCommand(STATUS_LOCATOR + " --name=" + locator.getMemberId() + "1");
-
-    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
-    assertThat(result.getMessageFromContent()).contains(expectedError);
-  }
-
-  @Test
   public void testWithDirOnline() throws Exception {
     gfsh.connectAndVerify(locator.getPort(), PortType.locator);
 
@@ -190,34 +129,6 @@ public class StatusLocatorCommandDunitTest {
     assertStatusCommandOutput(result.getMessageFromContent(), state);
   }
 
-  @Test
-  public void testWithInvalidDirOnline() throws Exception {
-    final String expectedError =
-        Launcher_Builder_WORKING_DIRECTORY_NOT_FOUND_ERROR_MESSAGE.toString(LOCATOR_TERM_NAME);
-
-    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
-
-    CommandResult result = gfsh.executeCommand(STATUS_LOCATOR + " --dir=/invalid/working/dir");
-
-    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
-    assertThat(result.getMessageFromContent()).contains(expectedError);
-  }
-
-  @Test
-  public void testWithInvalidDirOffline() throws Exception {
-    final String expectedError =
-        Launcher_Builder_WORKING_DIRECTORY_NOT_FOUND_ERROR_MESSAGE.toString(LOCATOR_TERM_NAME);
-
-    if (gfsh.isConnected()) {
-      gfsh.disconnect();
-    }
-
-    CommandResult result = gfsh.executeCommand(STATUS_LOCATOR + " --dir=/invalid/working/dir");
-
-    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
-    assertThat(result.getMessageFromContent()).contains(expectedError);
-  }
-
   public void assertStatusCommandOutput(String locatorStatusCommandMessage, LocatorState state) {
     assertSoftly(softly -> {
       softly.assertThat(locatorStatusCommandMessage)
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StopLocatorCommandDUnitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StopLocatorCommandDUnitTest.java
new file mode 100644
index 0000000..5903796
--- /dev/null
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StopLocatorCommandDUnitTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import static org.apache.geode.distributed.internal.DistributionConfig.GEMFIRE_PREFIX;
+import static org.apache.geode.management.cli.Result.Status.ERROR;
+import static org.apache.geode.management.cli.Result.Status.OK;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.GROUP;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__DIR;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__J;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__LOCATORS;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__MEMBER_NAME;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__PORT;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.STOP_LOCATOR;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.STOP_LOCATOR__DIR;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.STOP_LOCATOR__MEMBER;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.STOP_SERVICE__GFSH_NOT_CONNECTED_ERROR_MESSAGE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.Query;
+import javax.management.QueryExp;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.internal.lang.ObjectUtils;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import org.apache.geode.test.dunit.rules.ClusterStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.junit.rules.GfshCommandRule;
+
+public class StopLocatorCommandDUnitTest {
+  private static MemberVM locator;
+  private static final String memberName = "locatorToStop";
+  private static final String groupName = "locatorGroup";
+  private static String locatorConnectionString;
+  private File workingDir;
+  private static Integer locatorPort, jmxPort, toStopJmxPort;
+
+  @Rule
+  public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  @ClassRule
+  public static final GfshCommandRule gfsh = new GfshCommandRule();
+
+  @ClassRule
+  public static final ClusterStartupRule cluster = new ClusterStartupRule();
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    jmxPort = AvailablePortHelper.getRandomAvailableTCPPort();
+
+    Properties props = new Properties();
+    props.setProperty(ConfigurationProperties.JMX_MANAGER_PORT, jmxPort.toString());
+    props.setProperty(ConfigurationProperties.JMX_MANAGER_HOSTNAME_FOR_CLIENTS, "localhost");
+
+    locator = cluster.startLocatorVM(0, props);
+
+    locatorConnectionString = "localhost[" + locator.getPort() + "]";
+
+    gfsh.connectAndVerify(locator);
+  }
+
+  @AfterClass
+  public static void afterClass() throws Exception {
+    gfsh.connectAndVerify(locator);
+    gfsh.executeAndAssertThat("shutdown --include-locators").statusIsSuccess();
+  }
+
+  @Before
+  public void before() throws Exception {
+    workingDir = temporaryFolder.newFolder();
+    int[] availablePorts = AvailablePortHelper.getRandomAvailableTCPPorts(2);
+    locatorPort = availablePorts[0];
+    toStopJmxPort = availablePorts[1];
+
+    final String command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+        .addOption(START_LOCATOR__PORT, locatorPort.toString())
+        .addOption(GROUP, groupName)
+        .addOption(START_LOCATOR__J,
+            "-D" + GEMFIRE_PREFIX + "jmx-manager-port=" + toStopJmxPort.toString())
+        .addOption(START_LOCATOR__J,
+            "-D" + GEMFIRE_PREFIX + "jmx-manager-hostname-for-clients=localhost")
+        .addOption(START_LOCATOR__DIR, workingDir.getCanonicalPath())
+        .getCommandString();
+
+    gfsh.connectAndVerify(locator);
+    gfsh.executeCommand(command);
+  }
+
+  @After
+  public void after() throws IOException {
+    gfsh.executeCommand("stop locator --dir=" + workingDir.getCanonicalPath());
+  }
+
+  @Test
+  public void testWithDisconnectedGfsh() throws Exception {
+    final String expectedError = CliStrings.format(STOP_SERVICE__GFSH_NOT_CONNECTED_ERROR_MESSAGE,
+        CliStrings.LOCATOR_TERM_NAME);
+    final String command = new CommandStringBuilder(STOP_LOCATOR)
+        .addOption(STOP_LOCATOR__MEMBER, memberName)
+        .getCommandString();
+
+    if (gfsh.isConnected()) {
+      gfsh.disconnect();
+    }
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    gfsh.connectAndVerify(locator);
+
+    assertThat(result.getStatus()).isEqualTo(ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithMemberName() {
+    final String command = new CommandStringBuilder(STOP_LOCATOR)
+        .addOption(STOP_LOCATOR__MEMBER, memberName)
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(OK);
+    gfsh.executeAndAssertThat("list members").doesNotContainOutput(memberName);
+  }
+
+  @Test
+  public void testWithMemberID() {
+    int port = jmxPort; // this assignment is needed to pass a local var into the invocation below
+    final String memberID = locator.invoke(() -> getMemberId(port));
+
+    final String command = new CommandStringBuilder(STOP_LOCATOR)
+        .addOption(STOP_LOCATOR__MEMBER, memberID)
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(OK);
+    gfsh.executeAndAssertThat("list members").doesNotContainOutput(memberName);
+  }
+
+  @Test
+  public void testWithDirOnline() throws IOException {
+    final String command = new CommandStringBuilder(STOP_LOCATOR)
+        .addOption(STOP_LOCATOR__DIR, workingDir.getCanonicalPath())
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(OK);
+    gfsh.executeAndAssertThat("list members").doesNotContainOutput(memberName);
+  }
+
+  @Test
+  public void testWithDirOffline() throws Exception {
+    if (gfsh.isConnected()) {
+      gfsh.disconnect();
+    }
+
+    final String command = new CommandStringBuilder(STOP_LOCATOR)
+        .addOption(STOP_LOCATOR__DIR, workingDir.getCanonicalPath())
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    gfsh.connect(locator);
+
+    assertThat(result.getStatus()).isEqualTo(OK);
+    gfsh.executeAndAssertThat("list members").doesNotContainOutput(memberName);
+  }
+
+  @Test
+  public void testWithInvalidMemberName() {
+    final String command = new CommandStringBuilder(STOP_LOCATOR)
+        .addOption(STOP_LOCATOR__MEMBER, "invalidMemberName")
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(ERROR);
+    gfsh.executeAndAssertThat("list members").containsOutput(memberName);
+  }
+
+  @Test
+  public void testWithInvalidMemberID() {
+    final String command = new CommandStringBuilder(STOP_LOCATOR)
+        .addOption(STOP_LOCATOR__MEMBER, "42")
+        .getCommandString();
+
+    CommandResult result = gfsh.executeCommand(command);
+
+    assertThat(result.getStatus()).isEqualTo(ERROR);
+    gfsh.executeAndAssertThat("list members").containsOutput(memberName);
+  }
+
+  protected static String getMemberId(int port) throws Exception {
+    JMXConnector conn = null;
+
+    try {
+      final ObjectName objectNamePattern = ObjectName.getInstance("GemFire:type=Member,*");
+      final QueryExp query = Query.eq(Query.attr("Name"), Query.value(memberName));
+      final JMXServiceURL url =
+          new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/jmxrmi");
+      System.out.println(url.toString());
+
+      assertThat(url).isNotNull();
+      conn = JMXConnectorFactory.connect(url);
+      assertThat(conn).isInstanceOf(JMXConnector.class);
+
+      final MBeanServerConnection connection = conn.getMBeanServerConnection();
+      assertThat(connection).isInstanceOf(MBeanServerConnection.class);
+
+      final Set<ObjectName> objectNames = connection.queryNames(objectNamePattern, query);
+      assertThat(objectNames).isNotNull().isNotEmpty().hasSize(1);
+
+      final ObjectName objectName = objectNames.iterator().next();
+      final Object memberId = connection.getAttribute(objectName, "Id");
+
+      return ObjectUtils.toString(memberId);
+    } finally {
+      if (conn != null) {
+        conn.close();
+      }
+    }
+  }
+}
diff --git a/geode-junit/src/main/java/org/apache/geode/test/compiler/ClassBuilder.java b/geode-junit/src/main/java/org/apache/geode/test/compiler/ClassBuilder.java
index 58167ae..846abb7 100644
--- a/geode-junit/src/main/java/org/apache/geode/test/compiler/ClassBuilder.java
+++ b/geode-junit/src/main/java/org/apache/geode/test/compiler/ClassBuilder.java
@@ -18,6 +18,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.Serializable;
 import java.net.URI;
@@ -37,6 +38,8 @@ import javax.tools.JavaFileObject;
 import javax.tools.SimpleJavaFileObject;
 import javax.tools.ToolProvider;
 
+import org.apache.commons.io.IOUtils;
+
 /**
  * Test framework utility class to programmatically create classes, JARs and ClassLoaders that
  * include the classes.
@@ -137,6 +140,15 @@ public class ClassBuilder implements Serializable {
    */
   public void writeJarFromContent(final String className, final String content,
       final OutputStream outStream) throws IOException {
+
+    byte[] bytes = compileClass(className, content);
+
+    createJar(className, outStream, bytes);
+    return;
+  }
+
+  private void createJar(String className, OutputStream outStream, byte[] bytes)
+      throws IOException {
     JarOutputStream jarOutputStream = new JarOutputStream(outStream);
 
     // Add the class file to the JAR file
@@ -148,12 +160,23 @@ public class ClassBuilder implements Serializable {
     JarEntry entry = new JarEntry(formattedName);
     entry.setTime(System.currentTimeMillis());
     jarOutputStream.putNextEntry(entry);
-    jarOutputStream.write(compileClass(className, content));
+    jarOutputStream.write(bytes);
     jarOutputStream.closeEntry();
 
     jarOutputStream.close();
   }
 
+  public void writeJarFromClass(Class clazz, File jar) throws IOException {
+    String className = clazz.getName();
+    String classAsPath = className.replace('.', '/') + ".class";
+    InputStream stream = clazz.getClassLoader().getResourceAsStream(classAsPath);
+    byte[] bytes = IOUtils.toByteArray(stream);
+    try (FileOutputStream out = new FileOutputStream(jar)) {
+      createJar(classAsPath, out, bytes);
+    }
+
+  }
+
   /**
    * Creates a ClassLoader that contains an empty class with the given name using the given content.
    * The className may have a package separated by /. For example: my/package/myclass
diff --git a/geode-junit/src/main/java/org/apache/geode/test/compiler/TestObject.java b/geode-junit/src/main/java/org/apache/geode/test/compiler/TestObject.java
new file mode 100644
index 0000000..5b01479
--- /dev/null
+++ b/geode-junit/src/main/java/org/apache/geode/test/compiler/TestObject.java
@@ -0,0 +1,21 @@
+/*
+ * 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.test.compiler;
+
+public class TestObject {
+  public void forClassBuilderTest() {
+    // this class is just for testing purposes
+  }
+}
diff --git a/geode-junit/src/test/java/org/apache/geode/test/compiler/ClassBuilderTest.java b/geode-junit/src/test/java/org/apache/geode/test/compiler/ClassBuilderTest.java
new file mode 100644
index 0000000..dbc70b7
--- /dev/null
+++ b/geode-junit/src/test/java/org/apache/geode/test/compiler/ClassBuilderTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.test.compiler;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class ClassBuilderTest {
+  @Rule
+  public TemporaryFolder tmpFolder = new TemporaryFolder();
+
+  @Test
+  public void writeJarFromClass() throws IOException, ClassNotFoundException {
+    File jar = tmpFolder.newFile("test.jar");
+    URL[] url = new URL[] {jar.toURI().toURL()};
+    ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
+
+    URLClassLoader classLoader = new URLClassLoader(url, systemClassLoader) {
+      @Override
+      public Class<?> loadClass(String name) throws ClassNotFoundException {
+        try {
+          return findClass(name);
+        } catch (ClassNotFoundException e) {
+          if (name.equals(Object.class.getName())) {
+            return super.loadClass(name);
+          }
+        }
+        return null;
+      }
+    };
+
+    // write class to jar
+    new ClassBuilder().writeJarFromClass(TestObject.class, jar);
+
+    // load class from the jar
+    Class clazz = classLoader.loadClass(TestObject.class.getName());
+
+    assertNotEquals(clazz, null);
+    assertEquals(clazz.getClassLoader(), classLoader);
+  }
+}