From c776f5d0f8bfaf9ec9eef5fc55e9738aad61fd2a Mon Sep 17 00:00:00 2001 From: fecaille Date: Thu, 24 Mar 2016 15:46:04 +0100 Subject: [PATCH] Optim: more generic server side JS engine --- .../cloud/saas/util/JavaScriptEngine.java | 76 +++++++++++++++++++ .../cloud/saas/web/ViewController.java | 34 ++++++--- 2 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/opengroupe/cloud/saas/util/JavaScriptEngine.java diff --git a/src/main/java/com/opengroupe/cloud/saas/util/JavaScriptEngine.java b/src/main/java/com/opengroupe/cloud/saas/util/JavaScriptEngine.java new file mode 100644 index 0000000..a68d918 --- /dev/null +++ b/src/main/java/com/opengroupe/cloud/saas/util/JavaScriptEngine.java @@ -0,0 +1,76 @@ +package com.opengroupe.cloud.saas.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.function.Function; + +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +public class JavaScriptEngine { + + private final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("nashorn"); + + public JavaScriptEngine polyfillToNashorn() { + this.loadFromClassPath("static/js/nashorn-polyfill.js"); + return this; + } + + public JavaScriptEngine eval(String script) { + try { + this.scriptEngine.eval(script); + } catch (ScriptException e) { + throw new IllegalStateException("Failed to eval " + script + "!", e); + } + return this; + } + + public JavaScriptEngine loadFromClassPath(String file) { + try { + this.scriptEngine.eval(readFromClassPath(file)); + } catch (ScriptException e) { + throw new IllegalStateException("Failed to loadFromClassPath " + file + "!", e); + } + return this; + } + + public Object invokeFunction(String functionName, Object... args) { + try { + return ((Invocable) this.scriptEngine).invokeFunction(functionName, args); + } catch (ScriptException | NoSuchMethodException e) { + throw new IllegalArgumentException("Failed to invoke " + functionName, e); + } + } + + public T invokeFunction(String functionName, Function converter, Object... args) { + return converter.apply(invokeFunction(functionName, args)); + } + + private String readFromClassPath(String path) { + try (InputStream in = getClass().getClassLoader().getResourceAsStream(path)) { + if (in == null) { + throw new IllegalArgumentException(path + " is not found!"); + } + return copyToString(in, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new IllegalStateException("Failed to read " + path, e); + } + } + + private static String copyToString(InputStream in, Charset charset) throws IOException { + StringBuilder out = new StringBuilder(); + try (InputStreamReader reader = new InputStreamReader(in, charset);) { + char[] buffer = new char[4096]; + int bytesRead = -1; + while ((bytesRead = reader.read(buffer)) != -1) { + out.append(buffer, 0, bytesRead); + } + return out.toString(); + } + } +} diff --git a/src/main/java/com/opengroupe/cloud/saas/web/ViewController.java b/src/main/java/com/opengroupe/cloud/saas/web/ViewController.java index d9b88bc..f75b9e4 100644 --- a/src/main/java/com/opengroupe/cloud/saas/web/ViewController.java +++ b/src/main/java/com/opengroupe/cloud/saas/web/ViewController.java @@ -3,6 +3,7 @@ package com.opengroupe.cloud.saas.web; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @@ -12,33 +13,42 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.opengroupe.cloud.saas.domain.Comment; import com.opengroupe.cloud.saas.service.CommentService; -import com.opengroupe.cloud.saas.util.React; +import com.opengroupe.cloud.saas.util.JavaScriptEngine; @Controller public class ViewController { - + @Autowired private CommentService service; - + + @Bean + JavaScriptEngine nashornEngine() { + return new JavaScriptEngine().polyfillToNashorn() + .loadFromClassPath("META-INF/resources/webjars/react/0.14.7/react.min.js") + .loadFromClassPath("META-INF/resources/webjars/marked/0.3.2/marked.js") + .loadFromClassPath("static/js/app.js") + .loadFromClassPath("static/js/app.render.js"); + } @Autowired - private ObjectMapper objectMapper; + ObjectMapper objectMapper; + @Autowired + JavaScriptEngine nashorn; - private React react = new React(); - @RequestMapping("/greeting") - public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) { + public String greeting(@RequestParam(value = "name", required = false, defaultValue = "World") String name, + Model model) { model.addAttribute("name", name); return "greeting"; } - + @RequestMapping("/index") public String index(Model model) throws JsonProcessingException { List comments = service.getAll(); - String commentBox = react.renderCommentBox(comments); - String data = objectMapper.writeValueAsString(comments); - model.addAttribute("markup", commentBox); - model.addAttribute("data", data); + String markup = nashorn.invokeFunction("renderServer", String::valueOf, comments); + String data = objectMapper.writeValueAsString(comments); + model.addAttribute("markup", markup); + model.addAttribute("data", data); return "index"; } }