Rhino エンジンには、スクリプト ファイルの実行を途中で停止できる API がありますか。たとえば、無限ループがあるスクリプト ファイルがあります。途中で実行を停止するにはどうすればよいですか?
もちろん、Rhino エンジンを起動した jvm を停止してスクリプトを実行することもできます。しかし、プログラムでスクリプトを開始し、Rhino Engine もアプリケーションと同じ JVM で実行しているため、その理由で jvm セッション全体を強制終了したくありません。
実行中の JavaScript の実行を停止するには、次の方法を使用します。
1) ダミーのデバッガーを作成し、最初に作成したコンテキストにアタッチします。
mContext = Context.enter();
ObservingDebuggerobservingDebugger = new ObservingDebugger();
mContext.setDebugger(observingDebugger, new Integer(0));
mContext.setGeneratingDebug(true);
mContext.setOptimizationLevel(-1);
ObservingDebugger コードは次のようになります。
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.debug.DebugFrame;
import org.mozilla.javascript.debug.DebuggableScript;
import org.mozilla.javascript.debug.Debugger;
public class ObservingDebugger implements Debugger
{
boolean isDisconnected = false;
private DebugFrame debugFrame = null;
public boolean isDisconnected() {
return isDisconnected;
}
public void setDisconnected(boolean isDisconnected) {
this.isDisconnected = isDisconnected;
if(debugFrame != null){
((ObservingDebugFrame)debugFrame).setDisconnected(isDisconnected);
}
}
public ObservingDebugger() {
}
public DebugFrame getFrame(Context cx, DebuggableScript fnOrScript)
{
if(debugFrame == null){
debugFrame = new ObservingDebugFrame(isDisconnected);
}
return debugFrame;
}
@Override
public void handleCompilationDone(Context arg0, DebuggableScript arg1, String arg2) { } }
// internal ObservingDebugFrame class
class ObservingDebugFrame implements DebugFrame
{
boolean isDisconnected = false;
public boolean isDisconnected() {
return isDisconnected;
}
public void setDisconnected(boolean isDisconnected) {
this.isDisconnected = isDisconnected;
}
ObservingDebugFrame(boolean isDisconnected)
{
this.isDisconnected = isDisconnected;
}
public void onEnter(Context cx, Scriptable activation,
Scriptable thisObj, Object[] args)
{ }
public void onLineChange(Context cx, int lineNumber)
{
if(isDisconnected){
throw new RuntimeException("Script Execution terminaed");
}
}
public void onExceptionThrown(Context cx, Throwable ex)
{ }
public void onExit(Context cx, boolean byThrow,
Object resultOrException)
{ }
@Override
public void onDebuggerStatement(Context arg0) { } }
ObservingDebugger クラスはブール変数「isDisconnected」を管理し、ユーザーが停止ボタンをクリックすると (実行を停止したい場合)、この変数は true に設定されます。次のように変数が true に設定されると、Rhino の実行はすぐに終了します。
observingDebugger.setDisconnected(true);
解決策を探している他の人のために、ContextFactory の javadoc には、10 秒以上実行されているスクリプトを停止する方法が詳しく説明されています。
https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/ContextFactory.java
ExecutorService とタイムアウトした future.get を使用して、新しいスレッドでスクリプトを実行します。
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(threadEvaluation);
try {
System.out.println("Started..");
future.get(100, TimeUnit.MILLISECONDS);
System.out.println("Finished!");
} catch (TimeoutException e) {
future.cancel(true);
System.out.println("Terminated!");
}
このアプローチは、スクリプトを実行しているスレッドを停止しないことに注意してください! これを行うには、スクリプトを実行しているスレッドに割り込みが通知されるため、この状況を定期的に監視するカスタム ContextFactory を作成できます。
public class InterruptableContextFactory extends ContextFactory {
public static boolean initialized = false;
public static void init() {
if (!initialized) {
ContextFactory.initGlobal(new InterruptableContextFactory());
initialized = true;
}
}
@Override
protected void observeInstructionCount(Context cx, int instructionCount) {
System.out.println(instructionCount + " javascript instructions!");
if (Thread.currentThread().isInterrupted()) {
throw new Error("script execution aborted");
}
}
@Override
protected Context makeContext() {
Context cx = super.makeContext();
//set a number of instructions here
cx.setInstructionObserverThreshold(10000);
return cx;
}
}
Context オブジェクトを作成する前に、この ContextFactory をデフォルトとして使用するようにアプリケーションを構成する必要があります。
InterruptableContextFactory.init()
Callable の call メソッド内で、エラーをキャプチャできます。
try {
cx.setOptimizationLevel(9);
cx.setInstructionObserverThreshold(10000);
ScriptableObject scope = cx.initStandardObjects();
// your code here
} catch (Error e) {
System.out.println("execution was aborted: " + e.getMessage());
} finally {
Context.exit();
}
Rhinoエンジンにはこれを行うためのメカニズムがないようです(悪いRhino!)、内部でスレッドを作成するかどうかを判断するのは難しいためThreadGroup
、Rhinoエンジンを作成し、ロードして実行し、そのグループのスレッド内からそのスクリプトを削除したい場合は、ThreadGroup.stop()
. はい、廃止されましたが、Rhino ライブラリ側の協力がないことを考えると、実際には他に方法はありません。