un petit annuaire
Using the Xalan XSLT engine within a java servlet
Introduction
Separation of style from content allows for the same data to be presented in different ways and is the clear answer to the multiplication of connected devices (Palm, Pocket PC, Interactive TV) that can access networked ressources using their own language (WML, WebClipping, HTML, XHTML, cHTML, etc..).In this article, we show how to produce HTML and WML content from an XML data source, and the appropriate XSLT stylesheets. The XSLT transformation is done using the Xalan transformation engine, an open-source java project hosted by the Apache foundation. The Xalan engine is called from a java servlet, running in the Tomcat servlet engine.
Requirements
In this article, I will assume that you have installed the Tomcat servlet engine, and the Xalan XSLT engine. Both are available from java.apache.org and xml.apache.org. I also assume that xalan.jar, xerces.jar (the XML parser bundled with Xalan) and the servlet classes are present in your classpath. If not, try something like:XALAN_HOME=/path/to/xalan.jar
XERCES_HOME=/path/to/xerces.jar
export CLASSPATH=$CLASSPATH:$XALAN_HOME:$XERCES_HOME
Creating a new web application within Tomcat
Although you could install your java classes in an existing web application (the one called "examples", for instance), it is probably better to create a new web application. This is nicely explained in "Deploying web applications to Tomcat" by James Goodwill.Assuming $TOMCAT_HOME is the path to the Tomcat directory, first change directory to webapps (the root of all web applications) and create the following directories:
/xslt
/xslt/WEB-INF
/xslt/WEB-INF/classes
To do so, type the following commands:
> cd $TOMCAT_HOME/webapps
> mkdir xslt
> cd xslt
> mkdir WEB-INF
> cd WEB-INF
> mkdir classes
> cd classes
The /xslt/WEB-INF/classes directory is the place where you will store all the servlets classes described below.
Once the directories are created, you should install a servlet context, by editing the file $TOMCAT_HOME/conf/server.xml, and adding the following lines:
<Context path="/xslt" docBase="webapps/xslt" debug="0" reloadable="true" >
</Context>
path="/xslt" tells Tomcat that all requests starting with /onjava belong to the onjava web application.
docBase="webapps/xslt" tells the servlet container that the web application is located on webapps/xslt"
Once you have done these operations, you need to restart Tomcat.
Xalan-J : a Java XSLT engine
We use Xalan as our XSLT engine. While we embed Xalan within our servlet, Xalan-J can also be used in a command line way to perform XSLT transformation and produce static output files. For example, imagine you want to produce a static HTML file called slashdot.html from a XML file called slashdot.xml.You just need to type the following line:
> java org.apache.xalan.xslt.Process -in slashdot.xml -xsl slashdot.xsl -out slashdot.html
If you want the output to be displayed on the screen, simply omit the -out flag and argument.
The XSLT processor class
Xalan-J implements the TrAX (Transformation API for XML) interface. From the Xalan documentation: "A TRaX TransformerFactory is an object that processes transformation instructions, and produces Templates (in the technical terminology). A Templates object provides a Transformer, which transforms one or more Sources into one or more Results. To use the TRaX interface, you create a TransformerFactory, which may directly provide a Transformers, or which can provide Templates from a variety of Sources. The Templates object is a processed or compiled representation of the transformation instructions, and provides a Transformer. The Transformer processes a Source according to the instructions found in the Templates, and produces a Result".In the code below, the XSLT processor class contains a method that takes as input a XML source, a XSL source, a servlet request (it is not used here) and a servlet response, and it returns the output of the transformation to the servlet output stream.
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.xml.sax.SAXException;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
public class XalanXslProcessorBean {
TransformerFactory tFactory;
//the constructor simply gets a new TransformerFactory instance
public XalanXslProcessorBean() {
tFactory = TransformerFactory.newInstance();
}
//this method takes as input a XML source, a XSL source, and returns the output of the transformation to the servlet output stream
public void process(StreamSource xmlSource,
StreamSource xslSource,
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException, SAXException {
try {
Templates templates = tFactory.newTemplates(xslSource);
Transformer transformer = templates.newTransformer();
transformer.transform(xmlSource, new StreamResult(response.getOutputStream()));
}
catch (Exception e) {
//should log some message here
}
}
}
The XML data
The XML file we will use was grabbed from the slashdot.org site, and contains the slashdot news in XML form. It needs to be saved to your disk (and called slashdot.xml for example).<?xml version="1.0"?><backslash xmlns:backslash="http://slashdot.org/backslash.dtd">
<story>
<title>NASA Proposes Launch Solar Sail Vehicle For 2010</title>
<url>http://slashdot.org/article.pl?sid=00/05/15/058238</url>
<time>2000-05-15 07:54:15</time>
<author>timothy</author>
<department>ralph-nader-will-have-to-hire-a-chase-car</department>
<topic>space</topic>
<comments>99</comments>
<section>articles</section>
<image>topicspace.gif</image>
</story>
<story>
<title>Linuxcare Responds To Tim O'Reilly's Article</title>
<url>http://slashdot.org/article.pl?sid=00/05/15/0254252</url>
<time>2000-05-15 02:57:07</time>
<author>timothy</author>
<department>consider-source-horses-mouth-grain-of-salt</department>
<topic>linuxbiz</topic>
<comments>142</comments>
<section>articles</section>
<image>topiclinuxbiz.gif</image>
</story>
<story>
<title>New Internet VCR Service</title>
<url>http://slashdot.org/article.pl?sid=00/05/14/2048217</url>
<time>2000-05-14 20:51:57</time>
<author>timothy</author>
<department>this-is-cool-but-can-they-do-that?</department>
<topic>news</topic>
<comments>189</comments>
<section>articles</section>
<image>topicnews.gif</image>
</story>
<story>
<title>Google Releases WAP Search Tool</title>
<url>http://slashdot.org/article.pl?sid=00/05/14/1240252</url>
<time>2000-05-14 17:49:11</time>
<author>emmett</author>
<department>wireless</department>
<topic>internet</topic>
<comments>141</comments>
<section>articles</section>
<image>topicinternet.jpg</image>
</story>
<story>
<title>No More Unreal Ports For Linux?</title>
<url>http://slashdot.org/article.pl?sid=00/05/14/1439224</url>
<time>2000-05-14 16:32:11</time>
<author>timothy</author>
<department>one-web-one-program-happy-mothers-day</department>
<topic>games</topic>
<comments>250</comments>
<section>articles</section>
<image>topicgames.jpg</image>
</story>
<story>
<title>Pioneer Introduces 1st DVD Recorder (In Japan)</title>
<url>http://slashdot.org/article.pl?sid=00/05/14/152210</url>
<time>2000-05-14 15:50:48</time>
<author>CmdrTaco</author>
<department>steam-rising-from-the-riaas-forehead</department>
<topic>tv</topic>
<comments>98</comments>
<section>articles</section>
<image>topictv.jpg</image>
</story>
<story>
<title>QuakeForge And QuakeWorld Forever Merge</title>
<url>http://slashdot.org/article.pl?sid=00/05/14/1447248</url>
<time>2000-05-14 15:07:27</time>
<author>CmdrTaco</author>
<department>and-then-there-was-one</department>
<topic>quake</topic>
<comments>57</comments>
<section>articles</section>
<image>topicquake.gif</image>
</story>
<story>
<title>What Happens When Open Source And Work Collide?</title>
<url>http://slashdot.org/article.pl?sid=00/05/09/016208</url>
<time>2000-05-14 14:04:07</time>
<author>Cliff</author>
<department>sticky-situations</department>
<topic>programming</topic>
<comments>170</comments>
<section>askslashdot</section>
<image>topicprogramming.gif</image>
</story>
<story>
<title>Black Holes Don't Exist???</title>
<url>http://slashdot.org/article.pl?sid=00/05/14/1339252</url>
<time>2000-05-14 13:39:24</time>
<author>Roblimo</author>
<department>pop-science-can-be-fun</department>
<topic>science</topic>
<comments>162</comments>
<section>articles</section>
<image>topicscience.gif</image>
</story>
<story>
<title>Los Alamos Lab: We're OK, You're OK</title>
<url>http://slashdot.org/article.pl?sid=00/05/14/0143228</url>
<time>2000-05-14 04:44:44</time>
<author>timothy</author>
<department>sir-please-step-*away*-from-the-plutonium-bin</department>
<topic>news</topic>
<comments>278</comments>
<section>articles</section>
<image>topicnews.gif</image>
</story>
</backslash>
The XSLT stylesheet
An XSL stylesheet basically consists of a set of templates. Each template "matches" some set of elements in the original XML data and then describes the contribution that the matched element makes to the final output.An XSLT template is defined by a xsl:template tag, whose "match" parameter determines where this template applies. For example <xsl:template match="/"> ... </xsl:template> applies to the root element of the XML document, while <xsl:template match="backslash/story"> matches every story element that has backslash as father. Templates are generally applied recursively, i.e. a template calls another templates using the xsl:apply-templates tag.
<xsl:value-of> inserts the value of an expression to the final output. Note that {element} can also be used to insert the value of element.
Here is the HTML stylesheet (named slashdot.xsl) we will use:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="backslash/story"/>
</body>
</html>
</xsl:template>
<xsl:template match="backslash/story">
<li><a href="{url}"><xsl:value-of select="title"/></a></li>
</xsl:template>
</xsl:stylesheet>
Outputting HTML with a servlet
This java code implements a basic servlet, which uses the XalanXslProcessorBean class defined above. It sets the Content-Type portion of the HTTP header to text/html, creates two StreamSources objects from both the xml and the xsl files, and perform the transformationimport java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.stream.*;
public class XslProcessorServlet extends HttpServlet {
XalanXslProcessorBean processor;
public void init(ServletConfig config) {
processor = new XalanXslProcessorBean();
}
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
//sets the Content-Type portion of the HTTP header to text/html
response.setContentType("text/html");
try {
processor.process(new StreamSource("slashdot.xml"), new StreamSource("slashdot.xsl"), request, response);
}
catch (Exception e) {
}
}
}
Since slashdot.xml is often updated, you may prefer to fetch it directly from the slashdot.org site: you then need to change the processor.process(...) line to: processor.process(new StreamSource(new InputStreamReader((new URL("http://slashdot.org/slashdot.xml")).openStream())), new StreamSource("slashdot.xsl"), request, response);
Then restart your servlet container if needed, and reload the page.
Outputting HTML and WML with a servlet
Suppose that you want to make your content available to both HTML and WML navigators. Basically, you just need a XSLT stylesheet that can transform XML to HTML, and another one that can transform XML to WML (Wireless Meta Language). You then need to implement a mechanism that can use the appropriate stylesheet, depending on the navigator information contained in the HTTP request header.
Here is the WML stylesheet. Note the xsl:output tag, which is the only way to produce the <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> string in the WML output.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"
doctype-public="-//WAPFORUM//DTD WML 1.1//EN"
media-type="text/vnd.wap.wml"
doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"
encoding="ISO-8859-1"/>
<xsl:template match="/">
<wml>
<template>
<do type="prev" name="Previous" label="Back">
<prev/>
</do>
</template>
<card id="card1" title="Slashdot news">
<p>
<xsl:apply-templates select="backslash/story"/>
</p>
</card>
</wml>
</xsl:template>
<xsl:template match="backslash/story">
<a href="{url}"><xsl:value-of select="title"/></a><br/>
</xsl:template>
</xsl:stylesheet>
Note that the following servlet code now contains some code to fetch the user agent from the HTTP header, and uses the WML stylesheet when the user agent string contains the word (Nokia). Obviously, this only works with a Nokia phone or with some Nokia emulator.
import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.stream.*;
public class XslProcessorServlet extends HttpServlet {
XalanXslProcessorBean processor;
public void init(ServletConfig config) {
processor = new XalanXslProcessorBean();
}
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
//fetch the user agent part of the HTTP header
String useragent = request.getHeader("user-agent");
//if the user agent contains the string "Nokia", then use the WML stylesheet, otherwise use the HTML one
StreamSource xslsource;
if (useragent.indexOf("Nokia") >= 0) {
//send the correct Content-Type
response.setContentType("text/vnd.wap.wml");
xslsource = new StreamSource("slashdot_wml.xsl");
} else {
response.setContentType("text/html");
xslsource = new StreamSource("slashdot_html.xsl");
}
try {
processor.process(new StreamSource("slashdot.xml"), xslsource, request, response);
}
catch (Exception e) {
}
}
}
blogspirit
xanga
g-blog
blog-city
blogsome
blogg
msn
oldiblog
blogeasy
joueb
alfablog
avatale
blogstudio
blog.ca
blog.com
bloghi.com
ebloggy.com
blogigo
blogsharing
blogsource
blogspot
bloxster
cool-blog
Version XML - Cette page est peut-être encore valide XHTML1.1 et CSS sans tableaux.
