2014-12-30 13 views
11

Chcę wykonać JavaScript w serwlecie. Czy jest możliwe ponowne użycie tego samego silnika skryptowego we wszystkich wywołaniach serwletów? Instancje Serwlety są współużytkowane przez wiele wątków. Czy to wymaga utworzenia nowego aparatu Scripting Engine na żądanie? Byłaby to niedopuszczalna kara za wyniki. Jako przykład, czy jest następujący zapis kodu?Reuse Nashorn ScriptEngine in Servlet

public class MyServlet extends HttpServlet { 

private ScriptEngineManager factory; 
private ScriptEngine engine; 

@Override 
public void init() throws ServletException { 
    factory = new ScriptEngineManager(); 
    engine = factory.getEngineByName("nashorn"); 
} 

@Override 
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { 
    try (PrintWriter writer = res.getWriter()) { 
     ScriptContext newContext = new SimpleScriptContext(); 
     newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); 
     Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE); 
     engineScope.put("writer", writer); 
     Object value = engine.eval("writer.print('Hello, World!');", engineScope); 
     writer.close(); 
    } catch (IOException | ScriptException ex) { 
     Logger.getLogger(AsyncServlet.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 

}

Jeśli to nie jest bezpieczne, co byłoby najlepszym sposobem na uniknięcie tworzenia silnik za zamówienie? Korzystając z puli silników?

Edit: Czy możliwe jest ponowne jeden i ten sam silnik i jeden i ten sam JavaScriptObject, co powoduje, jak przy ocenie JS-funkcja dla wszystkich żądań serwletów, jeśli funkcja nie zmienia wspólne obiekt, ale używa tylko argumentów podanych w wywołaniu? Spójrz na następującą adaptację powyższego przykładu:

public class MyServlet extends HttpServlet { 

private ScriptEngineManager factory; 
private ScriptEngine engine; 
private ScriptObjectMirror script; 

@Override 
public void init() throws ServletException { 
    try { 
     factory = new ScriptEngineManager(); 
     engine = factory.getEngineByName("nashorn"); 
     script = (ScriptObjectMirror)engine.eval("function(writer) {writer.print('Hello, World!');}"); 
    } catch (ScriptException ex) { 
     Logger.getLogger(MyServlet.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 

@Override 
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { 
    try (PrintWriter writer = res.getWriter()) { 
     script.call(null, writer); 
     writer.close(); 
    } catch (IOException ex) { 
     Logger.getLogger(MyServlet.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 

Czy to jest bezpieczne?

Odpowiedz

14

W javax.script.ScriptEngineFactory znajduje się metoda getParameter(String key).

Za pomocą specjalnego klucza THREADING otrzymujesz informację o wątkach dla tej konkretnej fabryki silników.

Ten niewielki program wypisuje te informacje dla każdego zarejestrowanego fabryce silników:

import javax.script.ScriptEngineFactory; 
import javax.script.ScriptEngineManager; 

public class ScriptEngineTest { 
    public static void main(String[] args) { 
    final ScriptEngineManager mgr = new ScriptEngineManager(); 
    for(ScriptEngineFactory fac: mgr.getEngineFactories()) { 
     System.out.println(String.format("%s (%s), %s (%s), %s", fac.getEngineName(), 
      fac.getEngineVersion(), fac.getLanguageName(), 
      fac.getLanguageVersion(), fac.getParameter("THREADING"))); 
    } 
    } 
} 

Dla Java 7 jest to:

Mozilla Rhino (1.7 release 3 PRERELEASE), ECMAScript (1.8), MULTITHREADED 

Dla Java 8:

Oracle Nashorn (1.8.0_25), ECMAScript (ECMA - 262 Edition 5.1), null 

null środki implementacja silnika nie jest bezpieczna dla wątków.

W serwlecie można użyć urządzenia ThreadLocal do przechowywania oddzielnego silnika dla każdego wątku, co umożliwia ponowne użycie silnika w przypadku kolejnych żądań obsługiwanych przez ten sam wątek.

public class MyServlet extends HttpServlet { 

    private ThreadLocal<ScriptEngine> engineHolder; 

    @Override 
    public void init() throws ServletException { 
    engineHolder = new ThreadLocal<ScriptEngine>() { 
     @Override 
     protected ScriptEngine initialValue() { 
     return new ScriptEngineManager().getEngineByName("nashorn"); 
     } 
    }; 
    } 

    @Override 
    public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { 
    try (PrintWriter writer = res.getWriter()) { 
     ScriptContext newContext = new SimpleScriptContext(); 
     newContext.setBindings(engineHolder.get().createBindings(), ScriptContext.ENGINE_SCOPE); 
     Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE); 
     engineScope.put("writer", writer); 
     Object value = engineHolder.get().eval("writer.print('Hello, World!');", engineScope); 
     writer.close(); 
    } catch (IOException | ScriptException ex) { 
     Logger.getLogger(MyServlet.class.getName()).log(Level.SEVERE, null, ex); 
    } 
    } 
} 
+0

dziękuję! to odpowiada na moje pytanie. i dziękuję za podpowiedź, aby używać lokalnych wątków. – Gregor

+3

Proszę zobaczyć: http://stackoverflow.com/questions/30140103/should-i-use-a-separate-scriptengine-and-compiledscript-instances-per-each-threa – igr

+0

Myślę, że powinieneś przekazać 'newContext' do' eval' zamiast 'engineScope' – gamliela

Powiązane problemy