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);
+ }
+}