Read XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition Online
Authors: Michael Kay
The stylesheet and the servlet interface could also be extended to generate an index of names, as in the previous example, but as that's a simple task I'll leave you to work that out for yourself.
The next task is to write the Java code of the servlet. The code below uses the JAXP interface with a minor extension to request the XSLT processor to perform validation of source documents.
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
public class GedServlet extends HttpServlet {
The
init()
method of a servlet is called when the servlet is first initialized. In this method we set a system property to ensure that the XSLT processor we use is the schema-aware version of Saxon. In a production environment, it would be appropriate to read the values of this system properties from the
web.xml
configuration file.
public void init(javax.servlet.ServletConfig conf)
throws javax.servlet.ServletException {
super.init (conf);
System.setProperty(“javax.xml.transform.TransformerFactory”,
“com.saxonica.SchemaAwareTransformerFactory”);
}
The
service()
method of the servlet responds to an individual request from a user browser. It sets a
StreamSource
to the file identified by the
tree
parameter in the URL. It looks in its local data to see if the compiled stylesheet is already there; if not, it creates it. It then creates a
Transformer
, sets a couple of stylesheet parameters, and calls the JAXP
transform()
method to run the transformation, sending the result to the servlet output destination (which of course causes the result to appear at the browser).
/**
* Respond to an HTTP request
*/
public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType(“text/html”);
try {
String clear = req.getParameter(“clear”);
if (clear!=null && clear.equals(“yes”)) {
resetData();
}
String family = req.getParameter(“tree”);
Source source = new StreamSource(
new File(getServletContext().getRealPath(
“/” + family + “.xml”)));
Result result = new StreamResult(res.getOutputStream());
Templates style = getStyleSheet();
Transformer transformer = style.newTransformer();
transformer.setParameter(“id”, req.getParameter(“id”));
transformer.setParameter(“tree”, family);
transformer.transform(source, result);
} catch (Exception err) {
PrintStream ps = new PrintStream(res.getOutputStream());
ps.println(“Error applying stylesheet: ” + err.getMessage());
err.printStackTrace(ps);
}
}
When the stylesheet is first invoked, it is prepared and stored in memory as a
Templates
object. This method causes Saxon to validate the source document by setting the
SCHEMA_VALIDATION
property in the
TransformerFactory
: this attribute is specific to Saxon.
/**
* Get the prepared stylesheet from memory; prepare it if necessary
*/
private synchronized Templates getStyleSheet()
throws TransformerConfigurationException {
if (stylesheet == null) {
File sheet = new File(getServletContext().getRealPath(
“/ged-servlet.xsl”));
TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(
“http://saxon.sf.net/feature/schema-validation”,
new Integer(1));
stylesheet = factory.newTemplates(new StreamSource(sheet));
}
return stylesheet;
}
The
factory.setAttribute()
line is specific to Saxon, and indicates that source documents are to be schema-validated. If you want your code to be portable across different XSLT processors (which is the whole point of using the JAXP interface), then it would be best to catch the
IllegalArgumentException
that's thrown when the processor doesn't recognize this particular attribute.
The rest is straightforward:
/**
* Reset data held in memory
*/
private synchronized void resetData() {
stylesheet = null;
}
private Templates stylesheet = null;
}
The XML file holding the family tree data must be in a file
tree
.xml
where
tree
identifies the specific family tree, in our case
kennedy6.xml
. This must be in the home directory for the Web application containing the servlet, as defined by the configuration parameters for your Web server. The two stylesheet modules
person.xsl
and
ged-servlet.xsl
, and the schema
gedSchema.xsd
, must also be in this directory.
The servlet keeps in memory a copy of the compiled stylesheet (the JAXP
Templates
object): it makes this copy the first time it is needed.
It would also make sense to keep in memory a DOM
Document
object representing each family tree, to avoid overhead of parsing and validating the full XML document to display each individual. However, I haven't attempted to do that in this demonstration.
Installing and Configuring the Servlet
To run servlets you need to install a servlet container such as Tomcat, available from
www.apache.org
. For production use, Tomcat normally runs as an add-on to the Apache Web server, but for testing purposes, it also has an HTTP server of its own built in. There's no space here to go into all the details of installing a servlet container like Tomcat, but for quick reference, this section shows where I put the application files to get this example working.
Figure 19-2
shows the directory structure after installing Tomcat 6 (the details, of course, may vary).
Installing the software itself is very straightforward, but configuring it can be a little tricky. You need to follow the steps below:
1.
Choose a root directory for your application. If you're just experimenting, it's simplest to use the preconfigured examples directory. On my machine that's in
c:/lib/apache-tomcat-6.0.14/webapps/examples
. All the other file locations that follow are relative to this directory.
2.
Place the Saxon JAR files where Tomcat can find them, typically in the
WEB-INF/lib
directory.
3.
Place the Saxon-SA license key where Saxon will be able to find it, typically in the directory
WEB-INF/classes
.
4.
Place the servlet code,
GedServlet.class
, in
WEB-INF/classes
, as shown in
Figure 19-2
.
5.
Edit the file
WEB-INF/web.xml
to record details of the servlet. Search for the
HelloWorldExample
, and add the entry.
Similarly, search for the
HelloWorldExample
, and add:
6.
Add five data files to the application root directory, as shown in
Figure 19-3
: specifically, the two stylesheet modules
ged-servlet.xsl
and
person.xsl
, the two schema modules
gedSchema.xsd
and
xhtml1-transitional.xsd
, and the data file
kennedy6.xml
.
To start Tomcat up, double-click on the
startup.bat
file in the
bin
directory. This brings up an old-fashioned console that displays progress messages. Then, assuming you have defaulted everything in your configuration, open up your browser and enter the URL:
http://localhost:8080/
This will display Tomcat's home page: in the bottom-left corner is a link to
Servlets Examples
. Try one or two of the examples, for example Hello World, which should display
Hello World!
on your browser, to prove that Tomcat is working. If all is well, try: