2009-10-21 33 views
6

tôi đã viết một servlet để đọc lại mã script java và xử lý nó và trả về câu trả lời. cho rằng tôi đã sử dụng java scripting APIjava scripting API - cách dừng đánh giá

trong mã bên dưới nếu script = "print ('Hello, World')"; mã sẽ kết thúc in chính xác "hello world". nhưng nếu tập lệnh = "while (true);" kịch bản sẽ lặp vô tận.

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); 
    } 
} 

câu hỏi của tôi là làm cách nào để tôi xóa quá trình eval trong trường hợp mất quá nhiều thời gian (giả sử 15 giây)?

cảm ơn

Trả lời

3

Chạy đánh giá trong một luồng riêng và ngắt sau 15 giây bằng cách sử dụng Thread.interrupt(). Điều này sẽ ngăn chặn eval và ném một InterruptedException, mà bạn có thể bắt và trả về một trạng thái lỗi.

Một giải pháp tốt hơn là có một số loại giao diện asynch cho công cụ tạo tập lệnh, nhưng theo như tôi thấy điều này không tồn tại.

EDIT:

Như sfussenegger chỉ ra, làm gián đoạn không hoạt động với động cơ kịch bản, vì nó không bao giờ ngủ hoặc vào bất kỳ trạng thái chờ đợi để có được gián đoạn. Niether tôi có thể tìm thấy bất kỳ cuộc gọi lại định kỳ nào trong các đối tượng ScriptContext hoặc Bindings có thể được sử dụng như một cái móc để kiểm tra sự gián đoạn. Có một phương thức hoạt động, mặc dù: Thread.stop(). Nó không được chấp nhận và vốn không an toàn vì một số lý do, nhưng để hoàn thành, tôi sẽ đăng mã thử nghiệm của tôi ở đây cùng với việc thực hiện Chris Winters để so sánh. phiên bản của Chris sẽ timeout nhưng rời khỏi nền sợi chạy, ngắt() không làm gì và stop() giết chết các chủ đề và sơ yếu lý lịch kiểm soát để các chủ đề chính:

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(); 
    } 
} 
+2

-1 Nếu đường đi không đợi, sẽ không có ngoại lệ. Do đó, nó sẽ không hoạt động với một vòng lặp vô tận. Mã sẽ phải kiểm tra 'Thread.currentThread(). Interrupted()' để các đề xuất của bạn hoạt động. – sfussenegger

+0

Argh, bỏ lỡ điều đó. Điều đó chỉ để lại điểm dừng không được chấp nhận() tôi tin? –

+0

đã thay đổi phiếu bầu của tôi – sfussenegger

3

Dưới đây là một số mã cho thấy việc thực hiện trong tương lai và các Chủ đề .stop() một. Đây là một vấn đề thú vị, và nó chỉ ra sự cần thiết cho một móc trong một ScriptEngine để có thể ngăn chặn bất cứ kịch bản nó đang chạy vì lý do gì. Tôi tự hỏi liệu điều này sẽ phá vỡ các giả định trong hầu hết các triển khai kể từ khi họ giả định eval() sẽ được thực hiện trong một môi trường đơn luồng (chặn)?

Dù sao, kết quả thực thi mã bên dưới:

// 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) 

Dưới đây là toàn bộ chương trình:

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

Điều này tiếp tục kiểm soát sau 15 giây với một ngoại lệ thời gian chờ, nhưng để lại chuỗi nền chạy, do đó, câu hỏi làm thế nào để làm gián đoạn nó vẫn còn. –

+0

Duh! Cảm ơn nhận xét, đã thêm Hủy trong tương lai. –

+1

hi, cảm ơn vì giải pháp thanh lịch, nhưng ngay cả sau khi hủy Tương lai, luồng nền vẫn đang chạy tiêu tốn tài nguyên CPU. do đó câu hỏi vẫn còn .. – special0ne

2

Nếu bạn không muốn sử dụng Thread.stop() (và bạn thực sự nên), dường như không có cách nào để đạt được yêu cầu của bạn với API javax.script.

Nếu bạn sử dụng công cụ Rhino trực tiếp và hiệu suất thực tế không quá quan trọng, bạn có thể thực hiện móc trong Context.observeInstructionCount để ngắt hoặc thực thi tập lệnh chấm dứt sớm. Hook được gọi cho mỗi lệnh JavaScript được thực hiện sau khi bạn đạt đến ngưỡng (số lệnh) được thiết lập với setInstructionObserverThreshold. Bạn cần tự đo thời gian thực hiện vì bạn chỉ được cung cấp số lượng lệnh được thực thi và điều này có thể có tác động hiệu suất có liên quan. Tôi không chắc chắn, nhưng móc cũng có thể chỉ được gọi nếu công cụ script chạy trong chế độ diễn giải và không phải nếu mã JavaScript được biên dịch.

0

Context.observeInstructionCount chỉ được gọi trong chế độ diễn giải, do đó, đó là lần truy cập hiệu suất đáng kể.Tôi chắc chắn hy vọng đội tê giác đến với một cách tốt hơn.

-1

Tôi biết đây là một chuỗi cũ hơn, nhưng tôi có giải pháp trực tiếp hơn để ngừng đánh giá JavaScript: Gọi hàm "thoát" mà Nashorn cung cấp.

Bên trong lớp tôi sử dụng để chạy các động cơ kịch bản, tôi bao gồm:

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; 
    } 

Bây giờ, cuộc gọi:

myAwesomeClass.shutdownNow(); 

Các kịch bản sẽ ngừng ngay lập tức.

+1

Thu hẹp. Cuộc gọi "thoát" không thoát khỏi công cụ tập lệnh nhưng toàn bộ JVM. –

+0

Tôi không đồng ý; đó không phải là trường hợp trong dự án của tôi. Công cụ tập lệnh dừng lại, phần còn lại của chương trình của tôi tiếp tục chạy trên JVM. – user761576

0

Kịch bản lệnh Nashorn được biên dịch thành .class "files" & được tải khi đang di chuyển. Vì vậy, việc đánh giá kịch bản tương tự như tải một lớp Java đã được biên dịch và chạy giống nhau. Trừ khi bạn lập trình cho gián đoạn một cách rõ ràng, bạn không thể ngừng đánh giá kịch bản. Không có "trình thông dịch kịch bản" mà "cuộc thăm dò" cho trạng thái ngắt. Bạn đã gọi rõ ràng Thread.sleep hoặc API Java khác bị gián đoạn từ một chuỗi khác.