8 changed files with 407 additions and 7 deletions
@ -0,0 +1,121 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,28 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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