Интерпретация JavaScript в Java с помощью Rhino: приостановка/возобновление скриптов
Я использую пакет javax.script. * JDK. В частности, я использую механизм JavaScript, который, из того, что я читал, похоже, основан на разработанном Mozilla JavaScript-in-Java-интерпретаторе 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 discretion...
// upon reaching this comment, we know now that Java has told JavaScript that it okay to resume, so the next line will now be executed...
print("world");
}
Если "пауза" / "ломающаяся" часть включает привязку функции Java и передачу ее ссылки на текущий ScriptEngine или что-то еще, что круто со мной. Я думаю, что это, вероятно, будет включать: приостановку JavaScript из Java.
Я сделал несколько поисковых запросов и обнаружил, что ключевое слово здесь выглядит как "продолжения". Из того, что я могу сказать, Rhino поддерживает только продолжения в интерпретируемом режиме (в сравнении с скомпилированным режимом), который, как я вижу, достигается путем установки "контекста" на -2. Поскольку встроенный JDK ScriptEngine, похоже, не упоминает ничего о контекстах (или, может быть, мне его не хватает), значит ли это, что я должен загружать и использовать библиотеку Mozilla Rhino напрямую?
И есть ли продолжения носорогов, что мне нужно для этого? Я нашел полезный учебник о продолжениях Rhino, но, прочитав его, я не уверен на 100%, если это будет возможно чтобы выполнить то, что я описал выше. Если это то, что я ищу, тогда мой следующий вопрос касается упоминания о "сериализации": означает ли это, что, когда я возобновляю свой script, все переменные будут отменены, если я не сериализую их?
Обновление: Похоже, это возможно с 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, но прямо сейчас она ничего не делает. Следующей моей целью будет сформировать его код таким образом, чтобы он сохранял продолжение и script информацию как объекты Java, так что Java может возобновить script позже.
В настоящий момент то, что он делает, приостанавливает/ "ломает" script после "Hello", но перед печатью "мир", это доказывает мне, что можно приостановить script таким образом.
Итак, все, что я должен был оставить, чтобы понять на этом этапе, - это возобновление продолжения. Обратите внимание, что вышеописанное работает с использованием механизма сценариев JDK по умолчанию (мне не нужно беспокоиться о интерпретируемом режиме и скомпилированном режиме на данный момент - он по умолчанию используется для интерпретируемого режима), но он выглядит как процесс возобновления a script потребуется библиотека Mozilla Rhino.
Ответы
Ответ 1
Хорошо, мне потребовалось много часов, чтобы прорыть документацию, учебники и примеры, а также разместить здесь и в Rhino Google Group, но мне удалось скомпилировать рабочее решение. Поскольку, кажется, нет полного примера, я опубликую свои результаты здесь для всех, кто наткнется на это в будущем.
Собственно, мои выводы, вероятно, слишком долго, чтобы публиковать здесь, поэтому я решил написать учебник в своем блоге:
http://www.joshforde.com/blog/?p=7
Надеюсь, что это поможет кому-то. Насколько я знаю, это единственный полный учебник Rhino, в котором показано, как выполнить все следующие операции: инициализировать Rhino, загрузить script из файла JavaScript (*.js), автоматически связать все функции в определенном Java class (например 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 (который является исключением), а затем использовать ContinuationPending
позже, чтобы возобновить script.
Уф. Это было сложно, но все это стоит того.
Ответ 2
Вы не объяснили, почему вы это делаете, но я эмулировал программу, которая взаимодействует с конечным пользователем, например:
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, чтобы вы могли посмотреть исходный код с любым браузером.
Надеюсь, что это поможет
Ответ 3
Вы можете использовать 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
on await()
следует обрабатывать либо вызывающим, либо делать что-то более разумное в await()
.