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