The functionality described here is quite new. It is not in Kawa version 1.8 - you will have to get the latest code from CVS.
The KawaPageServlet
facility makes it trivial
it set up a web server so any specific URL causes a module (or script)
in any Kawa language (Scheme, XQuery, or BRL/KRL) to be
automatically executed, and the result sent back as the HTTP response.
Many web servers make it easy to execute a script
using a script processor which is selected depending on
the extension of the requested URL. That is why you see
lots of URLs that end in .cgi
, .php
,
or .jsp
. This is bad, because it exposes the
server-side implementation to the user: Not only are such URLs
ugly, but they make it difficult to change the server without
breaking people's bookmarks and search engines.
A server will usually provide a mechanism to use prettier URLs,
but doing so requires extra effort, so many web-masters don't.
With KawaPageServlet
, if you want a script to be executed
in response to a URL http://host/app/foo/bar
you give
the script the name app/foo/bar
, in the appropriate
server directory (as explained below). You choose the name bar
.
You can use the name bar.html
, even though the file
named bar.html
isn't actually an HTML - rather it
produces html when evaluated. Or better: just use a name without an
extension at all. The KawaPageServlet
will figure out
what kind of script it is based on the content of the file, by
looking at the first line, as detailed below.
A script named +default+
is run if there isn't a matching
script.
Create a file with the path info/uri
containing the following:
(: this is a script using kawa:xquery :) let $uri := qexo:request-uri() return <p>The request-uri is: {$uri}.</p>
Then this script will be excuted in response to a request
http://host:port/app/info/uri
.
The first line is a comment that identifies the language
as XQuery. The response back
will be the request URI
(as returned by the getRequestURI
method
of HttpServletRequest
).
This is the server-specific part of the requested URL,
so the response in this example would be:
The request-uri is: /utils/foo/info.
Create a file named +default+
containing the following:
;; This is -*- scheme -*- (require 'http) (make-element 'p "servlet-path: " (request-servlet-path))
This becomes the default script for HTTP requests that aren't
handled by a more specific script.
The first line is a comment that identifies this as a Scheme module.
The second line imports various useful definitions,
including the request-servlet-path
function, which
returns the servlet path
, which is the
part of the requested URL that is relative to the current web application.
Thus a request for
http://host:port/app/this/is/a/test
will return:
servlet-path: /this/is/a/test
KawaPageServlet
You need a servlet-capable web-server, such as
Tomcat,
and you need to install the Kawa .jar
library.
You can read these
instructions for installing Tomcat and Kawa.
A server like Tomcat can run one or more web applications,
where each application has its own top-level name and configuration.
You can choose any name you like for your web application, as long as
it doesn't have funny characters or conflicts with other web application
on the same server.
In the following example we assume you've chosen
appname
as the name if your web application.
We also assume $CATALINA_HOME
is the name of the
directory on your server that contains the Tomcat files.
First create a directory for the web application,
and a WEB-INF
directory within it:
mkdir $CATALINA_HOME/webapps/appname mkdir $CATALINA_HOME/webapps/appname/WEB-INF
Create the file $CATALINA_HOME/webapps/appname/WEB-INF/web.xml
containing the following:
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app> <display-name>My KawaPageServer application</display-name> <servlet> <servlet-name>KawaPageServlet</servlet-name> <servlet-class>gnu.kawa.servlet.KawaPageServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>KawaPageServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
The important part is the servlet-mapping
section.
The url-mapping
specifies that the
/
KawaPageServlet
class is the default mapping:
It will handle all HTTP requests for the
appname
application, unless you add another
servlet-mapping
with a more specific url-pattern
.
I.e. if the server is on host
and listening
to port
then all requests that start with:
http://host:port/appnamewill be handled by
KawaPageServlet
.
When KawaPageServlet
receives a request for:
http://host:port/appname/anything
it will look for a file:
$CATALINA_HOME/webapps/appname/anything
If such a file exists, the script will be executed, as described below.
If not, it will look for a file name +default+
in the
same directory. If that desn't exist either, it will look in the
parent directory, then the grand-parent directory, and so
on until it gets to the appname
web application
root directory. So the default script is this:
$CATALINA_HOME/webapps/appname/+default+
If that doesn't exist then KawaPageServlet
returns a 404 page not found
error.
Once KawaPageServlet
has found a script, it looks at the
first line to see if it can recognize the kind (language) of the script.
Normally this would be a comment that contains the name of a programming
language that KawaPageServlet
knows about.
KawaPageServlet
recognizes the following magic strings
in the first line of a script:
kawa:scheme
kawa:xquery
kawa:language
language
known to Kawa.
KawaPageServlet
also recognizes Emacs-style
mode specifiers
:
-*- scheme -*-
-*- xquery -*-
-*- emacs-lisp -*-
-*- elisp -*-
-*- common-lisp -*-
-*- lisp -*-
Also, it also recognizes comments in the first two columns of the line:
;;
(:
If KawaPageServlet
doesn't recognize the language of a script
(and it isn't named +default+
) then
it assumes the file is a data file.
It asks the servlet engine to figure out the content type
(using the getMimeType
method of ServletContext
),
and just copies the file into the response.
KawaPageServlet
automatically compiles a script
into a Java class. The class is internal to the server,
and is not written out to disk.
(There is an unsupported option to write the compiled file to a class file,
but there is no support to use previously-compiled classes.)
The server then creates a module instance to handle the actual
request, and runs a run
method of the object.
On subsequence requests for the same script, the same class and
instance are reused; only the run
is re-executed.
If the script is changed, then it is re-compiled and a new module
instance created. This makes it very easy to develop and modify a script.
(KawaPageServlet
for performance reasons doesn't check
more than once a second whether a script has been modified.)