• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
JavaScript EE, Part 1: Run JavaScript files on the server side
 

JavaScript EE, Part 1: Run JavaScript files on the server side

on

  • 960 views

 

Statistics

Views

Total Views
960
Views on SlideShare
960
Embed Views
0

Actions

Likes
0
Downloads
3
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    JavaScript EE, Part 1: Run JavaScript files on the server side JavaScript EE, Part 1: Run JavaScript files on the server side Document Transcript

    • JavaScript EE, Part 1: Run JavaScript files on the server side Learn how to use the javax.script API in Ajax and Java EE applications Skill Level: Intermediate Andrei Cioroianu Senior Java Developer and Consultant Devsphere 16 Dec 2008 Combine JavaScript with Java™ code on the server to get the freedom to use the same JavaScript routines on both servers and clients. In addition, the techniques presented throughout this series will allow you to maintain a single code base for both Ajax and non-Ajax clients. Because much of the server-side code would still be written in the Java language, you'll find it necessary to expose the Java Platform, Enterprise Edition (Java EE) features to JavaScript. In this series, learn how to run JavaScript files on the server side, call remote JavaScript functions with Ajax, and use the Java Scripting API with the JavaServer Pages (JSP) technology. Typical Ajax applications use JavaScript on the client side and a different language, such as Java, on the server side. As a result, developers must implement some of their routines twice, using JavaScript for the Web browser and another language for the server. This double-coding issue can be avoided by using JavaScript combined with Java code on the server side, getting full support of scripting languages through the javax.script API. In addition, the Java SE Development Kit (JDK) 6 already includes Mozilla's Rhino JavaScript engine, which means no setup is required. In this first article of the series, you will use a simple script runner that lets you execute JavaScript files within a Java EE application. Scripts will have access to the so-called "implicit objects" that are used in JSP pages, such as application, session, request, and response. Most of the samples consist of reusable code so that you can easily start using JavaScript on the server in your own applications. Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 1 of 19
    • developerWorks® ibm.com/developerWorks Using the javax.script API This section provides an overview of the javax.script API. You will learn how to execute scripts that access Java objects, invoke JavaScript functions from your Java code, and implement a caching mechanism for the compiled scripts. Executing scripts The javax.script API is very simple. You start by creating a ScriptEngineManager instance, which lets you obtain a ScriptEngine object (see Listing 1), using one of the following methods: • getEngineByName() • getEngineByExtension() • getEngineByMimeType() Listing 1. Getting a ScriptEngine instance import javax.script.*; ... ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); ... engine.eval(...); You can also obtain the list of available script engines with getEngineFactories(). Currently, only the JavaScript engine is bundled with JDK 6, but ScriptEngineManager implements a discovery mechanism for third-party engines that support JSR-223 Scripting for the Java Platform (see Resources). You just have to place the JAR files of the script engines in your CLASSPATH. After you get the javax.script.ScriptEngine instance, you can call eval() to execute scripts. You can also export Java objects as script variables, passing a Bindings instance to the eval() method. Listing 2 contains the ScriptDemo.java example, which exports two variables named demoVar and strBuf, executes a script named DemoScript.js, and then gets the variables to output their modified values. Listing 2. The ScriptDemo.java example package jsee.demo; import javax.script.*; import java.io.*; public class ScriptDemo { Run JavaScript files on the server side Page 2 of 19 © Copyright IBM Corporation 2008. All rights reserved.
    • ibm.com/developerWorks developerWorks® public static void main(String args[]) throws Exception { // Get the JavaScript engine ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); // Set JavaScript variables Bindings vars = new SimpleBindings(); vars.put("demoVar", "value set in ScriptDemo.java"); vars.put("strBuf", new StringBuffer("string buffer")); // Run DemoScript.js Reader scriptReader = new InputStreamReader( ScriptDemo.class.getResourceAsStream("DemoScript.js")); try { engine.eval(scriptReader, vars); } finally { scriptReader.close(); } // Get JavaScript variables Object demoVar = vars.get("demoVar"); System.out.println("[Java] demoVar: " + demoVar); System.out.println(" Java object: " + demoVar.getClass().getName()); System.out.println(); Object strBuf = vars.get("strBuf"); System.out.println("[Java] strBuf: " + strBuf); System.out.println(" Java object: " + strBuf.getClass().getName()); System.out.println(); Object newVar = vars.get("newVar"); System.out.println("[Java] newVar: " + newVar); System.out.println(" Java object: " + newVar.getClass().getName()); System.out.println(); } } The DemoScript.js file (shown in Listing 3) contains a function named printType(), which is used to output the type of each script variable. The example calls the append() method of the strBuf object, modifies the value of demoVar, and sets a new script variable named newVar. If the object passed to printType() has a getClass() method, it must be a Java object whose class name is obtained with obj.getClass().name. This JavaScript expression calls the getName() method of the object's java.lang.Class instance. If the object doesn't have a getClass member, printType() calls the toSource() method, which all JavaScript objects must have. Listing 3. The DemoScript.js example println("Start script rn"); // Output the type of an object function printType(obj) { if (obj.getClass) println(" Java object: " + obj.getClass().name); else println(" JS object: " + obj.toSource()); println(""); } Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 3 of 19
    • developerWorks® ibm.com/developerWorks // Print variable println("[JS] demoVar: " + demoVar); printType(demoVar); // Call method of Java object strBuf.append(" used in DemoScript.js"); println("[JS] strBuf: " + strBuf); printType(strBuf); // Modify variable demoVar = "value set in DemoScript.js"; println("[JS] demoVar: " + demoVar); printType(demoVar); // Set a new variable var newVar = { x: 1, y: { u: 2, v: 3 } } println("[JS] newVar: " + newVar); printType(newVar); println("End script rn"); Listing 4 contains the output of the ScriptDemo.java example. The first thing you'll notice is that demoVar is exported as a JavaScript String while the type of strBuf remains java.lang.StringBuffer. Primitive variables and Java strings are exported as native JavaScript objects. Any other Java objects (including arrays) are exported unaltered. Listing 4. The output of ScriptDemo.java Start script [JS] demoVar: value set in ScriptDemo.java JS object: (new String("value set in ScriptDemo.java")) [JS] strBuf: string buffer used in DemoScript.js Java object: java.lang.StringBuffer [JS] demoVar: value set in DemoScript.js JS object: (new String("value set in DemoScript.js")) [JS] newVar: [object Object] JS object: ({x:1, y:{u:2, v:3}}) End script [Java] demoVar: value set in DemoScript.js Java object: java.lang.String [Java] strBuf: string buffer used in DemoScript.js Java object: java.lang.StringBuffer [Java] newVar: [object Object] Java object: sun.org.mozilla.javascript.internal.NativeObject After running the script, the engine takes all variables (including new ones) and performs the inverse conversion, turning JavaScript primitives and strings into Java objects. Other JavaScript objects are wrapped into Java objects, which use some engine-specific internal API, such as sun.org.mozilla.javascript.internal.NativeObject. Run JavaScript files on the server side Page 4 of 19 © Copyright IBM Corporation 2008. All rights reserved.
    • ibm.com/developerWorks developerWorks® Because you probably want to use only standard APIs, all data exchange between the Java code and the executed script should be done through primitive variables, strings, and Java objects (for example, beans) whose properties and methods can be accessed very easily in the JavaScript code. Simply said, don't try to access native JavaScript objects in your Java code. Use Java objects in the JavaScript code instead. Invoking functions In the previous example, you've seen that it is possible to call Java methods from JavaScript. Now you'll learn how to invoke JavaScript functions from your Java code. First of all, you must execute the script containing the function that you want to call. Then, you'll cast the ScriptEngine instance to javax.script.Invocable, which provides invokeFunction() and invokeMethod(). If your script implements all the methods of a Java interface, you can also use getInterface() to obtain a Java object whose methods are coded in the scripting language. The InvDemo.java example (shown in Listing 5) executes a script named InvScript.js, which contains the demoFunction() routine. After casting the ScriptEngine instance to Invocable, the Java example passes the function name and the parameters to the engine's invokeFunction() method, which returns the value returned by demoFunction(). Listing 5. The InvDemo.java example package jsee.demo; import javax.script.*; import java.io.*; public class InvDemo { public static void main(String args[]) throws Exception { // Get the JavaScript engine ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); // Run InvScript.js Reader scriptReader = new InputStreamReader( InvDemo.class.getResourceAsStream("InvScript.js")); try { engine.eval(scriptReader); } finally { scriptReader.close(); } // Invoke a JavaScript function if (engine instanceof Invocable) { Invocable invEngine = (Invocable) engine; Object result = invEngine.invokeFunction("demoFunction", 1, 2.3); System.out.println("[Java] result: " + result); System.out.println(" Java object: " + result.getClass().getName()); System.out.println(); } else System.out.println("NOT Invocable"); } Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 5 of 19
    • developerWorks® ibm.com/developerWorks } The InvScript.js file (see Listing 6) contains the demoFunction() routine and the same printType() function that was used in the previous script example. Listing 6. The InvScript.js example println("Start script rn"); function printType(obj) { if (obj.getClass) println(" Java object: " + obj.getClass().name); else println(" JS object: " + obj.toSource()); println(""); } function demoFunction(a, b) { println("[JS] a: " + a); printType(a); println("[JS] b: " + b); printType(b); var c = a + b; println("[JS] c: " + c); printType(c); return c; } println("End script rn"); If you look at the output of InvDemo.java (shown in Listing 7), you'll observe that the numeric parameters are converted to JavaScript objects and the value returned by demoFunction() is obtained as a Java object. These conversions are performed only for primitives and strings. Any other objects are passed unchanged between the JVM and the JavaScript engine, and vice-versa. Listing 7. The output of InvDemo.java Start script End script [JS] a: 1 JS object: (new Number(1)) [JS] b: 2.3 JS object: (new Number(2.3)) [JS] c: 3.3 JS object: (new Number(3.3)) [Java] result: 3.3 Java object: java.lang.Double Note that javax.script.Invocable is an optional interface, which some script engines may not implement. The JavaScript engine that comes with JDK 6 does support this interface. Run JavaScript files on the server side Page 6 of 19 © Copyright IBM Corporation 2008. All rights reserved.
    • ibm.com/developerWorks developerWorks® Compiling scripts Interpreting scripts every time they are executed wastes CPU resources. If you run the same scripts multiple times, you can significantly reduce the execution time by compiling the scripts, using the methods provided by another optional interface named javax.script.Compilable, which is supported by the JavaScript engine of JDK 6. The CachedScript class (see Listing 8) takes a script file and recompiles it only when the source code is modified. The getCompiledScript() method calls the script engine's compile(), which returns a javax.script.CompiledScript object whose eval() methods execute the script. Listing 8. The CachedScript class package jsee.cache; import javax.script.*; import java.io.*; import java.util.*; public class CachedScript { private Compilable scriptEngine; private File scriptFile; private CompiledScript compiledScript; private Date compiledDate; public CachedScript(Compilable scriptEngine, File scriptFile) { this.scriptEngine = scriptEngine; this.scriptFile = scriptFile; } public CompiledScript getCompiledScript() throws ScriptException, IOException { Date scriptDate = new Date(scriptFile.lastModified()); if (compiledDate == null || scriptDate.after(compiledDate)) { Reader reader = new FileReader(scriptFile); try { compiledScript = scriptEngine.compile(reader); compiledDate = scriptDate; } finally { reader.close(); } } return compiledScript; } } The ScriptCache class (shown in Listing 9) implements a repository for the compiled scripts, using a java.util.LinkedHashMap object. The map's initial capacity is set to a maximum number of cached scripts and the load factor is 1. These two parameters guarantee that cacheMap will never need a rehash. By default, the LinkedHashMap class uses the insert-order for its entries. The third parameter of the LinkedHashMap() constructor must be true to use the access-order for the map's entries instead of the default order. Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 7 of 19
    • developerWorks® ibm.com/developerWorks After the maximum capacity of the cache is reached, the removeEldestEntry() method starts returning true so that an entry is automatically removed from cacheMap each time a new compiled script is added to the cache. By using the auto-removal mechanism of LinkedHashMap in combination with the access-order, ScriptCache ensures that the Least Recently Used (LRU) script will be removed from the full cache when a new script is added. Listing 9. The ScriptCache class package jsee.cache; import javax.script.*; import java.io.*; import java.util.*; public abstract class ScriptCache { public static final String ENGINE_NAME = "JavaScript"; private Compilable scriptEngine; private LinkedHashMap<String, CachedScript> cacheMap; public ScriptCache(final int maxCachedScripts) { ScriptEngineManager manager = new ScriptEngineManager(); scriptEngine = (Compilable) manager.getEngineByName(ENGINE_NAME); cacheMap = new LinkedHashMap<String, CachedScript>( maxCachedScripts, 1, true) { protected boolean removeEldestEntry(Map.Entry eldest) { return size() > maxCachedScripts; } }; } public abstract File getScriptFile(String key); public synchronized CompiledScript getScript(String key) throws ScriptException, IOException { CachedScript script = cacheMap.get(key); if (script == null) { script = new CachedScript(scriptEngine, getScriptFile(key)); cacheMap.put(key, script); } return script.getCompiledScript(); } public ScriptEngine getEngine() { return (ScriptEngine) scriptEngine; } } The next section uses the ScriptCache class, implementing the abstract getScriptFile() method and using getScript() to retrieve compiled scripts from the cache. Building a script runner In this section, you'll learn how to create a simple Java servlet that implements a Run JavaScript files on the server side Page 8 of 19 © Copyright IBM Corporation 2008. All rights reserved.
    • ibm.com/developerWorks developerWorks® URL-script mapping, which allows you to invoke server-side scripts from the Web browser. In addition, the servlet will expose several Java EE objects as variables that can be used in your JavaScript code. You'll also learn how to use script contexts that let you run multiple concurrent scripts with a single JavaScript engine. Initializing the servlet The name of the servlet class is JSServlet. Its init() method (see Listing 10) gets several configuration parameters and creates a ScriptCache object. The servlet's script cache uses getRealPath() to obtain the path of the script file that is mapped to a given URI. Listing 10. The init() method of JSServlet package jsee.servlet; import javax.script.*; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import jsee.cache.*; public class JSServlet extends HttpServlet { private String cacheControlHeader; private String contentTypeHeader; private ScriptCache scriptCache; public void init() throws ServletException { ServletConfig config = getServletConfig(); cacheControlHeader = config.getInitParameter("Cache-Control"); contentTypeHeader = config.getInitParameter("Content-Type"); int maxCachedScripts = Integer.parseInt( config.getInitParameter("Max-Cached-Scripts")); scriptCache = new ScriptCache(maxCachedScripts) { public File getScriptFile(String uri) { return new File(getServletContext().getRealPath(uri)); } }; } ... } Listing 11 contains the servlet's parameters, which are specified in the web.xml file. The Cache-Control header has nothing to do with the script cache. Both headers will be part of the HTTP responses returned by the servlet. The no-cache value will tell the browser not to cache the servlet's response, which should be treated as text/plain. Listing 11. The web.xml file <web-app ...> <servlet> <servlet-name>JSServlet</servlet-name> <servlet-class>jsee.servlet.JSServlet</servlet-class> <init-param> <param-name>Cache-Control</param-name> Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 9 of 19
    • developerWorks® ibm.com/developerWorks <param-value>no-cache</param-value> </init-param> <init-param> <param-name>Content-Type</param-name> <param-value>text/plain</param-value> </init-param> <init-param> <param-name>Max-Cached-Scripts</param-name> <param-value>1000</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JSServlet</servlet-name> <url-pattern>*.jss</url-pattern> </servlet-mapping> </web-app> As you can see in Listing 11, the *.jss pattern is mapped to the servlet. This means that JSServlet will handle all requests whose URLs end with the .jss extension. When the user enters such a URL in the Web browser or clicks a .jss link, the browser sends the HTTP request to the Web server (for example, Apache), which should be configured to dispatch it to the servlet container (for example, Tomcat). If the servlet container acts as Web server as well, no extra configuration is required. When the servlet container gets the request whose URL ends with .jss, it invokes the service() method that JSServlet inherits from javax.servlet.http.HttpServlet. This method calls either doGet() or doPost(), depending on the HTTP method of the request. Both methods are overridden by JSServlet as you'll see later in this section. 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" Run JavaScript files on the server side Page 10 of 19 © Copyright IBM Corporation 2008. All rights reserved.
    • ibm.com/developerWorks developerWorks® 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. Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 11 of 19
    • developerWorks® ibm.com/developerWorks 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); } ... } Handling requests You could simply call the above runScript() method to execute the script that is mapped to the URL of the HTTP request. In a real application, however, you'll probably want to do some initialization before running the script and some finalization after the script's execution. It is possible to run multiple scripts, sequentially, in the same context. For example, a script could define a set of variables and functions. Then, another script could do some processing, using the variables and functions of the previous script that was executed in the same context. The servlet's handleRequest() method (shown in Listing 14) sets the HTTP headers, runs the init.jss script, removes the context path from the request's URI, executes the script that has the obtained URI, and then runs another script named finalize.jss. Listing 14. The handleRequest() method of JSServlet Run JavaScript files on the server side Page 12 of 19 © Copyright IBM Corporation 2008. All rights reserved.
    • ibm.com/developerWorks developerWorks® public class JSServlet extends HttpServlet { ... protected void handleRequest( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (cacheControlHeader != null) response.setHeader("Cache-Control", cacheControlHeader); if (contentTypeHeader != null) response.setContentType(contentTypeHeader); ScriptContext scriptContext = createScriptContext(request, response); try { runScript("/init.jss", scriptContext); String uri = request.getRequestURI(); uri = uri.substring(request.getContextPath().length()); try { runScript(uri, scriptContext); } catch (FileNotFoundException x) { response.sendError(404, request.getRequestURI()); } runScript("/finalize.jss", scriptContext); } catch (ScriptException x) { x.printStackTrace(response.getWriter()); throw new ServletException(x); } } ... } The doGet() and doPost() methods (see Listing 15) of JSServlet call handleRequest(). Listing 15. The doGet() and doPost() methods of JSServlet public class JSServlet extends HttpServlet { ... public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { handleRequest(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { handleRequest(request, response); } } Developing server-side scripts This section contains several examples of server-side scripts, which show how you can get the request parameters, access the properties of your JavaBeans, and generate JSON responses. Pre-processing and post-processing In the previous section, you've seen that JSServlet invokes init.jss (shown in Listing 16) before executing the requested script. If you want to measure how long it Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 13 of 19
    • developerWorks® ibm.com/developerWorks takes to execute your scripts, you can store the start time into a variable as shown below. Listing 16. The init.jss script var debug = true; var debugStartTime = java.lang.System.nanoTime(); Then, you can calculate the execution time in finalize.jss (see Listing 17). The time is printed as a JavaScript comment so that JSServlet can generate valid JSON responses. Listing 17. The finalize.jss script var debugEndTime = java.lang.System.nanoTime(); if (debug) println("// Time: " + (debugEndTime - debugStartTime) + " ns"); More code will be added to init.jss and finalize.jss through this series. Getting request parameters Scripts invoked with the help of JSServlet can access request parameters with request.getParameter() and request.getParameterValues(), which return Java objects. You might prefer a shorter syntax and you might want to work with JavaScript objects instead of Java strings and arrays. This can be done very easily by adding the following lines to init.jss (see Listing 18). Listing 18. Getting request parameters in init.jss var param = new Object(); var paramValues = new Object(); function initParams() { var paramNames = request.getParameterNames(); while (paramNames.hasMoreElements()) { var name = paramNames.nextElement(); param[name] = String(request.getParameter(name)); paramValues[name] = new Array(); var values = request.getParameterValues(name); for (var i = 0; i < values.length; i++) paramValues[name][i] = String(values[i]); } } initParams(); Let's say you request a script named ParamDemo.jss with the URL shown in Listing 19. Listing 19. URL example for requesting a script Run JavaScript files on the server side Page 14 of 19 © Copyright IBM Corporation 2008. All rights reserved.
    • ibm.com/developerWorks developerWorks® http://localhost:8080/jsee/ParamDemo.jss?firstName=John&lastName=Smith In ParamDemo.jss (see Listing 20), you can get the two request parameters with param.firstName and param.lastName. Listing 20. The ParamDemo.jss example println("Hello " + param.firstName + " " + param.lastName); Accessing JavaBeans The application, session and request scopes of a Web application store bean instances, which you can obtain and replace with the getAttribute() and setAttribute() methods of ServletContext, HttpSession, and HttpServletRequest, respectively. You can also use the getBean() and setBean() functions (shown in Listing 21), which must be placed into the init.jss file so that any script can call them. The scope parameter should be one of the following strings: • "application" • "session" • "request" Listing 21. The getBean() and setBean() functions of init.jss function getBean(scope, id) { return eval(scope).getAttribute(id); } function setBean(scope, id, bean) { if (!bean) bean = eval(id); return eval(scope).setAttribute(id, bean); } Now say you want to keep an instance of DemoBean (see Listing 22) in the session scope. Listing 22. The DemoBean.java example package jsee.demo; public class DemoBean { private int counter; public int getCounter() { return counter; } public void setCounter(int counter) { this.counter = counter; } Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 15 of 19
    • developerWorks® ibm.com/developerWorks } The BeanDemo.jss script (shown in Listing 23) imports the package containing the JavaBean with importPackage(Packages.jsee.demo). Then, the script tries to get the bean instance from the session scope with getBean(). If the bean is not found, BeanDemo.jss creates the object and places it into the session scope with setBean(). At the end, the script increments and prints the bean's counter property. Listing 23. The BeanDemo.jss example importPackage(Packages.jsee.demo); var bean = getBean("session", "demo"); if (!bean) { bean = new DemoBean(); setBean("session", "demo", bean); } bean.counter++; println(bean.counter); If you import a package that starts with java, javax, org, edu, com, or net, you don't have to use the Packages prefix in importPackage(). You may also import individual classes with importClass(). Returning JSON responses Listing 24 shows an example that generates a JSON response containing some information about the JavaScript engine and the script's URI. The example uses the JavaScript syntax to create the json object whose source code is obtained as a String with the toSource() method. Listing 24. The JsonDemo.jss example var json = { engine: { name: String(factory.engineName), version: String(factory.engineVersion), threading: String(factory.getParameter("THREADING")) }, language: { name: String(factory.languageName), version: String(factory.languageVersion) }, script: { uri: String(request.requestURI) } }; println(json.toSource()); All Java objects, which are retrieved from the properties of factory and request in this example, must be converted to JavaScript objects so that toSource() can Run JavaScript files on the server side Page 16 of 19 © Copyright IBM Corporation 2008. All rights reserved.
    • ibm.com/developerWorks developerWorks® work properly. Listing 25 contains the script's output: Listing 25. The output of JsonDemo.jss ({language:{name:"ECMAScript", version:"1.6"}, engine:{name:"Mozilla Rhino", threading:"MULTITHREADED", version:"1.6 release 2"}, script:{uri:"/jsee/JsonDemo.jss"}}) Conclusion In this article, you've learned how to compile and execute JavaScript files, using the javax.script API. You also learned how to implement an LRU cache based on java.util.LinkedHashMap, how to export Java objects as script variables, how to implement a URL-script mapping, and how to build JavaScript files that are executed on the server-side. Stay tuned for the next part of this series where you'll learn how to call remote JavaScript functions with Ajax. Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 17 of 19
    • developerWorks® ibm.com/developerWorks Downloads Description Name Size Download method Sample application for this article jsee_part1_src.zip25KB HTTP Information about download methods Run JavaScript files on the server side Page 18 of 19 © Copyright IBM Corporation 2008. All rights reserved.
    • ibm.com/developerWorks developerWorks® Resources Learn • JSR-223 Scripting for the Java Platform is the specification containing all the details for using scripting languages in Java applications. • The Java Scripting Programmer's Guide presents the Java Scripting API and provides more code examples. • The javax.script package contains all the classes and interfaces of the Java Scripting API. • The developerWorks Web development zone is packed with tools and information for Web 2.0 development. • The developerWorks Ajax resource center contains a growing library of Ajax content as well as useful resources to get you started developing Ajax applications today. Get products and technologies • Mozilla Rhino is the JavaScript engine bundled with Java SE Development Kit (JDK) 6. About the author Andrei Cioroianu Andrei Cioroianu is the founder of Devsphere, a provider of Java EE development and Web 2.0/Ajax consulting services. He's been using Java and Web technologies since 1997 and has over 10 years of professional experience in solving complex technical problems and managing the full life cycle of commercial products, custom applications, and open-source frameworks. You can reach Andrei through the contact form at www.devsphere.com. Run JavaScript files on the server side © Copyright IBM Corporation 2008. All rights reserved. Page 19 of 19