CommandRunner Class — spring-boot Architecture
Architecture documentation for the CommandRunner class in CommandRunner.java from the spring-boot codebase.
Entity Profile
Relationship Graph
Source Code
cli/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/CommandRunner.java lines 42–302
public class CommandRunner implements Iterable<Command> {
private static final Set<CommandException.Option> NO_EXCEPTION_OPTIONS = EnumSet
.noneOf(CommandException.Option.class);
private final String name;
private final List<Command> commands = new ArrayList<>();
private Class<?>[] optionCommandClasses = {};
private Class<?>[] hiddenCommandClasses = {};
/**
* Create a new {@link CommandRunner} instance.
* @param name the name of the runner or {@code null}
*/
public CommandRunner(@Nullable String name) {
this.name = StringUtils.hasLength(name) ? name + " " : "";
}
/**
* Return the name of the runner or an empty string. Non-empty names will include a
* trailing space character so that they can be used as a prefix.
* @return the name of the runner
*/
public String getName() {
return this.name;
}
/**
* Add the specified commands.
* @param commands the commands to add
*/
public void addCommands(Iterable<Command> commands) {
Assert.notNull(commands, "'commands' must not be null");
for (Command command : commands) {
addCommand(command);
}
}
/**
* Add the specified command.
* @param command the command to add.
*/
public void addCommand(Command command) {
Assert.notNull(command, "'command' must not be null");
this.commands.add(command);
}
/**
* Set the command classes which should be considered option commands. An option
* command is a special type of command that usually makes more sense to present as if
* it is an option. For example '--version'.
* @param commandClasses the classes of option commands.
* @see #isOptionCommand(Command)
*/
public void setOptionCommands(Class<?>... commandClasses) {
Assert.notNull(commandClasses, "'commandClasses' must not be null");
this.optionCommandClasses = commandClasses;
}
/**
* Set the command classes which should be hidden (i.e. executed but not displayed in
* the available commands list).
* @param commandClasses the classes of hidden commands
*/
public void setHiddenCommands(Class<?>... commandClasses) {
Assert.notNull(commandClasses, "'commandClasses' must not be null");
this.hiddenCommandClasses = commandClasses;
}
/**
* Returns if the specified command is an option command.
* @param command the command to test
* @return {@code true} if the command is an option command
* @see #setOptionCommands(Class...)
*/
public boolean isOptionCommand(Command command) {
return isCommandInstanceOf(command, this.optionCommandClasses);
}
private boolean isHiddenCommand(Command command) {
return isCommandInstanceOf(command, this.hiddenCommandClasses);
}
private boolean isCommandInstanceOf(Command command, Class<?>[] commandClasses) {
for (Class<?> commandClass : commandClasses) {
if (commandClass.isInstance(command)) {
return true;
}
}
return false;
}
@Override
public Iterator<Command> iterator() {
return getCommands().iterator();
}
protected final List<Command> getCommands() {
return Collections.unmodifiableList(this.commands);
}
/**
* Find a command by name.
* @param name the name of the command
* @return the command or {@code null} if not found
*/
public @Nullable Command findCommand(String name) {
for (Command candidate : this.commands) {
String candidateName = candidate.getName();
if (candidateName.equals(name) || (isOptionCommand(candidate) && ("--" + candidateName).equals(name))) {
return candidate;
}
}
return null;
}
/**
* Run the appropriate and handle and errors.
* @param args the input arguments
* @return a return status code (non boot is used to indicate an error)
*/
public int runAndHandleErrors(String... args) {
String[] argsWithoutDebugFlags = removeDebugFlags(args);
boolean debug = argsWithoutDebugFlags.length != args.length;
if (debug) {
System.setProperty("debug", "true");
}
try {
ExitStatus result = run(argsWithoutDebugFlags);
// The caller will hang up if it gets a non-zero status
if (result != null && result.isHangup()) {
return (result.getCode() > 0) ? result.getCode() : 0;
}
return 0;
}
catch (NoArgumentsException ex) {
showUsage();
return 1;
}
catch (Exception ex) {
return handleError(debug, ex);
}
}
private String[] removeDebugFlags(String[] args) {
List<String> rtn = new ArrayList<>(args.length);
boolean appArgsDetected = false;
for (String arg : args) {
// Allow apps to have a --debug argument
appArgsDetected |= "--".equals(arg);
if ("--debug".equals(arg) && !appArgsDetected) {
continue;
}
rtn.add(arg);
}
return StringUtils.toStringArray(rtn);
}
/**
* Parse the arguments and run a suitable command.
* @param args the arguments
* @return the outcome of the command
* @throws Exception if the command fails
*/
protected ExitStatus run(String... args) throws Exception {
if (args.length == 0) {
throw new NoArgumentsException();
}
String commandName = args[0];
String[] commandArguments = Arrays.copyOfRange(args, 1, args.length);
Command command = findCommand(commandName);
if (command == null) {
throw new NoSuchCommandException(commandName);
}
beforeRun(command);
try {
return command.run(commandArguments);
}
finally {
afterRun(command);
}
}
/**
* Subclass hook called before a command is run.
* @param command the command about to run
*/
protected void beforeRun(Command command) {
}
/**
* Subclass hook called after a command has run.
* @param command the command that has run
*/
protected void afterRun(Command command) {
}
private int handleError(boolean debug, Exception ex) {
Set<CommandException.Option> options = NO_EXCEPTION_OPTIONS;
if (ex instanceof CommandException commandException) {
options = commandException.getOptions();
if (options.contains(CommandException.Option.RETHROW)) {
throw commandException;
}
}
boolean couldNotShowMessage = false;
if (!options.contains(CommandException.Option.HIDE_MESSAGE)) {
couldNotShowMessage = !errorMessage(ex.getMessage());
}
if (options.contains(CommandException.Option.SHOW_USAGE)) {
showUsage();
}
if (debug || couldNotShowMessage || options.contains(CommandException.Option.STACK_TRACE)) {
printStackTrace(ex);
}
return 1;
}
protected boolean errorMessage(@Nullable String message) {
Log.error((message != null) ? message : "Unexpected error");
return message != null;
}
protected void showUsage() {
Log.infoPrint("usage: " + this.name);
for (Command command : this.commands) {
if (isOptionCommand(command)) {
Log.infoPrint("[--" + command.getName() + "] ");
}
}
Log.info("");
Log.info(" <command> [<args>]");
Log.info("");
Log.info("Available commands are:");
for (Command command : this.commands) {
if (!isOptionCommand(command) && !isHiddenCommand(command)) {
String usageHelp = command.getUsageHelp();
String description = command.getDescription();
Log.info(String.format("%n %1$s %2$-15s%n %3$s", command.getName(),
(usageHelp != null) ? usageHelp : "", (description != null) ? description : ""));
}
}
Log.info("");
Log.info("Common options:");
Log.info(String.format("%n %1$s %2$-15s%n %3$s", "--debug", "Verbose mode",
"Print additional status information for the command you are running"));
Log.info("");
Log.info("");
Log.info("See '" + this.name + "help <command>' for more information on a specific command.");
}
protected void printStackTrace(Exception ex) {
Log.error("");
Log.error(ex);
Log.error("");
}
}
Domain
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free