Speak your own programming language with Web scripting

An introduction to JSR 223's Web scripting framework

1 2 Page 2
Page 2 of 2

I've talked about what name-values pairs the application scope, session scope, and request scope contain. But I haven't said when and where to set the name-value pairs in those scopes, or have I? Recall that in the previous section, I said a script servlet initializes the HTTP script context for every HTTP request the servlet receives in the getContext() method. The getContext() method calls the initialize() method on the HTTP script context to set the name-value pairs in the three scopes.

An HTTP script context keeps references to a Servlet, an HttpServletRequest, and an HttpServletResponse, and has methods such as getServlet(), getRequest(), and getResponse for you to get to those references. When getContext() calls initialize(), it passes the initialize() method a Servlet, an HttpServletRequest, and an HttpServletResponse. The initialize() method then initializes the HTTP script context's references to a Servlet, an HttpServletRequest, and an HttpServletResponse with those three parameters. A script servlet needs to initialize the HTTP script context for every HTTP request, otherwise the name-value pairs in the request scope, session scope, and application scope might become stale.

Before we leave this section, let's talk more about the code. The construct that JSR 223 defines to model the HTTP script context is the javax.script.http.HttpScriptContext interface. Note that HttpScriptContext extends javax.script.ScriptContext. This supports my earlier statement that an HTTP script context is a script context. The investment we made in learning about script context will now prove rewarding: Because HttpScriptContext extends ScriptContext, from our knowledge of ScriptContext, we immediately know that we can get and set scopes in an HTTP script context by calling ScriptContext's getBindings() and setBindings() methods. And we also know that we can get and set name-value pairs in a scope by calling ScriptContext's getAttribute() and setAttribute() methods, or by calling the get() and put() method on the scope itself.

If you look at the JSR 223 reference implementation, you'll notice that it provides an implementation of the HttpScriptContext interface in javax.script.http.GenericHttpScriptContext. Unfortunately, it is dated and cannot work with Java SE 6 beta build 80. So I implemented SimpleHttpScriptContext as a quick fix for GenericHttpScriptContext. The problem with GenericHttpScriptContext is that it's missing the implementation of two methods: getBindings() and setBindings(). To fix that, I made SimpleHttpScriptContext inherit everything from GenericHttpScriptContext and augmented it with the two missing methods.

Besides the methods for accessing scopes and name-value pairs that it inherits from ScriptContext, HttpScriptContext has methods for getting various configurations as defined in JSR 223's Web scripting framework.

Configurations

The specification's Web scripting framework defines a handful of configurations that administrators can set to control the execution of script engines. By administrators, I mean people in charge of deploying a script engine in a servlet container. Their roles differ from those of script engine providers. So far in this article, I have talked about script servlet and HTTP script context. It's the script engine provider's responsibility to meet the requirements and consider the details related to those two concepts. If you play the role of administrator and your main purpose of learning Web scripting is so that you know how to deploy a script engine in a servlet container, you could ignore the previous text and just start here.

For those readers who play the role of script engine providers, it's your responsibility to implement in the script engine the expected behavior of the configurations. Otherwise, administrators will be surprised when they deploy your script engine. Let's first look at how administrators deploy script engines in a servlet container. Then we'll look at what script engine providers need to do to implement the expected behavior of the configurations.

To administrators, the work of deploying a script engine in a servlet container consists of two parts: deploying the engine's script servlet and setting configurations. In our discussion of a script servlet, we learned that a script servlet is really a bona fide servlet. So it should not come as a surprise that deploying a script servlet in a servlet container is the same as deploying a typical servlet.

Using Tomcat as an example, here's how I deploy the BoolScript engine's script servlet: First, I add a context entry in the file {CATALINA_HOME}\conf\server.xml. The context entry represents the Web application that will host our script engine and script code. We have already seen the context entry and where to put it in the section "Set Up the Example Code." Next, I want to tell the servlet container that all requests for BoolScript files (i.e., files with .bool file extensions) are to be handled by the BoolScript script servlet. This is done in {CATALINA_HOME}\webapps\BoolScript\WEB-INF\web.xml like this:

 

<servlet> <servlet-name>BoolScriptServlet</servlet-name> <servlet-class> net.sf.model4lang.boolscript.engine.BoolScriptServlet </servlet-class> </servlet>

<servlet-mapping> <servlet-name>BoolScriptServlet</servlet-name> <url-pattern>*.bool</url-pattern> </servlet-mapping>

As you can see, the XML settings above reference the Java class BoolScriptServlet. That class is in the boolscript.jar file, which we place in the {CATALINA_HOME}\webapps\BoolScript\WEB-INF\lib folder so that the servlet container can find it.

Now, the BoolScript engine is deployed in the servlet container, and we are ready to set the configurations that will control the execution of the BoolScript engine within the Web application. The table below lists all the configurations defined in JSR 223's Web scripting framework.

NameExample valuesDefault valueDescription
script-disable true, falsefalse Whether the script servlet should execute scripts at all
script-use-session true, falsetrueWhether scripts executed by the script servlet should have access to HTTP session information
script-methods GET, POSTGET, POSTA comma-delimited list of HTTP request methods the script servlet accepts
script-directory/etc/xyzno directoryThe directory where script files reside
script-display-resultstrue, falsefalseWhether the script servlet should output evaluation result of a script file
allow-languagesbool scriptAny languageA comma-delimited list of script languages the script servlet accepts

Most of the configurations are self-explanatory. Here, I discuss script-disable and script-directory in more detail. You can refer to JSR 223's Web scripting framework if you need more information on other configurations.

Look at {CATALINA_HOME}\webapps\BoolScript\WEB-INF\web.xml and you will see that I set the configurations like this:

 

<context-param> <param-name>script-disable</param-name> <param-value>false</param-value> </context-param>

<context-param> <param-name>script-use-session</param-name> <param-value>false</param-value> </context-param>

<context-param> <param-name>script-methods</param-name> <param-value>GET,POST</param-value> </context-param>

<context-param> <param-name>script-display-results</param-name> <param-value>true</param-value> </context-param>

The script-disable setting controls whether the BoolScript script servlet should execute scripts at all. If you set it to true, you will get a message that says "Access to the specified resource has been forbidden," or something similar, in the Web browser when you browse to http://localhost:8080/BoolScript/logic.bool.

For those readers who play the role of script engine providers, let's see how we implement this configuration behavior in the BoolScript engine. The code is in GenericHttpScriptServlet.java and looks like this:

  //Check Web Scripting configurations.
   //We only perform check on script-disable here as an example.
        if(httpScriptCtxt.disableScript()) {
            httpRes.setStatus(403);
            return;
        }
        
        //Perform check on the rest of configurations such as 
        //script-use-session, script-methods, allow-languages here.

As mentioned earlier, this is boilerplate code that every script servlet will implement, and in the production release of Java SE 6, we will most likely get this code for free by deriving our script servlet class from some class similar to GenericHttpScriptServlet. The GenericHttpScriptServlet class, being a temporary fix, doesn't check all the configurations. It only performs a check on the disable-script configuration as an example.

Besides disable-script, another configuration I'd like to talk about in more detail is script-directory. In web.xml, I didn't set any value for script-directory. When the script-directory setting is missing, that means javax.script.http.HttpScriptContext's getScriptSource() method should follow the same rules used to locate JSP code to locate script code. The file BoolScript-binary.zip contains sample script code in the file logic.bool, and we place it in the {CATALINA_HOME}\webapps\BoolScript folder because that's where we would put the Web application's JSP code if any existed.

Conclusion

In this article, we took the BoolScript language from my previous article, equipped it with Web scripting capabilities, deployed it in the Tomcat servlet container, and tested our work by browsing to a sample script file in a Web browser. You can get any future updates to the BoolScript language from the Model4Lang open source project. The project also has a JSR 223 implementation for the Scheme programming language.

Chaur Wu is a software developer and published author. He has coauthored books on design patterns and software modeling. He is the project administrator of Model4Lang, an open source project dedicated to a model-based approach to language design and construction.

Learn more about this topic

1 2 Page 2
Page 2 of 2