diff --git a/intellij-plugin/src/javarepl/plugin/JavaREPLLanguageConsole.java b/intellij-plugin/src/javarepl/plugin/JavaREPLLanguageConsole.java index 9a8ddbd..e707ff6 100644 --- a/intellij-plugin/src/javarepl/plugin/JavaREPLLanguageConsole.java +++ b/intellij-plugin/src/javarepl/plugin/JavaREPLLanguageConsole.java @@ -70,8 +70,8 @@ import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; -import javarepl.client.ExpressionTemplate; import javarepl.client.JavaREPLClient; +import javarepl.rendering.ExpressionTemplate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/javarepl/Main.java b/src/javarepl/Main.java index ba1b75e..a22f986 100644 --- a/src/javarepl/Main.java +++ b/src/javarepl/Main.java @@ -67,7 +67,7 @@ private static JavaREPLClient clientFor(Option hostname, Option private static JavaREPLClient connectToRemoteInstance(String hostname, Integer port) { JavaREPLClient replClient = new JavaREPLClient(hostname, port); - if (!replClient.isAlive()) { + if (!replClient.status().isRunning()) { console.printError("ERROR: Could not connect to remote REPL instance at http://" + hostname + ":" + port); System.exit(0); } else { @@ -112,9 +112,9 @@ public void run() { } private static boolean waitUntilInstanceStarted(JavaREPLClient client) throws Exception { - for (int i = 0; i < 50; i++) { - Thread.sleep(100); - if (client.isAlive()) + for (int i = 0; i < 500; i++) { + Thread.sleep(10); + if (client.status().isRunning()) return true; } diff --git a/src/javarepl/Repl.java b/src/javarepl/Repl.java index dae7e40..f41f5eb 100644 --- a/src/javarepl/Repl.java +++ b/src/javarepl/Repl.java @@ -63,9 +63,7 @@ public static void main(String... args) throws Exception { if (sandboxed) sandboxApplication(); - for (String expression : consoleConfig.expressions) { - console.execute(expression); - } + console.start(); do { console.execute(reader.readExpression().getOrNull()); diff --git a/src/javarepl/client/JavaREPLClient.java b/src/javarepl/client/JavaREPLClient.java index dd91eb8..05ddf6f 100644 --- a/src/javarepl/client/JavaREPLClient.java +++ b/src/javarepl/client/JavaREPLClient.java @@ -7,6 +7,8 @@ import com.googlecode.utterlyidle.Response; import com.googlecode.utterlyidle.handlers.ClientHttpHandler; import javarepl.completion.CompletionResult; +import javarepl.console.ConsoleStatus; +import javarepl.rendering.ExpressionTemplate; import static com.googlecode.funclate.Model.persistent.parse; import static com.googlecode.totallylazy.Option.none; @@ -15,6 +17,7 @@ import static com.googlecode.utterlyidle.RequestBuilder.get; import static com.googlecode.utterlyidle.RequestBuilder.post; import static javarepl.client.EvaluationLog.Type; +import static javarepl.console.ConsoleStatus.Idle; public final class JavaREPLClient { private final String hostname; @@ -53,11 +56,11 @@ public synchronized CompletionResult completions(String expr) throws Exception { sequence(model.getValues("candidates", String.class))); } - public synchronized boolean isAlive() { + public synchronized ConsoleStatus status() { try { - return parse(client.handle(get(url("status")).build()).entity().toString()).get("isAlive", Boolean.class); + return ConsoleStatus.valueOf(parse(client.handle(get(url("status")).build()).entity().toString()).get("status", String.class)); } catch (Exception e) { - return false; + return Idle; } } diff --git a/src/javarepl/console/Console.java b/src/javarepl/console/Console.java index 0a71067..1750ced 100644 --- a/src/javarepl/console/Console.java +++ b/src/javarepl/console/Console.java @@ -1,11 +1,22 @@ package javarepl.console; -import com.googlecode.yadic.Container; +import javarepl.completion.CompletionResult; +import javarepl.rendering.ExpressionTemplate; public interface Console { + ConsoleResult execute(String expression); - Container context(); + CompletionResult completion(String expression); + + ExpressionTemplate template(String expression); + + ConsoleStatus status(); + + ConsoleHistory history(); + + void start(); void shutdown(); + } diff --git a/src/javarepl/console/ConsoleStatus.java b/src/javarepl/console/ConsoleStatus.java new file mode 100644 index 0000000..770e75d --- /dev/null +++ b/src/javarepl/console/ConsoleStatus.java @@ -0,0 +1,9 @@ +package javarepl.console; + +public enum ConsoleStatus { + Idle, Starting, Running, Terminating, Terminated; + + public Boolean isRunning() { + return this == Running; + } +} diff --git a/src/javarepl/console/DelegatingConsole.java b/src/javarepl/console/DelegatingConsole.java index 5fac0d6..5204805 100644 --- a/src/javarepl/console/DelegatingConsole.java +++ b/src/javarepl/console/DelegatingConsole.java @@ -1,6 +1,7 @@ package javarepl.console; -import com.googlecode.yadic.Container; +import javarepl.completion.CompletionResult; +import javarepl.rendering.ExpressionTemplate; public abstract class DelegatingConsole implements Console { private final Console delegate; @@ -13,11 +14,27 @@ public ConsoleResult execute(String expression) { return delegate.execute(expression); } - public Container context() { - return delegate.context(); + public CompletionResult completion(String expression) { + return delegate.completion(expression); + } + + public ExpressionTemplate template(String expression) { + return delegate.template(expression); + } + + public ConsoleStatus status() { + return delegate.status(); + } + + public ConsoleHistory history() { + return delegate.history(); } public void shutdown() { delegate.shutdown(); } + + public void start() { + delegate.start(); + } } diff --git a/src/javarepl/console/SimpleConsole.java b/src/javarepl/console/SimpleConsole.java index eec6299..82acef6 100644 --- a/src/javarepl/console/SimpleConsole.java +++ b/src/javarepl/console/SimpleConsole.java @@ -10,19 +10,28 @@ import javarepl.completion.*; import javarepl.console.commands.Command; import javarepl.console.commands.Commands; +import javarepl.expressions.Expression; +import javarepl.rendering.ExpressionTemplate; import static com.googlecode.totallylazy.Predicates.always; import static com.googlecode.totallylazy.Predicates.notNullValue; +import static com.googlecode.totallylazy.Sequences.sequence; import static com.googlecode.totallylazy.Strings.blank; import static com.googlecode.totallylazy.Strings.startsWith; +import static javarepl.Utils.randomIdentifier; import static javarepl.completion.Completers.javaKeywordCompleter; import static javarepl.completion.TypeResolver.functions.defaultPackageResolver; import static javarepl.console.ConsoleHistory.historyFromFile; +import static javarepl.console.ConsoleLog.Type.ERROR; import static javarepl.console.ConsoleResult.emptyResult; +import static javarepl.console.ConsoleStatus.*; import static javarepl.console.commands.Command.functions.completer; +import static javarepl.rendering.EvaluationClassRenderer.renderExpressionClass; +import static javarepl.rendering.ExpressionTokenRenderer.EXPRESSION_TOKEN; public final class SimpleConsole implements Console { private final Container context; + private ConsoleStatus status = Idle; public SimpleConsole(ConsoleConfig config) { registerShutdownHook(); @@ -45,10 +54,29 @@ public SimpleConsole(ConsoleConfig config) { )); - evaluator().addResults(config.results); + context.get(Evaluator.class).addResults(config.results); } public ConsoleResult execute(String expression) { + if (status == Running) { + return executeExpression(expression); + } else { + return new ConsoleResult(expression, sequence(new ConsoleLog(ERROR, "Console is not running (" + status + ")"))); + } + } + + public CompletionResult completion(String expression) { + return context.get(Completer.class).apply(expression); + } + + public ExpressionTemplate template(String expression) { + Evaluator evaluator = context.get(Evaluator.class); + Expression parsedExpression = evaluator.parseExpression(expression); + + return new ExpressionTemplate(renderExpressionClass(evaluator.context(), randomIdentifier("Evaluation"), parsedExpression), EXPRESSION_TOKEN); + } + + private ConsoleResult executeExpression(String expression) { context.get(ConsoleHistory.class).add(expression); ConsoleResult result = evaluationRules().apply(expression); return result; @@ -58,8 +86,24 @@ public Container context() { return context; } - public Evaluator evaluator() { - return context.get(Evaluator.class); + public ConsoleStatus status() { + return status; + } + + public ConsoleHistory history() { + return context.get(ConsoleHistory.class); + } + + public void start() { + if (status() == Idle) { + status = Starting; + + for (String expression : context.get(ConsoleConfig.class).expressions) { + execute(expression); + } + + status = Running; + } } private void registerShutdownHook() { @@ -71,8 +115,10 @@ public void run() { } public void shutdown() { + status = Terminating; context.get(ConsoleHistory.class).save(); context.get(Evaluator.class).clearOutputDirectory(); + status = Terminated; } private Rules evaluationRules() { diff --git a/src/javarepl/console/rest/RestConsoleResource.java b/src/javarepl/console/rest/RestConsoleResource.java index 53589fb..99fbf9f 100644 --- a/src/javarepl/console/rest/RestConsoleResource.java +++ b/src/javarepl/console/rest/RestConsoleResource.java @@ -5,19 +5,13 @@ import com.googlecode.totallylazy.Option; import com.googlecode.utterlyidle.MediaType; import com.googlecode.utterlyidle.annotations.*; -import javarepl.Evaluator; -import javarepl.completion.Completer; import javarepl.completion.CompletionResult; -import javarepl.console.ConsoleHistory; import javarepl.console.ConsoleLog; import javarepl.console.ConsoleResult; -import javarepl.expressions.Expression; +import javarepl.rendering.ExpressionTemplate; import static com.googlecode.funclate.Model.persistent.model; import static javarepl.Utils.applicationVersion; -import static javarepl.Utils.randomIdentifier; -import static javarepl.rendering.EvaluationClassRenderer.renderExpressionClass; -import static javarepl.rendering.ExpressionTokenRenderer.EXPRESSION_TOKEN; public class RestConsoleResource { private final RestConsole console; @@ -42,7 +36,7 @@ public Model version() { @Produces(MediaType.APPLICATION_JSON) public Model status() { return model() - .add("isAlive", true); + .add("status", console.status()); } @POST @@ -72,19 +66,17 @@ public Model call(String expression) throws Exception { @Path("template") @Produces(MediaType.APPLICATION_JSON) public Model template(@QueryParam("expression") String expr) { - Evaluator evaluator = console.context().get(Evaluator.class); - Expression expression = evaluator.parseExpression(expr); - + ExpressionTemplate template = console.template(expr); return model() - .add("template", renderExpressionClass(evaluator.context(), randomIdentifier("Evaluation"), expression)) - .add("token", EXPRESSION_TOKEN); + .add("template", template.template()) + .add("token", template.token()); } @GET @Path("completions") @Produces(MediaType.APPLICATION_JSON) public Model completions(@QueryParam("expression") String expr) { - CompletionResult result = console.context().get(Completer.class).apply(expr); + CompletionResult result = console.completion(expr); return model() .add("expression", result.expression()) .add("position", result.position().toString()) @@ -95,7 +87,7 @@ public Model completions(@QueryParam("expression") String expr) { @Path("history") @Produces(MediaType.APPLICATION_JSON) public Model history() { - return model().add("history", console.context().get(ConsoleHistory.class).items().toList()); + return model().add("history", console.history().items().toList()); } private static Mapper commandResultToModel() { diff --git a/src/javarepl/client/ExpressionTemplate.java b/src/javarepl/rendering/ExpressionTemplate.java similarity index 76% rename from src/javarepl/client/ExpressionTemplate.java rename to src/javarepl/rendering/ExpressionTemplate.java index 4a82d06..165a966 100644 --- a/src/javarepl/client/ExpressionTemplate.java +++ b/src/javarepl/rendering/ExpressionTemplate.java @@ -1,10 +1,10 @@ -package javarepl.client; +package javarepl.rendering; public final class ExpressionTemplate { private final String template; private final String token; - ExpressionTemplate(String template, String token) { + public ExpressionTemplate(String template, String token) { this.template = template; this.token = token; } diff --git a/src/javarepl/web/WebConsole.java b/src/javarepl/web/WebConsole.java index 38f4165..fc0f0b2 100644 --- a/src/javarepl/web/WebConsole.java +++ b/src/javarepl/web/WebConsole.java @@ -31,8 +31,8 @@ public void run() { }; } - public synchronized Option createClient() { - WebConsoleClientHandler clientHandler = new WebConsoleClientHandler(); + public synchronized Option createClient(Option command) { + WebConsoleClientHandler clientHandler = new WebConsoleClientHandler(command); clients = clients.insert(clientHandler.id(), clientHandler); return some(clientHandler); } diff --git a/src/javarepl/web/WebConsoleClientHandler.java b/src/javarepl/web/WebConsoleClientHandler.java index 0472ab6..1cbff71 100644 --- a/src/javarepl/web/WebConsoleClientHandler.java +++ b/src/javarepl/web/WebConsoleClientHandler.java @@ -2,11 +2,13 @@ import com.googlecode.totallylazy.Mapper; import com.googlecode.totallylazy.Option; +import com.googlecode.totallylazy.Strings; import com.googlecode.utterlyidle.RequestBuilder; import com.googlecode.utterlyidle.Response; import com.googlecode.utterlyidle.Status; import com.googlecode.utterlyidle.handlers.ClientHttpHandler; import javarepl.Repl; +import javarepl.console.ConsoleStatus; import java.util.UUID; @@ -18,14 +20,19 @@ import static com.googlecode.utterlyidle.Status.GATEWAY_TIMEOUT; import static com.googlecode.utterlyidle.Status.INTERNAL_SERVER_ERROR; import static javarepl.Utils.randomServerPort; +import static javarepl.console.ConsoleStatus.Idle; import static javarepl.console.TimingOutConsole.EXPRESSION_TIMEOUT; import static javarepl.console.TimingOutConsole.INACTIVITY_TIMEOUT; public final class WebConsoleClientHandler { private final String id = UUID.randomUUID().toString(); + private final Option commands; private Option port = none(); private Option process = none(); + public WebConsoleClientHandler(Option commands) { + this.commands = commands; + } public String id() { return id; @@ -40,32 +47,31 @@ private void createProcess() { try { port = some(randomServerPort()); ProcessBuilder builder = new ProcessBuilder("java", "-Xmx128M", "-cp", System.getProperty("java.class.path"), Repl.class.getCanonicalName(), - "--sandboxed", "--ignoreConsole", "--port=" + port.get(), "--expressionTimeout=5", "--inactivityTimeout=300"); + "--sandboxed", "--ignoreConsole", "--port=" + port.get(), "--expressionTimeout=5", "--inactivityTimeout=300", commands.map(Strings.format("--expression=%s")).getOrElse("")); builder.redirectErrorStream(true); process = some(builder.start()); waitUntilProcessStarted(); - } catch (Exception e) { throw new RuntimeException(e); } } } - private boolean isAlive() { + private ConsoleStatus status() { try { - return parse(new ClientHttpHandler().handle(get("http://localhost:" + port.get() + "/status").build()).entity().toString()) - .get("isAlive", Boolean.class); + return ConsoleStatus.valueOf(parse(new ClientHttpHandler().handle(get("http://localhost:" + port.get() + "/status").build()).entity().toString()) + .get("status", String.class)); } catch (Exception e) { - return false; + return Idle; } } private boolean waitUntilProcessStarted() throws Exception { - for (int i = 0; i < 50; i++) { - Thread.sleep(100); - if (isAlive()) + for (int i = 0; i < 500; i++) { + Thread.sleep(10); + if (status().isRunning()) return true; } diff --git a/src/javarepl/web/WebConsoleResource.java b/src/javarepl/web/WebConsoleResource.java index 9b5f6d0..ac929cd 100644 --- a/src/javarepl/web/WebConsoleResource.java +++ b/src/javarepl/web/WebConsoleResource.java @@ -28,8 +28,9 @@ public WebConsoleResource(WebConsole agent) { @Hidden @Path("create") @Produces(MediaType.APPLICATION_JSON) - public Model create() { - Option clientHandler = agent.createClient(); + public Model create(@FormParam("command") Option command) { + + Option clientHandler = agent.createClient(command); return clientHandler.map(clientHandlerToModel()).get() .add("welcomeMessage", welcomeMessage() + "\n\n"); diff --git a/src/javarepl/web/javarepl.js b/src/javarepl/web/javarepl.js index b784421..073c9fd 100644 --- a/src/javarepl/web/javarepl.js +++ b/src/javarepl/web/javarepl.js @@ -1,5 +1,20 @@ document.write('
') +// return a parameter value from the current URL +function getParam(sname) { + var params = location.search.substr(location.search.indexOf("?") + 1); + var sval = ""; + params = params.split("&"); + // split param and value into individual pieces + for (var i = 0; i < params.length; i++) { + temp = params[i].split("="); + if ([temp[0]] == sname) { + sval = temp[1]; + } + } + return sval; +} + function removeClient(clientId) { $.ajax({type: 'POST', async: false, url: '/remove', data: 'id=' + clientId}); } @@ -25,9 +40,10 @@ $(window).bind('beforeunload', function () { $(document).ready(function () { var console = $('
'); + var command = getParam("command"); $('#console').append(console); - $.ajax({type: 'POST', async: false, url: '/create'}) + $.ajax({type: 'POST', async: false, url: '/create', data: (command ? "command=" + command : "")}) .done(function (data) { clientId = data.id; welcomeMessage = data.welcomeMessage diff --git a/test/javarepl/client/JavaREPLClientTest.java b/test/javarepl/client/JavaREPLClientTest.java index 9d53a57..4a630bf 100644 --- a/test/javarepl/client/JavaREPLClientTest.java +++ b/test/javarepl/client/JavaREPLClientTest.java @@ -4,6 +4,7 @@ import javarepl.console.ConsoleConfig; import javarepl.console.SimpleConsole; import javarepl.console.rest.RestConsole; +import javarepl.rendering.ExpressionTemplate; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -13,6 +14,7 @@ import static javarepl.Utils.applicationVersion; import static javarepl.Utils.randomServerPort; import static javarepl.client.EvaluationLog.Type.SUCCESS; +import static javarepl.console.ConsoleStatus.Running; import static javarepl.rendering.ExpressionTokenRenderer.EXPRESSION_TOKEN; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -27,6 +29,8 @@ public class JavaREPLClientTest { public void setUp() throws Exception { int port = randomServerPort(); console = new RestConsole(new SimpleConsole(ConsoleConfig.consoleConfig()), port); + console.start(); + client = new JavaREPLClient("localhost", port); } @@ -78,6 +82,6 @@ public void returnsVersion() throws Exception { @Test public void checksIfEndpointIsAlive() throws Exception { - assertThat(client.isAlive(), is(true)); + assertThat(client.status(), is(Running)); } } diff --git a/test/javarepl/console/SimpleConsoleTest.java b/test/javarepl/console/SimpleConsoleTest.java index ab5bea5..26e959d 100644 --- a/test/javarepl/console/SimpleConsoleTest.java +++ b/test/javarepl/console/SimpleConsoleTest.java @@ -197,6 +197,7 @@ public void supportsReevaluatingAllExpression() { private static ConsoleResult executing(String... items) { ConsoleConfig config = consoleConfig(); SimpleConsole console = new SimpleConsole(config); + console.start(); System.setOut(new ConsoleLoggerPrintStream(SUCCESS, Predicates.never(), config.logger)); System.setErr(new ConsoleLoggerPrintStream(ERROR, Predicates.never(), config.logger)); diff --git a/test/javarepl/console/rest/RestConsoleTest.java b/test/javarepl/console/rest/RestConsoleTest.java index 914a4ab..f4ec1ee 100644 --- a/test/javarepl/console/rest/RestConsoleTest.java +++ b/test/javarepl/console/rest/RestConsoleTest.java @@ -17,6 +17,7 @@ import static java.util.Arrays.asList; import static javarepl.Utils.applicationVersion; import static javarepl.Utils.randomServerPort; +import static javarepl.console.ConsoleStatus.Running; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -29,6 +30,8 @@ public class RestConsoleTest { @Before public void setUp() throws Exception { console = new RestConsole(new SimpleConsole(ConsoleConfig.consoleConfig()), randomServerPort()); + console.start(); + client = new ClientHttpHandler(); prefixUrl = "http://localhost:" + console.port(); } @@ -91,7 +94,7 @@ public void shouldReturnCorrectStatus() throws Exception { assertThat(response.status(), is(Status.OK)); assertThat(body(response), is(model() - .add("isAlive", true))); + .add("status", Running.toString()))); }