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 2015/12/11 22:23:09 UTC

[35/50] [abbrv] incubator-geode git commit: GEODE-563: Moving gfsh tests from closed

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/eddef322/gemfire-assembly/src/test/java/com/gemstone/gemfire/management/internal/configuration/SharedConfigurationEndToEndDUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-assembly/src/test/java/com/gemstone/gemfire/management/internal/configuration/SharedConfigurationEndToEndDUnitTest.java b/gemfire-assembly/src/test/java/com/gemstone/gemfire/management/internal/configuration/SharedConfigurationEndToEndDUnitTest.java
new file mode 100644
index 0000000..383012e
--- /dev/null
+++ b/gemfire-assembly/src/test/java/com/gemstone/gemfire/management/internal/configuration/SharedConfigurationEndToEndDUnitTest.java
@@ -0,0 +1,434 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.management.internal.configuration;
+
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.cache.RegionShortcut;
+import com.gemstone.gemfire.cache.wan.GatewaySender.OrderPolicy;
+import com.gemstone.gemfire.distributed.Locator;
+import com.gemstone.gemfire.distributed.internal.DistributionConfig;
+import com.gemstone.gemfire.distributed.internal.InternalLocator;
+import com.gemstone.gemfire.internal.AvailablePortHelper;
+import com.gemstone.gemfire.internal.ClassBuilder;
+import com.gemstone.gemfire.internal.FileUtil;
+import com.gemstone.gemfire.internal.JarDeployer;
+import com.gemstone.gemfire.internal.admin.remote.ShutdownAllRequest;
+import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
+import com.gemstone.gemfire.internal.lang.StringUtils;
+import com.gemstone.gemfire.management.cli.Result.Status;
+import com.gemstone.gemfire.management.internal.cli.CliUtil;
+import com.gemstone.gemfire.management.internal.cli.HeadlessGfsh;
+import com.gemstone.gemfire.management.internal.cli.commands.CliCommandTestBase;
+import com.gemstone.gemfire.management.internal.cli.i18n.CliStrings;
+import com.gemstone.gemfire.management.internal.cli.result.CommandResult;
+import com.gemstone.gemfire.management.internal.cli.util.CommandStringBuilder;
+import dunit.DistributedTestCase;
+import dunit.Host;
+import dunit.SerializableCallable;
+import dunit.VM;
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+public class SharedConfigurationEndToEndDUnitTest extends CliCommandTestBase {
+  private static final int TIMEOUT = 10000;
+  private static final int INTERVAL = 500;
+  private static final String REGION1 = "R1";
+  private static final String REGION2 = "R2";
+  private static final String INDEX1 = "ID1";
+  private transient ClassBuilder classBuilder = new ClassBuilder();
+  public static Set<String> serverNames = new HashSet<String>();
+  public static Set<String> jarFileNames = new HashSet<String>();
+
+  public SharedConfigurationEndToEndDUnitTest(String name) {
+    super(name);
+    // TODO Auto-generated constructor stub
+  }
+
+  private static final long serialVersionUID = -2276690105585944041L;
+
+  public Set<String> startServers(HeadlessGfsh gfsh, String locatorString, int numServers, String serverNamePrefix, int startNum) throws ClassNotFoundException, IOException {
+    Set<String> serverNames = new HashSet<String>();
+
+    final int[] serverPorts = AvailablePortHelper.getRandomAvailableTCPPorts(numServers);
+    for (int i=0; i<numServers; i++) {
+      int port = serverPorts[i];
+      String serverName = serverNamePrefix+ Integer.toString(i+startNum) + "-" + port;
+      CommandStringBuilder csb = new CommandStringBuilder(CliStrings.START_SERVER);
+      csb.addOption(CliStrings.START_SERVER__NAME, serverName);
+      csb.addOption(CliStrings.START_SERVER__LOCATORS, locatorString);
+      csb.addOption(CliStrings.START_SERVER__SERVER_PORT, Integer.toString(port));
+      CommandResult cmdResult = executeCommand(gfsh, csb.getCommandString());
+      assertEquals(Status.OK, cmdResult.getStatus());
+    }
+    return serverNames;
+  }
+
+  public void testStartServerAndExecuteCommands() throws InterruptedException, ClassNotFoundException, IOException, ExecutionException {
+    addExpectedException("EntryDestroyedException");
+    Object[] result = setup();
+    final int locatorPort = (Integer) result[0];
+    final String jmxHost = (String) result[1];
+    final int jmxPort = (Integer) result[2];
+    final int httpPort = (Integer) result[3];
+    final String locatorString = "localHost[" + locatorPort + "]";
+
+    final HeadlessGfsh gfsh = new HeadlessGfsh("gfsh2", 300);
+    assertNotNull(gfsh);
+    shellConnect(jmxHost, jmxPort, httpPort, gfsh);
+
+    serverNames.addAll(startServers(gfsh, locatorString, 2, "Server", 1));
+    doCreateCommands();
+    serverNames.addAll(startServers(gfsh, locatorString, 1, "NewMember", 4));
+    verifyRegionCreateOnAllMembers(REGION1);
+    verifyRegionCreateOnAllMembers(REGION2);
+    verifyIndexCreationOnAllMembers(INDEX1);
+    verifyAsyncEventQueueCreation();
+   
+
+
+    //shutdown everything
+    getLogWriter().info("Shutting down all the members");
+    shutdownAll();
+    deleteSavedJarFiles();
+  }
+
+
+  private void doCreateCommands() {
+    createRegion(REGION1, RegionShortcut.REPLICATE, null);
+    createRegion(REGION2, RegionShortcut.PARTITION, null);
+    createIndex(INDEX1 , "AAPL", REGION1, null);
+    createAndDeployJar("Deploy1.jar");
+    createAsyncEventQueue("q1");
+    final String autoCompact = "true";
+    final String allowForceCompaction = "true";
+    final String compactionThreshold = "50";
+    final String duCritical = "90";
+    final String duWarning = "85";
+    final String maxOplogSize = "1000";
+    final String queueSize = "300";
+    final String timeInterval = "10";
+    final String writeBufferSize="100";
+    final String diskStoreName = "ds1";
+    final String diskDirs = "ds1";
+    
+    createDiskStore(diskStoreName, diskDirs, autoCompact, allowForceCompaction, compactionThreshold, duCritical, duWarning, maxOplogSize, queueSize, timeInterval, writeBufferSize);
+  }
+
+
+  protected void executeAndVerifyCommand(String commandString) {
+    CommandResult cmdResult = executeCommand(commandString);
+    getLogWriter().info("Command Result : \n" + commandResultToString(cmdResult));
+    assertEquals(Status.OK, cmdResult.getStatus());
+    assertFalse(cmdResult.failedToPersist());
+  }
+
+  private void createRegion(String regionName, RegionShortcut regionShortCut, String group) {
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.CREATE_REGION);
+    csb.addOption(CliStrings.CREATE_REGION__REGION, regionName);
+    csb.addOption(CliStrings.CREATE_REGION__REGIONSHORTCUT, regionShortCut.name());
+    executeAndVerifyCommand(csb.getCommandString());
+  }
+
+  private void destroyRegion(String regionName) {
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.DESTROY_REGION);
+    csb.addOption(CliStrings.DESTROY_REGION__REGION, regionName);
+    executeAndVerifyCommand(csb.getCommandString());
+  }
+
+  private void stopServer(String serverName) {
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.STOP_SERVER);
+    csb.addOption(CliStrings.STOP_SERVER__MEMBER, serverName);
+    executeAndVerifyCommand(csb.getCommandString());
+  }
+
+  public void createAsyncEventQueue(String queueName) {
+    String queueCommandsJarName = "testEndToEndSC-QueueCommands.jar";
+    final File jarFile = new File(queueCommandsJarName);
+
+    try {
+      ClassBuilder classBuilder = new ClassBuilder();
+      byte[] jarBytes = classBuilder.createJarFromClassContent("com/qcdunit/QueueCommandsDUnitTestListener",
+          "package com.qcdunit;" +
+              "import java.util.List; import java.util.Properties;" +
+              "import com.gemstone.gemfire.internal.cache.xmlcache.Declarable2; import com.gemstone.gemfire.cache.asyncqueue.AsyncEvent;" +
+              "import com.gemstone.gemfire.cache.asyncqueue.AsyncEventListener;" +
+              "public class QueueCommandsDUnitTestListener implements Declarable2, AsyncEventListener {" +
+              "Properties props;" +
+              "public boolean processEvents(List<AsyncEvent> events) { return true; }" +
+              "public void close() {}" +
+              "public void init(final Properties props) {this.props = props;}" +
+          "public Properties getConfig() {return this.props;}}");
+
+      FileUtils.writeByteArrayToFile(jarFile, jarBytes);
+      CommandStringBuilder csb = new CommandStringBuilder(CliStrings.DEPLOY);
+      csb.addOption(CliStrings.DEPLOY__JAR, queueCommandsJarName);
+      executeAndVerifyCommand(csb.getCommandString());
+
+      csb = new CommandStringBuilder(CliStrings.CREATE_ASYNC_EVENT_QUEUE);
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__ID, queueName);
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__LISTENER, "com.qcdunit.QueueCommandsDUnitTestListener");
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__BATCH_SIZE, "100");
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__BATCHTIMEINTERVAL, "200");
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__DISPATCHERTHREADS, "4");
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__ENABLEBATCHCONFLATION, "true");
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__DISKSYNCHRONOUS, "true");
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__MAXIMUM_QUEUE_MEMORY, "1000");
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__ORDERPOLICY, OrderPolicy.KEY.toString());
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__PERSISTENT, "true");
+      csb.addOption(CliStrings.CREATE_ASYNC_EVENT_QUEUE__PARALLEL, "true");
+      
+      executeAndVerifyCommand(csb.getCommandString());
+
+    } catch (IOException e) {
+      e.printStackTrace();
+    } finally {
+      FileUtils.deleteQuietly(jarFile);
+    }
+  }
+  private void createDiskStore(String diskStoreName, 
+      String diskDirs, 
+      String autoCompact, 
+      String allowForceCompaction, 
+      String compactionThreshold, 
+      String duCritical, 
+      String duWarning,
+      String maxOplogSize,
+      String queueSize,
+      String timeInterval,
+      String writeBufferSize) {
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.CREATE_DISK_STORE);
+    csb.addOption(CliStrings.CREATE_DISK_STORE__NAME, diskStoreName);
+    csb.addOption(CliStrings.CREATE_DISK_STORE__DIRECTORY_AND_SIZE, diskDirs);
+    csb.addOptionWithValueCheck(CliStrings.CREATE_DISK_STORE__AUTO_COMPACT, autoCompact);
+    csb.addOptionWithValueCheck(CliStrings.CREATE_DISK_STORE__ALLOW_FORCE_COMPACTION, allowForceCompaction);
+    csb.addOptionWithValueCheck(CliStrings.CREATE_DISK_STORE__COMPACTION_THRESHOLD, compactionThreshold);
+    csb.addOptionWithValueCheck(CliStrings.CREATE_DISK_STORE__DISK_USAGE_CRITICAL_PCT, duCritical);
+    csb.addOptionWithValueCheck(CliStrings.CREATE_DISK_STORE__DISK_USAGE_WARNING_PCT, duWarning);
+    csb.addOptionWithValueCheck(CliStrings.CREATE_DISK_STORE__MAX_OPLOG_SIZE, maxOplogSize);
+    csb.addOptionWithValueCheck(CliStrings.CREATE_DISK_STORE__QUEUE_SIZE, queueSize);
+    csb.addOptionWithValueCheck(CliStrings.CREATE_DISK_STORE__TIME_INTERVAL, timeInterval);
+    csb.addOptionWithValueCheck(CliStrings.CREATE_DISK_STORE__WRITE_BUFFER_SIZE, writeBufferSize);
+    executeAndVerifyCommand(csb.getCommandString());
+  }
+  
+  private void destroyDiskStore(String diskStoreName, String group) {
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.DESTROY_DISK_STORE);
+    csb.addOption(CliStrings.DESTROY_DISK_STORE__NAME, diskStoreName);
+    csb.addOptionWithValueCheck(CliStrings.DESTROY_DISK_STORE__GROUP, group);
+    executeAndVerifyCommand(csb.toString());
+  }
+  public void createIndex(String indexName, String expression, String regionName, String group) {
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.CREATE_INDEX);
+    csb.addOption(CliStrings.CREATE_INDEX__NAME, indexName);
+    csb.addOption(CliStrings.CREATE_INDEX__EXPRESSION, expression);
+    csb.addOption(CliStrings.CREATE_INDEX__REGION, regionName);
+    executeAndVerifyCommand(csb.getCommandString());
+  }
+
+  public void destoyIndex(String indexName, String regionName, String group) {
+    if (StringUtils.isBlank(indexName) && StringUtils.isBlank(regionName) && StringUtils.isBlank(group)) {
+      return;
+    }
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.DESTROY_INDEX);
+    if (!StringUtils.isBlank(indexName)) {
+      csb.addOption(CliStrings.DESTROY_INDEX__NAME, indexName);
+    }
+
+    if (!StringUtils.isBlank(regionName)) {
+      csb.addOption(CliStrings.DESTROY_INDEX__REGION, regionName);
+    }
+
+    if (!StringUtils.isBlank(group)) {
+      csb.addOption(CliStrings.DESTROY_INDEX__GROUP, group);
+    }
+    executeAndVerifyCommand(csb.getCommandString());
+  }
+
+  public void createAndDeployJar(String jarName) {
+    File newDeployableJarFile = new File(jarName);
+    try {
+      this.classBuilder.writeJarFromName("ShareConfigClass", newDeployableJarFile);
+      CommandStringBuilder csb = new CommandStringBuilder(CliStrings.DEPLOY);
+      csb.addOption(CliStrings.DEPLOY__JAR, jarName);
+      executeAndVerifyCommand(csb.getCommandString());
+      jarFileNames.add(jarName);
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  public void deleteSavedJarFiles() {
+    try {
+      FileUtil.deleteMatching(new File("."), "^" + JarDeployer.JAR_PREFIX + "Deploy1.*#\\d++$");
+      FileUtil.delete(new File("Deploy1.jar"));
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+  }
+
+  public Object[] setup() {
+    disconnectAllFromDS();
+    final int [] ports = AvailablePortHelper.getRandomAvailableTCPPorts(3);
+    final int locator1Port = ports[0];
+    final String locator1Name = "locator1-" + locator1Port;
+    VM locatorAndMgr = Host.getHost(0).getVM(3);
+
+    Object[] result = (Object[]) locatorAndMgr.invoke(new SerializableCallable() {
+      @Override
+      public Object call() {
+        int httpPort;
+        int jmxPort;
+        String jmxHost;
+
+        try {
+          jmxHost = InetAddress.getLocalHost().getHostName();
+        }
+        catch (UnknownHostException ignore) {
+          jmxHost = "localhost";
+        }
+
+        final int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2);
+
+        jmxPort = ports[0];
+        httpPort = ports[1];
+
+        final File locatorLogFile = new File("locator-" + locator1Port + ".log");
+
+        final Properties locatorProps = new Properties();
+        locatorProps.setProperty(DistributionConfig.NAME_NAME, locator1Name);
+        locatorProps.setProperty(DistributionConfig.MCAST_PORT_NAME, "0");
+        locatorProps.setProperty(DistributionConfig.LOG_LEVEL_NAME, "config");
+        locatorProps.setProperty(DistributionConfig.ENABLE_CLUSTER_CONFIGURATION_NAME, "true");
+        locatorProps.setProperty(DistributionConfig.JMX_MANAGER_NAME, "true");
+        locatorProps.setProperty(DistributionConfig.JMX_MANAGER_START_NAME, "true");
+        locatorProps.setProperty(DistributionConfig.JMX_MANAGER_BIND_ADDRESS_NAME, String.valueOf(jmxHost));
+        locatorProps.setProperty(DistributionConfig.JMX_MANAGER_PORT_NAME, String.valueOf(jmxPort));
+        locatorProps.setProperty(DistributionConfig.HTTP_SERVICE_PORT_NAME, String.valueOf(httpPort));
+
+        try {
+          final InternalLocator locator = (InternalLocator) Locator.startLocatorAndDS(locator1Port, locatorLogFile, null,
+              locatorProps);
+          DistributedTestCase.WaitCriterion wc = new DistributedTestCase.WaitCriterion() {
+            @Override
+            public boolean done() {
+              return locator.isSharedConfigurationRunning();
+            }
+
+            @Override
+            public String description() {
+              return "Waiting for shared configuration to be started";
+            }
+          };
+          DistributedTestCase.waitForCriterion(wc, TIMEOUT, INTERVAL, true);
+        } catch (IOException ioex) {
+          fail("Unable to create a locator with a shared configuration");
+        }
+
+        final Object[] result = new Object[4];
+        result[0] = locator1Port;
+        result[1] = jmxHost;
+        result[2] = jmxPort;
+        result[3] = httpPort;
+        return result;
+      }
+    });
+
+    HeadlessGfsh gfsh = getDefaultShell();
+    String jmxHost = (String)result[1];
+    int jmxPort = (Integer)result[2];
+    int httpPort = (Integer)result[3];
+
+    shellConnect(jmxHost, jmxPort, httpPort, gfsh);
+    // Create a cache in VM 1
+    VM dataMember = Host.getHost(0).getVM(1);
+    dataMember.invoke(new SerializableCallable() {
+      @Override
+      public Object call() {
+        Properties localProps = new Properties();
+        localProps.setProperty(DistributionConfig.MCAST_PORT_NAME, "0");
+        localProps.setProperty(DistributionConfig.LOCATORS_NAME, "localhost:" + locator1Port);
+        localProps.setProperty(DistributionConfig.NAME_NAME, "DataMember");
+        getSystem(localProps);
+        Cache cache = getCache();
+        assertNotNull(cache);
+        return CliUtil.getAllNormalMembers(cache);
+      }
+    });
+    return result;
+  }
+
+  private void shutdownAll() throws IOException {
+    VM locatorAndMgr = Host.getHost(0).getVM(3);
+    locatorAndMgr.invoke(new SerializableCallable() {
+      /**
+       * 
+       */
+      private static final long serialVersionUID = 1L;
+
+      @Override
+      public Object call() throws Exception {
+        GemFireCacheImpl cache = (GemFireCacheImpl)CacheFactory.getAnyInstance();
+        ShutdownAllRequest.send(cache.getDistributedSystem().getDistributionManager(), -1);
+        return null;
+      }
+    });
+
+    locatorAndMgr.invoke(SharedConfigurationDUnitTest.locatorCleanup);
+    //Clean up the directories
+    if (!serverNames.isEmpty()) {
+      for (String serverName : serverNames) {
+        final File serverDir = new File(serverName);
+        FileUtils.cleanDirectory(serverDir);
+        FileUtils.deleteDirectory(serverDir);
+      }
+    }
+    serverNames.clear();
+    serverNames = null;
+  }
+
+  private void verifyRegionCreateOnAllMembers(String regionName) {
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.DESCRIBE_REGION);
+    csb.addOption(CliStrings.DESCRIBE_REGION__NAME, regionName);
+    CommandResult cmdResult = executeCommand(csb.getCommandString());
+    String resultAsString = commandResultToString(cmdResult);
+
+    for (String serverName : serverNames) {
+      assertTrue(resultAsString.contains(serverName));
+    }
+  }     
+
+  private void verifyIndexCreationOnAllMembers(String indexName) {
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.LIST_INDEX);
+    CommandResult cmdResult = executeCommand(csb.getCommandString());
+    String resultAsString = commandResultToString(cmdResult);
+
+    for (String serverName : serverNames) {
+      assertTrue(resultAsString.contains(serverName));
+    }
+  }
+  
+  private void verifyAsyncEventQueueCreation() {
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.LIST_ASYNC_EVENT_QUEUES);
+    CommandResult cmdResult = executeCommand(csb.toString());
+    String resultAsString = commandResultToString(cmdResult);
+    
+    for (String serverName : serverNames) {
+      assertTrue(resultAsString.contains(serverName));
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/eddef322/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/HeadlessGfsh.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/HeadlessGfsh.java b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/HeadlessGfsh.java
new file mode 100644
index 0000000..9ca9809
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/HeadlessGfsh.java
@@ -0,0 +1,376 @@
+/*
+ * 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 com.gemstone.gemfire.management.internal.cli;
+
+import com.gemstone.gemfire.management.internal.cli.shell.Gfsh;
+import com.gemstone.gemfire.management.internal.cli.shell.GfshConfig;
+import com.gemstone.gemfire.management.internal.cli.shell.jline.GfshUnsupportedTerminal;
+import edu.umd.cs.findbugs.annotations.SuppressWarnings;
+import jline.ConsoleReader;
+import org.springframework.shell.core.ExitShellRequest;
+import org.springframework.shell.event.ShellStatus.Status;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Level;
+
+
+/**
+ * This is headless shell which can be used to submit random commands and get command-result It is used for commands
+ * testing but can be used as for anything like programmatically sending commands to operate on GemFire Distributed
+ * systems. TODO : Merge HeadlessGfsh and HeadlessGfshShell TODO : Provide constructor for optionally specifying
+ * GfshConfig to provide logDirectory and logLevel
+ *
+ * @author tushark
+ */
+@SuppressWarnings("rawtypes")
+public class HeadlessGfsh implements ResultHandler {
+
+  public static final String ERROR_RESULT = "_$_ERROR_RESULT";
+
+  private HeadlessGfshShell shell = null;
+  private LinkedBlockingQueue queue = new LinkedBlockingQueue<>();
+  private long timeout = 20;
+  public String outputString = null;
+
+  public HeadlessGfsh(String name, int timeout) throws ClassNotFoundException, IOException {
+    this(name, timeout, null);
+  }
+
+  public HeadlessGfsh(String name, int timeout, Properties envProps) throws ClassNotFoundException, IOException {
+    this.timeout = timeout;
+    System.setProperty("jline.terminal", GfshUnsupportedTerminal.class.getName());
+    this.shell = new HeadlessGfshShell(name, this);
+    this.shell.setEnvProperty(Gfsh.ENV_APP_RESULT_VIEWER, "non-basic");
+
+    if (envProps != null) {
+      for (String key : envProps.stringPropertyNames()) {
+        this.shell.setEnvProperty(key, envProps.getProperty(key));
+      }
+    }
+
+    // This allows us to avoid race conditions during startup - in particular a NPE on the ConsoleReader which is
+    // created in a separate thread during start()
+    CountDownLatch shellStarted = new CountDownLatch(1);
+    this.shell.addShellStatusListener((oldStatus, newStatus) -> {
+      if (newStatus.getStatus() == Status.STARTED) {
+        shellStarted.countDown();
+      }
+    });
+
+    this.shell.start();
+    this.setThreadLocalInstance();
+
+    try {
+      shellStarted.await();
+    } catch (InterruptedException e) {
+      e.printStackTrace(System.out);
+    }
+  }
+
+  public void setThreadLocalInstance() {
+    shell.setThreadLocalInstance();
+  }
+
+  //TODO : Have non-blocking method also where we move executeCommand call to separate thread-pool
+  public boolean executeCommand(String command) {
+    boolean status = false;
+    try {
+      outputString = null;
+      status = shell.executeCommand(command);
+    } catch (Exception e) {
+      outputString = e.getMessage();
+    }
+    return status;
+  }
+
+  int getCommandExecutionStatus() {
+    return shell.getCommandExecutionStatus();
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public void handleExecutionResult(Object result, String sysout) {
+    queue.add(result);
+    outputString = sysout;
+  }
+
+  public Object getResult() throws InterruptedException {
+    //Dont wait for when some command calls gfsh.stop();
+    if (shell.stopCalledThroughAPI) return null;
+    try {
+      Object result = queue.poll(timeout, TimeUnit.SECONDS);
+      queue.clear();
+      return result;
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  public void clear() {
+    queue.clear();
+    outputString = null;
+  }
+
+  public void clearEvents() {
+    queue.clear();
+    outputString = null;
+  }
+
+  public void terminate() {
+    shell.terminate();
+  }
+
+  public boolean isConnectedAndReady() {
+    return shell.isConnectedAndReady();
+  }
+
+  public String getErrorString() {
+    return shell.errorString;
+  }
+
+  public boolean hasError() {
+    return shell.hasError();
+  }
+
+  public String getError() {
+    return shell.errorString;
+  }
+
+  public static class HeadlessGfshShell extends Gfsh {
+
+    private ResultHandler handler = null;
+    private final Lock lock = new ReentrantLock();
+    private final Condition endOfShell = lock.newCondition();
+    private ByteArrayOutputStream output = null;
+    private String errorString = null;
+    private boolean hasError = false;
+    boolean stopCalledThroughAPI = false;
+
+    protected HeadlessGfshShell(String testName, ResultHandler handler) throws ClassNotFoundException, IOException {
+      super(false, new String[]{}, new HeadlessGfshConfig(testName));
+      this.handler = handler;
+    }
+
+    public void setThreadLocalInstance() {
+      gfshThreadLocal.set(this);
+    }
+
+    protected void handleExecutionResult(Object result) {
+      if (!result.equals(ERROR_RESULT)) {
+        super.handleExecutionResult(result);
+        handler.handleExecutionResult(result, output.toString());
+        output.reset();
+      } else {
+        //signal waiting queue with error condition with empty output
+        output.reset();
+        handler.handleExecutionResult(result, output.toString());
+      }
+    }
+
+    int getCommandExecutionStatus() {
+      return getLastExecutionStatus();
+    }
+
+    public void terminate() {
+      closeShell();
+      stopPromptLoop();
+      stop();
+    }
+
+    public void stop() {
+      stopCalledThroughAPI = true;
+    }
+
+    private void stopPromptLoop() {
+      lock.lock();
+      try {
+        endOfShell.signalAll();
+      } finally {
+        lock.unlock();
+      }
+    }
+
+    public String getErrorString() {
+      return errorString;
+    }
+
+    public boolean hasError() {
+      return hasError;
+    }
+
+    /**
+     * We override this method just to fool runner thread in reading from nothing. It waits for Condition endOfShell
+     * which is signalled when terminate is called. This achieves clean shutdown of runner thread.
+     */
+    @Override
+    public void promptLoop() {
+      lock.lock();
+      try {
+        while (true) {
+          try {
+            endOfShell.await();
+          } catch (InterruptedException e) {
+            //e.printStackTrace();
+          }
+          this.exitShellRequest = ExitShellRequest.NORMAL_EXIT;
+          setShellStatus(Status.SHUTTING_DOWN);
+          break;
+        }
+      } finally {
+        lock.unlock();
+      }
+    }
+
+    private static void setGfshOutErr(PrintStream outToUse) {
+      Gfsh.gfshout = outToUse;
+      Gfsh.gfsherr = outToUse;
+    }
+
+    /**
+     * This prints out error messages when Exceptions occur in shell. Capture it and set error flag=true and send
+     * ERROR_RESULT on the queue to signal thread waiting for CommandResult
+     */
+    @Override
+    public void logWarning(String message, Throwable t) {
+      super.logWarning(message, t);
+      errorString = message;
+      hasError = true;
+      //signal waiting queue with error condition
+      handleExecutionResult(ERROR_RESULT);
+    }
+
+    /**
+     * This prints out error messages when Exceptions occur in shell. Capture it and set error flag=true and send
+     * ERROR_RESULT on the queue to signal thread waiting for CommandResult
+     */
+    @Override
+    public void logSevere(String message, Throwable t) {
+      super.logSevere(message, t);
+      errorString = message;
+      hasError = true;
+      //signal waiting queue with error condition
+      handleExecutionResult(ERROR_RESULT);
+    }
+
+    /**
+     * Setup console-reader to capture Shell output
+     */
+    @Override
+    protected ConsoleReader createConsoleReader() {
+      try {
+        output = new ByteArrayOutputStream(1024 * 10);
+        PrintStream sysout = new PrintStream(output);
+        Writer wrappedOut = new BufferedWriter(new OutputStreamWriter(sysout));
+        setGfshOutErr(sysout);
+        return new ConsoleReader(new FileInputStream(FileDescriptor.in), new PrintWriter(wrappedOut));
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+
+  /**
+   * HeadlessGfshConfig for tests. Taken from TestableGfsh
+   */
+  static class HeadlessGfshConfig extends GfshConfig {
+    {
+      // set vm as a gfsh vm
+      CliUtil.isGfshVM = true;
+    }
+
+    private File parentDir;
+    private String fileNamePrefix;
+    private String name;
+    private String generatedHistoryFileName = null;
+
+    public HeadlessGfshConfig(String name) {
+      this.name = name;
+
+      if (isDUnitTest(this.name)) {
+        fileNamePrefix = this.name;
+      } else {
+        fileNamePrefix = "non-hydra-client";
+      }
+
+      parentDir = new File("gfsh_files");
+      parentDir.mkdirs();
+    }
+
+    private static boolean isDUnitTest(String name) {
+      boolean isDUnitTest = false;
+      if (name != null) {
+        String[] split = name.split("_");
+        if (split.length != 0 && split[0].endsWith("DUnitTest")) {
+          isDUnitTest = true;
+        }
+      }
+      return isDUnitTest;
+    }
+
+    @Override
+    public String getLogFilePath() {
+      return new File(parentDir, getFileNamePrefix() + "-gfsh.log").getAbsolutePath();
+    }
+
+    private String getFileNamePrefix() {
+      String timeStamp = new java.sql.Time(System.currentTimeMillis()).toString();
+      timeStamp = timeStamp.replace(':', '_');
+      return fileNamePrefix + "-" + timeStamp;
+    }
+
+    @Override
+    public String getHistoryFileName() {
+      if (generatedHistoryFileName == null) {
+        String fileName = new File(parentDir, (getFileNamePrefix() + "-gfsh.history")).getAbsolutePath();
+        generatedHistoryFileName = fileName;
+        return fileName;
+      } else {
+        return generatedHistoryFileName;
+      }
+    }
+
+    @Override
+    public boolean isTestConfig() {
+      return true;
+    }
+
+    @Override
+    public Level getLogLevel() {
+      // Keep log level fine for tests
+      return Level.FINE;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/eddef322/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/HeadlessGfshJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/HeadlessGfshJUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/HeadlessGfshJUnitTest.java
new file mode 100644
index 0000000..0807898
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/HeadlessGfshJUnitTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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 com.gemstone.gemfire.management.internal.cli;
+
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.distributed.DistributedSystem;
+import com.gemstone.gemfire.distributed.internal.DistributionConfig;
+import com.gemstone.gemfire.internal.AvailablePort;
+import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
+import com.gemstone.gemfire.management.internal.MBeanJMXAdapter;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import javax.management.ObjectName;
+import java.io.IOException;
+import java.util.Properties;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * TODO : Add more tests for error-catch, different type of results etc
+ *
+ * @author tushark
+ */
+@Category(UnitTest.class)
+public class HeadlessGfshJUnitTest {
+
+  @SuppressWarnings({"unused", "deprecation", "unused"})
+  @Test
+  public void testHeadlessGfshTest() throws ClassNotFoundException, IOException, InterruptedException {
+    GemFireCacheImpl cache = null;
+    DistributedSystem ds = null;
+    Properties pr = new Properties();
+    pr.put("name", "testHeadlessGfshTest");
+    pr.put(DistributionConfig.JMX_MANAGER_NAME, "true");
+    pr.put(DistributionConfig.JMX_MANAGER_START_NAME, "true");
+    int port = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
+    pr.put(DistributionConfig.JMX_MANAGER_PORT_NAME, String.valueOf(port));
+    pr.put(DistributionConfig.HTTP_SERVICE_PORT_NAME, "0");
+    pr.put(DistributionConfig.MCAST_PORT_NAME, "0");
+
+    ds = DistributedSystem.connect(pr);
+    cache = (GemFireCacheImpl) CacheFactory.create(ds);
+    ObjectName name = MBeanJMXAdapter.getDistributedSystemName();
+
+    HeadlessGfsh gfsh = new HeadlessGfsh("Test", 25);
+    for (int i = 0; i < 5; i++) {
+      gfsh.executeCommand("connect --jmx-manager=localhost[" + port + "]");
+      Object result = gfsh.getResult();
+      assertTrue(gfsh.isConnectedAndReady());
+      assertNotNull(result);
+      gfsh.clear();
+      gfsh.executeCommand("list members");
+      result = gfsh.getResult();
+      assertNotNull(result);
+      gfsh.executeCommand("disconnect");
+      gfsh.getResult();
+    }
+
+    long l1 = System.currentTimeMillis();
+    gfsh.executeCommand("exit");
+    long l2 = System.currentTimeMillis();
+    gfsh.getResult();
+    long l3 = System.currentTimeMillis();
+    System.out.println("L3-l2=" + (l3 - l2) + " Total time= " + (l3 - l1) / 1000);
+    gfsh.terminate();
+    cache.close();
+    ds.disconnect();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/eddef322/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/ResultHandler.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/ResultHandler.java b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/ResultHandler.java
new file mode 100644
index 0000000..2b90b60
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/ResultHandler.java
@@ -0,0 +1,23 @@
+/*
+ * 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 com.gemstone.gemfire.management.internal.cli;
+
+public interface ResultHandler {
+  
+  void handleExecutionResult(Object result, String sysout);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/eddef322/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/TableBuilderJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/TableBuilderJUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/TableBuilderJUnitTest.java
new file mode 100644
index 0000000..e5f1d86
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/TableBuilderJUnitTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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 com.gemstone.gemfire.management.internal.cli;
+
+import com.gemstone.gemfire.management.internal.cli.result.TableBuilder;
+import com.gemstone.gemfire.management.internal.cli.result.TableBuilder.Row;
+import com.gemstone.gemfire.management.internal.cli.result.TableBuilder.RowGroup;
+import com.gemstone.gemfire.management.internal.cli.result.TableBuilder.Table;
+import com.gemstone.gemfire.management.internal.cli.result.TableBuilderHelper;
+import com.gemstone.gemfire.management.internal.cli.shell.Gfsh;
+import com.gemstone.gemfire.test.junit.categories.IntegrationTest;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Random;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * TODO: fails when running integrationTest from gradle command-line or in Eclipse on Windows 7
+ * <p>
+ * com.gemstone.gemfire.management.internal.cli.TableBuilderJUnitTest > testBasicScrapping FAILED
+ * java.lang.AssertionError: Expected length < 100 is 101 at org.junit.Assert.fail(Assert.java:88) at
+ * com.gemstone.gemfire.management.internal.cli.TableBuilderJUnitTest.doTableBuilderTestUnit(TableBuilderJUnitTest.java:115)
+ * at com.gemstone.gemfire.management.internal.cli.TableBuilderJUnitTest.testBasicScrapping(TableBuilderJUnitTest.java:134)
+ * <p>
+ * com.gemstone.gemfire.management.internal.cli.TableBuilderJUnitTest > testManyColumns FAILED java.lang.AssertionError:
+ * Expected length < 100 is 101 at org.junit.Assert.fail(Assert.java:88) at com.gemstone.gemfire.management.internal.cli.TableBuilderJUnitTest.doTableBuilderTestUnit(TableBuilderJUnitTest.java:115)
+ * at com.gemstone.gemfire.management.internal.cli.TableBuilderJUnitTest.testManyColumns(TableBuilderJUnitTest.java:155)
+ *
+ * @author tushark
+ */
+@Category(IntegrationTest.class)
+public class TableBuilderJUnitTest {
+
+  @Rule
+  public TestName testName = new TestName();
+
+  private final Table createTable(int rows, int cols, int width, String separator) {
+    Table resultTable = TableBuilder.newTable();
+    resultTable.setTabularResult(true);
+    resultTable.setColumnSeparator(separator);
+
+    resultTable.newBlankRow();
+    resultTable.newRow().newLeftCol("Displaying all fields for member: ");
+    resultTable.newBlankRow();
+    RowGroup rowGroup = resultTable.newRowGroup();
+    Row row = rowGroup.newRow();
+    for (int colIndex = 0; colIndex < cols; colIndex++) {
+      row.newCenterCol("Field" + colIndex);
+    }
+
+    rowGroup.newRowSeparator('-', false);
+
+    int counter = rows;
+    for (int i = 0; i < counter; i++) {
+      row = rowGroup.newRow();
+      for (int k = 0; k < cols; k++) {
+        row.newLeftCol(getString(i, width / cols));
+      }
+    }
+    resultTable.newBlankRow();
+
+    return resultTable;
+  }
+
+  private Object getString(int i, int width) {
+    StringBuilder sb = new StringBuilder();
+    Random random = new Random();
+    int k = 0;
+    double d = random.nextDouble();
+    // .09 probability
+    if (d <= 0.9) {
+      k = random.nextInt(width);
+    } else {
+      k = width / 2 + random.nextInt(width);
+    }
+    random.nextInt(10);
+    for (int j = 0; j < k; j++) {
+      sb.append(i);
+      if (sb.length() > k) break;
+    }
+    return sb.toString();
+  }
+
+  private HeadlessGfsh createShell(Properties props) throws ClassNotFoundException, IOException {
+    String shellId = getClass().getSimpleName() + "_" + testName;
+    HeadlessGfsh shell = new HeadlessGfsh(shellId, 30, props);
+    return shell;
+  }
+
+  private void doTableBuilderTestUnit(int rows, int cols, String sep, boolean shouldTrim,
+      boolean expectTooManyColEx) throws ClassNotFoundException, IOException {
+    int width = Gfsh.getCurrentInstance().getTerminalWidth();
+    Table table = createTable(rows, cols, width, sep);
+    String st = table.buildTable();
+    System.out.println(st);
+
+    String[] array = st.split("\n");
+
+    int line = 0;
+    for (String s : array) {
+      System.out.println("For line " + line++ + " length is " + s.length() + " isWider = " + (s.length() > width));
+
+      if (shouldTrim) {
+        if (s.length() > width) {
+          fail("Expected length < " + width + " is " + s.length());
+        }
+      } else {
+        if (s.length() > 50 && s.length() <= width) {
+          fail("Expected length <= " + width + " is " + s.length());
+        }
+      }
+
+    }
+  }
+
+  /**
+   * Test Variations tablewide separator true false
+   */
+  @Test
+  public void testBasicScraping() throws ClassNotFoundException, IOException {
+    Properties props = new Properties();
+    props.setProperty(Gfsh.ENV_APP_RESULT_VIEWER, Gfsh.DEFAULT_APP_RESULT_VIEWER);
+    createShell(props);
+    assertTrue(TableBuilderHelper.shouldTrimColumns());
+    doTableBuilderTestUnit(15, 4, "|", true, false);
+  }
+
+
+  @Test
+  public void testSeparatorWithMultipleChars() throws ClassNotFoundException, IOException {
+    Properties props = new Properties();
+    props.setProperty(Gfsh.ENV_APP_RESULT_VIEWER, Gfsh.DEFAULT_APP_RESULT_VIEWER);
+    createShell(props);
+    assertTrue(TableBuilderHelper.shouldTrimColumns());
+    doTableBuilderTestUnit(15, 4, " | ", true, false);
+  }
+
+  /**
+   * multiple columns upto 8 : done
+   */
+  @Test
+  @Ignore("Bug 52051")
+  public void testManyColumns() throws ClassNotFoundException, IOException {
+    createShell(null);
+    assertTrue(TableBuilderHelper.shouldTrimColumns());
+    doTableBuilderTestUnit(15, 6, "|", true, true);
+  }
+
+  /**
+   * set gfsh env property result_viewer to basic disable for external reader
+   */
+  //
+  @Test
+  public void testDisableColumnAdjustment() throws ClassNotFoundException, IOException {
+    createShell(null);
+    assertFalse(TableBuilderHelper.shouldTrimColumns());
+    doTableBuilderTestUnit(15, 12, "|", false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/eddef322/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/CliCommandTestBase.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/CliCommandTestBase.java b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/CliCommandTestBase.java
new file mode 100644
index 0000000..a0fb8f8
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/CliCommandTestBase.java
@@ -0,0 +1,560 @@
+/*
+ * 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 com.gemstone.gemfire.management.internal.cli.commands;
+
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache30.CacheTestCase;
+import com.gemstone.gemfire.distributed.internal.DistributionConfig;
+import com.gemstone.gemfire.internal.AvailablePortHelper;
+import com.gemstone.gemfire.management.ManagementService;
+import com.gemstone.gemfire.management.internal.cli.CommandManager;
+import com.gemstone.gemfire.management.internal.cli.HeadlessGfsh;
+import com.gemstone.gemfire.management.internal.cli.i18n.CliStrings;
+import com.gemstone.gemfire.management.internal.cli.parser.CommandTarget;
+import com.gemstone.gemfire.management.internal.cli.result.CommandResult;
+import com.gemstone.gemfire.management.internal.cli.shell.Gfsh;
+import com.gemstone.gemfire.management.internal.cli.util.CommandStringBuilder;
+import dunit.Host;
+import dunit.SerializableCallable;
+import dunit.SerializableRunnable;
+import util.TestException;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Base class for all the CLI/gfsh command dunit tests.
+ *
+ * @author Tushar Khairnar
+ * @author Abhishek Chaudhari
+ * @author David Hoots
+ * @author John Blum
+ */
+public class CliCommandTestBase extends CacheTestCase {
+
+  private static final long serialVersionUID = 1L;
+
+  protected static final String USE_HTTP_SYSTEM_PROPERTY = "useHTTP";
+
+  private ManagementService managementService;
+
+  private transient HeadlessGfsh shell;
+
+  private boolean useHttpOnConnect = Boolean.getBoolean("useHTTP");
+
+  private int httpPort;
+  private int jmxPort;
+
+  private String jmxHost;
+
+  public CliCommandTestBase(String name) {
+    super(name);
+  }
+
+  public void setUp() throws Exception {
+    super.setUp();
+  }
+
+  @Override
+  public void tearDown2() throws Exception {
+    destroyDefaultSetup();
+    super.tearDown2();
+  }
+
+  /**
+   * Create all of the components necessary for the default setup. The provided properties will be used when creating
+   * the default cache. This will create GFSH in the controller VM (VM[4]) (no cache) and the manager in VM[0] (with
+   * cache). When adding regions, functions, keys, whatever to your cache for tests, you'll need to use
+   * Host.getHost(0).getVM(0).invoke(new SerializableRunnable() { public void run() { ... } } in order to have this
+   * setup run in the same VM as the manager.
+   * <p>
+   *
+   * @param props the Properties used when creating the cache for this default setup.
+   * @return the default testable GemFire shell.
+   */
+  @SuppressWarnings("serial")
+  protected final HeadlessGfsh createDefaultSetup(final Properties props) {
+    Object[] result = (Object[]) Host.getHost(0).getVM(0).invoke(new SerializableCallable() {
+      public Object call() {
+        final Object[] result = new Object[3];
+        final Properties localProps = (props != null ? props : new Properties());
+
+        try {
+          jmxHost = InetAddress.getLocalHost().getHostName();
+        } catch (UnknownHostException ignore) {
+          jmxHost = "localhost";
+        }
+
+        if (!localProps.containsKey(DistributionConfig.NAME_NAME)) {
+          localProps.setProperty(DistributionConfig.NAME_NAME, "Manager");
+        }
+
+        final int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2);
+
+        jmxPort = ports[0];
+        httpPort = ports[1];
+
+        localProps.setProperty(DistributionConfig.JMX_MANAGER_NAME, "true");
+        localProps.setProperty(DistributionConfig.JMX_MANAGER_START_NAME, "true");
+        localProps.setProperty(DistributionConfig.JMX_MANAGER_BIND_ADDRESS_NAME, String.valueOf(jmxHost));
+        localProps.setProperty(DistributionConfig.JMX_MANAGER_PORT_NAME, String.valueOf(jmxPort));
+        localProps.setProperty(DistributionConfig.HTTP_SERVICE_PORT_NAME, String.valueOf(httpPort));
+
+        getSystem(localProps);
+        verifyManagementServiceStarted(getCache());
+
+        result[0] = jmxHost;
+        result[1] = jmxPort;
+        result[2] = httpPort;
+
+        return result;
+      }
+    });
+
+    this.jmxHost = (String) result[0];
+    this.jmxPort = (Integer) result[1];
+    this.httpPort = (Integer) result[2];
+
+    return defaultShellConnect();
+  }
+
+  protected boolean useHTTPByTest() {
+    return false;
+  }
+
+  /**
+   * Destroy all of the components created for the default setup.
+   */
+  @SuppressWarnings("serial")
+  protected final void destroyDefaultSetup() {
+    if (this.shell != null) {
+      executeCommand(shell, "exit");
+      this.shell.terminate();
+      this.shell = null;
+    }
+
+    disconnectAllFromDS();
+
+    Host.getHost(0).getVM(0).invoke(new SerializableRunnable() {
+      public void run() {
+        verifyManagementServiceStopped();
+      }
+    });
+  }
+
+  /**
+   * Start the default management service using the provided Cache.
+   *
+   * @param cache Cache to use when creating the management service
+   */
+  private void verifyManagementServiceStarted(Cache cache) {
+    assert (cache != null);
+
+    this.managementService = ManagementService.getExistingManagementService(cache);
+    assertNotNull(this.managementService);
+    assertTrue(this.managementService.isManager());
+    assertTrue(checkIfCommandsAreLoadedOrNot());
+  }
+
+  public static boolean checkIfCommandsAreLoadedOrNot() {
+    CommandManager manager;
+    try {
+      manager = CommandManager.getInstance();
+      Map<String, CommandTarget> commands = manager.getCommands();
+      Set set = commands.keySet();
+      if (commands.size() < 1) {
+        return false;
+      }
+      return true;
+    } catch (ClassNotFoundException | IOException e) {
+      throw new RuntimeException("Could not load commands", e);
+    }
+  }
+
+  /**
+   * Stop the default management service.
+   */
+  private void verifyManagementServiceStopped() {
+    if (this.managementService != null) {
+      assertFalse(this.managementService.isManager());
+      this.managementService = null;
+    }
+  }
+
+  /**
+   * Connect the default shell to the default JMX server.
+   *
+   * @return The default shell.
+   */
+  private HeadlessGfsh defaultShellConnect() {
+    HeadlessGfsh shell = getDefaultShell();
+    shellConnect(this.jmxHost, this.jmxPort, this.httpPort, shell);
+    return shell;
+  }
+
+  /**
+   * Connect a shell to the JMX server at the given host and port
+   *
+   * @param host    Host of the JMX server
+   * @param jmxPort Port of the JMX server
+   * @param shell   Shell to connect
+   */
+  protected void shellConnect(final String host, final int jmxPort, final int httpPort, HeadlessGfsh shell) {
+    assert (host != null);
+    assert (shell != null);
+
+    final CommandStringBuilder command = new CommandStringBuilder(CliStrings.CONNECT);
+    String endpoint;
+
+    if (useHttpOnConnect) {
+      endpoint = "http://" + host + ":" + httpPort + "/gemfire/v1";
+      command.addOption(CliStrings.CONNECT__USE_HTTP, Boolean.TRUE.toString());
+      command.addOption(CliStrings.CONNECT__URL, endpoint);
+    } else {
+      endpoint = host + "[" + jmxPort + "]";
+      command.addOption(CliStrings.CONNECT__JMX_MANAGER, endpoint);
+    }
+
+    CommandResult result = executeCommand(shell, command.toString());
+
+    if (!shell.isConnectedAndReady()) {
+      throw new TestException(
+          "Connect command failed to connect to manager " + endpoint + " result=" + commandResultToString(result));
+    }
+
+    info("Successfully connected to managing node using " + (useHttpOnConnect ? "HTTP" : "JMX"));
+    assertEquals(true, shell.isConnectedAndReady());
+  }
+
+  /**
+   * Get the default shell (will create one if it doesn't already exist).
+   *
+   * @return The default shell
+   */
+  protected synchronized final HeadlessGfsh getDefaultShell() {
+    if (this.shell == null) {
+      this.shell = createShell();
+    }
+
+    return this.shell;
+  }
+
+  /**
+   * Create a HeadlessGfsh object.
+   *
+   * @return The created shell.
+   */
+  protected HeadlessGfsh createShell() {
+    try {
+      Gfsh.SUPPORT_MUTLIPLESHELL = true;
+      String shellId = getClass().getSimpleName() + "_" + getName();
+      HeadlessGfsh shell = new HeadlessGfsh(shellId, 30);
+      //Added to avoid trimming of the columns
+      info("Started testable shell: " + shell);
+      return shell;
+    } catch (ClassNotFoundException e) {
+      throw new TestException(getStackTrace(e));
+    } catch (IOException e) {
+      throw new TestException(getStackTrace(e));
+    }
+  }
+
+  /**
+   * Execute a command using the default shell and clear the shell events before returning.
+   *
+   * @param command Command to execute
+   * @return The result of the command execution
+   */
+  protected CommandResult executeCommand(String command) {
+    assert (command != null);
+
+    return executeCommand(getDefaultShell(), command);
+  }
+
+  /**
+   * Execute a command in the provided shell and clear the shell events before returning.
+   *
+   * @param shell   Shell in which to execute the command.
+   * @param command Command to execute
+   * @return The result of the command execution
+   */
+  protected CommandResult executeCommand(HeadlessGfsh shell, String command) {
+    assert (shell != null);
+    assert (command != null);
+
+    CommandResult commandResult = executeCommandWithoutClear(shell, command);
+    shell.clearEvents();
+    return commandResult;
+  }
+
+  /**
+   * Execute a command using the default shell. Useful for getting additional information from the shell after the
+   * command has been executed (using getDefaultShell().???). Caller is responsible for calling
+   * getDefaultShell().clearEvents() when done.
+   *
+   * @param command Command to execute
+   * @return The result of the command execution
+   */
+  @SuppressWarnings("unused")
+  protected CommandResult executeCommandWithoutClear(String command) {
+    assert (command != null);
+
+    return executeCommandWithoutClear(getDefaultShell(), command);
+  }
+
+  /**
+   * Execute a command in the provided shell. Useful for getting additional information from the shell after the command
+   * has been executed (using getDefaultShell().???). Caller is responsible for calling getDefaultShell().clearEvents()
+   * when done.
+   *
+   * @param shell   Shell in which to execute the command.
+   * @param command Command to execute
+   * @return The result of the command execution
+   */
+  protected CommandResult executeCommandWithoutClear(HeadlessGfsh shell, String command) {
+    assert (shell != null);
+    assert (command != null);
+
+    try {
+      info("Executing command " + command + " with command Mgr " + CommandManager.getInstance());
+    } catch (ClassNotFoundException cnfex) {
+      throw new TestException(getStackTrace(cnfex));
+    } catch (IOException ioex) {
+      throw new TestException(getStackTrace(ioex));
+    }
+
+    shell.executeCommand(command);
+    if (shell.hasError()) {
+      error("executeCommand completed with error : " + shell.getError());
+    }
+
+    CommandResult result = null;
+    try {
+      result = (CommandResult) shell.getResult();
+    } catch (InterruptedException ex) {
+      error("shell received InterruptedException");
+    }
+
+    if (result != null) {
+      result.resetToFirstLine();
+    }
+
+    return result;
+  }
+
+  /**
+   * Utility method for viewing the results of a command.
+   *
+   * @param commandResult Results to dump
+   * @param printStream   Stream to dump the results to
+   */
+  protected void printResult(final CommandResult commandResult, PrintStream printStream) {
+    assert (commandResult != null);
+    assert (printStream != null);
+
+    commandResult.resetToFirstLine();
+    printStream.print(commandResultToString(commandResult));
+  }
+
+  protected String commandResultToString(final CommandResult commandResult) {
+    assertNotNull(commandResult);
+
+    commandResult.resetToFirstLine();
+
+    StringBuilder buffer = new StringBuilder(commandResult.getHeader());
+
+    while (commandResult.hasNextLine()) {
+      buffer.append(commandResult.nextLine());
+    }
+
+    buffer.append(commandResult.getFooter());
+
+    return buffer.toString();
+  }
+
+  /**
+   * Utility method for finding the CommandResult object in the Map of CommandOutput objects.
+   *
+   * @param commandOutput CommandOutput Map to search
+   * @return The CommandResult object or null if not found.
+   */
+  protected CommandResult extractCommandResult(Map<String, Object> commandOutput) {
+    assert (commandOutput != null);
+
+    for (Object resultObject : commandOutput.values()) {
+      if (resultObject instanceof CommandResult) {
+        CommandResult result = (CommandResult) resultObject;
+        result.resetToFirstLine();
+        return result;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Utility method to determine how many times a string occurs in another string. Note that when looking for matches
+   * substrings of other matches will be counted as a match. For example, looking for "AA" in the string "AAAA" will
+   * result in a return value of 3.
+   *
+   * @param stringToSearch String to search
+   * @param stringToCount  String to look for and count
+   * @return The number of matches.
+   */
+  protected int countMatchesInString(final String stringToSearch, final String stringToCount) {
+    assert (stringToSearch != null);
+    assert (stringToCount != null);
+
+    int length = stringToSearch.length();
+    int count = 0;
+    for (int i = 0; i < length; i++) {
+      if (stringToSearch.substring(i).startsWith(stringToCount)) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  /**
+   * Determines if a string contains a trimmed line that matches the pattern. So, any single line whose leading and
+   * trailing spaces have been removed which contains a string that exactly matches the given pattern will be considered
+   * a match.
+   *
+   * @param stringToSearch String to search
+   * @param stringPattern  Pattern to search for
+   * @return True if a match is found, false otherwise
+   */
+  protected boolean stringContainsLine(final String stringToSearch, final String stringPattern) {
+    assert (stringToSearch != null);
+    assert (stringPattern != null);
+
+    Pattern pattern = Pattern.compile("^\\s*" + stringPattern + "\\s*$", Pattern.MULTILINE);
+    Matcher matcher = pattern.matcher(stringToSearch);
+    return matcher.find();
+  }
+
+  /**
+   * Counts the number of distinct lines in a String.
+   *
+   * @param stringToSearch  String to search for lines.
+   * @param countBlankLines Whether to count blank lines (true to count)
+   * @return The number of lines found.
+   */
+  protected int countLinesInString(final String stringToSearch, final boolean countBlankLines) {
+    assert (stringToSearch != null);
+
+    int length = stringToSearch.length();
+    int count = 0;
+    char character = 0;
+    boolean foundNonSpaceChar = false;
+
+    for (int i = 0; i < length; i++) {
+      character = stringToSearch.charAt(i);
+      if (character == '\r' && (i + 1) < length && stringToSearch.charAt(i + 1) == '\n') {
+        i++;
+      }
+      if (character == '\n' || character == '\r') {
+        if (countBlankLines) {
+          count++;
+        } else {
+          if (foundNonSpaceChar) {
+            count++;
+          }
+        }
+        foundNonSpaceChar = false;
+      } else if (character != ' ' && character != '\t') {
+        foundNonSpaceChar = true;
+      }
+    }
+
+    // Even if the last line isn't terminated, it still counts as a line
+    if (character != '\n' && character != '\r') {
+      count++;
+    }
+
+    return count;
+  }
+
+  /**
+   * Get a specific line from the string (using \n or \r as a line separator).
+   *
+   * @param stringToSearch String to get the line from
+   * @param lineNumber     Line number to get
+   * @return The line
+   */
+  protected String getLineFromString(final String stringToSearch, final int lineNumber) {
+    assert (stringToSearch != null);
+    assert (lineNumber > 0);
+
+    int length = stringToSearch.length();
+    int count = 0;
+    int startIndex = 0;
+    char character;
+    int endIndex = length;
+
+    for (int i = 0; i < length; i++) {
+      character = stringToSearch.charAt(i);
+      if (character == '\r' && (i + 1) < length && stringToSearch.charAt(i + 1) == '\n') {
+        i++;
+      }
+      if (character == '\n' || character == '\r') {
+        if (lineNumber == 1) {
+          endIndex = i;
+          break;
+        }
+        if (++count == lineNumber - 1) {
+          startIndex = i + 1;
+        } else if (count >= lineNumber) {
+          endIndex = i;
+          break;
+        }
+      }
+    }
+
+    return stringToSearch.substring(startIndex, endIndex);
+  }
+
+  protected static String getStackTrace(Throwable aThrowable) {
+    StringWriter sw = new StringWriter();
+    aThrowable.printStackTrace(new PrintWriter(sw, true));
+    return sw.toString();
+  }
+
+  protected void info(String string) {
+    getLogWriter().info(string);
+  }
+
+  protected void debug(String string) {
+    getLogWriter().fine(string);
+  }
+
+  protected void error(String string) {
+    getLogWriter().error(string);
+  }
+
+  protected void error(String string, Throwable e) {
+    getLogWriter().error(string, e);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/eddef322/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/ConfigCommandsDUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/ConfigCommandsDUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/ConfigCommandsDUnitTest.java
new file mode 100644
index 0000000..81536db
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/ConfigCommandsDUnitTest.java
@@ -0,0 +1,497 @@
+/*
+ * 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 com.gemstone.gemfire.management.internal.cli.commands;
+
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.server.CacheServer;
+import com.gemstone.gemfire.distributed.Locator;
+import com.gemstone.gemfire.distributed.internal.DistributionConfig;
+import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
+import com.gemstone.gemfire.distributed.internal.InternalLocator;
+import com.gemstone.gemfire.distributed.internal.SharedConfiguration;
+import com.gemstone.gemfire.internal.AvailablePort;
+import com.gemstone.gemfire.internal.AvailablePortHelper;
+import com.gemstone.gemfire.internal.FileUtil;
+import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
+import com.gemstone.gemfire.internal.cache.xmlcache.CacheXmlGenerator;
+import com.gemstone.gemfire.internal.logging.LogWriterImpl;
+import com.gemstone.gemfire.management.cli.Result;
+import com.gemstone.gemfire.management.cli.Result.Status;
+import com.gemstone.gemfire.management.internal.cli.i18n.CliStrings;
+import com.gemstone.gemfire.management.internal.cli.remote.CommandProcessor;
+import com.gemstone.gemfire.management.internal.cli.result.CommandResult;
+import com.gemstone.gemfire.management.internal.cli.util.CommandStringBuilder;
+import dunit.DistributedTestCase;
+import dunit.Host;
+import dunit.SerializableCallable;
+import dunit.SerializableRunnable;
+import dunit.VM;
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Dunit class for testing GemFire config commands : export config
+ *
+ * @author David Hoots
+ * @author Sourabh Bansod
+ * @since 7.0
+ */
+public class ConfigCommandsDUnitTest extends CliCommandTestBase {
+  private static final long serialVersionUID = 1L;
+
+  File managerConfigFile = new File("Manager-cache.xml");
+  File managerPropsFile = new File("Manager-gf.properties");
+  File vm1ConfigFile = new File("VM1-cache.xml");
+  File vm1PropsFile = new File("VM1-gf.properties");
+  File vm2ConfigFile = new File("VM2-cache.xml");
+  File vm2PropsFile = new File("VM2-gf.properties");
+  File shellConfigFile = new File("Shell-cache.xml");
+  File shellPropsFile = new File("Shell-gf.properties");
+  File subDir = new File("ConfigCommandsDUnitTestSubDir");
+  File subManagerConfigFile = new File(subDir, managerConfigFile.getName());
+
+  public ConfigCommandsDUnitTest(String name) {
+    super(name);
+  }
+
+  public void tearDown2() throws Exception {
+    deleteTestFiles();
+    invokeInEveryVM(new SerializableRunnable() {
+
+      @Override
+      public void run() {
+        try {
+          deleteTestFiles();
+        } catch (IOException e) {
+          fail("error", e);
+        }
+      }
+    });
+    super.tearDown2();
+  }
+
+  public void testDescribeConfig() throws ClassNotFoundException, IOException {
+    createDefaultSetup(null);
+    final String controllerName = "Member2";
+
+    /***
+     * Create properties for the controller VM
+     */
+    final Properties localProps = new Properties();
+    localProps.setProperty(DistributionConfig.MCAST_PORT_NAME, "0");
+    localProps.setProperty(DistributionConfig.LOG_LEVEL_NAME, "info");
+    localProps.setProperty(DistributionConfig.STATISTIC_SAMPLING_ENABLED_NAME, "true");
+    localProps.setProperty(DistributionConfig.ENABLE_TIME_STATISTICS_NAME, "true");
+    localProps.setProperty(DistributionConfig.NAME_NAME, controllerName);
+    localProps.setProperty(DistributionConfig.GROUPS_NAME, "G1");
+    getSystem(localProps);
+    Cache cache = getCache();
+    int ports[] = AvailablePortHelper.getRandomAvailableTCPPorts(1);
+    CacheServer cs = getCache().addCacheServer();
+    cs.setPort(ports[0]);
+    cs.setMaxThreads(10);
+    cs.setMaxConnections(9);
+    cs.start();
+
+    RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
+    List<String> jvmArgs = runtimeBean.getInputArguments();
+
+    getLogWriter().info("#SB Actual JVM Args : ");
+
+    for (String jvmArg : jvmArgs) {
+      getLogWriter().info("#SB JVM " + jvmArg);
+    }
+
+    InternalDistributedSystem system = (InternalDistributedSystem) cache.getDistributedSystem();
+    DistributionConfig config = system.getConfig();
+    config.setArchiveFileSizeLimit(1000);
+
+    String command = CliStrings.DESCRIBE_CONFIG + " --member=" + controllerName;
+    CommandProcessor cmdProcessor = new CommandProcessor();
+    cmdProcessor.createCommandStatement(command, Collections.EMPTY_MAP).process();
+
+    CommandResult cmdResult = executeCommand(command);
+
+    String resultStr = commandResultToString(cmdResult);
+    getLogWriter().info("#SB Hiding the defaults\n" + resultStr);
+
+    assertEquals(true, cmdResult.getStatus().equals(Status.OK));
+    assertEquals(true, resultStr.contains("G1"));
+    assertEquals(true, resultStr.contains(controllerName));
+    assertEquals(true, resultStr.contains("archive-file-size-limit"));
+    assertEquals(true, !resultStr.contains("copy-on-read"));
+
+    cmdResult = executeCommand(command + " --" + CliStrings.DESCRIBE_CONFIG__HIDE__DEFAULTS + "=false");
+    resultStr = commandResultToString(cmdResult);
+    getLogWriter().info("#SB No hiding of defaults\n" + resultStr);
+
+    assertEquals(true, cmdResult.getStatus().equals(Status.OK));
+    assertEquals(true, resultStr.contains("is-server"));
+    assertEquals(true, resultStr.contains(controllerName));
+    assertEquals(true, resultStr.contains("copy-on-read"));
+
+    cs.stop();
+  }
+
+  @SuppressWarnings("serial")
+  public void testExportConfig() throws IOException {
+    Properties localProps = new Properties();
+    localProps.setProperty(DistributionConfig.NAME_NAME, "Manager");
+    localProps.setProperty(DistributionConfig.GROUPS_NAME, "Group1");
+    createDefaultSetup(localProps);
+
+    // Create a cache in another VM (VM1)
+    Host.getHost(0).getVM(1).invoke(new SerializableRunnable() {
+      public void run() {
+        Properties localProps = new Properties();
+        localProps.setProperty(DistributionConfig.NAME_NAME, "VM1");
+        localProps.setProperty(DistributionConfig.GROUPS_NAME, "Group2");
+        getSystem(localProps);
+        getCache();
+      }
+    });
+
+    // Create a cache in a 3rd VM (VM2)
+    Host.getHost(0).getVM(2).invoke(new SerializableRunnable() {
+      public void run() {
+        Properties localProps = new Properties();
+        localProps.setProperty(DistributionConfig.NAME_NAME, "VM2");
+        localProps.setProperty(DistributionConfig.GROUPS_NAME, "Group2");
+        getSystem(localProps);
+        getCache();
+      }
+    });
+
+    // Create a cache in the local VM
+    localProps = new Properties();
+    localProps.setProperty(DistributionConfig.NAME_NAME, "Shell");
+    getSystem(localProps);
+    Cache cache = getCache();
+
+    // Test export config for all members
+    deleteTestFiles();
+    CommandResult cmdResult = executeCommand("export config");
+    assertEquals(Result.Status.OK, cmdResult.getStatus());
+
+    assertTrue(this.managerConfigFile.exists());
+    assertTrue(this.managerPropsFile.exists());
+    assertTrue(this.vm1ConfigFile.exists());
+    assertTrue(this.vm1PropsFile.exists());
+    assertTrue(this.vm2ConfigFile.exists());
+    assertTrue(this.vm2PropsFile.exists());
+    assertTrue(this.shellConfigFile.exists());
+    assertTrue(this.shellPropsFile.exists());
+
+    // Test exporting member
+    deleteTestFiles();
+    cmdResult = executeCommand("export config --member=Manager");
+    assertEquals(Result.Status.OK, cmdResult.getStatus());
+
+    assertTrue(this.managerConfigFile.exists());
+    assertFalse(this.vm1ConfigFile.exists());
+    assertFalse(this.vm2ConfigFile.exists());
+    assertFalse(this.shellConfigFile.exists());
+
+    // Test exporting group
+    deleteTestFiles();
+    cmdResult = executeCommand("export config --group=Group2");
+    assertEquals(Result.Status.OK, cmdResult.getStatus());
+
+    assertFalse(this.managerConfigFile.exists());
+    assertTrue(this.vm1ConfigFile.exists());
+    assertTrue(this.vm2ConfigFile.exists());
+    assertFalse(this.shellConfigFile.exists());
+
+    // Test export to directory
+    deleteTestFiles();
+    cmdResult = executeCommand("export config --dir=" + subDir.getAbsolutePath());
+    assertEquals(Result.Status.OK, cmdResult.getStatus());
+
+    assertFalse(this.managerConfigFile.exists());
+    assertTrue(this.subManagerConfigFile.exists());
+
+    // Test the contents of the file
+    StringWriter stringWriter = new StringWriter();
+    PrintWriter printWriter = new PrintWriter(stringWriter);
+    CacheXmlGenerator.generate(cache, printWriter, false, false, false);
+    String configToMatch = stringWriter.toString();
+
+    deleteTestFiles();
+    cmdResult = executeCommand("export config --member=Shell");
+    assertEquals(Result.Status.OK, cmdResult.getStatus());
+
+    char[] fileContents = new char[configToMatch.length()];
+    try {
+      FileReader reader = new FileReader(shellConfigFile);
+      reader.read(fileContents);
+    } catch (Exception ex) {
+      fail("Unable to read file contents for comparison", ex);
+    }
+
+    assertEquals(configToMatch, new String(fileContents));
+  }
+
+  public void testAlterRuntimeConfig() throws ClassNotFoundException, IOException {
+    final String controller = "controller";
+    createDefaultSetup(null);
+    Properties localProps = new Properties();
+    localProps.setProperty(DistributionConfig.NAME_NAME, controller);
+    localProps.setProperty(DistributionConfig.LOG_LEVEL_NAME, "error");
+    getSystem(localProps);
+    final GemFireCacheImpl cache = (GemFireCacheImpl) getCache();
+    final DistributionConfig config = cache.getSystem().getConfig();
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.ALTER_RUNTIME_CONFIG);
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__MEMBER, controller);
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__LOG__LEVEL, "info");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__LOG__FILE__SIZE__LIMIT, "50");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__ARCHIVE__DISK__SPACE__LIMIT, "32");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__ARCHIVE__FILE__SIZE__LIMIT, "49");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__STATISTIC__SAMPLE__RATE, "2000");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__STATISTIC__ARCHIVE__FILE, "stat.gfs");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__STATISTIC__SAMPLING__ENABLED, "true");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__LOG__DISK__SPACE__LIMIT, "10");
+    CommandResult cmdResult = executeCommand(csb.getCommandString());
+    String resultString = commandResultToString(cmdResult);
+    getLogWriter().info("Result\n");
+    getLogWriter().info(resultString);
+    assertEquals(true, cmdResult.getStatus().equals(Status.OK));
+    assertEquals(LogWriterImpl.INFO_LEVEL, config.getLogLevel());
+    assertEquals(50, config.getLogFileSizeLimit());
+    assertEquals(32, config.getArchiveDiskSpaceLimit());
+    assertEquals(2000, config.getStatisticSampleRate());
+    assertEquals("stat.gfs", config.getStatisticArchiveFile().getName());
+    assertEquals(true, config.getStatisticSamplingEnabled());
+    assertEquals(10, config.getLogDiskSpaceLimit());
+
+
+    CommandProcessor commandProcessor = new CommandProcessor();
+    Result result = commandProcessor.createCommandStatement("alter runtime", Collections.EMPTY_MAP).process();
+  }
+
+  public void testAlterRuntimeConfigRandom() {
+    final String member1 = "VM1";
+    final String controller = "controller";
+    createDefaultSetup(null);
+    Properties localProps = new Properties();
+    localProps.setProperty(DistributionConfig.NAME_NAME, controller);
+    localProps.setProperty(DistributionConfig.LOG_LEVEL_NAME, "error");
+    getSystem(localProps);
+    final GemFireCacheImpl cache = (GemFireCacheImpl) getCache();
+    final DistributionConfig config = cache.getSystem().getConfig();
+
+    Host.getHost(0).getVM(1).invoke(new SerializableRunnable() {
+      public void run() {
+        Properties localProps = new Properties();
+        localProps.setProperty(DistributionConfig.NAME_NAME, member1);
+        getSystem(localProps);
+        Cache cache = getCache();
+      }
+    });
+
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.ALTER_RUNTIME_CONFIG);
+    CommandResult cmdResult = executeCommand(csb.getCommandString());
+    String resultAsString = commandResultToString(cmdResult);
+    getLogWriter().info("#SB Result\n");
+    getLogWriter().info(resultAsString);
+    assertEquals(true, cmdResult.getStatus().equals(Status.ERROR));
+    assertTrue(resultAsString.contains(CliStrings.ALTER_RUNTIME_CONFIG__RELEVANT__OPTION__MESSAGE));
+
+    csb = new CommandStringBuilder(CliStrings.ALTER_RUNTIME_CONFIG);
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__LOG__DISK__SPACE__LIMIT, "2000000000");
+    cmdResult = executeCommand(csb.getCommandString());
+    resultAsString = commandResultToString(cmdResult);
+    getLogWriter().info("#SB Result\n");
+    getLogWriter().info(resultAsString);
+    assertEquals(true, cmdResult.getStatus().equals(Status.ERROR));
+
+  }
+
+  public void testAlterRuntimeConfigOnAllMembers() {
+    final String member1 = "VM1";
+    final String controller = "controller";
+    createDefaultSetup(null);
+    Properties localProps = new Properties();
+    localProps.setProperty(DistributionConfig.NAME_NAME, controller);
+    localProps.setProperty(DistributionConfig.LOG_LEVEL_NAME, "error");
+    getSystem(localProps);
+    final GemFireCacheImpl cache = (GemFireCacheImpl) getCache();
+    final DistributionConfig config = cache.getSystem().getConfig();
+
+    Host.getHost(0).getVM(1).invoke(new SerializableRunnable() {
+      public void run() {
+        Properties localProps = new Properties();
+        localProps.setProperty(DistributionConfig.NAME_NAME, member1);
+        getSystem(localProps);
+        Cache cache = getCache();
+      }
+    });
+    CommandStringBuilder csb = new CommandStringBuilder(CliStrings.ALTER_RUNTIME_CONFIG);
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__LOG__LEVEL, "info");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__LOG__FILE__SIZE__LIMIT, "50");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__ARCHIVE__DISK__SPACE__LIMIT, "32");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__ARCHIVE__FILE__SIZE__LIMIT, "49");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__STATISTIC__SAMPLE__RATE, "2000");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__STATISTIC__ARCHIVE__FILE, "stat.gfs");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__STATISTIC__SAMPLING__ENABLED, "true");
+    csb.addOption(CliStrings.ALTER_RUNTIME_CONFIG__LOG__DISK__SPACE__LIMIT, "10");
+    CommandResult cmdResult = executeCommand(csb.getCommandString());
+    String resultString = commandResultToString(cmdResult);
+    getLogWriter().info("#SB Result\n");
+    getLogWriter().info(resultString);
+    assertEquals(true, cmdResult.getStatus().equals(Status.OK));
+    assertEquals(LogWriterImpl.INFO_LEVEL, config.getLogLevel());
+    assertEquals(50, config.getLogFileSizeLimit());
+    assertEquals(49, config.getArchiveFileSizeLimit());
+    assertEquals(32, config.getArchiveDiskSpaceLimit());
+    assertEquals(2000, config.getStatisticSampleRate());
+    assertEquals("stat.gfs", config.getStatisticArchiveFile().getName());
+    assertEquals(true, config.getStatisticSamplingEnabled());
+    assertEquals(10, config.getLogDiskSpaceLimit());
+
+    // Validate the changes in the vm1
+    Host.getHost(0).getVM(1).invoke(new SerializableRunnable() {
+      public void run() {
+        GemFireCacheImpl cacheVM1 = (GemFireCacheImpl) getCache();
+        final DistributionConfig configVM1 = cacheVM1.getSystem().getConfig();
+        assertEquals(LogWriterImpl.INFO_LEVEL, configVM1.getLogLevel());
+        assertEquals(50, configVM1.getLogFileSizeLimit());
+        assertEquals(49, configVM1.getArchiveFileSizeLimit());
+        assertEquals(32, configVM1.getArchiveDiskSpaceLimit());
+        assertEquals(2000, configVM1.getStatisticSampleRate());
+        assertEquals("stat.gfs", configVM1.getStatisticArchiveFile().getName());
+        assertEquals(true, configVM1.getStatisticSamplingEnabled());
+        assertEquals(10, configVM1.getLogDiskSpaceLimit());
+      }
+    });
+  }
+
+  /**
+   * Asserts that altering the runtime config correctly updates the shared configuration.
+   * <p>
+   * Disabled: this test frequently fails during unit test runs. See ticket #52204
+   */
+  public void disabledtestAlterUpdatesSharedConfig() {
+    disconnectAllFromDS();
+
+    final String groupName = "testAlterRuntimeConfigSharedConfigGroup";
+
+    // Start the Locator and wait for shared configuration to be available
+    final int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
+    Host.getHost(0).getVM(3).invoke(new SerializableRunnable() {
+      @Override
+      public void run() {
+
+        final File locatorLogFile = new File("locator-" + locatorPort + ".log");
+        final Properties locatorProps = new Properties();
+        locatorProps.setProperty(DistributionConfig.NAME_NAME, "Locator");
+        locatorProps.setProperty(DistributionConfig.MCAST_PORT_NAME, "0");
+        locatorProps.setProperty(DistributionConfig.ENABLE_CLUSTER_CONFIGURATION_NAME, "true");
+        try {
+          final InternalLocator locator = (InternalLocator) Locator.startLocatorAndDS(locatorPort, locatorLogFile, null,
+              locatorProps);
+
+          DistributedTestCase.WaitCriterion wc = new DistributedTestCase.WaitCriterion() {
+            @Override
+            public boolean done() {
+              return locator.isSharedConfigurationRunning();
+            }
+
+            @Override
+            public String description() {
+              return "Waiting for shared configuration to be started";
+            }
+          };
+          DistributedTestCase.waitForCriterion(wc, 5000, 500, true);
+        } catch (IOException ioex) {
+          fail("Unable to create a locator with a shared configuration");
+        }
+      }
+    });
+
+    // Start the default manager
+    Properties managerProps = new Properties();
+    managerProps.setProperty(DistributionConfig.MCAST_PORT_NAME, "0");
+    managerProps.setProperty(DistributionConfig.LOCATORS_NAME, "localhost:" + locatorPort);
+    createDefaultSetup(managerProps);
+
+    // Create a cache in VM 1
+    VM vm = Host.getHost(0).getVM(1);
+    vm.invoke(new SerializableCallable() {
+      @Override
+      public Object call() throws Exception {
+        //Make sure no previous shared config is screwing up this test.
+        FileUtil.delete(new File("ConfigDiskDir_Locator"));
+        FileUtil.delete(new File("cluster_config"));
+        Properties localProps = new Properties();
+        localProps.setProperty(DistributionConfig.MCAST_PORT_NAME, "0");
+        localProps.setProperty(DistributionConfig.LOCATORS_NAME, "localhost:" + locatorPort);
+        localProps.setProperty(DistributionConfig.LOG_LEVEL_NAME, "error");
+        localProps.setProperty(DistributionConfig.GROUPS_NAME, groupName);
+        getSystem(localProps);
+
+        assertNotNull(getCache());
+        assertEquals("error", system.getConfig().getAttribute(DistributionConfig.LOG_LEVEL_NAME));
+        return null;
+      }
+    });
+
+    // Test altering the runtime config
+    CommandStringBuilder commandStringBuilder = new CommandStringBuilder(CliStrings.ALTER_RUNTIME_CONFIG);
+    commandStringBuilder.addOption(CliStrings.ALTER_RUNTIME_CONFIG__GROUP, groupName);
+    commandStringBuilder.addOption(CliStrings.ALTER_RUNTIME_CONFIG__LOG__LEVEL, "fine");
+    CommandResult cmdResult = executeCommand(commandStringBuilder.toString());
+    assertEquals(Result.Status.OK, cmdResult.getStatus());
+
+    // Make sure the shared config was updated
+    Host.getHost(0).getVM(3).invoke(new SerializableRunnable() {
+      @Override
+      public void run() {
+        SharedConfiguration sharedConfig = ((InternalLocator) Locator.getLocator()).getSharedConfiguration();
+        Properties gemfireProperties;
+        try {
+          gemfireProperties = sharedConfig.getConfiguration(groupName).getGemfireProperties();
+          assertEquals("fine", gemfireProperties.get(DistributionConfig.LOG_LEVEL_NAME));
+        } catch (Exception e) {
+          fail("Error occurred in cluster configuration service", e);
+        }
+      }
+    });
+  }
+
+  private final void deleteTestFiles() throws IOException {
+    this.managerConfigFile.delete();
+    this.managerPropsFile.delete();
+    this.vm1ConfigFile.delete();
+    this.vm1PropsFile.delete();
+    this.vm2ConfigFile.delete();
+    this.vm2PropsFile.delete();
+    this.shellConfigFile.delete();
+    this.shellPropsFile.delete();
+
+    FileUtils.deleteDirectory(this.subDir);
+  }
+}