Optim: more generic server side JS engine

This commit is contained in:
fecaille
2016-03-24 15:46:04 +01:00
parent 7a2d9de5e1
commit c776f5d0f8
2 changed files with 98 additions and 12 deletions

View File

@@ -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> T invokeFunction(String functionName, Function<Object, T> 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();
}
}
}

View File

@@ -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,7 +13,7 @@ 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 {
@@ -20,14 +21,23 @@ 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;
private React react = new React();
ObjectMapper objectMapper;
@Autowired
JavaScriptEngine nashorn;
@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";
}
@@ -35,10 +45,10 @@ public class ViewController {
@RequestMapping("/index")
public String index(Model model) throws JsonProcessingException {
List<Comment> 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";
}
}