Tuesday, December 23, 2008

HTML output to the browser from a Servlet based on XML data using Jakarta Xalan 2 and Xerces 2.

Creating HTML output to the browser from a Servlet based on XML data often comes up.
Here's a fully functional example on how to achieve this using Jakarta Xalan 2 and Xerces 2.


package somepackage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;

/**
* Generate servlet output based on XML input and an XSLT document.
* filename, XML Document (DOM Object), and an optional contenttype
* are to be passed in as requestparameters.
* @author Jeroen Wenting
*/
public class Generator extends HttpServlet
{
public static final String defContentType = "text/html; charset=UTF-8";
public static final String contentTypeHTML = defContentType;
// public static final String contentTypeXML = "text/xml; charset=UTF-8";
// public static final String contentTypeCSV = "application/vnd.ms-excel";
public static final String errorTemplate = "+++ERRORS+++";
private static HashMap cache;
/**
*
*/
public Generator()
{
super();
}

/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
doPost(request, response);
}

/**
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String contentType = null;
Document doc = null;
String xsl = null;

contentType = (String)request.getAttribute("contenttype");
if (contentType == null) contentType = defContentType;
doc = (Document)request.getAttribute("xml-doc");
xsl = (String)request.getAttribute("xsl-file-name");
String path = getServletContext().getRealPath("/WEB-INF/xsl/");
// Output goes in the response stream.
PrintWriter out = response.getWriter();

if (doc == null xsl == null)
{
String errorMsg = "";
if (doc == null)
errorMsg += "XML document missing from call to OutputGenerator<br />";
if (xsl == null)
errorMsg += "XSL filename missing from call to OutputGenerator<br />";
StringBuffer buf = generateError(errorMsg);
log(errorMsg);
out.write(buf.toString());
return;
}

// The servlet returns HTML.
response.setContentType(contentType);
if (cache == null) cache = new HashMap();
Transformer t = null;
// Get the XML input document and the stylesheet.
Source xmlSource = new DOMSource(doc);
// Perform the transformation, sending the output to the response.

// XSL processing can be time consuming, but this is mainly due to the overhead
// of compiling the XSL sheet.
// By caching the compiled sheets, the process is speeded up dramatically.
// Time saved on subsequent requests can be 99% or more (hundreds of milliseconds).
try
{
// check if the XSL sheet was found in cache, and use that if available
if (cache.containsKey(xsl)) t = (Transformer)cache.get(xsl);
else
{
// otherwise, load the XSL sheet from disk, compile it and store the compiled
// sheet in the cache
TransformerFactory tFactory = TransformerFactory.newInstance();
Source xslSource = new StreamSource(new File(path, xsl+".xsl"));
t = tFactory.newTransformer(xslSource);
cache.put(xsl, t);
}
// perform the XSL transformation of the XML Document into the servlet outputstream
t.transform(xmlSource, new StreamResult(out));
}
catch (TransformerConfigurationException e)
{
e.printStackTrace();
throw new ServletException(e);
}
catch (TransformerFactoryConfigurationError e)
{
e.printStackTrace();
throw new ServletException(e);
}
catch (TransformerException e)
{
e.printStackTrace();
throw new ServletException(e);
}

}

private StringBuffer generateError(String error) throws IOException
{

String path = getServletContext().getRealPath("/WEB-INF/templates/");
File f = new File(path, "callerror.html");
FileInputStream fs = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fs);
int numBytes = bis.available();
byte b[] = new byte[numBytes];
// read the template into a StringBuffer (via a byte array)
bis.read(b);
StringBuffer buf = new StringBuffer();
buf.append(b);
int start = buf.indexOf(errorTemplate);
int end = start + errorTemplate.length();
// replace placeholder with errormessage
// (will automatically adjust to take longer or shorter data into account)
buf.replace(start, end, error);
return buf;
}

}
[/code]

======================================================
Sample XSLT:

[code]
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:import href="title.xsl" />

<xsl:output method="html" indent="yes"/>
<xsl:template match="ehwa-doc">
<html>
<xsl:call-template name="title"/>
<body><xsl:attribute name="onload"><xsl:apply-templates select="focuscontrol"/></xsl:attribute>
<form>
<input type="hidden" value="1" name="runset" id="runset"/>
</form>
<xsl:call-template name="hello"/>
</body>
</html>
</xsl:template>

<xsl:template match="focuscontrol">
<xsl:value-of select="."/>
</xsl:template>

<xsl:template name="hello">
<xsl:value-of select="data" />
</xsl:template>

</xsl:stylesheet>
[/code]
======================================================
Error HTML template:

[code]
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<title></title>
</head>

<body>
+++ERRORS+++
</body>
</html>
[/code]

======================================================
Sample servlet to generate the XML and forward to the generator:

[code]
package somepackage;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.xerces.dom.DocumentImpl;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
* @author Jeroen Wenting
*/
public class XSLTest extends HttpServlet
{

/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException
{
doPost(arg0, arg1);
}

/**
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String xslFilename = getInitParameter("xslfile-html");
String beanName = getInitParameter("formbean");
String contentType = getInitParameter("contenttype");
request.setAttribute("xsl-file-name", xslFilename);
if (contentType != null) request.setAttribute("contenttype", contentType);
Document doc = new DocumentImpl();

Element root = doc.createElement("ehwa-doc");

// generate XML for transformation
Element elem = doc.createElement("focuscontrol");
elem.appendChild(doc.createTextNode("alert('loaded')"));
root.appendChild(elem);
elem = doc.createElement("data");
elem.appendChild(doc.createTextNode("Hello World!"));
root.appendChild(elem);

doc.appendChild(root);
request.setAttribute("xml-doc", doc);
RequestDispatcher rd = request.getRequestDispatcher("/servlet/generator");
rd.forward(request, response);
}

}

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.