title: Attempting to make Nashorn thread safe
body: <p>I've recently been working with the Play! framework and Nashorn in an attempt to render a Redux application. Initially, I had implemented multiple Nashorn engines in a ThreadPoolExecutor and used futures when running <code>engine.eval()</code>. The performance was terrible, I'm assuming due to the high I/O and the blocking future I was using.</p>
  
  <p>After becoming more familiar with Play! and their async/promise pattern, I attempted to start over with just a single <code>ScriptEngine</code>; per <a href="https://stackoverflow.com/a/30159424/5956783">https://stackoverflow.com/a/30159424/5956783</a>, the script engine itself is thread safe, however bindings are not. Here is the class in its entirety:</p>
  
  <pre><code>package services;
  
  import akka.actor.ActorSystem;
  
  import play.libs.F;
  import scala.concurrent.ExecutionContext;
  
  import java.io.FileReader;
  
  
  import javax.inject.Inject;
  import javax.inject.Singleton;
  import javax.script.*;
  
  @Singleton
  public class NashornEngine extends JSEngineAbstract {
  
      private NashornThread engine;
      private final ActorSystem actorSystem;
  
  
      protected class NashornThread {
          private ScriptEngine engine;
          private final ExecutionContext executionContext = actorSystem.dispatchers().lookup("js-engine-executor");
          private CompiledScript compiledScript;
  
  
          public NashornThread() {
              try {
                  String dir = System.getProperty("user.dir");
                  this.engine = new ScriptEngineManager(null).getEngineByName("nashorn");
                  this.compiledScript = ((Compilable) this.engine).compile(new FileReader(dir + "/public/javascripts/bundle.js"));
              } catch (Exception e) {
                  System.out.println(e.getMessage());
              }
          }
  
          public ScriptEngine getEngine() {
              return engine;
          }
  
          public F.Promise&lt;String&gt; getContent(String path, String globalCode) {
              return F.Promise.promise(() -&gt; this.executeMethod(path, globalCode), this.executionContext);
          }
  
          private String executeMethod(String path, String json) throws ScriptException {
              Bindings newBinding = engine.createBindings();
              try {
                  this.compiledScript.eval(newBinding);
                  getEngine().setBindings(newBinding, ScriptContext.ENGINE_SCOPE);
                  getEngine().eval("var global = this;");
                  getEngine().eval("var console = {log: print, error: print, warn: print};");
                  String result = getEngine().eval("App.renderApp('" + path + "', " + json + ")").toString();
  
                  return result;
              } catch(Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
      }
  
  
      @Inject
      public NashornEngine(ActorSystem actorSystem) {
          this.actorSystem = actorSystem;
          this.engine = new NashornThread();
      }
  
      @Override
      public F.Promise&lt;String&gt; getContent(String path, String globalCode) {
          try {
              F.Promise&lt;String&gt; result = engine.getContent(path, globalCode);
  
              return result.map((String r) -&gt; r);
          } catch (Exception e) {
              e.printStackTrace();
              return null;
          }
      }
  }
  </code></pre>
  
  <p>My JavaScript application exports an <code>App</code> object with the method <code>renderApp(path, state)</code> visible. This actually <em>does</em> work, but only 16 times (yes, always 16). Starting with iteration 17, I get the following exception and accompanying stack trace:</p>
  
  <pre><code>java.lang.ClassCastException: jdk.nashorn.internal.objects.NativeRegExpExecResult cannot be cast to jdk.nashorn.internal.objects.NativeArray
      at jdk.nashorn.internal.objects.NativeArray.getContinuousArrayDataCCE(NativeArray.java:1900)
      at jdk.nashorn.internal.objects.NativeArray.popObject(NativeArray.java:937)
      at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:660)
      at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228)
      at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23589$782286AA$\^eval\_.L:22918$L:22920$matchPattern(&lt;eval&gt;:23049)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23588$816752AAAAAA$\^eval\_.L:24077$L:24079$matchRouteDeep(&lt;eval&gt;:24168)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23587$820262DAA$\^eval\_.L:24077$L:24079$matchRoutes$L:24252$L:24253(&lt;eval&gt;:24254)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23586$809060$\^eval\_.L:23848$loopAsync$next(&lt;eval&gt;:23869)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23584$808906AAA$\^eval\_.L:23848$loopAsync(&lt;eval&gt;:23875)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23583$820189$\^eval\_.L:24077$L:24079$matchRoutes$L:24252(&lt;eval&gt;:24253)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23582$819862AAA$\^eval\_.L:24077$L:24079$matchRoutes(&lt;eval&gt;:24252)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23580$789440AA$\^eval\_.L:23151$L:23153$useRoutes$L:23209$match(&lt;eval&gt;:23239)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23518$847004AA$\^eval\_.L:25026$L:25028$match(&lt;eval&gt;:25084)
      at jdk.nashorn.internal.scripts.Script$Recompilation$23468$3872AA$\^eval\_.L:53$renderApp(&lt;eval&gt;:147)
      at jdk.nashorn.internal.scripts.Script$23467$\^eval\_.:program(&lt;eval&gt;:1)
      at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:640)
      at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228)
      at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
      at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:446)
      at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:403)
      at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:399)
      at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155)
      at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
      at services.NashornEngine$NashornThread.executeMethod(NashornEngine.java:62)
      at services.NashornEngine$NashornThread.lambda$getContent$0(NashornEngine.java:48)
      at play.core.j.FPromiseHelper$$anonfun$promise$2.apply(FPromiseHelper.scala:36)
      at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
      at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
      at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40)
      at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:397)
      at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
      at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
      at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
      at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
  </code></pre>
  
  <p>I am under the impression that creating a new binding with the compiled script would be treated as net-new to the engine, but that doesn't seem to be the case. What, if anything, am I doing wrong here?</p>
  
  <p>I have attempted to use an <code>Invocable</code> as well to invoke the method on the <code>App</code> object, but that doesn't make any difference.</p>
  
  <p><strong>EDIT</strong>
  I'm on an 8 core machine with hyper-threading, so that may explain the 16 successful attempts before the failed 17th attempt. Also, I've updated the <code>executeMethod</code> method to below:</p>
  
  <pre><code>private String executeMethod(String path, String json) throws ScriptException {
              Bindings newBinding = engine.createBindings();
              try {
                  this.compiledScript.eval(newBinding);
                  getEngine().eval("var global = this;", newBinding);
                  getEngine().eval("var console = {log: print, error: print, warn: print};", newBinding);
                  Object app = getEngine().eval("App", newBinding);
                  Invocable invEngine = (Invocable) getEngine();
                  String result = invEngine.invokeMethod(app, "renderApp", path, json).toString();
  //              String result = getEngine().eval("App.renderApp('" + path + "', " + json + ")", newBinding).toString();
  
                  return result;
              } catch(Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
  </code></pre>