pnuts.servlet module allows simple and easy servlet programming.
It supports two styles of Web scripting. One is HTML document which scripts are embedded within a special tag, which is similar to JSP and PHP. The other is that some program generates a web contents proactively, as Java servlet and CGI does. In this chapter, we call the former Dynamic Pages, and the latter Servlet Scripts.
Pnuts servlets require a servlet container which supports Java Servlet API 2.2 or better.
Servlet class Description pnuts.servlet.PnutsServlet When this class is used, a servlet script is executed for each request. pnuts.servlet.DynamicPageServlet When this class is used, a template-based dynamic page is executed for each request.
Other than those servlet classes, pnuts.servlet.URLRewriteServlet class is used for URL rewriting.
init-param the meaning module If one or more comma-separated module names are specified, those modules would be added to the context. initialScript If specified, the script is loaded when the servlet is initialized. script If specified, the script is executed for each request. Otherwise, the script file that corresponds the URL is used. locale The default locale
- Typically an empty string is specified.
- Locale information, language, country, and variant, can be specified as a '_' separated string in that order.
timezone The default timezone compile If true (default), scripts are compiled into Java bytecode. encoding Character encoding of script files. buffering If true (default), contents are buffered and Content-Length is set. execute-latest-script If true (default), timestamp of contents are checked and a script is compiled and loaded only if it is updated since last loaded. debug If true, the stack trace is appended to the log file when an error occurs. isolation If true (default), scripts are executed in an isolated scripting environment. workdir The name of the subdirectory in which converted dynamic pages are saved. The same place as .pea file can be specified as ".". If this parameter is not specified, dynamic pages are converted on-memory and not saved to a file. error-page If specified, redirect requests to the location when exception is not caught..
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/dtd/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>pnuts</servlet-name>
<servlet-class>pnuts.servlet.PnutsServlet</servlet-class>
<init-param>
<param-name>execute-latest-script</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>script</param-name>
<param-value>scripts/index.pnut</param-value>
</init-param>
<init-param>
<param-name>module</param-name>
<param-value>pnuts.servlet</param-value>
</init-param>
<init-param>
<param-name>compile</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>includeLineNo</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>buffering</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>pnuts</servlet-name>
<url-pattern>*</url-pattern>
</servlet-mapping>
</web-app>
The programming servlet scripts in Pnuts is basically same as ordinary Pnuts scripts. The only difference is that servlet scripts rely on pnuts.servlet module. Also the rich functionalities of the Java Servlet API are all available to the scripts.
response.setContentType("text/html")
print("<html><head></head><body>")
for (i : 1..6) {
print("Hello, World!!")
}
print("</body></html>")
The default output stream, to which the messages are printed, is a PrintWriter object. If OutputStream is required, response.getOutputStream() should be used instead of the default output stream.
use("pnuts.awt")
response.setContentType("image/png")
import("java.awt.Color")
im = makeImage(20, 20, function (g) {
g.setColor(Color::white)
g.fillRect(0, 0, 20, 20)
g.setColor(Color::orange)
g.fillOval(0, 0, 20, 20)
})
writeImage(im, "image/png", response.getOutputStream())
The directory where the servlet script is located is added to the application's classpath, i.e. WEB-INF/classes, WEB-INF/lib/*jar.
Scopes for variables in servlet scripting falls into the following scopes.
Script scope is a name space created for each servlet script specified in a URL. It is shared by requests that designate the same script. So, it is used for storing data which are not changed by requests, and defining functions which are called to construct a response.
In script scope, the following variable is pre-defined.
- this
- The Servlet object.
Request scope is a child name space of a script scope, which is created for each request. It is used for storing request-specific data. A servlet script specified in a URL are executed in request scope. Other script files loaded by load() or loadFile() are executed in the parent script scope.
In request scope, the following variables are defined.
- request
- The ServletRequest object passed to the Servlet.service() method
- response
- The ServletResponse object passed to the Servlet.service() method
Since request scope is a child packge of the script scope, variables in the script scope are visible from the request scope. But the opposite is not true.
It is possible to access the request scope from other scope, by using the Package object that requestScope() returns.
package(requestScope())
It is also possible to access the script scope from the request scope, by using the Package object that rootScope() returns.
root = rootScope() if (root.a == null) root.a = 1 else root.a++ println(root.a)
Session scope is a name space created by calling getSession() function. It can be shared by not only multiple requests, but also multiple scripts.
For HTML or XML templates, care must be taken for special characters such as <, >, and &.
escape() function is used for sanitizing the input that may include special characters.
response.setContentType("text/html")
head = "<TABLE BORDER=1>"
row = template("<TR><TD>%key%</TD><TD>%value%</TD></TR>", "%([A-Za-z]+)%")
footer = "</TABLE>"
props = System::getProperties()
m = map()
println(head)
for (i : props.keys()){
m.key = escape(i)
m.value = escape(string(props.get(i)))
applyTemplate(row, m)()
}
println(footer)
Note that URL string embedded in a HTML document should be encoded differently. The following example illustrates how to encode a part of URL with encodeURL().
templ = template("%link%", "%([A-Za-z]+)%")
m = map()
m.link = "http://host/index.pnut?name=" + encodeURL("%&?+")
applyTemplate(templ, m)()
An alternative is to use makeQueryString() function that makes a query string from a Map.
templ = template("%link%", "%([A-Za-z]+)%")
q = map()
q.name = "%&?+"
m = map()
m.link = "http://host/index.pnut?" + makeQueryString(q)
applyTemplate(templ, m)()
Dynamic pages of Pnuts are HTML documents with the special tag , <% and %> . The file name extension is .pea. Any Pnuts expression can be embedded in the special tag.
Dynamic pages are translated to the equivalent servlet script at the first request. Then the servlet script is executed for each request, unless the original.pea file is updated.
<%- ... %> Comment <%= ... %> Evaluate the expression and embed the result <% ... %> Evaluate the expression (does not embed the result) <%@ ... %> Directive.
<%@ include file=".."%> includes a page file.
<%@ include expr=".."%> includes the result of the script execution (at compile time).
<%@ escape %> escapes special characters in the following <%=..%> sections.
<%@ no-escape %> does not escape special characters in the following <%=..%> sections.
<% response.setContentType("text/html") %>
<% for (i : 1..6) { %>
<H<%=i%>> <%=i%>: Hello, World!! </H<%=i%>><p>
<%}%>
The following directive replace itself with the servlet script converted from the file.
<% response.setContentType("text/html") %>
<%@ include file="date.pea" %>
It is <%= date() %> now.
The following directive replace itself with the result of executing expreesion at compile time.
This page was compiled at <%@ include expr="Date()" %>.
For example, with the folowing web.xml, .pnut files are mapped to PnutsServlet.
<web-app>
<servlet>
<servlet-name>pnuts</servlet-name>
<servlet-class>pnuts.servlet.PnutsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pnuts</servlet-name>
<url-pattern>*.pnut</url-pattern>
</servlet-mapping>
</web-app>
response.setContentType("text/html")
println("<html><body>")
println("Hello ", getParameter("name"))
println("</body></html>")
With the folowing web.xml, .pea files are mapped to DynamicPageServlet.
<web-app>
<servlet>
<servlet-name>dynamicPages</servlet-name>
<servlet-class>pnuts.servlet.DynamicPageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dynamicPages</servlet-name>
<url-pattern>*.pea</url-pattern>
</servlet-mapping>
</web-app>
<html>
<body>
Hello <%= getParameter("name")%>
</body>
</html>
<web-app>
<servlet>
<servlet-name>pnuts</servlet-name>
<servlet-class>pnuts.servlet.PnutsServlet</servlet-class>
<init-param>
<param-name>initialScript</param-name>
<param-value>setup.pnut</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>pnuts</servlet-name>
<url-pattern>list/*</url-pattern>
</servlet-mapping>
</web-app>
setupActions("actions") setupPages("templates")
The setup.pnut compiles the scripts in "actions/" and the templates in "templates/". Once all files are compiled,
name = path[1] // name is defined in the request scope render("list") // format and display 'templates/list.pea'
<html> <body> Hello <%=escape(name)%> </body> </html>
path = requestPath() if (size(path) > 1){ dispatch(path[0]) // executes "actions/something.pnut" }
http://localhost/help/encodeURL
http://localhost/help.pnut?function=encodeURLThe former URL can be rewritten to the latter.
Configuration file should be a XML file that looks like the following. URL that matches the pattern in a rewrite-rule element is replaced by the corresponding replacement string. If no pattern matches or any of the patterns in exclude-pattern elements matches, the URL is used as it is.
init-param description configuration Configuration File (default: WEB-INF/classes/url_rewrite.conf) verbose If true, rewritten URLs are printed to System.err. validating If true, configuration file are validated.
<?xml version="1.0" encoding="UTF-8"?>
<url-rewrite>
<rewrite-rule>
<pattern>Regular Expression</pattern>
<replacement>Replacement String</replacement>
</rewrite-rule>
...
<exclude-pattern>/scripts/*pnut</exclude-pattern>
...
</url-rewrite>
Here is an example.
<?xml version="1.0" encoding="UTF-8"?>
<url-rewrite>
<rewrite-rule>
<pattern>[^/]*/help/(\w+)</pattern>
<replacement>help.pnut?function=$1</replacement>
</rewrite-rule>
</url-rewrite>
<web-app>
...
<servlet>
<servlet-name>url_rewrite</servlet-name>
<servlet-class>
pnuts.servlet.URLRewriteServlet
</servlet-class>
</servlet>
<servlet-mapping>
<url-pattern>help*</url-pattern>
<servlet-name>url_rewrite</servlet-name>
</servlet-mapping>
...