You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何终止Rhino脚本?Java应用中Rhino用户脚本超时终止方案咨询

How to Add Timeout to Rhino Script Execution

Absolutely, you can enforce a timeout for Rhino scripts to stop runaway execution (like infinite loops) by running the script in a separate thread and interrupting it once your time limit is hit. Here’s a practical, tested approach tailored to your use case:

Core Concept

Rhino executes scripts in the same thread that triggers Context.evaluateString(), so to add a timeout, we need to offload script execution to a background thread. Using Java’s ExecutorService and Future allows us to wait for the script to finish, and cancel the thread if it exceeds your 30-second limit.

Complete Implementation Code

Here’s a working example that wraps Rhino execution with a 30-second timeout:

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import java.util.concurrent.*;

public class RhinoTimeoutManager {
    // Use a single-thread executor for script execution (adjust pool size if needed)
    private static final ExecutorService scriptExecutor = Executors.newSingleThreadExecutor();

    public static Object runScriptWithTimeout(String userScript, long timeoutSeconds) throws Exception {
        // Submit script execution as a callable task
        Future<Object> scriptTask = scriptExecutor.submit(() -> {
            Context rhinoCtx = Context.enter();
            try {
                // Force interpreter mode (critical for reliable interrupts)
                rhinoCtx.setOptimizationLevel(-1);
                Scriptable globalScope = rhinoCtx.initStandardObjects();

                // Execute the user's script
                return rhinoCtx.evaluateString(globalScope, userScript, "UserSubmittedScript", 1, null);
            } finally {
                // Always clean up Rhino's thread-local context
                Context.exit();
            }
        });

        try {
            // Wait for completion or trigger timeout
            return scriptTask.get(timeoutSeconds, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            // Cancel the task and interrupt the running thread
            scriptTask.cancel(true);
            throw new RuntimeException("Script timed out after " + timeoutSeconds + " seconds");
        } catch (ExecutionException e) {
            // Re-throw any errors from the script itself
            throw new RuntimeException("Script failed to execute", e.getCause());
        }
    }

    public static void main(String[] args) {
        // Test with an infinite loop script
        String badScript = "while(true) { /* Infinite loop */ }";
        try {
            runScriptWithTimeout(badScript, 30);
        } catch (Exception e) {
            System.err.println(e.getMessage()); // Should print timeout message
        } finally {
            scriptExecutor.shutdown();
        }
    }
}

Key Details to Note

  • Interpreter Mode: Setting optimizationLevel(-1) disables Rhino’s bytecode compilation. Compiled scripts don’t check for thread interrupts as often, so interpreter mode ensures the script can be stopped quickly when timed out.
  • Thread Interruption: Calling future.cancel(true) sends an interrupt signal to the script’s thread. Rhino’s interpreter checks for interrupts during loop iterations and other execution points, so the script will terminate almost immediately.
  • Resource Cleanup: The finally block ensures Context.exit() is called every time, preventing leaks of Rhino’s thread-local resources.
  • Edge Cases: If your script calls native Java methods that don’t respond to interrupts (e.g., blocking I/O that ignores Thread.interrupted()), the timeout might not work instantly. For pure JavaScript code, though, this method is fully reliable.

Alternative: Context Factory with Instruction Monitoring

If you prefer not to use threads, you can use a custom ContextFactory to check for timeouts based on script instruction counts:

import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;

public class TimeoutAwareContextFactory extends ContextFactory {
    private final long timeoutMillis;
    private long executionStartTime;

    public TimeoutAwareContextFactory(long timeoutMillis) {
        this.timeoutMillis = timeoutMillis;
    }

    @Override
    protected void observeInstructionCount(Context cx, int instructionCount) {
        super.observeInstructionCount(cx, instructionCount);
        if (System.currentTimeMillis() - executionStartTime > timeoutMillis) {
            throw new RuntimeException("Script exceeded timeout limit");
        }
    }

    @Override
    protected Context makeContext() {
        executionStartTime = System.currentTimeMillis();
        Context cx = super.makeContext();
        // Check timeout every 1000 instructions (adjust for overhead vs responsiveness)
        cx.setInstructionObserverThreshold(1000);
        return cx;
    }
}

To use this factory:

// Set the custom factory as global before executing scripts
ContextFactory.initGlobal(new TimeoutAwareContextFactory(30000)); // 30 seconds

// Proceed with normal Rhino execution...
Context ctx = Context.enter();
try {
    Scriptable scope = ctx.initStandardObjects();
    ctx.evaluateString(scope, userScript, "UserScript", 1, null);
} finally {
    Context.exit();
}

This method works without threads but relies on Rhino’s instruction counter to check timeouts. It’s great for pure JS scripts but less reliable if the script calls long-running native code.

内容的提问来源于stack exchange,提问作者smoyano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:23:27