Подтвердить что ты не робот

Исключение исключения IllegalArgumentException от Nashorn - Это ошибка в Java 8?

Я использую Javascript-механизм Nashorn для оценки всего кода JavaScript на стороне сервера, написанного в java-приложении. Для повышения производительности я использую spring для инициализации JsEngine во время запуска, а также для оценки и кэширования всех основных инструментов, таких как Mustache и некоторые общие инструменты JS. Затем каждый раз, когда экран отображает этот предварительно оцененный JsEngine, будет использоваться для оценки кода JavaScript на странице. Он работает нормально на какое-то время, означает, что он отображает страницу как ожидалось, но начинает бросать следующее исключение, когда я продолжаю удалять один и тот же URL

Я не могу найти основную причину проблемы.

@Component
public class JsEngine {

    private ScriptEngine scriptEngine;

    @PostConstruct
    public void init() throws ScriptException, IOException{
        scriptEngine = new ScriptEngineManager().getEngineByName("nashorn");
        this.cacheAllCoreEngines();
        for(String key: defaultEngineSource.keySet()){
            scriptEngine.eval(defaultEngineSource.get(key));
        }
    }

    private void cacheAllCoreEngines()throws IOException{
       //read all core files such as mustache, etc. 
       defaultEngineSource.put("mustache",  FileUtil.readFileFromDisk("<actual path..>/mustache.js"));
    }

    public Object eval(String source) throws  ScriptException{
            .... code to handle exceptions 
            return scriptEngine.eval (source);
    }

}

JsEngine используется ниже,

public class AppRendererImpl implements  AppRenderer {

    @Autowired
    JsEngine jsEngine;

    public String render(){
     ....
     .... //Read source from disk or cache
     jsEngine.eval(source);....
    }     
}

Исключение после нескольких циклов рендеринга,

Исключение:
java.lang.IllegalArgumentException: типы целей и фильтров не соответствуют: (ScriptObject) Object, (Object) Object
    в java.lang.invoke.MethodHandleStatics.newIllegalArgumentException(MethodHandleStatics.java:115)
    в java.lang.invoke.MethodHandles.filterArgument(MethodHandles.java:2416)
    в java.lang.invoke.MethodHandles.filterArguments(MethodHandles.java:2403)
    в jdk.nashorn.internal.lookup.MethodHandleFactory $СтандартныеMethodHandleFunctionality.filterArguments(MethodHandleFactory.java:277)
    в jdk.nashorn.internal.runtime.WithObject.filter(WithObject.java:270)
    в jdk.nashorn.internal.runtime.WithObject.fixExpressionCallSite(WithObject.java:249)
    в jdk.nashorn.internal.runtime.WithObject.lookup(WithObject.java:169)
    в jdk.nashorn.internal.runtime.linker.NashornLinker.getGuardedInvocation(NashornLinker.java:96)
    в jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker.getGuardedInvocation(CompositeTypeBasedGuardingDynamicLinker.java:176)
    в jdk.internal.dynalink.support.CompositeGuardingDynamicLinker.getGuardedInvocation(CompositeGuardingDynamicLinker.java:124)
    в jdk.internal.dynalink.support.LinkerServicesImpl.getGuardedInvocation(LinkerServicesImpl.java:144)
    на jdk.internal.dynalink.DynamicLinker.relink(DynamicLinker.java:232)
    в jdk.nashorn.internal.scripts.Script $\ ^ eval _._ L6 $_L8 (: 21)
    в jdk.nashorn.internal.scripts.Script $\ ^ eval _._ L6 $_L40 (: 41)
    в jdk.nashorn.internal.scripts.Script $\ ^ eval_.runScript(: 1)
    в jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:498)
    в jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:206)
    в jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:378)
    в jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:546)
    в jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:528)
    в jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:524)
    в jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:194)
    в javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
    на com.nube.portal.engines.js.JsEngine.eval(JsEngine.java:111)
    на com.nube.portal.engines.html.tags.HtmlTagScript.eval(HtmlTagScript.java:66)
    ........

У меня есть специальный код, добавленный для копирования всех глобальных объектов на другую карту. Для облегчения некоторых других требований я должен получить доступ ко всему глобальному объекту как "nube". Я не знаю, создаст ли этот код какие-либо проблемы для частого запуска. Имейте в виду, что я не удаляю какие-либо объекты из контекста.

public void movePublicObjects(String prefix) throws NubeException{
    Bindings b1 = scriptEngine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
    Map<String, Object> nubeObjects = new HashMap<String, Object>();
    for(Entry<String, Object> entry: b1.entrySet()){
        if(!entry.getKey().equals("nube")){
            nubeObjects.put(entry.getKey(), entry.getValue());
        }
    }
    b1.put("nube", nubeObjects);
    return;
}

Этот код отлично работает, когда я определяю JsEngine как прототип, но производительность не очень хорошая. Как вы думаете, это ошибка в Нашорне?

4b9b3361

Ответ 1

IIRC, Spring @Component будет по умолчанию областью Singleton, поэтому ваш JsEngine единственный ScriptEngine экземпляр будет совместно использоваться в потоках.

Я нашел обсуждение в списке рассылки разработчиков Nashorn по вопросам безопасности потоков, в котором было сказано:

., Nashorn не является потокобезопасным по дизайну. Действительно, если вы оцениваете

new NashornScriptEngineFactory().getParameter("THREADING")

он вернет null, что означает: "реализация механизма не является потокобезопасной и не может использоваться для одновременного выполнения сценариев на нескольких потоках" - см. http://docs.oracle.com/javase/7/docs/api/javax/script/ScriptEngineFactory.html#getParameter(java.lang.String)

Внутренние библиотеки библиотеки Nashorn являются потокобезопасными., но программы JavaScript, выполняемые в одном экземпляре ядра, не являются потокобезопасными.

Это отличается от Rhino, если раньше вы это пробовали. Используя Nashorn, вам, вероятно, необходимо предпринять шаги для защиты вашего ScriptEngine от одновременного доступа, что может объяснить непредсказуемое поведение, которое вы наблюдаете.

Рассмотрите возможность создания пула, доступа к экземплярам механизма, которые являются нитями локальными, или измените область вашего компонента на прототип вместо одиночного.


Здесь потенциальное решение, использующее поточно-локальные экземпляры инициализированных движков script:

@Component
public class JsEngine {

    private final ThreadLocal<ScriptEngine> threadEngines =
        new ThreadLocal<ScriptEngine>() {
            @Override
            protected ScriptEngine initialValue() {
                ScriptEngine engine =
                    new ScriptEngineManager().getEngineByName("nashorn");
                for (String key: defaultEngineSource.keySet()) {
                    engine.eval(defaultEngineSource.get(key));
                }
                return engine;
            }
        };

    @PostConstruct
    public void init() throws ScriptException, IOException {
        this.cacheAllCoreEngines();
        // engine initialization moved to per-thread via ThreadLocal
    }

    private void cacheAllCoreEngines() throws IOException{
       //read all core files such as mustache, etc. 
       defaultEngineSource.put("mustache", FileUtil.readFileFromDisk("<actual path..>/mustache.js"));
    }

    public Object eval(String source) throws ScriptException{
        // .... code to handle exceptions 
        return threadEngines.get().eval(source);
    }
}

Для инициализации вашего движка (с усами и т.д.) для каждого нового потока, использующего ваш компонент, все равно будет накладные расходы. Однако, если ваше приложение использует пул потоков, то эти же потоки должны быть повторно использованы, и вы не будете платить эту стоимость за каждый вызов, как в случае с областью прототипа.

Ответ 2

Либо вы повредите что-то в постоянном контексте JS, который затем нарушает предположения оптимизатора, или это ошибка с ошибкой. Эта трассировка стека показывает, что ваш код отправился на вызов метода, а путь кода в среде выполнения сценариев удалось достичь места, где оптимизированная версия функции будет применяться к аргументам, которые не соответствуют ее предположениям (фильтры типа аргументов), поэтому он не справляется с этим исключением. Этот кодовый путь никогда не следует выполнять в согласованной среде выполнения, независимо от вашего JS-кода.