java脚本API-如何停止评估
我编写了一个servlet,它接收java脚本代码并对其进行处理,然后返回答案。为此,我使用了java脚本API 在下面的代码中,如果script=“print('Hello,World')”;代码将以正确打印“hello world”结束。 但是如果script=“while(true);”脚本将无休止地循环java脚本API-如何停止评估,java,api,scripting,Java,Api,Scripting,我编写了一个servlet,它接收java脚本代码并对其进行处理,然后返回答案。为此,我使用了java脚本API 在下面的代码中,如果script=“print('Hello,World')”;代码将以正确打印“hello world”结束。 但是如果script=“while(true);”脚本将无休止地循环 import javax.script.*; public class EvalScript { public static void main(String[] args) t
import javax.script.*;
public class EvalScript {
public static void main(String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// evaluate JavaScript code from String
engine.eval(script);
}
}
我的问题是,如果需要太长的时间(比如说15秒),我如何终止评估过程
谢谢在单独的线程中运行评估,并在15秒后使用thread.interrupt()中断它。这将停止评估并抛出InterruptedException,您可以捕获该异常并返回故障状态 更好的解决方案是为脚本引擎提供某种异步接口,但据我所知,这并不存在 编辑: 正如Sfussenger所指出的,中断不适用于脚本引擎,因为它从不休眠或进入任何等待状态以被中断。Niether我可以在ScriptContext或Bindings对象中找到任何可以用作检查中断的钩子的定期回调。不过,有一种方法确实有效:Thread.stop()。由于许多原因,它被弃用并且本质上是不安全的,但是为了完整性,我将在这里发布我的测试代码以及Chris Winters实现,以进行比较。Chris的版本将超时,但后台线程仍在运行,interrupt()不执行任何操作,stop()终止线程并恢复对主线程的控制:
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();
}
}
下面是一些显示未来实现和Thread.stop()的代码。这是一个有趣的问题,它指出了ScriptEngine中需要一个钩子来停止出于任何原因运行的任何脚本。我想知道这是否会打破大多数实现中的假设,因为它们假设
eval()
将在单线程(阻塞)环境中执行
无论如何,执行以下代码的结果:
// exec with Thread.stop()
$ java ExecJavascript
Java: Starting thread...
JS: Before infinite loop...
Java: ...thread started
Java: Thread alive after timeout, stopping...
Java: ...thread stopped
(program exits)
// exec with Future.cancel()
$ java ExecJavascript 1
Java: Submitting script eval to thread pool...
Java: ...submitted.
JS: Before infinite loop...
Java: Timeout! trying to future.cancel()...
Java: ...future.cancel() executed
(program hangs)
以下是完整的程序:
import java.util.concurrent.*;
import javax.script.*;
public class ExecJavascript
{
private static final int TIMEOUT_SEC = 5;
public static void main( final String ... args ) throws Exception
{
final ScriptEngine engine = new ScriptEngineManager()
.getEngineByName("JavaScript");
final String script =
"var out = java.lang.System.out;\n" +
"out.println( 'JS: Before infinite loop...' );\n" +
"while( true ) {}\n" +
"out.println( 'JS: After infinite loop...' );\n";
if ( args.length == 0 ) {
execWithThread( engine, script );
}
else {
execWithFuture( engine, script );
}
}
private static void execWithThread(
final ScriptEngine engine, final String script )
{
final Runnable r = new Runnable() {
public void run() {
try {
engine.eval( script );
}
catch ( ScriptException e ) {
System.out.println(
"Java: Caught exception from eval(): " + e.getMessage() );
}
}
};
System.out.println( "Java: Starting thread..." );
final Thread t = new Thread( r );
t.start();
System.out.println( "Java: ...thread started" );
try {
Thread.currentThread().sleep( TIMEOUT_SEC * 1000 );
if ( t.isAlive() ) {
System.out.println( "Java: Thread alive after timeout, stopping..." );
t.stop();
System.out.println( "Java: ...thread stopped" );
}
else {
System.out.println( "Java: Thread not alive after timeout." );
}
}
catch ( InterruptedException e ) {
System.out.println( "Interrupted while waiting for timeout to elapse." );
}
}
private static void execWithFuture( final ScriptEngine engine, final String script )
throws Exception
{
final Callable<Object> c = new Callable<Object>() {
public Object call() throws Exception {
return engine.eval( script );
}
};
System.out.println( "Java: Submitting script eval to thread pool..." );
final Future<Object> f = Executors.newCachedThreadPool().submit( c );
System.out.println( "Java: ...submitted." );
try {
final Object result = f.get( TIMEOUT_SEC, TimeUnit.SECONDS );
}
catch ( InterruptedException e ) {
System.out.println( "Java: Interrupted while waiting for script..." );
}
catch ( ExecutionException e ) {
System.out.println( "Java: Script threw exception: " + e.getMessage() );
}
catch ( TimeoutException e ) {
System.out.println( "Java: Timeout! trying to future.cancel()..." );
f.cancel( true );
System.out.println( "Java: ...future.cancel() executed" );
}
}
}
import java.util.concurrent.*;
导入javax.script.*;
公共类可执行JavaScript
{
私有静态最终整数超时_SEC=5;
公共静态void main(最终字符串…args)引发异常
{
final ScriptEngine=new ScriptEngineManager()
.getEngineByName(“JavaScript”);
最终字符串脚本=
“var out=java.lang.System.out;\n”+
“out.println('JS:Before infinite loop…');\n”+
“while(true){}\n”+
“out.println('JS:After infinite loop…');\n”;
如果(args.length==0){
execWithThread(引擎、脚本);
}
否则{
execWithFuture(引擎、脚本);
}
}
私有静态无效execWithThread(
最终脚本引擎(最终字符串脚本)
{
最终可运行r=新可运行(){
公开募捐{
试一试{
engine.eval(脚本);
}
捕获(脚本异常){
System.out.println(
“Java:从eval()捕获异常:”+e.getMessage());
}
}
};
println(“Java:开始线程…”);
最终螺纹t=新螺纹(r);
t、 start();
println(“Java:…线程已启动”);
试一试{
Thread.currentThread().sleep(超时秒*1000);
if(t.isAlive()){
println(“Java:Thread-alive-after-timeout,stopping…”);
t、 停止();
System.out.println(“Java:…线程停止”);
}
否则{
println(“Java:线程在超时后不活动。”);
}
}
捕捉(中断异常e){
System.out.println(“在等待超时时间过去时中断”);
}
}
私有静态void execWithFuture(最终脚本引擎、最终字符串脚本)
抛出异常
{
最终可调用c=新可调用(){
公共对象调用()引发异常{
返回引擎.eval(脚本);
}
};
println(“Java:将脚本评估提交到线程池…”);
最终未来f=Executors.newCachedThreadPool().submit(c);
System.out.println(“Java:…已提交”);
试一试{
最终对象结果=f.get(超时秒,TimeUnit.SECONDS);
}
捕捉(中断异常e){
println(“Java:在等待脚本时被中断…”);
}
捕获(执行例外){
println(“Java:Script抛出异常:”+e.getMessage());
}
捕获(超时异常e){
System.out.println(“Java:Timeout!正在尝试future.cancel()…”;
f、 取消(真);
System.out.println(“Java:…future.cancel()已执行”);
}
}
}
如果您不愿意使用Thread.stop()(您确实应该这么做),那么javax.script API似乎无法满足您的需求
如果直接使用Rhino引擎且实际性能不太重要,则可以在Context.observeInstructionCount中实现一个钩子来中断或提前终止脚本执行。在达到setInstructionObserverThreshold设置的阈值(指令计数)后,将为每个执行的JavaScript指令调用钩子。您需要自己测量执行时间,因为只向您提供执行的指令数量,这可能会对性能产生相关影响。我不确定,但是只有脚本引擎在解释模式下运行,而不是在编译JavaScript代码时,才能调用钩子。Context.observeInstructionCount仅在解释模式下调用,因此这是一个显著的性能影响。我当然希望Rhino团队能想出一个更好的方法。我知道这是一个较老的线程,但我有一个更直接的停止JavaScript评估的解决方案:调用Nashorn提供的“退出”函数 在我用来运行脚本引擎的类中,我包括
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;
}
myAwesomeClass.shutdownNow();
throw "_EXIT";
try{
eval(script);
}catch(ScriptException ex) {
if(ex.getMessage().startsWith("_EXIT")) {
// do nothing but exit nashorn
} else {
// handle the exception
}
}