GEODE-3103: GfshRule no longer clutters output

 - GfshRule writes via a logger rather than StdOut.  This will make it no 
longer clutter precheckin runs or the nightly build.
 - Introduce ProcessLogger to capture output from the Gfsh JVM so that tests 
can assert against the output.


Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/f63b9d14
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/f63b9d14
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/f63b9d14

Branch: refs/heads/feature/GEODE-3109
Commit: f63b9d145eee93f989998c9f36b3f2b7a85ce07e
Parents: b0fafd3
Author: Jared Stewart <jstew...@pivotal.io>
Authored: Wed Jun 21 13:21:56 2017 -0700
Committer: Jared Stewart <jstew...@pivotal.io>
Committed: Wed Jun 28 13:59:45 2017 -0700

----------------------------------------------------------------------
 geode-assembly/build.gradle                     |  1 +
 .../cli/commands/StatusLocatorRealGfshTest.java | 22 ++---
 .../test/dunit/rules/gfsh/GfshExecution.java    | 50 +++++++++++
 .../geode/test/dunit/rules/gfsh/GfshRule.java   | 75 ++++++++++------
 .../geode/test/dunit/rules/gfsh/GfshScript.java | 48 +++++++----
 .../test/dunit/rules/gfsh/ProcessLogger.java    | 90 ++++++++++++++++++++
 .../test/dunit/rules/gfsh/StreamGobbler.java    | 38 +++++++++
 .../internal/cli/util/CommandStringBuilder.java | 31 +++++--
 8 files changed, 292 insertions(+), 63 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/f63b9d14/geode-assembly/build.gradle
----------------------------------------------------------------------
diff --git a/geode-assembly/build.gradle b/geode-assembly/build.gradle
index 39bb542..3bfe57e 100755
--- a/geode-assembly/build.gradle
+++ b/geode-assembly/build.gradle
@@ -72,6 +72,7 @@ dependencies {
 
   testCompile 'org.apache.httpcomponents:httpclient:' + 
project.'httpclient.version'
   testCompile 'org.apache.httpcomponents:httpcore:' + 
project.'httpcore.version'
+  testCompile 'com.google.guava:guava:' + project.'guava.version'
 
   testRuntime files("${System.getProperty('java.home')}/../lib/tools.jar")
   testRuntime 
files("$buildDir/install/${distributions.main.baseName}/lib/geode-dependencies.jar")

http://git-wip-us.apache.org/repos/asf/geode/blob/f63b9d14/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StatusLocatorRealGfshTest.java
----------------------------------------------------------------------
diff --git 
a/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StatusLocatorRealGfshTest.java
 
b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StatusLocatorRealGfshTest.java
index 82ee240..55f1a9c 100644
--- 
a/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StatusLocatorRealGfshTest.java
+++ 
b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StatusLocatorRealGfshTest.java
@@ -14,16 +14,13 @@
  */
 package org.apache.geode.management.internal.cli.commands;
 
-import org.apache.geode.test.dunit.rules.gfsh.GfshRule;
-import org.apache.geode.test.dunit.rules.gfsh.GfshScript;
-import org.apache.geode.test.junit.categories.DistributedTest;
-import org.apache.geode.test.junit.categories.IntegrationTest;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
-import java.io.File;
-import java.util.concurrent.TimeUnit;
+import org.apache.geode.test.dunit.rules.gfsh.GfshRule;
+import org.apache.geode.test.dunit.rules.gfsh.GfshScript;
+import org.apache.geode.test.junit.categories.DistributedTest;
 
 @Category(DistributedTest.class)
 public class StatusLocatorRealGfshTest {
@@ -32,19 +29,16 @@ public class StatusLocatorRealGfshTest {
 
   @Test
   public void statusLocatorSucceedsWhenConnected() throws Exception {
-    gfshRule.execute(GfshScript.of("start locator 
--name=locator1").awaitAtMost(1, TimeUnit.MINUTES)
-        .expectExitCode(0));
+    GfshScript.of("start locator --name=locator1").execute(gfshRule);
 
-    gfshRule.execute(GfshScript.of("connect", "status locator --name=locator1")
-        .awaitAtMost(1, TimeUnit.MINUTES).expectExitCode(0));
+    GfshScript.of("connect", "status locator 
--name=locator1").execute(gfshRule);
   }
 
   @Test
   public void statusLocatorFailsWhenNotConnected() throws Exception {
-    gfshRule.execute(GfshScript.of("start locator 
--name=locator1").awaitAtMost(1, TimeUnit.MINUTES)
-        .expectExitCode(0));
+    GfshScript.of("start locator --name=locator1").withName("start 
locator").execute(gfshRule);
 
-    gfshRule.execute(GfshScript.of("status locator --name=locator1")
-        .awaitAtMost(1, TimeUnit.MINUTES).expectExitCode(1));
+    GfshScript.of("status locator --name=locator1").withName("status 
locator").expectFailure()
+        .execute(gfshRule);
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/f63b9d14/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshExecution.java
----------------------------------------------------------------------
diff --git 
a/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshExecution.java
 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshExecution.java
new file mode 100644
index 0000000..23f2a73
--- /dev/null
+++ 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshExecution.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional 
information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache 
License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the 
License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
KIND, either express
+ * or implied. See the License for the specific language governing permissions 
and limitations under
+ * the License.
+ */
+package org.apache.geode.test.dunit.rules.gfsh;
+
+import java.io.File;
+import java.util.List;
+
+public class GfshExecution {
+  private final Process process;
+  private final File workingDir;
+  private final ProcessLogger processLogger;
+
+  protected GfshExecution(Process process, File workingDir) {
+    this.process = process;
+    this.workingDir = workingDir;
+    this.processLogger = new ProcessLogger(process, workingDir.getName());
+  }
+
+  public List<String> getStdOutLines() {
+    return processLogger.getStdOutLines();
+  }
+
+  public List<String> getStdErrLines() {
+    return processLogger.getStdErrLines();
+  }
+
+  /**
+   * Note that this is the working directory of gfsh itself. If your script 
started a server or
+   * locator, this will be the parent directory of that member's working 
directory.
+   */
+  public File getWorkingDir() {
+    return workingDir;
+  }
+
+  public Process getProcess() {
+    return this.process;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/f63b9d14/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshRule.java
----------------------------------------------------------------------
diff --git 
a/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshRule.java
 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshRule.java
index 8109377..fc6f376 100644
--- 
a/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshRule.java
+++ 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshRule.java
@@ -24,11 +24,13 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 import org.junit.rules.ExternalResource;
 import org.junit.rules.TemporaryFolder;
 
 import 
org.apache.geode.management.internal.cli.commands.StatusLocatorRealGfshTest;
+import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
 import org.apache.geode.test.dunit.rules.RequiresGeodeHome;
 
 /**
@@ -39,25 +41,26 @@ import org.apache.geode.test.dunit.rules.RequiresGeodeHome;
  */
 public class GfshRule extends ExternalResource {
   private TemporaryFolder temporaryFolder = new TemporaryFolder();
-  private List<Process> processes = new ArrayList<>();
+  private List<GfshExecution> gfshExecutions;
   private Path gfsh;
 
-  public Process execute(String... commands) {
+  public GfshExecution execute(String... commands) {
     return execute(GfshScript.of(commands));
   }
 
-  public Process execute(GfshScript gfshScript) {
-    Process process;
+  protected GfshExecution execute(GfshScript gfshScript) {
+    GfshExecution gfshExecution;
     try {
-      process = gfshScript.toProcessBuilder(gfsh, 
temporaryFolder.getRoot()).start();
+      File workingDir = temporaryFolder.newFolder(gfshScript.getName());
+      Process process = toProcessBuilder(gfshScript, gfsh, workingDir).start();
+      gfshExecution = new GfshExecution(process, workingDir);
+      gfshExecutions.add(gfshExecution);
+      gfshScript.awaitIfNecessary(process);
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
 
-    processes.add(process);
-    gfshScript.awaitIfNecessary(process);
-
-    return process;
+    return gfshExecution;
   }
 
   @Override
@@ -65,6 +68,7 @@ public class GfshRule extends ExternalResource {
     gfsh = new RequiresGeodeHome().getGeodeHome().toPath().resolve("bin/gfsh");
     assertThat(gfsh).exists();
 
+    gfshExecutions = new ArrayList<>();
     temporaryFolder.create();
   }
 
@@ -74,21 +78,35 @@ public class GfshRule extends ExternalResource {
    */
   @Override
   protected void after() {
-    stopMembersQuietly();
-    processes.forEach(Process::destroyForcibly);
-    processes.forEach((Process process) -> {
-      try {
-        // Process.destroyForcibly() may not terminate immediately
-        process.waitFor(1, TimeUnit.MINUTES);
-      } catch (InterruptedException ignore) {
-        // We ignore this exception so that we still attempt the rest of the 
cleanup.
-      }
-    });
+    
gfshExecutions.stream().map(GfshExecution::getWorkingDir).collect(Collectors.toList())
+        .forEach(this::stopMembersQuietly);
+
+    
gfshExecutions.stream().map(GfshExecution::getProcess).map(Process::destroyForcibly)
+        .forEach((Process process) -> {
+          try {
+            // Process.destroyForcibly() may not terminate immediately
+            process.waitFor(1, TimeUnit.MINUTES);
+          } catch (InterruptedException ignore) {
+            // We ignore this exception so that we still attempt the rest of 
the cleanup.
+          }
+        });
+
     temporaryFolder.delete();
   }
 
-  private void stopMembersQuietly() {
-    File[] directories = 
temporaryFolder.getRoot().listFiles(File::isDirectory);
+  protected ProcessBuilder toProcessBuilder(GfshScript gfshScript, Path 
gfshPath, File workingDir) {
+    List<String> commandsToExecute = new ArrayList<>();
+    commandsToExecute.add(gfshPath.toAbsolutePath().toString());
+
+    for (String command : gfshScript.getCommands()) {
+      commandsToExecute.add("-e " + command);
+    }
+
+    return new ProcessBuilder(commandsToExecute).directory(workingDir);
+  }
+
+  private void stopMembersQuietly(File parentDirectory) {
+    File[] potentalMemberDirectories = 
parentDirectory.listFiles(File::isDirectory);
 
     Predicate<File> isServerDir = (File directory) -> 
Arrays.stream(directory.list())
         .anyMatch(filename -> filename.endsWith("server.pid"));
@@ -96,21 +114,24 @@ public class GfshRule extends ExternalResource {
     Predicate<File> isLocatorDir = (File directory) -> 
Arrays.stream(directory.list())
         .anyMatch(filename -> filename.endsWith("locator.pid"));
 
-    
Arrays.stream(directories).filter(isServerDir).forEach(this::stopServerInDir);
-    
Arrays.stream(directories).filter(isLocatorDir).forEach(this::stopLocatorInDir);
+    
Arrays.stream(potentalMemberDirectories).filter(isServerDir).forEach(this::stopServerInDir);
+    
Arrays.stream(potentalMemberDirectories).filter(isLocatorDir).forEach(this::stopLocatorInDir);
   }
 
   private void stopServerInDir(File dir) {
-    GfshScript stopServerScript = new GfshScript("stop server --dir=" + 
dir.getAbsolutePath())
-        .awaitQuietlyAtMost(1, TimeUnit.MINUTES);
+    String stopServerCommand =
+        new CommandStringBuilder("stop server").addOption("dir", 
dir).toString();
 
+    GfshScript stopServerScript = new 
GfshScript(stopServerCommand).awaitQuietly();
     execute(stopServerScript);
   }
 
   private void stopLocatorInDir(File dir) {
-    GfshScript stopServerScript = new GfshScript("stop locator --dir=" + 
dir.getAbsolutePath())
-        .awaitQuietlyAtMost(1, TimeUnit.MINUTES);
+    String stopLocatorCommand =
+        new CommandStringBuilder("stop locator").addOption("dir", 
dir).toString();
 
+    GfshScript stopServerScript = new 
GfshScript(stopLocatorCommand).awaitQuietly();
     execute(stopServerScript);
   }
+
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/f63b9d14/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshScript.java
----------------------------------------------------------------------
diff --git 
a/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshScript.java
 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshScript.java
index 3ee1402..30c7140 100644
--- 
a/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshScript.java
+++ 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshScript.java
@@ -16,25 +16,33 @@ package org.apache.geode.test.dunit.rules.gfsh;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-import java.io.File;
-import java.nio.file.Path;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.geode.management.internal.cli.util.ThreePhraseGenerator;
+
 public class GfshScript {
   private final String[] commands;
-  private Integer timeout;
-  private TimeUnit timeoutTimeUnit;
+  private String name = new ThreePhraseGenerator().generate('-');
+  private TimeUnit timeoutTimeUnit = TimeUnit.MINUTES;
+  private int timeout = 1;
   private boolean awaitQuietly = false;
-  private Integer expectedExitValue;
+  private int expectedExitValue = 0;
 
   public GfshScript(String... commands) {
     this.commands = commands;
   }
 
+  /**
+   * By default, this GfshScript will await at most 2 minutes and will expect 
success.
+   */
   public static GfshScript of(String... commands) {
     return new GfshScript(commands);
   }
 
+  public GfshScript withName(String name) {
+    this.name = name;
+    return this;
+  }
 
   public GfshScript expectExitCode(int expectedExitCode) {
     this.expectedExitValue = expectedExitCode;
@@ -42,6 +50,10 @@ public class GfshScript {
     return this;
   }
 
+  public GfshScript expectFailure() {
+    return expectExitCode(1);
+  }
+
   /**
    * Will cause the thread that executes {@link GfshScript#awaitIfNecessary} 
to wait, if necessary,
    * until the subprocess executing this Gfsh script has terminated, or the 
specified waiting time
@@ -68,16 +80,14 @@ public class GfshScript {
     return awaitAtMost(timeout, timeUnit);
   }
 
+  public GfshScript awaitQuietly() {
+    this.awaitQuietly = true;
 
-  protected ProcessBuilder toProcessBuilder(Path gfshPath, File workingDir) {
-    String[] gfshCommands = new String[commands.length + 1];
-    gfshCommands[0] = gfshPath.toAbsolutePath().toString();
-
-    for (int i = 0; i < commands.length; i++) {
-      gfshCommands[i + 1] = "-e " + commands[i];
-    }
+    return this;
+  }
 
-    return new ProcessBuilder(gfshCommands).inheritIO().directory(workingDir);
+  public GfshExecution execute(GfshRule gfshRule) {
+    return gfshRule.execute(this);
   }
 
   protected void awaitIfNecessary(Process process) {
@@ -87,9 +97,7 @@ public class GfshScript {
       awaitLoudly(process);
     }
 
-    if (expectedExitValue != null) {
-      assertThat(process.exitValue()).isEqualTo(expectedExitValue);
-    }
+    assertThat(process.exitValue()).isEqualTo(expectedExitValue);
   }
 
   private void awaitQuietly(Process process) {
@@ -121,4 +129,12 @@ public class GfshScript {
   private boolean shouldAwaitLoudly() {
     return shouldAwait() && !awaitQuietly;
   }
+
+  public String[] getCommands() {
+    return commands;
+  }
+
+  public String getName() {
+    return name;
+  }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/f63b9d14/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/ProcessLogger.java
----------------------------------------------------------------------
diff --git 
a/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/ProcessLogger.java
 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/ProcessLogger.java
new file mode 100644
index 0000000..47f0304
--- /dev/null
+++ 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/ProcessLogger.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional 
information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache 
License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the 
License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
KIND, either express
+ * or implied. See the License for the specific language governing permissions 
and limitations under
+ * the License.
+ */
+package org.apache.geode.test.dunit.rules.gfsh;
+
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import com.google.common.collect.Lists;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.config.Configurator;
+import 
org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
+import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
+import 
org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
+import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
+
+public class ProcessLogger {
+  private static final LoggerContext LOGGER_CONTEXT = createLoggerContext();
+  private final Logger logger;
+
+  private final Queue<String> stdOutLines = new ConcurrentLinkedQueue<>();
+  private final Queue<String> stdErrorLines = new ConcurrentLinkedQueue<>();
+  private final StreamGobbler stdOutGobbler;
+  private final StreamGobbler stdErrGobbler;
+
+  public ProcessLogger(Process process, String name) {
+    this.logger = LOGGER_CONTEXT.getLogger(name);
+
+    this.stdOutGobbler = new StreamGobbler(process.getInputStream(), 
this::consumeInfoMessage);
+    this.stdErrGobbler = new StreamGobbler(process.getErrorStream(), 
this::consumeErrorMessage);
+
+    stdOutGobbler.startInNewThread();
+    stdErrGobbler.startInNewThread();
+  }
+
+  private void consumeInfoMessage(String message) {
+    logger.info(message);
+    stdOutLines.add(message);
+  }
+
+  private void consumeErrorMessage(String message) {
+    logger.error(message);
+    stdErrorLines.add(message);
+  }
+
+  private static LoggerContext createLoggerContext() {
+    ConfigurationBuilder<BuiltConfiguration> builder =
+        ConfigurationBuilderFactory.newConfigurationBuilder();
+    builder.setStatusLevel(Level.ERROR);
+    builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, 
Filter.Result.NEUTRAL)
+        .addAttribute("level", Level.DEBUG));
+    AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", 
"CONSOLE")
+        .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
+    
appenderBuilder.add(builder.newLayout("PatternLayout").addAttribute("pattern",
+        "[%-5level %d{HH:mm:ss.SSS z}] (%c): %msg%n%throwable"));
+    appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, 
Filter.Result.NEUTRAL)
+        .addAttribute("marker", "FLOW"));
+    builder.add(appenderBuilder);
+    builder.add(builder.newLogger("org.apache.logging.log4j", Level.ERROR)
+        .add(builder.newAppenderRef("Stdout")).addAttribute("additivity", 
false));
+    
builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout")));
+
+    return Configurator.initialize(builder.build());
+  }
+
+  public List<String> getStdOutLines() {
+    return Lists.newArrayList(stdOutLines.iterator());
+  }
+
+  public List<String> getStdErrLines() {
+    return Lists.newArrayList(stdOutLines.iterator());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/f63b9d14/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/StreamGobbler.java
----------------------------------------------------------------------
diff --git 
a/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/StreamGobbler.java
 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/StreamGobbler.java
new file mode 100644
index 0000000..e8fd0cc
--- /dev/null
+++ 
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/StreamGobbler.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional 
information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache 
License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the 
License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
KIND, either express
+ * or implied. See the License for the specific language governing permissions 
and limitations under
+ * the License.
+ */
+package org.apache.geode.test.dunit.rules.gfsh;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.function.Consumer;
+
+class StreamGobbler implements Runnable {
+  private InputStream inputStream;
+  private Consumer<String> consumeInputLine;
+
+  public StreamGobbler(InputStream inputStream, Consumer<String> 
consumeInputLine) {
+    this.inputStream = inputStream;
+    this.consumeInputLine = consumeInputLine;
+  }
+
+  public void run() {
+    new BufferedReader(new 
InputStreamReader(inputStream)).lines().forEach(consumeInputLine);
+  }
+
+  public void startInNewThread() {
+    new Thread(this).start();
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/f63b9d14/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/CommandStringBuilder.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/CommandStringBuilder.java
 
b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/CommandStringBuilder.java
index cfb8a24..1f52239 100644
--- 
a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/CommandStringBuilder.java
+++ 
b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/CommandStringBuilder.java
@@ -14,7 +14,10 @@
  */
 package org.apache.geode.management.internal.cli.util;
 
+import java.io.File;
+
 import org.apache.commons.lang.StringUtils;
+
 import org.apache.geode.internal.lang.SystemUtils;
 import org.apache.geode.management.internal.cli.GfshParser;
 
@@ -22,15 +25,15 @@ import org.apache.geode.management.internal.cli.GfshParser;
 /**
  * - Helper class to build command strings, used in the Dunits for testing 
gfsh commands
  * 
- * 
  * @since GemFire 7.0
  */
 public class CommandStringBuilder {
-  private final String OPTION_MARKER = GfshParser.LONG_OPTION_SPECIFIER;
-  private final String EQUAL_TO = GfshParser.OPTION_VALUE_SPECIFIER;
-  private final String ARG_SEPARATOR = GfshParser.OPTION_SEPARATOR;
-  private final String OPTION_SEPARATOR = GfshParser.OPTION_SEPARATOR;
-  private final String SINGLE_SPACE = " ";
+  private static final String OPTION_MARKER = GfshParser.LONG_OPTION_SPECIFIER;
+  private static final String EQUAL_TO = GfshParser.OPTION_VALUE_SPECIFIER;
+  private static final String ARG_SEPARATOR = GfshParser.OPTION_SEPARATOR;
+  private static final String OPTION_SEPARATOR = GfshParser.OPTION_SEPARATOR;
+  private static final String SINGLE_SPACE = " ";
+  private static final String SINGLE_QUOTE = "\"";
 
   private final StringBuffer buffer;
   private volatile boolean hasOptions;
@@ -58,6 +61,10 @@ public class CommandStringBuilder {
     return this;
   }
 
+  public CommandStringBuilder addOption(String option, File value) {
+    return addOption(option, quoteArgument(value.getAbsolutePath()));
+  }
+
   public CommandStringBuilder addOptionWithValueCheck(String option, String 
value) {
     if (StringUtils.isNotBlank(value)) {
       return addOption(option, value);
@@ -87,4 +94,16 @@ public class CommandStringBuilder {
   public String toString() {
     return getCommandString();
   }
+
+  private String quoteArgument(String argument) {
+    if (!argument.startsWith(SINGLE_QUOTE)) {
+      argument = SINGLE_QUOTE + argument;
+    }
+
+    if (!argument.endsWith(SINGLE_QUOTE)) {
+      argument = argument + SINGLE_QUOTE;
+    }
+
+    return argument;
+  }
 }

Reply via email to