Доступ к переменной ScriptContext с помощью Nashorn JavaScript Engine (Java 8)

Я использовал следующий код с Rhino Движок JavaScript в Java:

@Test
public void testRhino() throws ScriptException {
    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("rhino");
    final String raw = "I am the raw value injected";
    final ScriptContext ctx = new SimpleScriptContext();
    ctx.setAttribute("raw", raw, ScriptContext.ENGINE_SCOPE);

    String script = "var result = 'I am a result';";
    script += "java.lang.System.out.println(raw);";
    script += "'I am a returned value';";

    final Object res = engine.eval(script, ctx);
    System.out.println(ctx.getAttribute("result"));
    System.out.println(res);
}

Вывод скрипта (используя Rhino) является:

I am the raw value injected
I am a result
I am a returned value

В пределах Nashorn Движок JavaScript, я не получаю значения для result:

@Test
public void testNashorn() throws ScriptException {
    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("nashorn");
    final String raw = "I am the raw value injected";
    final ScriptContext ctx = new SimpleScriptContext();
    ctx.setAttribute("raw", raw, ScriptContext.ENGINE_SCOPE);

    String script = "var result = 'I am a result';";
    script += "java.lang.System.out.println(raw);";
    script += "'I am a returned value';";

    final Object res = engine.eval(script, ctx);
    System.out.println(ctx.getAttribute("result"));
    System.out.println(res);
}

возвращается

I am the raw value injected
null
I am a returned value

Как я могу получить доступ к значению result переменная ScriptContext с использованием nashorn двигатель?

2 ответа

Решение

Если вы используете API ScriptEngine.createEngine для создания привязок ENGINE_SCOPE, он будет работать как положено:

import javax.script.*;

public class Main {
  public static void main(String[] args) throws Exception {

    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("nashorn");
    final String raw = "I am the raw value injected";
    final ScriptContext ctx = new SimpleScriptContext();

    // **This is the inserted line**
    ctx.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);

    ctx.setAttribute("raw", raw, ScriptContext.ENGINE_SCOPE);

    String script = "var result = 'I am a result';";
    script += "java.lang.System.out.println(raw);";
    script += "'I am a returned value';";

    final Object res = engine.eval(script, ctx);
    System.out.println(ctx.getAttribute("result"));
    System.out.println(res);
 }
}

Нашорн лечит Bindings Хранится в ScriptContext как "только для чтения". Любая попытка установить переменную, хранящуюся в Bindings объект (или для создания новой переменной) приведет к созданию новой переменной в nashorn.global который затеняет Bindings параметр с этим именем.

Вы можете использовать движок, чтобы "оценить" переменную, используя этот код:

System.out.println( engine.eval("result", ctx) );

Это, однако, довольно некрасиво. "result" сначала компилируется в скрипт, а затем этот скрипт оценивается, чтобы вернуть значение переменной. Хорошо для тестирования, но, возможно, слишком неэффективно для общего решения.

Лучший, но, возможно, более хрупкий метод состоит в том, чтобы извлечь переменную "nashorn.global" и запросить для нее нужное значение.

Bindings nashorn_global = (Bindings) ctx.getAttribute("nashorn.global");
System.out.println( nashorn_global.get("result") );

Смотрите также мой взлом / ответ в Захват глобальных переменных Nashorn для автоматического способа перемещения nashorn.global значения обратно к Map<String,Object> после оценки сценария.

Другие вопросы по тегам