mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Add ShellCommand and friends
This commit is contained in:
parent
c94fb7fc53
commit
cccb5d4610
@ -14,6 +14,7 @@
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
<start-class>org.springframework.boot.cli.SpringCli</start-class>
|
||||
<spring.profiles.active>default</spring.profiles.active>
|
||||
<jline.version>2.11</jline.version>
|
||||
</properties>
|
||||
<profiles>
|
||||
<profile>
|
||||
@ -49,6 +50,11 @@
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- Compile -->
|
||||
<dependency>
|
||||
<groupId>jline</groupId>
|
||||
<artifactId>jline</artifactId>
|
||||
<version>${jline.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.jopt-simple</groupId>
|
||||
<artifactId>jopt-simple</artifactId>
|
||||
|
@ -123,11 +123,12 @@ public class SpringCli {
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void run(String... args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
throw new NoArgumentsException();
|
||||
String commandName = "shell";
|
||||
if (args.length > 0) {
|
||||
commandName = args[0];
|
||||
}
|
||||
String commandName = args[0];
|
||||
String[] commandArguments = Arrays.copyOfRange(args, 1, args.length);
|
||||
String[] commandArguments = args.length > 1 ? Arrays.copyOfRange(args, 1,
|
||||
args.length) : new String[0];
|
||||
Command command = find(commandName);
|
||||
if (command == null) {
|
||||
throw new NoSuchCommandException(commandName);
|
||||
|
@ -0,0 +1,121 @@
|
||||
package org.springframework.boot.cli.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import jline.console.ConsoleReader;
|
||||
import jline.console.completer.ArgumentCompleter;
|
||||
import jline.console.completer.Completer;
|
||||
import jline.console.completer.NullCompleter;
|
||||
import jline.console.completer.StringsCompleter;
|
||||
|
||||
import org.springframework.boot.cli.Command;
|
||||
import org.springframework.boot.cli.CommandFactory;
|
||||
import org.springframework.boot.cli.Log;
|
||||
import org.springframework.boot.cli.OptionHelp;
|
||||
import org.springframework.boot.cli.SpringCli;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class CommandCompleter extends StringsCompleter {
|
||||
|
||||
private final Map<String, Completer> optionCompleters = new HashMap<String, Completer>();
|
||||
private List<Command> commands = new ArrayList<Command>();
|
||||
private ConsoleReader console;
|
||||
private String lastBuffer;
|
||||
|
||||
public CommandCompleter(ConsoleReader console, SpringCli cli) {
|
||||
this.console = console;
|
||||
|
||||
for(CommandFactory fac : ServiceLoader.load(CommandFactory.class, getClass().getClassLoader())) {
|
||||
commands.addAll(fac.getCommands(cli));
|
||||
}
|
||||
|
||||
List<String> names = new ArrayList<String>();
|
||||
for(Command c : commands) {
|
||||
names.add(c.getName());
|
||||
List<String> opts = new ArrayList<String>();
|
||||
for(OptionHelp optHelp : c.getOptionsHelp()) {
|
||||
opts.addAll(optHelp.getOptions());
|
||||
}
|
||||
optionCompleters.put(c.getName(), new ArgumentCompleter(
|
||||
new StringsCompleter(c.getName()),
|
||||
new StringsCompleter(opts),
|
||||
new NullCompleter()
|
||||
));
|
||||
}
|
||||
getStrings().addAll(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int complete(String buffer, int cursor, List<CharSequence> candidates) {
|
||||
int i = super.complete(buffer, cursor, candidates);
|
||||
if(buffer.indexOf(' ') < 1) {
|
||||
return i;
|
||||
}
|
||||
String name = buffer.substring(0, buffer.indexOf(' '));
|
||||
if("".equals(name.trim())) {
|
||||
return i;
|
||||
}
|
||||
for(Command c : commands) {
|
||||
if(!c.getName().equals(name)) {
|
||||
continue;
|
||||
}
|
||||
if(buffer.equals(lastBuffer)) {
|
||||
lastBuffer = buffer;
|
||||
try {
|
||||
console.println();
|
||||
console.println("Usage:");
|
||||
console.println(c.getName() + " " + c.getUsageHelp());
|
||||
List<List<String>> rows = new ArrayList<List<String>>();
|
||||
int maxSize = 0;
|
||||
for(OptionHelp optHelp : c.getOptionsHelp()) {
|
||||
List<String> cols = new ArrayList<String>();
|
||||
for(String s : optHelp.getOptions()) {
|
||||
cols.add(s);
|
||||
}
|
||||
String opts = StringUtils.collectionToDelimitedString(cols, " | ");
|
||||
if(opts.length() > maxSize) {
|
||||
maxSize = opts.length();
|
||||
}
|
||||
cols.clear();
|
||||
cols.add(opts);
|
||||
cols.add(optHelp.getUsageHelp());
|
||||
rows.add(cols);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder("\t");
|
||||
for(List<String> row : rows) {
|
||||
String col1 = row.get(0);
|
||||
String col2 = row.get(1);
|
||||
for(int j = 0; j < (maxSize - col1.length()); j++) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(col1).append(": ").append(col2);
|
||||
console.println(sb.toString());
|
||||
sb = new StringBuilder("\t");
|
||||
}
|
||||
|
||||
console.drawLine();
|
||||
} catch(IOException e) {
|
||||
Log.error(e.getMessage() + " (" + e.getClass().getName() + ")");
|
||||
}
|
||||
}
|
||||
Completer completer = optionCompleters.get(c.getName());
|
||||
if(null != completer) {
|
||||
i = completer.complete(buffer, cursor, candidates);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lastBuffer = buffer;
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.cli.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -32,12 +33,21 @@ import org.springframework.boot.cli.SpringCli;
|
||||
public class DefaultCommandFactory implements CommandFactory {
|
||||
|
||||
private static final List<Command> DEFAULT_COMMANDS = Arrays.<Command> asList(
|
||||
new VersionCommand(), new RunCommand(), new CleanCommand(),
|
||||
new TestCommand(), new GrabCommand());
|
||||
new VersionCommand(), new CleanCommand(), new TestCommand(),
|
||||
new GrabCommand());
|
||||
|
||||
@Override
|
||||
public Collection<Command> getCommands(SpringCli cli) {
|
||||
return DEFAULT_COMMANDS;
|
||||
Collection<Command> commands = new ArrayList<Command>(DEFAULT_COMMANDS);
|
||||
RunCommand run = new RunCommand();
|
||||
StopCommand stop = new StopCommand(run);
|
||||
ShellCommand shell = new ShellCommand(cli);
|
||||
PromptCommand prompt = new PromptCommand(shell);
|
||||
commands.add(run);
|
||||
commands.add(stop);
|
||||
commands.add(shell);
|
||||
commands.add(prompt);
|
||||
return commands;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package org.springframework.boot.cli.command;
|
||||
|
||||
import org.springframework.boot.cli.command.AbstractCommand;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class PromptCommand extends AbstractCommand {
|
||||
|
||||
private final ShellCommand runCmd;
|
||||
|
||||
public PromptCommand(ShellCommand runCmd) {
|
||||
super("prompt", "Change the prompt used with the current 'shell' command. Execute with no arguments to return to the previous value.");
|
||||
this.runCmd = runCmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... strings) throws Exception {
|
||||
if (strings.length > 0) {
|
||||
for (String string : strings) {
|
||||
runCmd.pushPrompt(string + " ");
|
||||
}
|
||||
} else {
|
||||
runCmd.popPrompt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.springframework.boot.cli.command;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.boot.cli.SpringCli;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class Shell {
|
||||
|
||||
public static void main(String... args) throws IOException {
|
||||
if (args.length == 0) {
|
||||
SpringCli.main("shell"); // right into the REPL by default
|
||||
} else {
|
||||
SpringCli.main(args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
package org.springframework.boot.cli.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import jline.console.ConsoleReader;
|
||||
import jline.console.completer.CandidateListCompletionHandler;
|
||||
|
||||
import org.codehaus.groovy.runtime.ProcessGroovyMethods;
|
||||
import org.springframework.boot.cli.SpringCli;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ShellCommand extends AbstractCommand {
|
||||
|
||||
private static final String DEFAULT_PROMPT = "$ ";
|
||||
private SpringCli springCli;
|
||||
private String prompt = DEFAULT_PROMPT;
|
||||
private Stack<String> prompts = new Stack<String>();
|
||||
|
||||
public ShellCommand(SpringCli springCli) {
|
||||
super("shell", "Start a nested shell (REPL).");
|
||||
this.springCli = springCli;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
|
||||
final ConsoleReader console = new ConsoleReader();
|
||||
console.addCompleter(new CommandCompleter(console, this.springCli));
|
||||
console.setHistoryEnabled(true);
|
||||
console.setCompletionHandler(new CandidateListCompletionHandler());
|
||||
|
||||
final InputStream sysin = System.in;
|
||||
final PrintStream sysout = System.out;
|
||||
final PrintStream syserr = System.err;
|
||||
|
||||
System.setIn(console.getInput());
|
||||
PrintStream out = new PrintStream(new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
console.getOutput().write(b);
|
||||
}
|
||||
});
|
||||
System.setOut(out);
|
||||
System.setErr(out);
|
||||
|
||||
String line;
|
||||
StringBuffer data = new StringBuffer();
|
||||
|
||||
try {
|
||||
|
||||
while (null != (line = console.readLine(this.prompt))) {
|
||||
if ("quit".equals(line.trim())) {
|
||||
break;
|
||||
}
|
||||
else if ("clear".equals(line.trim())) {
|
||||
console.clearScreen();
|
||||
continue;
|
||||
}
|
||||
List<String> parts = new ArrayList<String>();
|
||||
|
||||
if (line.contains("<<")) {
|
||||
int startMultiline = line.indexOf("<<");
|
||||
data.append(line.substring(startMultiline + 2));
|
||||
String contLine;
|
||||
while (null != (contLine = console.readLine("... "))) {
|
||||
if ("".equals(contLine.trim())) {
|
||||
break;
|
||||
}
|
||||
data.append(contLine);
|
||||
}
|
||||
line = line.substring(0, startMultiline);
|
||||
}
|
||||
|
||||
String lineToParse = line.trim();
|
||||
if (lineToParse.startsWith("!")) {
|
||||
lineToParse = lineToParse.substring(1).trim();
|
||||
}
|
||||
String[] segments = StringUtils.delimitedListToStringArray(lineToParse,
|
||||
" ");
|
||||
StringBuffer sb = new StringBuffer();
|
||||
boolean swallowWhitespace = false;
|
||||
for (String s : segments) {
|
||||
if ("".equals(s)) {
|
||||
continue;
|
||||
}
|
||||
if (s.startsWith("\"")) {
|
||||
swallowWhitespace = true;
|
||||
sb.append(s.substring(1));
|
||||
}
|
||||
else if (s.endsWith("\"")) {
|
||||
swallowWhitespace = false;
|
||||
sb.append(" ").append(s.substring(0, s.length() - 1));
|
||||
parts.add(sb.toString());
|
||||
sb = new StringBuffer();
|
||||
}
|
||||
else {
|
||||
if (!swallowWhitespace) {
|
||||
parts.add(s);
|
||||
}
|
||||
else {
|
||||
sb.append(" ").append(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
parts.add(sb.toString());
|
||||
}
|
||||
if (data.length() > 0) {
|
||||
parts.add(data.toString());
|
||||
data = new StringBuffer();
|
||||
}
|
||||
|
||||
if (parts.size() > 0) {
|
||||
if (line.trim().startsWith("!")) {
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder(parts);
|
||||
if (isJava7()) {
|
||||
inheritIO(pb);
|
||||
}
|
||||
pb.environment().putAll(System.getenv());
|
||||
Process process = pb.start();
|
||||
if (!isJava7()) {
|
||||
ProcessGroovyMethods.consumeProcessOutput(process,
|
||||
(OutputStream) sysout, (OutputStream) syserr);
|
||||
}
|
||||
process.waitFor();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!getName().equals(parts.get(0))) {
|
||||
this.springCli.runAndHandleErrors(parts
|
||||
.toArray(new String[parts.size()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
finally {
|
||||
|
||||
System.setIn(sysin);
|
||||
System.setOut(sysout);
|
||||
System.setErr(syserr);
|
||||
|
||||
console.shutdown();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void pushPrompt(String prompt) {
|
||||
this.prompts.push(this.prompt);
|
||||
this.prompt = prompt;
|
||||
}
|
||||
|
||||
public String popPrompt() {
|
||||
if (this.prompts.isEmpty()) {
|
||||
this.prompt = DEFAULT_PROMPT;
|
||||
}
|
||||
else {
|
||||
this.prompt = this.prompts.pop();
|
||||
}
|
||||
return this.prompt;
|
||||
}
|
||||
|
||||
private void inheritIO(ProcessBuilder pb) {
|
||||
ReflectionUtils.invokeMethod(
|
||||
ReflectionUtils.findMethod(ProcessBuilder.class, "inheritIO"), pb);
|
||||
}
|
||||
|
||||
private boolean isJava7() {
|
||||
if (ReflectionUtils.findMethod(ProcessBuilder.class, "inheritIO") != null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.springframework.boot.cli.command;
|
||||
|
||||
import org.springframework.boot.cli.command.AbstractCommand;
|
||||
import org.springframework.boot.cli.command.RunCommand;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class StopCommand extends AbstractCommand {
|
||||
|
||||
private final RunCommand runCmd;
|
||||
|
||||
public StopCommand(RunCommand runCmd) {
|
||||
super("stop", "Stop the currently-running application started with the 'run' command.");
|
||||
this.runCmd = runCmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... strings) throws Exception {
|
||||
runCmd.stop();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user