Shell Class — spring-boot Architecture
Architecture documentation for the Shell class in Shell.java from the spring-boot codebase.
Entity Profile
Relationship Graph
Source Code
cli/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/shell/Shell.java lines 51–233
public class Shell {
private static final Set<Class<?>> NON_FORKED_COMMANDS;
static {
Set<Class<?>> nonForked = new HashSet<>();
nonForked.add(VersionCommand.class);
NON_FORKED_COMMANDS = Collections.unmodifiableSet(nonForked);
}
private final ShellCommandRunner commandRunner;
private final ConsoleReader consoleReader;
private final EscapeAwareWhiteSpaceArgumentDelimiter argumentDelimiter = new EscapeAwareWhiteSpaceArgumentDelimiter();
private final ShellPrompts prompts = new ShellPrompts();
/**
* Create a new {@link Shell} instance.
* @throws IOException in case of I/O errors
*/
Shell() throws IOException {
attachSignalHandler();
this.consoleReader = new ConsoleReader();
this.commandRunner = createCommandRunner();
initializeConsoleReader();
}
private ShellCommandRunner createCommandRunner() {
ShellCommandRunner runner = new ShellCommandRunner();
runner.addCommand(new HelpCommand(runner));
runner.addCommands(getCommands());
runner.addAliases("exit", "quit");
runner.addAliases("help", "?");
runner.addAliases("clear", "cls");
return runner;
}
private Iterable<Command> getCommands() {
List<Command> commands = new ArrayList<>();
ServiceLoader<CommandFactory> factories = ServiceLoader.load(CommandFactory.class, getClass().getClassLoader());
for (CommandFactory factory : factories) {
for (Command command : factory.getCommands()) {
commands.add(convertToForkCommand(command));
}
}
commands.add(new PromptCommand(this.prompts));
commands.add(new ClearCommand(this.consoleReader));
commands.add(new ExitCommand());
return commands;
}
private Command convertToForkCommand(Command command) {
for (Class<?> nonForked : NON_FORKED_COMMANDS) {
if (nonForked.isInstance(command)) {
return command;
}
}
return new ForkProcessCommand(command);
}
private void initializeConsoleReader() {
this.consoleReader.setHistoryEnabled(true);
this.consoleReader.setBellEnabled(false);
this.consoleReader.setExpandEvents(false);
this.consoleReader
.addCompleter(new CommandCompleter(this.consoleReader, this.argumentDelimiter, this.commandRunner));
this.consoleReader.setCompletionHandler(new CandidateListCompletionHandler());
}
private void attachSignalHandler() {
SignalUtils.attachSignalHandler(this::handleSigInt);
}
/**
* Run the shell until the user exists.
* @throws Exception on error
*/
public void run() throws Exception {
printBanner();
try {
runInputLoop();
}
catch (Exception ex) {
if (!(ex instanceof ShellExitException)) {
throw ex;
}
}
}
private void printBanner() {
String version = getClass().getPackage().getImplementationVersion();
version = (version != null) ? " (v" + version + ")" : "";
System.out.println(ansi("Spring Boot", Code.BOLD).append(version, Code.FAINT));
System.out.println(ansi("Hit TAB to complete. Type 'help' and hit RETURN for help, and 'exit' to quit."));
}
private void runInputLoop() throws Exception {
String line;
while ((line = this.consoleReader.readLine(getPrompt())) != null) {
while (line.endsWith("\\")) {
line = line.substring(0, line.length() - 1);
line += this.consoleReader.readLine("> ");
}
if (StringUtils.hasLength(line)) {
String[] args = this.argumentDelimiter.parseArguments(line);
this.commandRunner.runAndHandleErrors(args);
}
}
}
private String getPrompt() {
String prompt = this.prompts.getPrompt();
return ansi(prompt, Code.FG_BLUE).toString();
}
private AnsiString ansi(String text, Code... codes) {
return new AnsiString(this.consoleReader.getTerminal()).append(text, codes);
}
/**
* Final handle an interrupt signal (CTRL-C).
*/
protected void handleSigInt() {
if (this.commandRunner.handleSigInt()) {
return;
}
System.out.println(String.format("%nThanks for using Spring Boot"));
System.exit(1);
}
/**
* Extension of {@link CommandRunner} to deal with {@link RunProcessCommand}s and
* aliases.
*/
private static class ShellCommandRunner extends CommandRunner {
private volatile @Nullable Command lastCommand;
private final Map<String, String> aliases = new HashMap<>();
ShellCommandRunner() {
super(null);
}
void addAliases(String command, String... aliases) {
for (String alias : aliases) {
this.aliases.put(alias, command);
}
}
@Override
public @Nullable Command findCommand(String name) {
if (name.startsWith("!")) {
return new RunProcessCommand(name.substring(1));
}
if (this.aliases.containsKey(name)) {
name = this.aliases.get(name);
}
return super.findCommand(name);
}
@Override
protected void beforeRun(Command command) {
this.lastCommand = command;
}
@Override
protected void afterRun(Command command) {
}
boolean handleSigInt() {
Command command = this.lastCommand;
if (command instanceof RunProcessCommand runProcessCommand) {
return runProcessCommand.handleSigInt();
}
return false;
}
}
}
Domain
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free