|
How to Use eXtensible Stylesheet Language Transformations (XSLT) in
Java[tm] Servlets to Transform JAXM SOAPMessages
by Michelle Cope
(May 2002)
We want to hear from you! Please send us your
FEEDBACK.
The following code sample may contain actual software programs in source
code form. This source code is made available for developers to use as needed,
pursuant to the terms and conditions of this license.
This code sample illustrates a use of XSLT in serlvets to transform Java[tm]
APIs for XML Messaging (JAXM) SOAP Messages. The sample application
is based on an airline flight booking system using a server/client configuration.
The SOAP client generates a SOAP message to request all flights that match
certain request criteria, such as the flight origin and destination.
In reply, the SOAP server will return a SOAP message with a list of matching
flights. The SOAPClient will then extract the list of matching flights and
transform the list into an HTML table which is displayed on the SOAPClient's
browser. In the sample application, the SOAPClient and SOAPServer are servlets.
The SOAPClient is coded in
FlightClientServlet.java
and the SOAPServer is coded in
FlightServerServlet.java.
The SOAPClient is broken into three main parts: the configuration of an
XSLT transformation processor, the sending of the SOAP request message,
and finally the transformation of the reply, which is output to the SOAP
client's browser.
The SOAPClient's init()
method sets up the transformer instance as follows:
... private SOAPConnection con; private Templates templates;
ServletContext ctxt; final String stylesheetParamName = "stylesheetName";
public void init(ServletConfig config) throws ServletException{
//Set up soap connection
try{ SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance(); con = scf.createConnection(); }catch(Exception e){ System.out.println("ERROR"+ e.getMessage()); }
//Set up transformer super.init(config); ctxt = config.getServletContext();
Enumeration params = ctxt.getInitParameterNames();
String stylesheetName = ""; while(params.hasMoreElements()){ String paramname = (String)params.nextElement(); if(paramname.equals(stylesheetParamName)){ stylesheetName = ctxt.getInitParameter(paramname); break; } }
String fullPathxslName = ctxt.getRealPath(stylesheetName);
if (stylesheetName.equals("") | fullPathxslName.equals(null)){ throw new ServletException("ERROR: Cannot find xsl SS"); }else{
try{ TransformerFactory tf = TransformerFactory.newInstance(); Source source = new StreamSource(fullPathxslName); System.out.println("FullPathXSLName"+fullPathxslName);
templates = tf.newTemplates(source); }catch(TransformerFactoryConfigurationError tfce){ throw new ServletException("ERROR: Cannot create transformerFactory" + tfce.getMessage()); }catch(TransformerConfigurationException tce){ throw new ServletException("ERROR:Cannot create template object"+ tce.getMessage()); } }//End of else. }
...
The init() method
extracts a virtual path for the XSLT stylesheet from the web descriptor
using the parameter, "stylesheetName". (See
web.xml in the distributed Web ARchive (WAR) file for details.)
It then translates the virtual path into an absolute path to locate the actual
XSLT stylesheet using the javax.servlet.http.ServletContext.getRealPath()
method. In the example, the stylesheet is expected to be located directly
under the context directory of the web application, for example
<TOMCAT_HOME>/webapps/FlightApplication/myxsl.xsl. Provided
that the stylesheet exists, a
javax.xml.transform.Templates object is generated from a
javax.xml.transform.TransformerFactory instance. A reference to
the templates object is retained in a global variable. A
javax.xml.transform.Templates object represents a single compilation
of the stylesheet in memory. The template object can be used safely in concurrently
executing threads and hence, its use is ideal in a servlet that does not
implement the SingleThreadModel. A servlet that serves multiple requests
simultaneously can use the template object for transformation without explicit
synchronization. Another way to transform output is to create a
javax.xml.transform.Transformer object from a
javax.xml.transform.TransformerFactory instance. However, it is not
threadsafe. If the same javax.xml.transform.Transformer
instance was used concurrently to serve multiple requests, it would lead
to unpredictable results. Hence, new instances of both the
TransformerFactory and
Transformer must be created in either the
doPost(), doGet() or
service() methods for each new request, incurring a set up overhead.
The doGet() method
constructs the SOAPMessage using the JAXM classes and sends the request
to the SOAP server. The SOAPServer returns a SOAP message with a list of
matching flights. The SOAPClient receives the reply in the
doGet() method and generates the HTML table.
... if(reply != null){ FileOutputStream replyFile = new FileOutputStream("MatchingFlightRequest.msg"); reply.writeTo(replyFile);
//Create a transform instance from template. //Might want to read into buffer before sending to response. try{ Transformer transformer = templates.newTransformer(); //Get the SOAPMessage as a content stream Source soapMessageSource = reply.getSOAPPart().getContent();
res.setContentType("text/html"); PrintWriter pw = res.getWriter(); StreamResult result = new StreamResult(pw);
transformer.transform(soapMessageSource, result);
pw.flush(); pw.close(); }catch(TransformerConfigurationException tce){ throw new ServletException("ERROR: can't create transformer"+ tce.getMessage()); }catch(IOException e){ throw new ServletException("ERROR: Problem retrieving printWriter"); }catch(TransformerException te){ throw new ServletException("ERROR: Cannot transform source"+ te.getMessage()); } }else throw new ServletException("ERROR: didn't receive reply"); ...
The above code creates a
javax.xml.transform.Transformer object from the template object.
This provides a single execution of the compiled stylesheet. The SOAPMessage's
content is extracted into a
javax.xml.transform.Source object which is then transformed into
a javax.xml.transform.Stream.StreamResult
object using the transform(Source,
Result) method of javax.xml.transform.Transformer
. The StreamResult object
wraps a java.io.PrintWriter
object which can send character data to the client. By using the
java.io.PrintWriter object, the transformed input is immediately displayed
on the client's browser as an HTML table:
List of matching Flights
| BA123 | GLA | LHR | 01/01/2002 | British Airways | | BA4234 | GLA | LHR | 01/01/2002 | British Airways | | AA4959 | GLA | LHR | 01/01/2002 | American Airways |
This is generated from a SOAP Message reply of the following format:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Header/> <soap-env:Body> <Flights> <Flight> <Flight_Number>BA123</Flight_Number> <Origin>GLA</Origin> <Destination>LHR</Destination> <Carrier>British Airways</Carrier> <Date>01/01/2002</Date> </Flight> <Flight> <Flight_Number>BA4234</Flight_Number> <Origin>GLA</Origin> <Destination>LHR</Destination> <Carrier>British Airways</Carrier> <Date>01/01/2002</Date> </Flight> <Flight> <Flight_Number>AA4959</Flight_Number> <Origin>GLA</Origin> <Destination>LHR</Destination> <Carrier>American Airways</Carrier> <Date>01/01/2002</Date> </Flight> </Flights> </soap-env:Body> </soap-env:Envelope>
The following XSLT stylesheet was used to generate the HTML output. It
matches on the <Flights>
element of the SOAP body, and generates an HTML table which has a row
for each child <Flight>
element.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" indent="no"/> <xsl:template match="Flights"> <html> <head><title>Matching Request Flights</title></head> <body> <h1>List of matching Flights </h1> <table border="3">
<xsl:for-each select="Flight"> <tr> <td><xsl:value-of select="Flight_Number"/></td> <td><xsl:value-of select="Origin"/></td> <td><xsl:value-of select="Destination"/></td> <td><xsl:value-of select="Date"/> </td> <td><xsl:value-of select="Carrier"/></td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
The application is packaged in the WAR file,
flightApplication.war
. The application can be deployed on Apache Tomcat 4.0 using the latest
JAXM release (1.0.1-ea2). The JAXM class files and related documents are
part of the Java[tm] Web Services Developers Pack and in the latest Java[tm] XML Pack software.
Instructions are in the packs on how to use the JAXM class files with Tomcat.
To run the application, use the following url:
http://<hostname>/flightApplication/index.html where
<hostname> is the name of the machine with Tomcat deployed.
The application can be improved by better handling of exceptions. Presently
all exceptions are caught and are either rethrown as
javax.servlet.ServletException exceptions or written out to standard
output. The transformed output could be buffered rather than sent immediately
to be displayed on the client.
DOC ID #1341
|