java scripting API - how to stop the evaluation

后端 未结 7 1622
孤城傲影
孤城傲影 2020-12-09 06:10

i have writen a servlet that recives a java script code and process it and returns the answer. for that i have used the java scripting API

in the code below if scrip

相关标签:
7条回答
  • 2020-12-09 06:52

    An old thread but the one I came across first when trying to solve this issue so I would like to answer it here rather than find a more recent one.

    Another way that avoids using java.lang.Thread's deprecated stop method is to throw an exception in the javascript:

    throw "_EXIT";
    

    and not catch it in the script but let it fall through to java and catch it in (the still running) java code:

    try{
       eval(script);
    }catch(ScriptException ex) {
        if(ex.getMessage().startsWith("_EXIT")) {
            // do nothing but exit nashorn
        } else {
            // handle the exception
        }
    }
    

    I can confirm the nashorn exit method exits java, not just the engine, and I have written my own (js) exit function that simply throws the _EXIT exception.

    P

    0 讨论(0)
  • 2020-12-09 07:01

    Run the evaluation in a separate thread and interrupt it after 15s using Thread.interrupt(). This will stop the eval and throw an InterruptedException, which you can catch and return a failure status.

    A better solution would be to have some sort of asynch interface to the scripting engine, but as far as I could see this does not exist.

    EDIT:

    As sfussenegger pointed out, interrupting does not work with the script engine, since it never sleeps or enters any wait state to get interrupted. Niether could I find any periodical callback in the ScriptContext or Bindings objects which could be used as a hook to check for interruptions. There is one method which does work, though : Thread.stop(). It is deprecated and inherently unsafe for a number of reasons, but for completeness I will post my test code here along with Chris Winters implementation for comparison. Chris's version will timeout but leave the background thread running, the interrupt() does nothing and the stop() kills the thread and resumes control to the main thread:

    import javax.script.*;
    import java.util.concurrent.*;
    
    class ScriptRunner implements Runnable {
    
        private String script;
        public ScriptRunner(String script) {
                this.script = script;
        }
    
        public ScriptRunner() {
                this("while(true);");
        }
    
        public void run() {
                try {
                // create a script engine manager
                ScriptEngineManager factory = new ScriptEngineManager();
                // create a JavaScript engine
                ScriptEngine engine = factory.getEngineByName("JavaScript");
                // evaluate JavaScript code from String
                System.out.println("running script :'" + script + "'");
                engine.eval(script);
                System.out.println("stopped running script");
                } catch(ScriptException se) {
                        System.out.println("caught exception");
                        throw new RuntimeException(se);
                }
                System.out.println("exiting run");
        }
    }
    
    public class Inter {
    
        public void run() {
                try {
                 Executors.newCachedThreadPool().submit(new ScriptRunner()).get(15, TimeUnit.SECONDS);
                } catch(Exception e) {
                        throw new RuntimeException(e);
                }
        }
    
        public void run2() {
                try {
                Thread t = new Thread(new ScriptRunner());
                t.start();
                Thread.sleep(1000);
                System.out.println("interrupting");
                t.interrupt();
                Thread.sleep(5000);
                System.out.println("stopping");
                t.stop();
                } catch(InterruptedException ie) {
                        throw new RuntimeException(ie);
                }
        }
    
        public static void main(String[] args) {
                new Inter().run();
        }
    }
    
    0 讨论(0)
  • 2020-12-09 07:02

    If you are unwilling to use Thread.stop() (and you really should be), there seem to be no way to achieve your requirement with the javax.script API.

    If you use the Rhino engine directly and actual performance is not too important, you can implement a hook in Context.observeInstructionCount to interrupt or prematurely terminate script execution. The hook is invoked for each executed JavaScript instruction after you reach the threshold (instruction count) set with setInstructionObserverThreshold. You need to measure execution time yourself, since you are only provided the number of instructions executed and this may have a relevant performance impact. I'm not sure, but the hook may also only be invoked if the script engine runs in interpreted mode and not if the JavaScript code is compiled.

    0 讨论(0)
  • 2020-12-09 07:07

    Context.observeInstructionCount is only called in interpreted mode, so that's a significant performance hit. I sure hope the Rhino team comes up with a better way.

    0 讨论(0)
  • 2020-12-09 07:10

    Nashorn scripts are compiled to .class "files" & loaded on the fly. So, script evaluation is similar to loading a compiled Java .class and running the same. Unless you program explicitly for interruption, you can't stop script evaluation. There is no "script interpreter" that "polls" for interrupt status. You've to explicitly call Thread.sleep or other Java API that be interrupted from another thread.

    0 讨论(0)
  • 2020-12-09 07:11

    I know this is an older thread, but I have a more direct solution for stopping the JavaScript eval: Invoke the "exit" function that Nashorn provides.

    Inside the class I use to run the script engine, I include:

    private Invocable invocable_ = null;
    private final ExecutorService pool_ = Executors.newFixedThreadPool(1);  
    
    public boolean runScript(String fileName)
        {
        pool_.submit(new Callable<Boolean>() 
            {       
            ScriptEngine engine 
                = new ScriptEngineManager().getEngineByName("nashorn");
            public Boolean call() throws Exception
                {
                try
                    {
                    invocable_ = (Invocable)engine;
                    engine.eval(
                            new InputStreamReader(
                                    new FileInputStream(fileName), 
                                    Charset.forName("UTF-8")) );
                    return true;
                    }
                catch (ScriptException ex)
                    {
                    ...
                    return false;
                    } 
                catch (FileNotFoundException ex)
                    {
                    ...
                    return false;
                    }                   
                }
            });
    
        return true;
        }
    
    public void
    shutdownNow()
        {
    
        try
            {
            invocable_.invokeFunction("exit");
            } 
        catch (ScriptException ex)
            {
            ...
            } 
        catch (NoSuchMethodException ex)
            {
            ...
            }
    
        pool_.shutdownNow();
        invocable_ = null;
        }
    

    Now, call:

    myAwesomeClass.shutdownNow();
    

    The script will stop immediately.

    0 讨论(0)
提交回复
热议问题