Skip to content

Commit

Permalink
#1 replace creation of ScriptEngines bound to each thread with Thread…
Browse files Browse the repository at this point in the history
…Local with a pool of Nashorn ScriptEngines (w/ use of commons-pool)
  • Loading branch information
dasniko committed Sep 2, 2016
1 parent 2be882d commit e4fbb5f
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 57 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@
<artifactId>weld-servlet-core</artifactId>
<version>3.0.0.Alpha12</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/dasniko/ozark/react/NashornPool.java
Original file line number Diff line number Diff line change
@@ -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<ScriptEngine> 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<ScriptEngine> getObjectPool() {
return this.pool;
}


}
74 changes: 74 additions & 0 deletions src/main/java/dasniko/ozark/react/NashornPoolFactory.java
Original file line number Diff line number Diff line change
@@ -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<ScriptEngine> {

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<String> 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<ScriptEngine> wrap(ScriptEngine scriptEngine) {
return new DefaultPooledObject<>(scriptEngine);
}

private List<String> 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);
}
}
66 changes: 9 additions & 57 deletions src/main/java/dasniko/ozark/react/React.java
Original file line number Diff line number Diff line change
@@ -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<ScriptEngine> engineHolder = ThreadLocal.withInitial(() -> {
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");

// initial basically needed files for dealing with react
List<String> 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<String> 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<ScriptEngine> 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);
}
}
}

0 comments on commit e4fbb5f

Please sign in to comment.