فهرست منبع

Optim: more generic server side JS engine

fecaille 9 سال پیش
والد
کامیت
c776f5d0f8

+ 76 - 0
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> 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();
+		}
+	}
+}

+ 22 - 12
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<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";
 	}
 }