我已经编写了一个servlet,它可以重现java脚本代码并对其进行处理并返回答案。因为我使用了java脚本API
如果脚本=“print('Hello,World')”,则在下面的代码中;代码将正确打印“hello world”。 但如果script =“while(true);”脚本将无休止地循环。
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秒),我如何杀死eval过程?
感谢
答案 0 :(得分:3)
在单独的线程中运行评估,并在15秒后使用Thread.interrupt()中断它。这将停止eval并抛出InterruptedException,您可以捕获并返回失败状态。
更好的解决方案是为脚本引擎提供某种异步接口,但据我所知,这不存在。
编辑:
正如sfussenegger指出的那样,中断对脚本引擎不起作用,因为它从不睡眠或进入任何等待状态以中断。 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();
}
}
答案 1 :(得分:3)
这里有一些显示Future实现和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" );
}
}
}
答案 2 :(得分:2)
如果您不愿意使用Thread.stop()(并且您确实应该这样做),似乎无法使用javax.script API实现您的要求。
如果直接使用Rhino引擎并且实际性能不是很重要,可以在Context.observeInstructionCount中实现一个钩子来中断或过早终止脚本执行。在达到使用setInstructionObserverThreshold设置的阈值(指令计数)后,将为每个执行的JavaScript指令调用该挂钩。您需要自己测量执行时间,因为您只提供了执行的指令数量,这可能会对性能产生相关影响。我不确定,但只有在脚本引擎以解释模式运行时才会调用钩子,而不是在编译JavaScript代码时调用。
答案 3 :(得分:0)
Context.observeInstructionCount仅在解释模式下调用,因此性能受到重大影响。我当然希望Rhino团队能够找到更好的方法。
答案 4 :(得分:0)
Nashorn脚本编译为.class“files”&amp;在飞行中加载。因此,脚本评估类似于加载已编译的Java .class并运行相同的。除非您明确编程中断,否则无法停止脚本评估。没有“脚本解释器”可以“轮询”中断状态。 您必须显式调用Thread.sleep或其他从另一个线程中断的Java API。
答案 5 :(得分:0)
一个老话题,但是我在尝试解决此问题时首先遇到的那个话题,所以我想在这里回答,而不是找到一个新的话题。
避免使用java.lang.Thread不推荐使用的stop方法的另一种方法是在javascript中引发异常:
throw "_EXIT";
和 not 在脚本中捕获它,但让它进入Java并在(仍在运行的)Java代码中捕获它:
try{
eval(script);
}catch(ScriptException ex) {
if(ex.getMessage().startsWith("_EXIT")) {
// do nothing but exit nashorn
} else {
// handle the exception
}
}
我可以确认nashorn exit
方法退出了Java,而不仅仅是引擎退出了,并且我编写了自己的(js)退出函数,该函数简单地引发_EXIT异常。
P
答案 6 :(得分:-1)
我知道这是一个较老的线程,但我有一个更直接的解决方案来停止JavaScript eval:调用&#34;退出&#34; 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();
脚本会立即停止。