From e4fbb5f7360fdd78cd226d972db8bbc3bcc2a49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Ko=CC=88bler?= Date: Fri, 2 Sep 2016 15:06:28 +0200 Subject: [PATCH] #1 replace creation of ScriptEngines bound to each thread with ThreadLocal with a pool of Nashorn ScriptEngines (w/ use of commons-pool) --- build.gradle | 1 + pom.xml | 5 ++ .../java/dasniko/ozark/react/NashornPool.java | 34 +++++++++ .../ozark/react/NashornPoolFactory.java | 74 +++++++++++++++++++ src/main/java/dasniko/ozark/react/React.java | 66 +++-------------- 5 files changed, 123 insertions(+), 57 deletions(-) create mode 100644 src/main/java/dasniko/ozark/react/NashornPool.java create mode 100644 src/main/java/dasniko/ozark/react/NashornPoolFactory.java diff --git a/build.gradle b/build.gradle index dda11f1..514de34 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ dependencies { compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.22.2' compile 'javax.enterprise:cdi-api:2.0-EDR1' compile 'org.jboss.weld.servlet:weld-servlet-core:3.0.0.Alpha12' + compile 'org.apache.commons:commons-pool2:2.4.2' // some needed webjars (frontend-stuff) compile 'org.webjars:jquery:2.2.3' compile 'org.webjars:materializecss:0.97.5' diff --git a/pom.xml b/pom.xml index 71f93dc..2637dfa 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,11 @@ weld-servlet-core 3.0.0.Alpha12 + + org.apache.commons + commons-pool2 + 2.4.2 + org.webjars jquery diff --git a/src/main/java/dasniko/ozark/react/NashornPool.java b/src/main/java/dasniko/ozark/react/NashornPool.java new file mode 100644 index 0000000..c221223 --- /dev/null +++ b/src/main/java/dasniko/ozark/react/NashornPool.java @@ -0,0 +1,34 @@ +package dasniko.ozark.react; + +import org.apache.commons.pool2.ObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Initialized; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Produces; +import javax.script.ScriptEngine; + +/** + * @author Niko Köbler, http://www.n-k.de, @dasniko + */ +@ApplicationScoped +public class NashornPool { + + private GenericObjectPool pool; + + public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMinIdle(1); + + pool = new GenericObjectPool<>(new NashornPoolFactory(), config); + } + + @Produces + public ObjectPool getObjectPool() { + return this.pool; + } + + +} diff --git a/src/main/java/dasniko/ozark/react/NashornPoolFactory.java b/src/main/java/dasniko/ozark/react/NashornPoolFactory.java new file mode 100644 index 0000000..d52a40e --- /dev/null +++ b/src/main/java/dasniko/ozark/react/NashornPoolFactory.java @@ -0,0 +1,74 @@ +package dasniko.ozark.react; + +import org.apache.commons.pool2.BasePooledObjectFactory; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.impl.DefaultPooledObject; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Niko Köbler, http://www.n-k.de, @dasniko + */ +public class NashornPoolFactory extends BasePooledObjectFactory { + + private static final String DEFAULT_JS_RESOURCE_PATH = "/js"; + private static final String RESOURCE_PATH = System.getProperty("mvc.reactjs.resourcePath", DEFAULT_JS_RESOURCE_PATH); + + @Override + public ScriptEngine create() throws Exception { + ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn"); + + // initial basically needed files for dealing with react + List jsResources = new ArrayList<>(); + jsResources.add("nashorn-polyfill.js"); + + // add all resources from custom application + jsResources.addAll(getAllFilesFromResourcePath()); + // and load/evaluate them within nashorn + jsResources.forEach(p -> { + try { + nashorn.eval(read(p)); + } catch (ScriptException e) { + throw new RuntimeException(e); + } + }); + + return nashorn; + } + + @Override + public PooledObject wrap(ScriptEngine scriptEngine) { + return new DefaultPooledObject<>(scriptEngine); + } + + private List getAllFilesFromResourcePath() { + URL url = this.getClass().getClassLoader().getResource(RESOURCE_PATH); + if (url != null && url.getProtocol().equals("file")) { + try { + return Arrays.stream(new File(url.toURI()).list()).map(p -> RESOURCE_PATH + "/" + p).collect(Collectors.toList()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } else { + // TODO do something + return new ArrayList<>(); + } + } + + private Reader read(String path) { + InputStream in = getClass().getClassLoader().getResourceAsStream(path); + return new InputStreamReader(in); + } +} diff --git a/src/main/java/dasniko/ozark/react/React.java b/src/main/java/dasniko/ozark/react/React.java index b38c97f..ca96f77 100644 --- a/src/main/java/dasniko/ozark/react/React.java +++ b/src/main/java/dasniko/ozark/react/React.java @@ -1,82 +1,34 @@ package dasniko.ozark.react; import jdk.nashorn.api.scripting.ScriptObjectMirror; +import org.apache.commons.pool2.ObjectPool; +import javax.inject.Inject; import javax.script.Invocable; import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; /** * @author Niko Köbler, http://www.n-k.de, @dasniko */ public class React { - private static final String DEFAULT_JS_RESOURCE_PATH = "/js"; - private static final String RESOURCE_PATH = System.getProperty("mvc.reactjs.resourcePath", DEFAULT_JS_RESOURCE_PATH); - - private ThreadLocal engineHolder = ThreadLocal.withInitial(() -> { - ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn"); - - // initial basically needed files for dealing with react - List jsResources = new ArrayList<>(); - jsResources.add("nashorn-polyfill.js"); - - // add all resources from custom application - jsResources.addAll(getAllFilesFromResourcePath()); - // and load/evaluate them within nashorn - jsResources.forEach(p -> { - try { - nashorn.eval(read(p)); - } catch (ScriptException e) { - throw new RuntimeException(e); - } - }); - - return nashorn; - }); - - private List getAllFilesFromResourcePath() { - URL url = this.getClass().getClassLoader().getResource(RESOURCE_PATH); - if (url != null && url.getProtocol().equals("file")) { - try { - return Arrays.asList(new File(url.toURI()).list()).stream().map(p -> RESOURCE_PATH + "/" + p).collect(Collectors.toList()); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } else { - // TODO do something - return new ArrayList<>(); - } - } + @Inject + private ObjectPool scriptEnginePool; String render(String function, Object object) { try { Object html; + ScriptEngine scriptEngine = scriptEnginePool.borrowObject(); if (function.contains(".")) { String[] parts = function.split("\\."); - html = ((ScriptObjectMirror) engineHolder.get().get(parts[0])).callMember(parts[1], object); + html = ((ScriptObjectMirror) scriptEngine.get(parts[0])).callMember(parts[1], object); } else { - html = ((Invocable) engineHolder.get()).invokeFunction(function, object); + html = ((Invocable) scriptEngine).invokeFunction(function, object); } + scriptEnginePool.returnObject(scriptEngine); return String.valueOf(html); } catch (Exception e) { throw new IllegalStateException("failed to render react component", e); } } - - private Reader read(String path) { - InputStream in = getClass().getClassLoader().getResourceAsStream(path); - return new InputStreamReader(in); - } -} \ No newline at end of file +}