Add support for multiple non-interactive commmands
Also: * Rename ThemeResolver bean to avoid clash w/ Spring Boot registered ThemeResolver * Move shell runner precedence to public static field to allow extension/access
This commit is contained in:
@@ -27,7 +27,7 @@ public interface ShellRunner {
|
||||
/**
|
||||
* Checks if a particular shell runner can execute.
|
||||
*
|
||||
* @param args the application argumets
|
||||
* @param args the application arguments
|
||||
* @return true if shell runner can execute
|
||||
*/
|
||||
boolean canRun(ApplicationArguments args);
|
||||
|
||||
@@ -15,9 +15,14 @@
|
||||
*/
|
||||
package org.springframework.shell.jline;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jline.reader.ParsedLine;
|
||||
import org.jline.reader.Parser;
|
||||
import org.jline.reader.impl.DefaultParser;
|
||||
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -25,9 +30,9 @@ import org.springframework.shell.Input;
|
||||
import org.springframework.shell.InputProvider;
|
||||
import org.springframework.shell.Shell;
|
||||
import org.springframework.shell.ShellRunner;
|
||||
import org.springframework.shell.Utils;
|
||||
import org.springframework.shell.context.InteractionMode;
|
||||
import org.springframework.shell.context.ShellContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link ShellRunner} that executes commands without entering interactive shell mode.
|
||||
@@ -38,66 +43,106 @@ import org.springframework.util.StringUtils;
|
||||
* @author Janne Valkealahti
|
||||
* @author Chris Bono
|
||||
*/
|
||||
@Order(InteractiveShellRunner.PRECEDENCE - 50)
|
||||
@Order(NonInteractiveShellRunner.PRECEDENCE)
|
||||
public class NonInteractiveShellRunner implements ShellRunner {
|
||||
|
||||
/**
|
||||
* The precedence at which this runner is ordered by the DefaultApplicationRunner - which also controls
|
||||
* the order it is consulted on the ability to handle the current shell.
|
||||
*/
|
||||
public static final int PRECEDENCE = InteractiveShellRunner.PRECEDENCE - 50;
|
||||
|
||||
private final Shell shell;
|
||||
|
||||
private final ShellContext shellContext;
|
||||
|
||||
private Function<ApplicationArguments, List<String>> argsToShellCommand = (args) -> Arrays.asList(args.getSourceArgs());
|
||||
private Parser lineParser;
|
||||
|
||||
private Function<ApplicationArguments, List<String>> commandsFromInputArgs;
|
||||
|
||||
public NonInteractiveShellRunner(Shell shell, ShellContext shellContext) {
|
||||
this.shell = shell;
|
||||
this.shellContext = shellContext;
|
||||
this.lineParser = new DefaultParser();
|
||||
this.commandsFromInputArgs = (args) ->
|
||||
Collections.singletonList(String.join(" ", args.getSourceArgs()));
|
||||
}
|
||||
|
||||
public void setArgsToShellCommand(Function<ApplicationArguments, List<String>> argsToShellCommand) {
|
||||
this.argsToShellCommand = argsToShellCommand;
|
||||
/**
|
||||
* Sets the function that creates the command() to run from the input application arguments.
|
||||
*
|
||||
* @param commandsFromInputArgs function that takes input application arguments and creates zero or more commands
|
||||
* where each command is a string that specifies the command and options
|
||||
* (eg. 'history --file myHistory.txt')
|
||||
*/
|
||||
public void setCommandsFromInputArgs(Function<ApplicationArguments, List<String>> commandsFromInputArgs) {
|
||||
this.commandsFromInputArgs = commandsFromInputArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line parser used to parse commands.
|
||||
*
|
||||
* @param lineParser the line parser used to parse commands
|
||||
*/
|
||||
public void setLineParser(Parser lineParser) {
|
||||
this.lineParser = lineParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRun(ApplicationArguments args) {
|
||||
return !argsToShellCommand.apply(args).isEmpty();
|
||||
return !commandsFromInputArgs.apply(args).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
|
||||
List<String> commands = this.argsToShellCommand.apply(args);
|
||||
InputProvider inputProvider = new StringInputProvider(commands);
|
||||
List<String> commands = this.commandsFromInputArgs.apply(args);
|
||||
List<ParsedLine> parsedLines = commands.stream()
|
||||
.map(rawCommandLine -> lineParser.parse(rawCommandLine, rawCommandLine.length() + 1))
|
||||
.collect(Collectors.toList());
|
||||
MultiParsedLineInputProvider inputProvider = new MultiParsedLineInputProvider(parsedLines);
|
||||
shell.run(inputProvider);
|
||||
}
|
||||
|
||||
private class StringInputProvider implements InputProvider {
|
||||
/**
|
||||
* An {@link InputProvider} that returns an input for each entry in a list of {@link ParsedLine parsed lines}.
|
||||
*/
|
||||
static class MultiParsedLineInputProvider implements InputProvider {
|
||||
|
||||
private final List<String> commands;
|
||||
private final List<ParsedLineInput> parsedLineInputs;
|
||||
private int inputIdx;
|
||||
|
||||
private boolean done;
|
||||
|
||||
StringInputProvider(List<String> commands) {
|
||||
this.commands = commands;
|
||||
MultiParsedLineInputProvider(List<ParsedLine> parsedLines) {
|
||||
this.parsedLineInputs = parsedLines.stream()
|
||||
.map(ParsedLineInput::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Input readInput() {
|
||||
if (!done) {
|
||||
done = true;
|
||||
return new Input() {
|
||||
@Override
|
||||
public List<String> words() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawText() {
|
||||
return StringUtils.collectionToDelimitedString(commands, " ");
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
if (inputIdx == parsedLineInputs.size()) {
|
||||
return null;
|
||||
}
|
||||
return parsedLineInputs.get(inputIdx++);
|
||||
}
|
||||
|
||||
private static class ParsedLineInput implements Input {
|
||||
|
||||
private final ParsedLine parsedLine;
|
||||
|
||||
ParsedLineInput(ParsedLine parsedLine) {
|
||||
this.parsedLine = parsedLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawText() {
|
||||
return parsedLine.line();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> words() {
|
||||
return Utils.sanitizeInput(parsedLine.words());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,10 +40,16 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
//tag::documentation[]
|
||||
@Order(InteractiveShellRunner.PRECEDENCE - 100)
|
||||
@Order(ScriptShellRunner.PRECEDENCE)
|
||||
public class ScriptShellRunner implements ShellRunner {
|
||||
//end::documentation[]
|
||||
|
||||
/**
|
||||
* The precedence at which this runner is ordered by the DefaultApplicationRunner - which also controls
|
||||
* the order it is consulted on the ability to handle the current shell.
|
||||
*/
|
||||
public static final int PRECEDENCE = InteractiveShellRunner.PRECEDENCE - 100;
|
||||
|
||||
private final Parser parser;
|
||||
|
||||
private final Shell shell;
|
||||
|
||||
Reference in New Issue
Block a user