XSLT transformation from a servlet

jwenting 0 Tallied Votes 928 Views Share

The question how to create 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.

The system is quite simple, most of the code is concerned with housekeeping chores rather than the actual HTML generation and output.

[code]
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);
	}


}
[/code]
sreeraj4u 0 Newbie Poster

this is very good

majestic0110 187 Nearly a Posting Virtuoso

Very nice, constructed a similar project myself (in C# as opposed to Java) the main difficulty I encountered was achieving the transformation on a specific XML(user specified) file. This involved uploading that file (via file upload - ASP.NET) to a server, then transforming that to a HTML file (on server) then removing the uploaded (and no longer needed) XML file on the server. From here, the user could download the newly transformed HTML file. These things have to be considered though lol.... Love the Snippet!

j_abdou_a51 0 Newbie Poster

hot work !!!

lemomo12345 0 Newbie Poster

hello nice code but I have a question I try the code to understand "" I want to know if you change something in web.xml be making the implementation t.h.y

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.