Using script contexts
Threading models for script engines
JSR-223 Scripting for the Java Platform (see Resources) defines three types of script engines:
* Multithreaded engines that are capable of executing concurrent scripts that may modify variables seen by other threads
* Thread-isolated engines, which are multithreaded too, but each thread has its own engine scope for keeping variables
* Stateless engines, which are defined as thread-isolated engines that let the engine scope unmodified after the execution of any script
The type of script engine can be obtained with engine.getFactory().getParameter("THREADING"), which may return "MULTITHREADED", "THREAD-ISOLATED", or "STATELESS"
Each script-engine instance has a default context where you can store variables with the put() method and the output of any executed script is directed to System.out by default. In a server environment, you'll want to run concurrent scripts with each of them having its own context. The javax.script API satisfies this need, providing the ScriptContext interface and the SimpleScriptContext implementation.
Mozilla's Rhino JavaScript engine is a multithreaded engine (see the sidebar), allowing you to execute concurrent scripts that share the same context. In our case, however, you want to isolate the engine scopes and the output of the scripts running in different threads, which means you must create a new ScriptContext instance for each HTTP request.
Listing 12 shows the createScriptContext() method of the JSServlet class. This method sets the context's writer property in order to send the script's output to the writer of the response object when the script is executed. This means that everything you pass to print() or println() in your script will be included in the servlet's response.
In addition, createScriptContext() defines the following script variables with the setAttribute() method of the script context:
Table 1. Variables available in scripts that are executed by JSServlet
Script variable
Description
config
the javax.servlet.ServletConfig instance of the servlet
application
the javax.servlet.ServletContext instance of the Web app
session
the javax.servlet.http.HttpSession object
request
the javax.servlet.http.HttpServletRequest object
response
the javax.servlet.http.HttpServletResponse object
out
the java.io.PrintWriter object that is used to output the response
factory
the javax.script.ScriptEngineFactory of the script engine
The factory variable can be used to obtain information about the JavaScript engine, such as the language version or the engine version. The rest of the variables have the same roles that they have in JSP pages.
Listing 12. The createScriptContext() method of JSServlet
public class JSServlet extends HttpServlet {
...
protected ScriptContext createScriptContext(
HttpServletRequest request, HttpServletResponse response)
throws IOException {
ScriptContext scriptContext = new SimpleScriptContext();
scriptContext.setWriter(response.getWriter());
int scope = ScriptContext.ENGINE_SCOPE;
scriptContext.setAttribute("config", getServletConfig(), scope);
scriptContext.setAttribute("application", getServletContext(), scope);
scriptContext.setAttribute("session", request.getSession(), scope);
scriptContext.setAttribute("request", request, scope);
scriptContext.setAttribute("response", response, scope);
scriptContext.setAttribute("out", response.getWriter(), scope);
scriptContext.setAttribute("factory",
scriptCache.getEngine().getFactory(), scope);
return scriptContext;
}
...
}
The runScript() method (see Listing 13) gets a compiled script from the cache and calls the eval() method, passing the given script context as parameter.
Listing 13. The runScript() method of JSServlet
public class JSServlet extends HttpServlet {
...
protected void runScript(String uri, ScriptContext scriptContext)
throws ScriptException, IOException {
scriptCache.getScript(uri).eval(scriptContext);
}
...
} |