问题
Does the Rhino engine have an api that can stop the execution of a script fie in the middle. For example, I have a script file in which there is an infinte loop. How can I stop the execution in the middle?
Of course, I can stop the jvm which started the Rhino engine to excecute the script. But I don't want to kill the entire jvm session for that reason as i have started the script programatically and the Rhino Engine also running in the same JVM as my application.
回答1:
Stopping the execution of running JavaScript can be done by using following way.
1) Create a dummy Debugger and attach it to the context created initially.
mContext = Context.enter();
ObservingDebugger observingDebugger = new ObservingDebugger();
mContext.setDebugger(observingDebugger, new Integer(0));
mContext.setGeneratingDebug(true);
mContext.setOptimizationLevel(-1);
The ObservingDebugger code looks as follows.
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 class will manage the boolean variable "isDisconnected" and when user clicks on the stop button (wants to stop the execution) then this variable is set to true. Once the variable is set to true as follows the Rhino Execution will immediately terminates.
observingDebugger.setDisconnected(true);
回答2:
For anyone else looking for a solution, the javadoc for ContextFactory details how to stop a script running more than 10 seconds:
https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/ContextFactory.java
回答3:
I execute scripts in a new thread using an ExecutorService and timed-out 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!");
}
Notice that this approach will not stop the thread executing the script! In order to do so, as the thread executing your script will be notified to be interrupted, you can create a custom ContextFactory that monitors periodically this situation:
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;
}
}
Before creating any Context object, you need to configure your application to use this ContextFactory as default, just invoke
InterruptableContextFactory.init()
Inside your Callable's call method, you can capture the Error:
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();
}
回答4:
The Rhino engine doesn't appear to have any mechanism for doing this (bad Rhino!) and it's hard to tell whether it creates threads internally, so the only solution you've got is to create a ThreadGroup, load and execute the Rhino engine and its script from inside a thread in that group, and when you want to kill it off, use ThreadGroup.stop(). Yes, it's deprecated but there's really no other way to do it given that there's no cooperation on the part of the Rhino library.
来源:https://stackoverflow.com/questions/10246030/stopping-the-rhino-engine-in-middle-of-execution