如何杀死 Rhino 脚本

How to kill a Rhino script

我们正在使用 Rhino 在我们的 Java 应用程序中执行 Java 脚本。我们使用了一些 Rhino 特有的功能,因此我们无法升级到 Nashorn。 我们的问题是脚本是用户创建的,我们执行的时候,如果出现死循环之类的错误,就一直执行下去。我们想将时间限制设置为 30 秒。 有没有办法在超时时终止脚本?

您可以通过创建虚拟调试器来停止来自 运行 的循环,并将其直接添加到 java 脚本中,如下所示:

mContext = Context.enter();
ObservingDebugger observingDebugger = new ObservingDebugger();
mContext.setDebugger(observingDebugger, new Integer(0));
mContext.setGeneratingDebug(true);
mContext.setOptimizationLevel(-1);

然后通过 java 程序,您创建一个 class 如下所示:

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) { } }

然后设置程序的定时器,你导入:

import java.util.Timer;

然后为 ObservingDebugger 设置定时器首选项:

timer.schedule(new TimerTask() {
  @Override
  public void run() {
   // code here
  }
}, 2*60*1000);
// Since Java-8
timer.schedule(() -> /* your database code here */, 2*60*1000);

您应该扩展 ContextFactory class 并覆盖方法 observeInstructionCount(Context ctx, int instructionCount)。 Rhino 会定期调用此方法,您可以通过以下方式检查 运行 到目前为止的时间:

public class ScriptDynamicScopeFactory extends ContextFactory {
    @Override
    protected Context makeContext() {
        ScriptContext ctx = new ScriptContext();
        ctx.setInstructionObserverThreshold(10000);
        return ctx;
    }

    @Override
    protected void observeInstructionCount(Context ctx, int instructionCount) {
      long currentTime = System.currentTimeMillis();
      long executionTime = (currentTime - ((ScriptContext) ctx).startTime());
      // do something if execution time is greater then your timeout
    }
}

请注意,您还需要覆盖 makeContext() 以设置调用观察者的频率。请记住,这是执行的指令数,这意味着它不会每隔 X 毫秒就被一致地调用一次。如果一条指令需要很多时间(例如,调用您的 Java 应用程序),这可能效果不佳,但我认为它在几乎所有情况下都能很好地完成工作。