Интерпретация JavaScript в Java с помощью Rhino: приостановка / возобновление работы сценариев - PullRequest
15 голосов
/ 23 декабря 2011

Я использую пакет javax.script. * JDK.В частности, я использую движок JavaScript, который, судя по тому, что я прочитал, основан на интерпретаторе JavaScript-in-Java, разработанном Mozilla под названием Rhino.

Чего я надеюсь добитьсяв основном, чтобы мой JavaScript мог «останавливать» себя в определенной точке кода (скажем, на полпути через вызов функции) и возобновлять себя только позже, когда Java это позволяет.

Чтобы проиллюстрировать, чтоЯ имею в виду, представьте этот код JavaScript:

function myJSFunction() {
    print("Hello ");
    mysteriousPauseFunction(); // this is the part I'm wondering about.  basically, the script should break here and resume later at Java's discretion...
    // upon reaching this comment, we know now that Java has told JavaScript that it's okay to resume, so the next line will now be executed...
    print("world");
}

Если часть «пауза» / «разрыв» включает в себя привязку функции Java и передачу ей ссылки на текущий ScriptEngine или что-то еще, это круто для меня.Я думаю, что это, вероятно, будет связано с приостановкой JavaScript изнутри Java.

Я немного погуглил и обнаружил, что ключевое слово здесь выглядит как «продолжение».Из того, что я могу сказать, Rhino поддерживает только продолжения в интерпретируемом режиме (в отличие от скомпилированного режима), что, как я вижу, достигается путем установки «контекста» в -2.Поскольку встроенный JDK ScriptEngine, кажется, ничего не упоминает о контекстах (или, может быть, я его упускаю), означает ли это, что мне придется вместо этого загружать и использовать библиотеку Mozilla Rhino напрямую?

И Rhino?продолжения что мне нужно для этого?Я нашел полезное руководство по продолжениям Rhino, но, прочитав его, я не уверен на 100%, сможет ли это выполнить то, что я описал выше.Если это , что я ищу, тогда мой следующий вопрос касается упомянутой «сериализации»: означает ли это, что когда я возобновлю свой сценарий, все переменные будут сброшены, если я их не сериализую??

Обновление: Похоже, это возможно с Rhino.Вот что у меня есть в моем JavaScript;после кода я объясню, что он делает ...

var end = new Continuation();

function myJSFunction()
{
    print("Hello ");
    var kont = new Continuation();
    storePause(script, kont); // script is previously bound by Java into the JavaScript.  it is a reference to the script itself.
    end();
    print("world");

}

Моя функция "storePause ()" - это функция Java, которую я написал, и она привязана к JavaScript, но сейчасничего не делает.Моя следующая цель будет сделать его код таким, чтобы он сохранял информацию о продолжении и сценарии как объекты Java, чтобы Java могла возобновить выполнение сценария позднее.

Прямо сейчас, то, что он делает, это приостанавливает / «ломает»сценарий после «Hello» печатается, но до «world», так что это доказывает мне, что можно приостановить сценарий таким образом.

Итак, все, что мне нужно было выяснить наэтот момент, как возобновить продолжение.Обратите внимание, что вышеперечисленное работает по умолчанию с использованием механизма сценариев JDK (на данном этапе мне не нужно было беспокоиться о интерпретируемом режиме по сравнению с компилируемым режимом - похоже, по умолчанию это интерпретированный режим), но это похоже на процесс возобновления скриптапотребуется библиотека Mozilla's Rhino.

Ответы [ 3 ]

5 голосов
/ 26 декабря 2011

Хорошо, мне потребовалось много часов, чтобы покопаться в документации, учебных пособиях и примерах, а также опубликовать здесь и в Rhino Google Group , но мне удалось скомпилировать работающее решение.Поскольку, кажется, нет полного примера, я опубликую свои выводы здесь для тех, кто сталкивается с этим в будущем.

На самом деле, мои выводы, вероятно, слишком длинные, чтобы публиковать здесь, поэтому я решил написатьучебник в моем блоге:

http://www.joshforde.com/blog/?p=7

Надеюсь, что кому-то поможет.Насколько я знаю, это единственный полный учебник по Rhino, который показывает, как сделать все следующее: инициализировать Rhino, загрузить скрипт из файла JavaScript (* .js), автоматически связать все функции в определенном классе Java(например, ScriptFunctions) как глобальные функции в JavaScript, и, наконец, вызов функции JavaScript и обработку продолжений для этого вызова.

В основном , проблема заключалась в том, что мне нужно было сначала загрузить исходный код Mozilla Rhinoкод (поскольку версия, упакованная в JDK, устарела и не поддерживает продолжения), перепишите весь мой код, чтобы использовать официальный синтаксис пакета Rhino (он сильно отличается от синтаксиса JDK ScriptingEngine), напишите функцию Java, которая выдаетИсключение ContinuationPending и связать его с JavaScript, чтобы JavaScript мог его вызывать (потому что выброс ContinuationPending непосредственно из JavaScript приводит к созданию исключения JavaScriptException, а не к ContinuationPending и даже к попытке вызвать getCause ()в этом случае JavaScriptException приводит к нулевому значению), а затем в моем коде Java, который вызывает мою функцию JavaScript («myJSFunction» в моем исходном примере), есть блоки try / catch для проверки ContinuationPending (который является исключением), а затем используйте этоПродолжение Ожидание позже, чтобы возобновить выполнение сценария.

Фу.Это было тяжело, но теперь это того стоит.

0 голосов
/ 30 сентября 2013

Вы не объяснили, почему вы это делаете, но я эмулировал программу, которая взаимодействует с конечным пользователем, например:

print('Hello!'); 
a=Number(input('enter a number')); 
b=Number(input('and another number')); 
print('the sum of '+a+' plus '+b+' is '+(a+b))

У меня все работает, просто создав печатьи функция ввода в javascript, которая проверяет состояние программы.

вы можете увидеть демо здесь.

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

Надеюсь, это поможет

0 голосов
/ 24 декабря 2011

Вы можете использовать wait / notify:

public final class Pause {
  private final Object lock = new Object();

  public void await() throws InterruptedException {
    synchronized (lock) {
      lock.wait();
    }
  }

  public void resumeAll() {
    synchronized (lock) {
      lock.notifyAll();
    }
  }
}

Использование:

final Pause pause = new Pause();

class Resumer implements Runnable {
  @Override public void run() {
    try {
      Thread.sleep(5000);
      pause.resumeAll();
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
  }
}
new Thread(new Resumer()).start();

SimpleBindings bindings = new SimpleBindings();
bindings.put("pause", pause);
String script = "print('Hello, ');\n"
              + "pause.await();\n"
              + "println('ECMAScript!');\n";
new ScriptEngineManager().getEngineByName("ECMAScript")
                         .eval(script, bindings);

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

Примечание: InterruptedException в await() должен обрабатываться либовызывающий или делая что-то более разумное в await().

...