Compiling XQuery to a Servlet

Using Qexo, you can run an XQuery program as a Servlet. This makes it easy to have them be executed by a web server.

Simplest way to run XQuery in a web server

See this article for a simpler how-to for deploying a servlet.

How to write and compile an XQuery servlet

Suppose the file hello.xql contains an XQuery "program". (Below we show an example of what you can do with Kawa-XQuery servlets.) You compile hello.xql to servlet using the following command.

$KAWA --xquery --servlet -C hello.xql
Here $KAWA is how you invoke the Kawa application. For example if you use kawa-1.7.jar, you can do:
java -jar kawa-1.7.jar --xquery --servlet -C hello.xql

The -C flag tells Kawa to compile the following source files. The --xquery flags tells Kawa that the source files are in the XQuery language. (The default language is Scheme.) The --servlet tells Kawa to generate a servlet.

The result of the compilation is usually a single class file hello.class that implements javax.servlet.http.HttpServlet. Sometimes Kawa will need to generate some extra helper classes; these will all have the form hello$XXX.class. To copy or install all the generated classes you should type hello*.class.

Installing and running your servlet under Tomcat

There are a number of Web servers that can run servlets, and most of them have various fancy ways of creating "web applications". Here are some simple instructions for running your servlet under Tomcat. Tomcat is a web server written in Java and released by the Apache Foundation's Jakarta project. The instructions here assume version 4.0 of Tomcat, and that $CATALINA_HOME is where Tomcat is installed.

You first need to install the Kawa jar file kawa-1.7.jar somewhere where Tomcat can find it. For example install it in $CATALINA_HOME/lib/kawa-1.7.jar.

You next need to install your servlet. In Tomcat (and the Servlet specification) a servlet is part of a "web application". If you call your application myutils, you need to create directories $CATALINA_HOME/webapps/myutils, $CATALINA_HOME/webapps/myutils/WEB-INF, and $CATALINA_HOME/webapps/myutils/WEB-INF/classes. You then copy your servlet class files hello*.class into the latter directory.

After you have started up Tomcat, it by default listens to port 8080. To try out your servlet, point a browser to the URL http://localhost:8080/myutils/servlet/hello.

See the Tomcat documentation for how you can customize how Tomcat maps URLs to servlets and how they are executed.

More information

The Kawa manual has more information. Though it concentrates on writing Scheme programs, much of it also applies to XQuery programs. For example Kawa includes a cgi-wrapper program that lets you run servlets as if they were CGI scripts. There are also useful functions for getting HTTP request information, setting HTTP response headers, and the unescaped-data function for raw HTMP/XML output..

An example servlet explained

This example generates a response listing some of the properties of the request. An explanation follows.

define function invoke-row ($field,$style) {
  <tr><td>{$field}</td><td><{$style}>{invoke($request,$field)}</></td></tr>}

define function do-invoke ($field,$style) {
  invoke-row($field,$style),"
" }

<p>Hello, the request was {$request}.
The response was {$response}.</p>,
<pre>The request URI was (using path) {$request/requestURI}.
The request method (using path) was {$request/method}.
The path-info (using path) was {$request/pathInfo}</pre>,
<table>
<tr><th>Field name (using invoke)</th><th>field value</th></tr>
{do-invoke("getRequestURI","strong"),
do-invoke("getContentType","code"),
do-invoke("getProtocol","u"),
do-invoke("getRemoteAddr","code"),
do-invoke("getRemoteHost","code"),
do-invoke("getScheme","code"),
do-invoke("getServerName","code"),
do-invoke("getServerPort","code"),
do-invoke("getAuthType","code"),
do-invoke("getMethod","code"),
do-invoke("getPathInfo","code"),
do-invoke("getPathTranslated","code"),
do-invoke("getQueryString","code"),
do-invoke("getRemoteUser","code"),
do-invoke("getRequestURI","code"),
do-invoke("getServletPath","code")
}</table>

This program illustrates how you can use the prefined variables $request and $response. The former is the most useful, as it contains the information that the browser sent to the servlet. The $response variable has more exotic uses for controlling the response. Both variables are passed to the servlet by the servlet engine, and have the types javax.servlet.http.HttpRequest and javax.servlet.http.HttpResponse respectively.

The program first defines the function invoke-row, which takes two parameters, in addition to the implicit $request parameter. The body of the function is an "element constructor expression" which contructs a table row (<tr>) element. This looks like plain HTML (actually XML), but you can nest arbitrary expressions in them using curly braces to "escape" nested expressions. The $field parameter is a string that names a 0-argument Java method of the $request object. The built-in Kawa method invoke takes an object, a Java method name, and optionally some methods args. It calls the named method with the given arguments. The result from invoke is wrapped in an HTML style tag that is specified by the $style parameter. The result is made into a table row.

The do-invoke function is just a simple wrapper that calls invoke-row. It then appends a newline using the comma operator, which concatenates the operands to create a sequence. In this case the result is a sequence with two values: a <tr> element node, and a newline string. This effecticely adds a newline in the HTML after the <tr> element, which makes the HTML more readable. The other reason for adding the do-invoke function is to illustrate how you can pass complex values around, with the result of one function being "pasted" into the result of another. Note that the Kawa implementation does not actually do any copying in this case; the elements are written directly into the servlet result buffer.

The main body of the function is two "element constructor expressions", the pre and the table constuctors, which again are concatenated using the comma operator, resuling in a sequence consisting of two elements.

The pre element contaisn the nested expression $request/requestURI. This is a convenience feature of Kawa-XQuery. It means to extract the requestURI "property" of the $request object. A property name like "requestURI" is mapped into a method name "getRequestURI", and basically the expression $request/requestURI is equivalent to invoke(request, "getRequestURI").

The final part of the program just makes a lot of calls to do-invoke to build a big <table>.

The Kawa servlet wrapper automatically wraps the entire result in a <html>...</html> wrapper.

You can see a sample of the generated output, using the URL http://localhost:8080/myutils/servlet/hello/mypath.

Development and Debugging

The Qexo debugging page has some general notes on Qexo error behaviour and debugging. Here are some notes specific to using servlets and KawaPageServlet.

If you request an .xql after editing it, KawaPageServlet will automatically re-compile it. This makes it easy to test out changes. If you make a syntax error, the result sent to the browser will contain the error messages from the compiler.

If a run-time errors causes an uncaught run-time exception, Tomcat will return to your browser a stack trace showing which methods were active when the exception was created. The methods that KawaPageServlet compiles from your XQuery program will be in that stack trace with (if you're lucky) line numbers referring to the lines in your XQuery program. You may have to dig through the stack trace, and ignore methods belonging to the Kawa or Tomcat run-time environment. You may also have to look for the root cause.

Tomcat re-directs the standard error output to the file $CATALINA_HOME/logs/catalina.out. This includes data written using the standard trace function.


Per Bothner
Last modified: Wed Dec 1 16:40:47 PST 2004