mirror of
https://github.com/google/nomulus.git
synced 2025-05-13 16:07:15 +02:00
Do output encapsulation in a try/with
Move the shell output encapsulation so that we don't double-wrap on a premature exit. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=219136896
This commit is contained in:
parent
c375b0d5f4
commit
a76300f76c
2 changed files with 115 additions and 68 deletions
|
@ -145,6 +145,80 @@ public class ShellCommand implements Command {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class OutputEncapsulator {
|
||||||
|
private PrintStream orgStdout;
|
||||||
|
private PrintStream orgStderr;
|
||||||
|
|
||||||
|
private EncapsulatingOutputStream encapsulatedOutputStream = null;
|
||||||
|
private EncapsulatingOutputStream encapsulatedErrorStream = null;
|
||||||
|
|
||||||
|
private Exception error;
|
||||||
|
|
||||||
|
private OutputEncapsulator() {
|
||||||
|
orgStdout = System.out;
|
||||||
|
orgStderr = System.err;
|
||||||
|
encapsulatedOutputStream = new EncapsulatingOutputStream(System.out, "out: ");
|
||||||
|
encapsulatedErrorStream = new EncapsulatingOutputStream(System.out, "err: ");
|
||||||
|
System.setOut(new PrintStream(encapsulatedOutputStream));
|
||||||
|
System.setErr(new PrintStream(encapsulatedErrorStream));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setError(Exception e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restoreOriginalStreams() {
|
||||||
|
try {
|
||||||
|
encapsulatedOutputStream.dumpLastLine();
|
||||||
|
encapsulatedErrorStream.dumpLastLine();
|
||||||
|
System.setOut(orgStdout);
|
||||||
|
System.setErr(orgStderr);
|
||||||
|
if (error != null) {
|
||||||
|
emitFailure(error);
|
||||||
|
} else {
|
||||||
|
emitSuccess();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a success command separator.
|
||||||
|
*
|
||||||
|
* <p>Dumps the last line of output prior to doing this.
|
||||||
|
*/
|
||||||
|
private void emitSuccess() {
|
||||||
|
System.out.println(SUCCESS);
|
||||||
|
System.out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a failure message obtained from the throwable.
|
||||||
|
*
|
||||||
|
* <p>Dumps the last line of output prior to doing this.
|
||||||
|
*/
|
||||||
|
private void emitFailure(Throwable e) {
|
||||||
|
System.out.println(
|
||||||
|
FAILURE
|
||||||
|
+ e.getClass().getName()
|
||||||
|
+ " "
|
||||||
|
+ e.getMessage().replace("\\", "\\\\").replace("\n", "\\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Run "func" with output encapsulation. */
|
||||||
|
static void run(CommandRunner runner, String[] args) {
|
||||||
|
OutputEncapsulator encapsulator = new OutputEncapsulator();
|
||||||
|
try {
|
||||||
|
runner.run(args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
encapsulator.setError(e);
|
||||||
|
} finally {
|
||||||
|
encapsulator.restoreOriginalStreams();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Run the shell until the user presses "Ctrl-D". */
|
/** Run the shell until the user presses "Ctrl-D". */
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -154,23 +228,6 @@ public class ShellCommand implements Command {
|
||||||
String line;
|
String line;
|
||||||
DateTime lastTime = clock.nowUtc();
|
DateTime lastTime = clock.nowUtc();
|
||||||
while ((line = getLine()) != null) {
|
while ((line = getLine()) != null) {
|
||||||
PrintStream orgStdout = null;
|
|
||||||
PrintStream orgStderr = null;
|
|
||||||
EncapsulatingOutputStream encapsulatedOutputStream = null;
|
|
||||||
EncapsulatingOutputStream encapsulatedErrorStream = null;
|
|
||||||
|
|
||||||
|
|
||||||
// Wrap standard output and error if requested. We have to do so here in run because the flags
|
|
||||||
// haven't been processed in the constructor.
|
|
||||||
if (encapsulateOutput) {
|
|
||||||
orgStdout = System.out;
|
|
||||||
orgStderr = System.err;
|
|
||||||
encapsulatedOutputStream = new EncapsulatingOutputStream(System.out, "out: ");
|
|
||||||
encapsulatedErrorStream = new EncapsulatingOutputStream(System.out, "err: ");
|
|
||||||
System.setOut(new PrintStream(encapsulatedOutputStream));
|
|
||||||
System.setErr(new PrintStream(encapsulatedErrorStream));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we're not idle for too long. Only relevant when we're "extra careful"
|
// Make sure we're not idle for too long. Only relevant when we're "extra careful"
|
||||||
if (!dontExitOnIdle
|
if (!dontExitOnIdle
|
||||||
&& beExtraCareful
|
&& beExtraCareful
|
||||||
|
@ -184,27 +241,17 @@ public class ShellCommand implements Command {
|
||||||
if (lineArgs.length == 0) {
|
if (lineArgs.length == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Exception lastError = null;
|
|
||||||
try {
|
// Wrap standard output and error if requested. We have to do so here in run because the flags
|
||||||
runner.run(lineArgs);
|
// haven't been processed in the constructor.
|
||||||
} catch (Exception e) {
|
if (encapsulateOutput) {
|
||||||
lastError = e;
|
OutputEncapsulator.run(runner, lineArgs);
|
||||||
System.err.println("Got an exception:\n" + e);
|
} else {
|
||||||
}
|
try {
|
||||||
try {
|
runner.run(lineArgs);
|
||||||
if (encapsulatedOutputStream != null) {
|
} catch (Exception e) {
|
||||||
encapsulatedOutputStream.dumpLastLine();
|
System.err.println("Got an exception:\n" + e);
|
||||||
encapsulatedErrorStream.dumpLastLine();
|
|
||||||
System.setOut(orgStdout);
|
|
||||||
System.setErr(orgStderr);
|
|
||||||
if (lastError == null) {
|
|
||||||
emitSuccess();
|
|
||||||
} else {
|
|
||||||
emitFailure(lastError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!encapsulateOutput) {
|
if (!encapsulateOutput) {
|
||||||
|
@ -243,29 +290,6 @@ public class ShellCommand implements Command {
|
||||||
return resultBuilder.build().toArray(new String[0]);
|
return resultBuilder.build().toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit a success command separator.
|
|
||||||
*
|
|
||||||
* <p>Dumps the last line of output prior to doing this.
|
|
||||||
*/
|
|
||||||
private void emitSuccess() {
|
|
||||||
System.out.println(SUCCESS);
|
|
||||||
System.out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit a failure message obtained from the throwable.
|
|
||||||
*
|
|
||||||
* <p>Dumps the last line of output prior to doing this.
|
|
||||||
*/
|
|
||||||
private void emitFailure(Throwable e) {
|
|
||||||
System.out.println(
|
|
||||||
FAILURE
|
|
||||||
+ e.getClass().getName()
|
|
||||||
+ " "
|
|
||||||
+ e.getMessage().replace("\\", "\\\\").replace("\n", "\\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static class JCommanderCompletor implements Completor {
|
static class JCommanderCompletor implements Completor {
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,9 @@ public class ShellCommandTest {
|
||||||
PrintStream orgStdout;
|
PrintStream orgStdout;
|
||||||
PrintStream orgStderr;
|
PrintStream orgStderr;
|
||||||
|
|
||||||
|
ByteArrayOutputStream stdout;
|
||||||
|
ByteArrayOutputStream stderr;
|
||||||
|
|
||||||
public ShellCommandTest() {}
|
public ShellCommandTest() {}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -269,14 +272,7 @@ public class ShellCommandTest {
|
||||||
@Test
|
@Test
|
||||||
public void testEncapsulatedOutput_command() throws Exception {
|
public void testEncapsulatedOutput_command() throws Exception {
|
||||||
RegistryToolEnvironment.ALPHA.setup();
|
RegistryToolEnvironment.ALPHA.setup();
|
||||||
|
captureOutput();
|
||||||
// capture output (have to do this before the shell command is created)
|
|
||||||
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
|
|
||||||
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
|
|
||||||
System.setOut(new PrintStream(stdout));
|
|
||||||
System.setErr(new PrintStream(stderr));
|
|
||||||
System.setIn(new ByteArrayInputStream("command1\n".getBytes(UTF_8)));
|
|
||||||
|
|
||||||
ShellCommand shellCommand =
|
ShellCommand shellCommand =
|
||||||
new ShellCommand(
|
new ShellCommand(
|
||||||
args -> {
|
args -> {
|
||||||
|
@ -296,6 +292,33 @@ public class ShellCommandTest {
|
||||||
+ "SUCCESS\n");
|
+ "SUCCESS\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncapsulatedOutput_noCommand() throws Exception {
|
||||||
|
captureOutput();
|
||||||
|
ShellCommand shellCommand =
|
||||||
|
createShellCommand(
|
||||||
|
args -> {
|
||||||
|
System.out.println("first line");
|
||||||
|
},
|
||||||
|
Duration.ZERO,
|
||||||
|
"",
|
||||||
|
"do something");
|
||||||
|
shellCommand.encapsulateOutput = true;
|
||||||
|
shellCommand.run();
|
||||||
|
assertThat(stderr.toString()).isEmpty();
|
||||||
|
assertThat(stdout.toString())
|
||||||
|
.isEqualTo("out: first line\nSUCCESS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void captureOutput() {
|
||||||
|
// capture output (have to do this before the shell command is created)
|
||||||
|
stdout = new ByteArrayOutputStream();
|
||||||
|
stderr = new ByteArrayOutputStream();
|
||||||
|
System.setOut(new PrintStream(stdout));
|
||||||
|
System.setErr(new PrintStream(stderr));
|
||||||
|
System.setIn(new ByteArrayInputStream("command1\n".getBytes(UTF_8)));
|
||||||
|
}
|
||||||
|
|
||||||
@Parameters(commandDescription = "Test command")
|
@Parameters(commandDescription = "Test command")
|
||||||
static class TestCommand implements Command {
|
static class TestCommand implements Command {
|
||||||
enum OrgType {
|
enum OrgType {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue