Преимущество:
Недостаток:
Рекомендация: Оставьте GAC MS и друзьям. Гигабайт является очень дешевым теперь.
Here's some code showing the Future implementation and the Thread.stop() one. This is an interesting problem, and it points out the need for a hook in a ScriptEngine to be able to halt whatever script it's running for whatever reason. I wonder whether this would break the assumptions in most implementations since they assume eval()
will be executed in a single-threaded (blocking) environment?
Anyway, the results of executing the code below:
// 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)
Here's the full program:
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" );
}
}
}
Запустить оценку в отдельном потоке и прервать ее через 15 секунд с помощью Thread.interrupt (). Это остановит eval и выдаст InterruptedException, которое вы можете поймать и вернуть статус сбоя.
Лучшим решением было бы иметь какой-то асинхронный интерфейс для механизма сценариев, но, насколько я мог видеть, этого не происходит. существует.
РЕДАКТИРОВАТЬ:
Как указал sfussenegger, прерывание не работает с механизмом сценария, поскольку он никогда не спит и не входит в какое-либо состояние ожидания для прерывания. Я не мог найти периодического обратного вызова в объектах ScriptContext или Bindings, которые можно было бы использовать в качестве ловушки для проверки прерываний. Однако есть один метод, который действительно работает: Thread.stop (). Он устарел и небезопасен по ряду причин. но для полноты картины я опубликую здесь свой тестовый код вместе с реализацией Криса Винтерса для сравнения. У версии Криса истечет тайм-аут, но фоновый поток останется запущенным, прерывание () ничего не делает, а 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 () (а вам это действительно следует), кажется, чтобы не было возможности выполнить ваше требование с помощью javax.script API.
Если вы используете движок Rhino напрямую и фактическая производительность не слишком важна, вы можете реализовать ловушку в Context.observeInstructionCount, чтобы прервать или преждевременно завершить выполнение скрипта. Ловушка вызывается для каждой выполненной инструкции JavaScript после достижения порога (количества инструкций), установленного с помощью setInstructionObserverThreshold. Вам необходимо самостоятельно измерить время выполнения, поскольку вам предоставляется только количество выполненных инструкций, и это может иметь существенное влияние на производительность. Я не уверен,