Recipe 5.6 Generating an XML View from a JSPProblemYou want to automatically generate an XML view from a JSP page. SolutionCreate a custom tag and a TagLibraryValidator class, from which you can output the XML view of a JSP to a file. DiscussionAn XML view is an XML form of a JSP page that the JSP container generates during the translation phase, an intermediary stage before the container converts the JSP to its page implementation class (a servlet). A TagLibraryValidator class can use the XML view to validate the use of custom tags in the JSP prior to the JSP's conversion to a servlet. An XML view is very similar to a JSP document, which is an XML form of a JSP page that JSP developers can write and add to their web applications. The differences between the two XML files according to the JSP specification v2.0 are:
Java developers can add subclasses of
javax.servlet.jsp.
It is useful to examine the XML view of a JSP page in order to debug a
TagLibraryValidator
class that you are using in a custom tag library, or to
First, Example 5-13 shows the XML view-
Example 5-13. Generating the XML view of a JSP page
<%@ taglib uri="/toxml_view" prefix="t" %>
<html>
<head>
<title>Test tld</title>
</head>
<body bgcolor="#ffffff">
Hello, this page is using the toxml tag to look at its XML View.
<t:toxml filename="my_xmlview"/>
</body>
</html>
The
t:toxml
tag is an empty element that signals the validator class to generate a file containing an XML view. The file will be named
my_xmlview.xml
(the validator class adds the
.xml
extension). The tag
<taglib>
<taglib-uri>/toxml_view</taglib-uri>
<taglib-location>/WEB-INF/tlds/xml_gen.tld</taglib-location>
</taglib>
Example 5-14 shows the TLD file for this tag library, which specifies the validator class and the simple custom tag (a marker) used in Example 5-11. I am not going to show the code for the toxml tag, since it does not contain any code of interest, beyond the fact that it has one String member variable called filename . The sole purpose of the tag's use is to put the validator class to work. The JSP container creates one validator instance for each tag library that includes a validator class. Example 5-14. The TLD file for the XML view custom tag
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>Validator test</short-name>
<description>Validator test</description>
<validator>
<validator-class>
com.jspservletcookbook.ToXmlValidator
</validator-class>
<description>
Saves XML views of JSP pages to the specified
directory.
</description>
</validator>
<tag>
<name>toxml</name>
<tag-class>com.jspservletcookbook.ToXml</tag-class>
<body-content>EMPTY</body-content>
<description>
This tag demonstrates the production of JSP XML view files.
</description>
<attribute>
<name>filename</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
<description>
This attribute provides the filename.</description>
</attribute>
</tag>
</taglib>
The com.jspservletcookbook.ToXmlValidator class, the library's validator, executes its validate method when a JSP page using the toxml tag is loaded. How does the validator class know where to save the files representing the JSP's XML view? The com.jspservletcookbook.ToXmlValidator class derives the directory path for saving its generated files from the properties file shown below. This allows any deployer of the custom tag to change the directory for the saved XML views, without touching the validator class's source code. The properties file is located in the same directory as the validator class. The path to this properties file is WEB-INF/classes/com/jspservletcookbook/validator.properties : directory=h:/home/xmlviews The filename is provided by the tag itself, as in: <t:toxml filename="my_xmlview" /> The entire file path for the XML view looks like: h:/home/xmlviews/my_xmlview.xml .
You now have all of the pieces together except for the all-important validator class, which is shown in Example 5-15. The validate method reads the directory property value using a java.util.ResourceBundle object. The validate method gets the filename by using the helper class that Example 5-16 shows. The validate method then generates the XML view of the JSP page by using the java.io.InputStream returned from PageData.getInputStream( ) . Example 5-15. A validator class for generating XML view files
package com.jspservletcookbook;
import javax.servlet.jsp.tagext.TagLibraryValidator;
import javax.servlet.jsp.tagext.ValidationMessage;
import javax.servlet.jsp.tagext.PageData;
import java.io.*;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
import java.util.Date;
public class ToXmlValidator extends TagLibraryValidator {
/** Creates new ToXmlValidator */
public ToXmlValidator( ) {
}
public ValidationMessage[] validate(java.lang.String prefix,
java.lang.String uri,PageData page){
ValidationMessage[] vam = null;
try{
ResourceBundle bundle =
ResourceBundle.getBundle("com.jspservletcookbook.validator");
String directory = bundle.getString("directory");
String fileName = getFilename(page);
//throw an Exception if the directory is invalid
if (directory == null)
throw new Exception(
"Received a null directory for the XML view file.");
//throw an Exception if the filename is invalid
if (fileName == null)
throw new IOException(
"Received a null filename for the XML view file.");
File file = new File(directory + "/" + fileName + ".xml");
FileWriter writer = new FileWriter(file);
BufferedReader in = new BufferedReader(
new InputStreamReader(page.getInputStream( )));
String line = "";
//write the XML view to the specified file
while ((line = in.readLine( )) != null ){
writer.write(line);
}
in.close( );
writer.close( );
} catch (IOException io){
//return a validation message
ValidationMessage vmsg = new
ValidationMessage(null,io.getMessage( ));
vam = new ValidationMessage[1];
vam[0] = vmsg;
return vam;
} catch (MissingResourceException mre){
//return a validation message
ValidationMessage vmsg = new
ValidationMessage(null,mre.getMessage( ));
vam = new ValidationMessage[1];
vam[0] = vmsg;
return vam;
} catch (Exception e){
//return a validation message
ValidationMessage vmsg = new
ValidationMessage(null,e.getMessage( ));
vam = new ValidationMessage[1];
vam[0] = vmsg;
return vam;
}
//return empty array
vam = new ValidationMessage[0];
return vam;
}
private String getFilename(PageData page) throws Exception {
try{
ValidateHandler handler = new ValidateHandler( );
return handler.getFilename(page);
} catch (Exception e){
throw e; }
}
}
Example 5-16 shows the
ValidateHandler
helper class that our validator uses to get the filename from the custom tag. The
ValidateHandler
makes a first pass through the XML view (before it is written to a file) to extract the filename that the
ValidateHandler handler = new ValidateHandler( ); return handler.getFilename(page);
The
ValidateHandler
uses the Java API for XML processing (JAXP) and the Simple API for XML (SAX) to parse the XML provided by
javax.servlet.jsp.tagext.PageData.getInputStream( )
. You have to place the
ValidateHandler
class inside of the
WEB-INF/classes
directory (or inside of a JAR file in
WEB-INF/lib
) so that your web application (the
ToXmlValidator
class) can find it. You can use any component you want to provide the SAX functionality that a web application needs. If you choose to use JAXP, and your web container is not yet bundled with the necessary JAXP
Example 5-16. A DefaultHandler that grabs the filename from the custom tag attribute
import org.xml.sax.Attributes;
import org.xml.sax.SAXParseException;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import java.io.IOException;
import javax.servlet.jsp.tagext.PageData;
public class ValidateHandler extends DefaultHandler {
private String fileName = "";
public void startElement(String nameSpaceuri,
String sname, String qname, Attributes attrs){
for(int i=0; i<attrs.getLength( );i++)
if("filename".equals(attrs.getLocalName(i)))
this.fileName=attrs.getValue(i);
}
public String getFilename(PageData page)
throws FactoryConfigurationError, ParserConfigurationException,
SAXException, IOException {
try{
SAXParserFactory factory = SAXParserFactory.newInstance( );
factory.setNamespaceAware(true);
SAXParser saxparser = factory.newSAXParser( );
saxparser.parse(page.getInputStream( ),this);
} catch (FactoryConfigurationError fe){
throw fe;
} catch (ParserConfigurationException pce){
throw pce;
} catch( SAXException se){
throw se;
} catch( java.io.IOException io){
throw io;
} finally {
return this.fileName; }
}
public void error(SAXParseException e)
throws SAXParseException
{
throw e;
}
}
Example 5-17 shows the XML view generated from the JSP page of Example 5-13 (with some
Example 5-17. The XML view of Example 5-13
<jsp:root jsp:id="0" xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"
xmlns:t="/toxml_view">
<jsp:text><![CDATA[]]></jsp:text>
<jsp:text><![CDATA[<html>]]></jsp:text>
<jsp:text><![CDATA[<head> ]]></jsp:text>
<jsp:text><![CDATA[<title>Test tld]]></jsp:text>
<jsp:text><![CDATA[</title>]]></jsp:text>
<jsp:text><![CDATA[</head>]]></jsp:text>
<jsp:text><![CDATA[<body bgcolor="#ffffff">Hello, this page is using the toxml tag to
look at its XML View.]]></jsp:text>
<t:toxml jsp:id="1" filename="my_xmlview"/>
<jsp:text><![CDATA[]]></jsp:text>
<jsp:text><![CDATA[</body>]]></jsp:text>
<jsp:text><![CDATA[</html>]]></jsp:text>
</jsp:root>
See AlsoRecipe 5.5 on creating a JSP from scratch as a JSP document; Chapter JSP.6 (JSP documents) of the JSP 2.0 specification; Chapter JSP.10 (XML views) of the JSP 2.0 specification. |