A Simple XSL Servlet

If you only have a hammer, you tend to see every problem as a nail.

Abraham Maslow

I try to restrain myself, but one of my more common workplace rants is:

If you start with xml and you end up with xml, then why aren’t you using xsl in between?

My colleagues commonly unmarshal xml to an object, manipulate the object, and marshal the object back to xml. The right choice when there are relatively complex manipulations in the middle. But xsl is a much better choice for simple manipulations:

  • Easier to review
  • Easier to test
  • Easier to modify
  • Easier to track change

Here’s a simple servlet that applies xsl transforms to posted request xml. First, the init method that reads in xsl tranform files located in /WEB-INF/xsl and stores the corresponding Templates objects in a HashMap (Templates are thread-safe).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void init(ServletConfig config)
throws ServletException
{

super.init(config);

//
// Force use of xalan for environment.xsl: xalan:checkEnvironment()
TransformerFactory factory = new org.apache.xalan.processor.TransformerFactoryImpl();

templates = new HashMap<String,Templates>();
String list = config.getInitParameter("transforms");
if ( list != null ) {
String[] names = list.split("\\s*,\\s*");
for ( String name : names ) {
if ( name.length() > 0 ) {
try {
InputStream in = getTemplateInputStream(getServletContext(),name);
if ( in != null ) {
Templates template = factory.newTemplates(new StreamSource(in));
templates.put( name,template );
}
}
catch (Exception e) { ; }
}
}
}
}

Next, a get method that returns the xsl transform whose name matches the request path information.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void doGet(HttpServletRequest request,
HttpServletResponse response)

throws java.io.IOException,
ServletException
{

//
// Return the xsl transform if specified in the path
String path = request.getPathInfo();
if ( path != null && path.length() > 1 ) {
path = path.substring(1);
if ( templates.get(path) != null ) {
response.setContentType("application/xml;charset=utf-8");
InputStream in = getTemplateInputStream(getServletContext(),path);
PrintWriter out = response.getWriter();
int ch;
while ( (ch=in.read()) > -1) {
out.write(ch);
}
return;
}
}

//
// Otherwise list loaded transforms
response.setContentType("application/text;charset=utf-8");
PrintWriter out = response.getWriter();
Object[] keys = templates.keySet().toArray();
out.println("Supported transforms are:");
for ( Object key : keys ) {
out.print("\t");
out.println(key.toString());
}
}
And finally, a post method that applies the xsl transform identified in the path information to the posted xml request body:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void doPost(HttpServletRequest request,
HttpServletResponse response)

throws java.io.IOException,
ServletException
{

InputStream in = request.getInputStream();
response.setContentType("application/xml;charset=utf-8");
PrintWriter out = response.getWriter();

//
// Apply named transform to the posted request data
String path = request.getPathInfo();
if ( path != null && path.length() > 1 ) {
try {
Templates template = templates.get(path.substring(1));
if ( template != null ) {
Transformer transform = template.newTransformer();
transform.transform( new StreamSource(in), new StreamResult(out));
out.close();
return;
}
}
catch ( TransformerException e ) {
response.setStatus(response.SC_INTERNAL_SERVER_ERROR);
return;
}
}

response.setStatus(response.SC_FORBIDDEN);
}
In the next installment, we’ll add some xsl transforms and take our servlet out for a spin.