mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Add ExitStatus to Command.run()
The main difference for now is the removal of the --nohup (slightly hacky) option in TestCommand. Now a TestCommand can signal to its caller that it wants to be hung up. Fixes gh-975
This commit is contained in:
parent
5e3cc95ccf
commit
3c6dfb72c5
@ -62,6 +62,85 @@ public interface Command {
|
||||
* @param args command arguments (this will not include the command itself)
|
||||
* @throws Exception
|
||||
*/
|
||||
void run(String... args) throws Exception;
|
||||
ExitStatus run(String... args) throws Exception;
|
||||
|
||||
/**
|
||||
* Encapsulation of the outcome of a command.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @see ExitStatus#OK
|
||||
* @see ExitStatus#ERROR
|
||||
*
|
||||
*/
|
||||
public static class ExitStatus {
|
||||
/**
|
||||
* Generic "OK" exit status with zero exit code and hangup=fa;se
|
||||
*/
|
||||
public static ExitStatus OK = new ExitStatus(0, "OK");
|
||||
/**
|
||||
* Generic "not OK" exit status with non-zero exit code and hangup=true
|
||||
*/
|
||||
public static ExitStatus ERROR = new ExitStatus(-1, "ERROR", true);
|
||||
private int code;
|
||||
private String name;
|
||||
private boolean hangup;
|
||||
|
||||
/**
|
||||
* Create a new ExitStatus with an exit code and name as specified.
|
||||
*
|
||||
* @param code the exit code
|
||||
* @param name the name
|
||||
*/
|
||||
public ExitStatus(int code, String name) {
|
||||
this(code, name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param code the exit code
|
||||
* @param name the name
|
||||
* @param hangup true if it is OK for the caller to hangup
|
||||
*/
|
||||
public ExitStatus(int code, String name, boolean hangup) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.hangup = hangup;
|
||||
}
|
||||
|
||||
/**
|
||||
* An exit code appropriate for use in System.exit()
|
||||
* @return an exit code
|
||||
*/
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* A name describing the outcome
|
||||
* @return a name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to signal that the caller can (or should) hangup. A server process with
|
||||
* non-daemon threads should set this to false.
|
||||
* @return the flag
|
||||
*/
|
||||
public boolean isHangup() {
|
||||
return hangup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the existing code to a hangup.
|
||||
*
|
||||
* @return a new ExitStatus with hangup=true
|
||||
*/
|
||||
public ExitStatus hangup() {
|
||||
return new ExitStatus(this.code, this.name, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.cli.command.Command.ExitStatus;
|
||||
import org.springframework.boot.cli.util.Log;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@ -166,8 +167,9 @@ public class CommandRunner implements Iterable<Command> {
|
||||
System.setProperty("debug", "true");
|
||||
}
|
||||
try {
|
||||
run(argsWithoutDebugFlags);
|
||||
return 0;
|
||||
ExitStatus result = run(argsWithoutDebugFlags);
|
||||
// The caller will hang up if it gets a non-zero status
|
||||
return result==null ? 0 : result.isHangup() ? (result.getCode()>0 ? result.getCode() : 1) : 0;
|
||||
}
|
||||
catch (NoArgumentsException ex) {
|
||||
showUsage();
|
||||
@ -197,7 +199,7 @@ public class CommandRunner implements Iterable<Command> {
|
||||
* @param args the arguments
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void run(String... args) throws Exception {
|
||||
protected ExitStatus run(String... args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
throw new NoArgumentsException();
|
||||
}
|
||||
@ -209,7 +211,7 @@ public class CommandRunner implements Iterable<Command> {
|
||||
}
|
||||
beforeRun(command);
|
||||
try {
|
||||
command.run(commandArguments);
|
||||
return command.run(commandArguments);
|
||||
}
|
||||
finally {
|
||||
afterRun(command);
|
||||
|
@ -48,8 +48,8 @@ public abstract class OptionParsingCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run(String... args) throws Exception {
|
||||
this.handler.run(args);
|
||||
public final ExitStatus run(String... args) throws Exception {
|
||||
return this.handler.run(args);
|
||||
}
|
||||
|
||||
protected OptionHandler getHandler() {
|
||||
|
@ -85,7 +85,7 @@ public class HelpCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
public ExitStatus run(String... args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
throw new NoHelpCommandArgumentsException();
|
||||
}
|
||||
@ -103,7 +103,7 @@ public class HelpCommand extends AbstractCommand {
|
||||
if (command.getHelp() != null) {
|
||||
Log.info(command.getHelp());
|
||||
}
|
||||
return;
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
}
|
||||
throw new NoSuchCommandException(commandName);
|
||||
|
@ -42,7 +42,7 @@ public class HintCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
public ExitStatus run(String... args) throws Exception {
|
||||
try {
|
||||
int index = (args.length == 0 ? 0 : Integer.valueOf(args[0]) - 1);
|
||||
List<String> arguments = new ArrayList<String>(args.length);
|
||||
@ -64,7 +64,9 @@ public class HintCommand extends AbstractCommand {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Swallow and provide no hints
|
||||
return ExitStatus.ERROR;
|
||||
}
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
private void showCommandHints(String starting) {
|
||||
|
@ -32,8 +32,9 @@ public class VersionCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
public ExitStatus run(String... args) {
|
||||
Log.info("Spring CLI v" + getClass().getPackage().getImplementationVersion());
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public class GrabCommand extends OptionParsingCommand {
|
||||
private static final class GrabOptionHandler extends CompilerOptionHandler {
|
||||
|
||||
@Override
|
||||
protected void run(OptionSet options) throws Exception {
|
||||
protected ExitStatus run(OptionSet options) throws Exception {
|
||||
SourceOptions sourceOptions = new SourceOptions(options);
|
||||
|
||||
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory
|
||||
@ -60,6 +60,7 @@ public class GrabCommand extends OptionParsingCommand {
|
||||
|
||||
GroovyCompiler groovyCompiler = new GroovyCompiler(configuration);
|
||||
groovyCompiler.compile(sourceOptions.getSourcesArray());
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ public class JarCommand extends OptionParsingCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(OptionSet options) throws Exception {
|
||||
protected ExitStatus run(OptionSet options) throws Exception {
|
||||
List<?> nonOptionArguments = new ArrayList<Object>(
|
||||
options.nonOptionArguments());
|
||||
Assert.isTrue(nonOptionArguments.size() >= 2,
|
||||
@ -127,6 +127,7 @@ public class JarCommand extends OptionParsingCommand {
|
||||
dependencies.removeAll(classpath);
|
||||
|
||||
writeJar(output, compiledClasses, classpathEntries, dependencies);
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
private void deleteIfExists(File file) {
|
||||
|
@ -38,6 +38,7 @@ import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.OptionSpecBuilder;
|
||||
|
||||
import org.springframework.boot.cli.command.Command.ExitStatus;
|
||||
import org.springframework.boot.cli.command.OptionParsingCommand;
|
||||
|
||||
/**
|
||||
@ -80,7 +81,7 @@ public class OptionHandler {
|
||||
this.closure = closure;
|
||||
}
|
||||
|
||||
public final void run(String... args) throws Exception {
|
||||
public final ExitStatus run(String... args) throws Exception {
|
||||
String[] argsToUse = args.clone();
|
||||
for (int i = 0; i < argsToUse.length; i++) {
|
||||
if ("-cp".equals(argsToUse[i])) {
|
||||
@ -88,18 +89,29 @@ public class OptionHandler {
|
||||
}
|
||||
}
|
||||
OptionSet options = getParser().parse(args);
|
||||
run(options);
|
||||
return run(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the command using the specified parsed {@link OptionSet}.
|
||||
* @param options the parsed option set
|
||||
* @return an ExitStatus
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void run(OptionSet options) throws Exception {
|
||||
protected ExitStatus run(OptionSet options) throws Exception {
|
||||
if (this.closure != null) {
|
||||
this.closure.call(options);
|
||||
Object result = this.closure.call(options);
|
||||
if (result instanceof ExitStatus) {
|
||||
return (ExitStatus) result;
|
||||
}
|
||||
if (result instanceof Boolean) {
|
||||
return (Boolean) result ? ExitStatus.OK : ExitStatus.ERROR;
|
||||
}
|
||||
if (result instanceof Integer) {
|
||||
return new ExitStatus((Integer) result, "Finished");
|
||||
}
|
||||
}
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
public String getHelp() {
|
||||
|
@ -85,7 +85,7 @@ public class RunCommand extends OptionParsingCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void run(OptionSet options) throws Exception {
|
||||
protected synchronized ExitStatus run(OptionSet options) throws Exception {
|
||||
|
||||
if (this.runner != null) {
|
||||
throw new RuntimeException(
|
||||
@ -105,6 +105,8 @@ public class RunCommand extends OptionParsingCommand {
|
||||
this.runner = new SpringApplicationRunner(configuration,
|
||||
sourceOptions.getSourcesArray(), sourceOptions.getArgsArray());
|
||||
this.runner.compileAndRun();
|
||||
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,9 +36,10 @@ class ClearCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
public ExitStatus run(String... args) throws Exception {
|
||||
this.consoleReader.setPrompt("");
|
||||
this.consoleReader.clearScreen();
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class ExitCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
public ExitStatus run(String... args) throws Exception {
|
||||
throw new ShellExitException();
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ class ForkProcessCommand extends RunProcessCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
public ExitStatus run(String... args) throws Exception {
|
||||
List<String> fullArgs = new ArrayList<String>();
|
||||
fullArgs.add("-cp");
|
||||
fullArgs.add(System.getProperty("java.class.path"));
|
||||
@ -75,6 +75,7 @@ class ForkProcessCommand extends RunProcessCommand {
|
||||
fullArgs.add(this.command.getName());
|
||||
fullArgs.addAll(Arrays.asList(args));
|
||||
run(fullArgs);
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ public class PromptCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... strings) throws Exception {
|
||||
public ExitStatus run(String... strings) throws Exception {
|
||||
if (strings.length > 0) {
|
||||
for (String string : strings) {
|
||||
this.prompts.pushPrompt(string + " ");
|
||||
@ -44,6 +44,7 @@ public class PromptCommand extends AbstractCommand {
|
||||
else {
|
||||
this.prompts.popPrompt();
|
||||
}
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,13 +41,19 @@ class RunProcessCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
run(Arrays.asList(args));
|
||||
public ExitStatus run(String... args) throws Exception {
|
||||
return run(Arrays.asList(args));
|
||||
}
|
||||
|
||||
protected void run(Collection<String> args) throws IOException {
|
||||
protected ExitStatus run(Collection<String> args) throws IOException {
|
||||
this.process = new RunProcess(this.command);
|
||||
this.process.run(args.toArray(new String[args.size()]));
|
||||
int code = this.process.run(args.toArray(new String[args.size()]));
|
||||
if (code == 0) {
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
else {
|
||||
return new ExitStatus(code, "EXTERNAL_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean handleSigInt() {
|
||||
|
@ -32,8 +32,9 @@ public class ShellCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
public ExitStatus run(String... args) throws Exception {
|
||||
new Shell().run();
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package org.springframework.boot.cli.command.test;
|
||||
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import org.springframework.boot.cli.command.Command;
|
||||
import org.springframework.boot.cli.command.OptionParsingCommand;
|
||||
@ -47,22 +46,14 @@ public class TestCommand extends OptionParsingCommand {
|
||||
private TestRunner runner;
|
||||
|
||||
@Override
|
||||
protected void doOptions() {
|
||||
option("nohup",
|
||||
"Flag to indicate that the JVM should not exit when tests are finished");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(OptionSet options) throws Exception {
|
||||
protected ExitStatus run(OptionSet options) throws Exception {
|
||||
SourceOptions sourceOptions = new SourceOptions(options);
|
||||
TestRunnerConfiguration configuration = new TestRunnerConfigurationAdapter(
|
||||
options, this);
|
||||
this.runner = new TestRunner(configuration, sourceOptions.getSourcesArray(),
|
||||
sourceOptions.getArgsArray());
|
||||
this.runner.compileAndRunTests();
|
||||
if (!options.has("nohup")) {
|
||||
System.exit(0); // TODO: non-zero if test fails?
|
||||
}
|
||||
return ExitStatus.OK.hangup();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,5 +68,7 @@ public class TestCommand extends OptionParsingCommand {
|
||||
super(options, optionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ public class SpringBootCompilerAutoConfiguration extends CompilerAutoConfigurati
|
||||
"org.springframework.boot.context.properties.ConfigurationProperties",
|
||||
"org.springframework.boot.context.properties.EnableConfigurationProperties",
|
||||
"org.springframework.boot.autoconfigure.EnableAutoConfiguration",
|
||||
"org.springframework.boot.context.properties.ConfigurationProperties",
|
||||
"org.springframework.boot.context.properties.EnableConfigurationProperties",
|
||||
"org.springframework.boot.groovy.GrabMetadata");
|
||||
imports.addStarImports("org.springframework.stereotype",
|
||||
"org.springframework.scheduling.annotation");
|
||||
|
@ -28,8 +28,9 @@ public class CustomCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
public ExitStatus run(String... args) throws Exception {
|
||||
System.err.println("Custom Command Hello");
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -75,10 +75,7 @@ public class CliTester implements TestRule {
|
||||
}
|
||||
|
||||
public String test(String... args) throws Exception {
|
||||
String[] argsToUse = new String[args.length + 1];
|
||||
System.arraycopy(args, 0, argsToUse, 1, args.length);
|
||||
argsToUse[0] = "--nohup";
|
||||
Future<TestCommand> future = submitCommand(new TestCommand(), argsToUse);
|
||||
Future<TestCommand> future = submitCommand(new TestCommand(), args);
|
||||
this.commands.add(future.get(this.timeout, TimeUnit.MILLISECONDS));
|
||||
return getOutput();
|
||||
}
|
||||
|
@ -48,11 +48,11 @@ public class RunProcess {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public void run(String... args) throws IOException {
|
||||
run(Arrays.asList(args));
|
||||
public int run(String... args) throws IOException {
|
||||
return run(Arrays.asList(args));
|
||||
}
|
||||
|
||||
protected void run(Collection<String> args) throws IOException {
|
||||
protected int run(Collection<String> args) throws IOException {
|
||||
ProcessBuilder builder = new ProcessBuilder(this.command);
|
||||
builder.command().addAll(args);
|
||||
builder.redirectErrorStream(true);
|
||||
@ -74,6 +74,12 @@ public class RunProcess {
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
try {
|
||||
return this.process.exitValue();
|
||||
}
|
||||
catch (IllegalThreadStateException e) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.endTime = System.currentTimeMillis();
|
||||
|
Loading…
Reference in New Issue
Block a user