2014-12-30 14 views
11

Desidero eseguire un JavaScript all'interno di un servlet. È possibile riutilizzare lo stesso motore di scripting su tutte le chiamate di servlet? Le istanze servlet sono condivise da più thread. Ciò richiede la creazione di un nuovo motore di scripting per richiesta? Sarebbe una penalità di prestazioni inaccettabile. Ad esempio, è il seguente codice salvato?Riutilizzare 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); 
    } 
} 

}

Se questo non è sicuro, quale sarebbe il modo migliore per evitare di creare un motore per ogni richiesta? Usando un pool di motori?

Edit: E 'possibile riutilizzare uno e lo stesso motore e la stessa cosa JavaScriptObject, che si traduce come la valutazione di un JS-funzione per tutte le richieste servlet, se la funzione non cambia condiviso oggetto ma usa solo gli argomenti dati con la chiamata? Guarda il seguente adattamento dell'esempio precedente:

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

È sicuro?

risposta

14

In javax.script.ScriptEngineFactory c'è un metodo getParameter(String key).

Con la chiave speciale THREADING si ottengono informazioni di filettatura per questo specifico motore di fabbrica.

Questo piccolo programma stampa le informazioni per ogni fabbrica di motori registrati:

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

Per Java 7 è:

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

Per Java 8:

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

null mezzi l'implementazione del motore non è thread-safe.

Nel vostro servlet è possibile utilizzare un ThreadLocal per tenere un motore separato per ciascun thread che consente di riutilizzare il motore per le richieste successive serviti dallo stesso thread.

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

grazie! questo risponde alla mia domanda. e grazie per il suggerimento di utilizzare i locali dei thread. – Gregor

+3

Si prega di consultare: http://stackoverflow.com/questions/30140103/should-i-use-a-separate-scriptengine-and-compiledscript-instances-per-each-threa – igr

+0

Penso che dovresti passare 'newContext' a' eval' invece di 'engineScope' – gamliela

Problemi correlati