2014-12-30 3 views
11

Я хочу выполнить JavaScript внутри сервлета. Можно ли повторно использовать один и тот же механизм сценариев во всех вызовах сервлета? Экземпляры сервлета разделяются несколькими потоками. Требуется ли для создания нового сценария для каждого запроса? Это было бы неприемлемым для исполнения. Например, сохраняется ли следующий код?Повторное использование Nashorn ScriptEngine в сервлет

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

}

Если это не безопасно, что было бы лучшим способом, чтобы избежать создания двигателя для каждого запроса? Используя пул двигателей?

Edit: Можно ли повторно использовать один и тот же двигатель и один и тот же JavaScriptObject, что приводит в качестве оценки в JS-функции, для всех запросов сервлет, если функция не изменяет любой общей объект, но использует только аргументы, данные с вызовом? Посмотрите на следующую адаптацию приведенного выше примера:

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

Это безопасно?

ответ

14

В javax.script.ScriptEngineFactory есть способ getParameter(String key).

С помощью специальной клавиши THREADING вы получаете информацию о резьбе для этой конкретной фабрики двигателей.

Эта небольшая программа выводит эту информацию для каждого зарегистрированного завода двигателя:

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

Для Java 7 это является:

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

Для Java 8:

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

null средства реализация двигателя не является потокобезопасной.

В вашем сервлете вы можете использовать ThreadLocal, чтобы держать отдельный движок для каждой нити, что позволяет повторно использовать двигатель для последующих запросов, обслуживаемых одной и той же резьбой.

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

спасибо! что отвечает на мой вопрос. и спасибо за подсказку использовать локаторы потоков. – Gregor

+3

Пожалуйста, смотрите: http://stackoverflow.com/questions/30140103/should-i-use-a-separate-scriptengine-and-compiledscript-instances-per-each-threa – igr

+0

Я думаю, вы должны передать 'newContext' в' eval' вместо 'engineScope' – gamliela

Смежные вопросы