【问题标题】:Stopping the Rhino Engine in middle of execution在执行过程中停止 Rhino 引擎
【发布时间】:2015-11-14 01:24:20
【问题描述】:

Rhino 引擎是否有可以停止执行的 api 脚本文件在中间。例如,我有一个脚本文件,其中 有一个无限循环。我怎样才能在中间停止执行?

当然,我可以停止启动 Rhino 引擎的 jvm 执行脚本。但是我不想因为这个原因终止整个 jvm 会话,因为我已经以编程方式启动了脚本,而且 Rhino 引擎也与我的应用程序在同一个 JVM 中运行。

【问题讨论】:

    标签: java rhino


    【解决方案1】:

    可以通过以下方式停止正在运行的JavaScript的执行。

    1) 创建一个虚拟调试器并将其附加到最初创建的上下文中。

    mContext = Context.enter();
    ObservingDebugger observingDebugger = 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 Execution 将立即终止。

    observingDebugger.setDisconnected(true);
    

    【讨论】:

      【解决方案2】:

      对于寻找解决方案的其他人,ContextFactory 的 javadoc 详细说明了如何停止运行超过 10 秒的脚本:

      https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/ContextFactory.java

      【讨论】:

      • 自定义 ContextFactory 方法非常有效。如果您在从 java 中读取 javadoc 时遇到问题,这里有一个 HTML 版本:jarvana.com/jarvana/view/org/mozilla/rhino/1.7R3/…
      • 我想知道这个和调试器方法对性能的影响。有什么想法吗?
      • 我想调试器方法对性能有一些影响,因为上下文正在生成调试信息。 ContextFactory 解决方案只是在执行一定数量的指令后触发。
      【解决方案3】:

      我使用 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 的调用方法中,你可以捕获错误:

          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】:

        Rhino 引擎 doesn't appear 有任何机制来执行此操作(坏 Rhino!)并且很难判断它是否在内部创建线程,因此您唯一的解决方案是创建一个 ThreadGroup,加载并从该组中的线程内执行 Rhino 引擎及其脚本,当你想杀死它时,使用ThreadGroup.stop()。是的,它已被弃用,但鉴于 Rhino 库方面没有合作,确实没有其他方法可以做到这一点。

        【讨论】:

        • @Donal:这可能不起作用,因为一旦我创建新线程并开始加载和执行 Rhino 引擎,一旦调用 Context 上的 evaluateReader() 方法,线程执行完成并且 Rhino 引擎内部管理执行。
        • 这就是为什么我建议把它放在自己的线程组中; Rhino 创建的任何其他线程仍将在该线程组(或子线程组)中,因此仍然可以全部杀死它们
        猜你喜欢
        • 2020-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-24
        • 1970-01-01
        • 2011-01-30
        • 1970-01-01
        • 2017-05-28
        相关资源
        最近更新 更多