Java Servlet & JSP Cookbook

Problem

You want to automatically generate an XML view from a JSP page.

Solution

Create a custom tag and a TagLibraryValidator class, from which you can output the XML view of a JSP to a file.

Discussion

An 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:

  • An XML view expands any include directives that a JSP document contains into their corresponding JSP fragments .

  • An XML view provides each XML element with a jsp:id attribute.

  • An XML view adds a jsp:root element as the root element of the document if the document does not already have a jsp:root element.

  • An XML view adds a jsp:directive.page element and pageEncoding attribute if they do not already exist, and sets the value of pageEncoding to "UTF-8".

  • An XML view adds a jsp:directive.page element and contentType attribute if they do not already exist, and sets the value of contentType according to Chapter JSP.4.2, "Response Character Encoding," of the JSP 2.0 specification (e.g., "text/xml" for a JSP document).

Java developers can add subclasses of javax.servlet.jsp. tagext .TagLibraryValidator to their web applications as tools for validating the application's use of custom tags. The JSP container (Jasper is the name of the Tomcat JSP container) makes available to the TagLibraryValidato r an XML view of a JSP page for the purpose of parsing XML elements in the page and validating whether or not they have been used correctly.

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 open up your JSP in an XML editor and evaluate its syntax from an XML perspective. Here is a nice way (okay, a bit of a hack!) to automatically generate a file representing the XML view of a JSP page. This recipe uses a javax.servlet.jsp.tagext.PageData object, which automatically returns the XML view of a JSP page as a java.io.InputStream . Here is how it works:

  1. Create a class that extends javax.servlet.jsp.tagext.TagLibraryValidator . These classes are used to validate the use of custom tags, and are explained in more detail in Chapter 23.

  2. Override the TagLibraryValidator.validate(String prefix , String uri , PageData page) method to write the XML view information from the PageData parameter to a file.

  3. Create a simple custom tag by extending javax.servlet.jsp.tagext.TagSupport . This tag "marks" a JSP page so that its XML view can be output to a file. The tag includes a "filename" attribute from which your validator class will get the filename for the XML view. The tag looks like this:

    <t:toxml filename="myxml_view" />

  4. Create a Tag Library Descriptor (TLD) file for this tag library, specifying the TagLibraryValidator class you created as the validator for this library:

    <validator> <validator-class> com.jspservletcookbook.ToXmlValidator </validator-class> <description> Saves XML views of JSP pages to the specified directory. </description> </validator>

  5. Place both the TagLibraryValidator and the TagSupport classes in the WEB-INF/classes directory of the web application, or inside a JAR file that is stored in WEB-INF/lib (the examples in this recipe assume this format, rather than placing the classes in a JAR).

  6. Place the TLD file in the WEB-INF/tlds directory.

  7. Add a taglib element referring to your tags and TLD to the WEB-INF/web.xml deployment descriptor.

The taglib element in web.xm l is not needed with JSP Version 1.2 and 2.0, since the JSP container automatically searches WEB-INF , as well as the META-INF directory of your application's JAR files, for any file that ends with the extension .tld .

  1. Create a properties file containing the directory path that you want to use for the automatically generated XML view. Store this properties file in WEB-INF/classes using the appropriate package names . This properties file is used to avoid the hardcoding of an absolute directory in the validator class's code.

  2. Use the custom tag in the JSP file(s) for which you want the XML views generated as files.

First, Example 5-13 shows the XML view- related custom tag in a JSP file.

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 otherwise has no effect on the appearance or behavior of this JSP. The following fragment of the deployment descriptor shows the taglib element specifying the URI that is used in Example 5-11s taglib directive. The taglib element in the deployment descriptor also specifies the location of the TLD file ( WEB-INF/tlds/xml_gen.tld ):

<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 .

The validator class adds the .xml extension when it creates the XML view file. The validator first extracts the filename from the toxml tag by using a SAX parser to parse the input stream from the javax.servlet.jsp.tagext.PageData object.

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 user has added with the toxml element's filename attribute. The ValidateHandler does all the work behind the scenes to parse the XML so that the validator class can get the filename with a simple method call:

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 components , then add the following JAR files to your WEB-INF/lib directory for a complete JAXP installation: jaxp-api.jar , dom.jar , sax.jar , xalan.jar , xercesImpl.jar , and xsltc.jar . You can download these components as part of the Java Web Services Developer Pack (http://java.sun.com/ webservices /webservicespack.html), and the JAXP libraries are included as part of Java 1.4.x.

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 carriage returns added). It might be ugly, but now you know what an XML view looks like! The HTML code is all treated as template data, enclosed in a jsp:text element and CDATA sections. The two XML elements, jsp:root and t:toxml , are given sequential ID numbers as part of their jsp:id attributes in the XML view. The TagLibraryValidator class can use these IDs to provide finely grained XML-related messages involving the validated JSP page.

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 Also

Recipe 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.

Категории