Skip to content

Instantly share code, notes, and snippets.

@eleinadani
Last active July 7, 2020 13:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eleinadani/7cf4391bcbb55b45344a8ebb8b0be212 to your computer and use it in GitHub Desktop.
Save eleinadani/7cf4391bcbb55b45344a8ebb8b0be212 to your computer and use it in GitHub Desktop.
// HTTP request handler
WebServer.create(configuration, route.get("/request", (req, res) -> {
// ( #1 ) An HTTP GET request is received. Get some request data
int requestId = getRequestId(req);
// Send the data to JavaScript. Perform computation in JS and
// send back to the HTTP client a response message when done.
executeJs(requestId).whenComplete((r, ex) -> {
if (ex != null) {
res.status(404)
.send("404 error");
} else {
// ( #5 ) Client response. Result 'r' is a JSON string created in JavaScript
res.send(r);
}
});
});
// JavaScript code that will be executed (for each request)
private static final String jsSource = "(async function(requestId) {" +
" // ( #2 ) The request id is validated in JS" +
" if (!validate(requestId)) return 'Bad request!';" +
" // ( #3 ) Process the request in Java" +
" let data = await computeFromJava(requestId);" +
" // ( #4 ) Create a JSON object for the response " +
" return JSON.stringify({requestId:requestId,result:data});" +
"})";
// Offload request handling to JavaScript
private CompletionStage<Object> executeJs(int requestId) {
CompletableFuture<Object> jsExecution = new CompletableFuture<>();
Context cx = getCurrentPolyglotContext();
// Access to a polyglot context should be synchronized. We use a lock here.
contextAccessLock.lock();
try {
// Enter the context
cx.enter();
Value jsAsyncFunction = cx.eval(JS, jsSource);
// Execute the JavaScript code. The `async` function returns a JS
// Promise object. The Java `then` reaction will be executed by
// the JavaScript engine when `await` completes.
jsAsyncFunction.execute(requestId)
.invokeMember(THEN, (Consumer<?>) jsExecution::complete)
.invokeMember(CATCH, (Consumer<Throwable>) jsExecution::completeExceptionally);
} finally {
cx.leave();
contextAccessLock.unlock();
}
return jsExecution;
}
@danielkec
Copy link

Hi @eleinadani, great article, I have just few notes for improvement of example:

  executeJs(requestId).whenComplete((r, ex) -> {
    if (ex != null) {
      res.status(404)
         .send("404 error");
    } else {
      //  ( #5 )  Client response. Result 'r' is a JSON string created in JavaScript
      res.send(r);
    }
  });

As I presume we don't want anyone else to complete jsAsyncFunction's future:

private CompletionStage<Object> executeJs(int requestId) {

Also using Helidon's reactive api could make the example even cooler:

Single.create(executeJs(requestId))
     .onError(ex -> res.status(404).send(ex.getMessage()))
     .thenAccept(res::send);

Thx for all the great features of Graal 🖖

@eleinadani
Copy link
Author

Great, thanks for the feedback :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment