package restx.specs.shell; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import jline.console.completer.ArgumentCompleter; import jline.console.completer.Completer; import jline.console.completer.StringsCompleter; import restx.AppSettings; import restx.Apps; import restx.core.shell.DepsShellCommand; import restx.core.shell.ShellAppRunner; import restx.core.shell.ShellAppRunner.CompileMode; import restx.factory.Component; import restx.factory.NamedComponent; import restx.factory.SingletonFactoryMachine; import restx.server.simple.simple.SimpleWebServer; import restx.shell.RestxShell; import restx.shell.ShellCommandRunner; import restx.shell.StdShellCommand; import java.awt.*; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * User: xavierhanin * Date: 4/10/13 * Time: 4:27 PM */ @Component public class SpecsShellCommand extends StdShellCommand { protected SpecsShellCommand() { super(ImmutableList.of("spec"), "restx spec commands: server, test, ... "); } @Override protected String resourceMan() { return "restx/specs/shell/spec.man"; } @Override protected Optional<ShellCommandRunner> doMatch(String line) { final List<String> args = splitArgs(line); if (args.size() < 2) { return Optional.absent(); } switch (args.get(1)) { case "server": return Optional.<ShellCommandRunner>of(new SpecServerCommandRunner(args)); case "test": if (args.size() > 2 && args.get(2).equals("server")) { return Optional.<ShellCommandRunner>of(new SpecTestServerCommandRunner(args)); } // keep other arguments to run spec test directly with spec test <spec/test/to/run.spec.yaml> } return Optional.absent(); } @Override public Iterable<Completer> getCompleters() { return ImmutableList.<Completer>of( new ArgumentCompleter(new StringsCompleter("spec"), new StringsCompleter("server", "test"), new StringsCompleter("server"))); } private class SpecTestServerCommandRunner implements ShellCommandRunner { private final List<String> args; public SpecTestServerCommandRunner(List<String> args) { this.args = new ArrayList<>(args); } @Override public void run(final RestxShell shell) throws Exception { String basePack; boolean quiet = false; if (args.size() > 3 && args.get(3).equalsIgnoreCase("--quiet")) { args.remove(3); quiet = true; } if (args.size() > 3) { basePack = args.get(3); } else { Optional<String> pack = Apps.with(shell.getFactory().getComponent(AppSettings.class)) .guessAppBasePackage(shell.currentLocation()); if (!pack.isPresent()) { shell.printIn("can't find base app package, src/main/java should contain a AppServer.java source file somewhere", RestxShell.AnsiCodes.ANSI_RED); shell.println(""); shell.println("alternatively you can provide the base package with `spec test server <base.pack>`"); return; } basePack = pack.get(); } AppSettings appSettings = shell.getFactory() .concat(new SingletonFactoryMachine<>(-10000, NamedComponent.of(String.class, "restx.app.package", basePack))) .getComponent(AppSettings.class); if (!DepsShellCommand.depsUpToDate(shell)) { shell.println("restx> deps install"); new DepsShellCommand().new InstallDepsCommandRunner().run(shell); } new ShellAppRunner(appSettings, "restx.tests.RestxSpecTestServer", CompileMode.RESOURCES_ONLY, quiet, false, Collections.<String>emptyList()).run(shell); } } private class SpecServerCommandRunner implements ShellCommandRunner { private final List<String> args; public SpecServerCommandRunner(List<String> args) { this.args = args; } @Override public void run(final RestxShell shell) throws Exception { final String routerPath; if (args.size() > 2) { routerPath = args.get(2); } else { routerPath = "/api"; } int port = 8888; if (args.size() > 3) { port = Integer.parseInt(args.get(3)); } System.setProperty("restx.factory.load", "onrequest"); final SimpleWebServer webServer = SimpleWebServer.builder() .setRouterPath(routerPath).setAppBase(".").setPort(port).build(); webServer.start(); String uri = webServer.baseUrl() + routerPath; shell.printIn("SPECS SERVER READY on " + uri + "/\n", RestxShell.AnsiCodes.ANSI_GREEN); shell.println("type `stop` to stop the server, `help` to get help on available commands"); shell.getConsoleReader().setPrompt("spec-server> "); shell.getConsoleReader().addCompleter(new StringsCompleter("stop", "open", "help")); boolean exit = false; while (!exit) { String line = shell.getConsoleReader().readLine().trim(); switch (line) { case "stop": exit = stop(shell, webServer); break; case "open": openInBrowser(shell, uri); break; case "help": help(shell); break; default: shell.println( "command not found. use `help` to get the list of commands."); } } } private void openInBrowser(RestxShell shell, String uri) throws IOException { try { Desktop.getDesktop().browse(new URI(uri)); } catch (UnsupportedOperationException e) { shell.printIn("can't open browser: " + e.getMessage(), RestxShell.AnsiCodes.ANSI_RED); } catch (IOException e) { shell.printIn("can't open browser: " + e.getMessage(), RestxShell.AnsiCodes.ANSI_RED); } catch (URISyntaxException e) { shell.printIn("can't open browser: " + e.getMessage(), RestxShell.AnsiCodes.ANSI_RED); } } private boolean stop(RestxShell consoleReader, SimpleWebServer webServer) { boolean exit; try { consoleReader.println("stopping server..."); webServer.stop(); exit = true; } catch (Exception e) { throw new RuntimeException(e); } return exit; } private void help(RestxShell shell) throws IOException { shell.printIn("stop", RestxShell.AnsiCodes.ANSI_GREEN); shell.println(" - to stop the server"); shell.printIn("open", RestxShell.AnsiCodes.ANSI_GREEN); shell.println(" - open a browser on the spec server"); shell.printIn("help", RestxShell.AnsiCodes.ANSI_GREEN); shell.println(" - this help"); shell.println(""); shell.println("to add new routes simply create/edit .spec.yaml file in\n" + "current directory or subdirectories."); } } }