2016-02-22 5 views
2

Я недавно работал с Play! и Nashorn в попытке сделать приложение Redux. Первоначально я реализовал несколько двигателей Nashorn в ThreadPoolExecutor и использовал фьючерсы при запуске engine.eval(). Производительность была ужасной, я предполагаю из-за высокого ввода-вывода и будущего блокировки, которое я использовал.Попытка сделать Nashorn нить безопасной

После ознакомления с Play! и их асинхронный/обещающий шаблон, я попытался начать с всего лишь ScriptEngine; за https://stackoverflow.com/a/30159424/5956783, сам скриптовый движок является потокобезопасным, однако привязки не являются. Вот класс во всей своей полноте:

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<String> getContent(String path, String globalCode) { 
      return F.Promise.promise(() -> 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<String> getContent(String path, String globalCode) { 
     try { 
      F.Promise<String> result = engine.getContent(path, globalCode); 

      return result.map((String r) -> r); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 
} 

Мой JavaScript приложение экспортирует App объект с методом renderApp(path, state) видимым. Это на самом деле делает работы, но только 16 раз (да, всегда 16). Начиная с итерацией 17, я получаю следующее исключение и сопроводительные трассировки стеки:

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(<eval>:23049) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$23588$816752AAAAAA$\^eval\_.L:24077$L:24079$matchRouteDeep(<eval>:24168) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$23587$820262DAA$\^eval\_.L:24077$L:24079$matchRoutes$L:24252$L:24253(<eval>:24254) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$23586$809060$\^eval\_.L:23848$loopAsync$next(<eval>:23869) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$23584$808906AAA$\^eval\_.L:23848$loopAsync(<eval>:23875) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$23583$820189$\^eval\_.L:24077$L:24079$matchRoutes$L:24252(<eval>:24253) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$23582$819862AAA$\^eval\_.L:24077$L:24079$matchRoutes(<eval>:24252) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$23580$789440AA$\^eval\_.L:23151$L:23153$useRoutes$L:23209$match(<eval>:23239) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$23518$847004AA$\^eval\_.L:25026$L:25028$match(<eval>:25084) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$23468$3872AA$\^eval\_.L:53$renderApp(<eval>:147) 
    at jdk.nashorn.internal.scripts.Script$23467$\^eval\_.:program(<eval>: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) 

Я нахожусь под впечатлением, что создание нового связывания с скомпилированным скриптом будет рассматриваться как сеть-новой к двигателю, но Безразлично Кажется, это так. Что, если что-нибудь, я делаю неправильно здесь?

Я попытался использовать Invocable, а также вызвать метод на объекте App, но это не имеет никакого значения.

EDIT Я на 8 машине ядра с Hyper-Threading, так что может объяснить 16 успешных попыток до неудачной попытки 17-го. Кроме того, я обновил метод executeMethod ниже:

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; 
      } 
     } 

ответ

1

К сожалению, вы попали в bug Nashorn. Хорошей новостью является то, что она уже исправлена, и исправление доступно в current early access releases.

+0

Да! Он работает отлично. Благодаря! –

2

Обновление до последней версии Nashorn или Java 1.8u76 устраняет проблему.

Смежные вопросы