If a WAR file is copied into the webapps directory while Tomcat is running, it will not be recognized. Simply restart
6.3 Another Servlet Example
6.3.2 XML and Stylesheets
Deciding how to structure your XML can have significant impact on your ability to customize the output of a web application. In our current example, the same XML file is used for all web pages.
This XML is shown in Example 6-3.
Example 6-3. Example XML output
<?xml version="1.0" encoding="UTF -8"?>
<page>
<!-- the next element is optional: -->
<!-- <requiredFieldsMissing/> -->
<personalData>
<firstName required="true">Eric</firstName>
<lastName required="true">Burke</lastName>
<daytimePhone required="true">636 -123-4567</daytimePhone>
<eveningPhone/>
<email required="true">[email protected]</email>
</personalData>
</page>
As you can see, the XML is very minimal. None of the captions, such as "First Name:", are included, because they are all specified in the XSLT stylesheets. Even the asterisk character (*) is omitted, giving the XSLT author complete control over how things are rendered. The XML is used only for data, so you can use the stylesheets to include graphics, render the output in a foreign language, or combine page fragments from other sources, such as headers and footers, into your web pages.
The <requiredFieldsMissing/> element is optional. If omitted, the XSLT stylesheet will not display error messages about missing fields. This is useful when the data is generated the first time because all fields will be blank, and you probably don't want to show a bunch of error messages. In our servlet, the doGet( ) method is called when the user first requests this web page, so it is here where we disable this element.
It is important to mention that this XML is used only for documentation purposes and for testing the XSLT stylesheets. Once you move into a production environment, the XML will be generated dynamically using the PersonalData and PersonalDataXML classes, so this static file will not be required. You will probably want to hang on to your static XML, however, as this will make it easier to experiment with changes to the XSLT.
The XSLT stylesheet that creates the HTML form is shown in Example 6-4. The stylesheets are substantially longer than the XML data, which is typical. In a more simplistic approach to servlet development, all of this logic would be hardcoded into the source code as a series of println(
) statements, making the servlet much larger and less flexible.
Example 6-4. editPersonalData.xslt
<?xml version="1.0" encoding="UTF -8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" indent="yes" encoding="UTF -8"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
doctypesystem="http://www.w3.org/TR/xhtml1/DTD/xhtml1 -transitional.dtd"/>
<!--
******************************************************** *******
** Top level template. Creates the framework for the XHTML page ************************************************************ -->
<xsl:template match="/">
<html>
<head><title>Edit Personal Information</title></head>
<body>
<xsl:apply-templates select="page/personalData"/>
</body>
</html>
</xsl:template>
<!--
***************************************************************
** Match the <personalData> element.
******************************************** ****************-->
<xsl:template match="personalData">
<h3>Personal Information</h3>
<xsl:if test="../requiredFieldsMissing">
<div style="color: red; font -size: larger;">
Error: one or more required fields are missing.
</div>
</xsl:if>
<i>Fields marked with (*) are required.</i>
<form action="/chap6/personalData" method="post">
<table border="0" cellpadding="2" cellspacing="0">
<!-- Select all immediate children, such as firstName,
<input type="submit" name="submitBtn" value="Submit"/>
</div>
match="firstName|lastName|daytimePhone|eveningPhone|email">
<tr>
<xsl:if test="@required='true'
and ../../requiredFieldsMissing and .=''">
<xsl:attribute name="style">
<xsl:text>color:red; font -weight:bold;</xsl:text>
</xsl:attribute>
<td>
<input type="text" name="{name( )}" value="{.}"/>
</td>
<td>
<xsl:if test="@required='true'">*</xsl:if>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
The first seven lines of editPersonalData.xslt contain boilerplate code that configures the XSLT processor to produce XHTML 1.0 using the transitional DTD. In particular, our result tree uses the
<i>...</i> tag, so we cannot use the XHTML strict DTD. The top level template matches the
"/" pattern as usual, outputting the framework for the XHTML document.
The next template matches the <personalData> element, producing a heading followed by an optional error message. The error message is displayed if the XML data contains the
<requiredFieldsMissing/> element, which is easily determined via the <xsl:if> element:
<xsl:template match="personalData">
<h3>Personal Information</h3>
<xsl:if test="../requiredFieldsMissing">
<div style="color: red; font-size: larger;">
Error: one or more required fields are missing.
</div>
</xsl:if>
This template then produces the <form> element, which specifies that HTTP POST should be used to submit the information. The action attribute indicates that this form will send its data to our servlet. As you will see, the form action[2] matches the URL pattern that we will set up in the deployment descriptor later in this chapter:
[2] To avoid hardcoding the form action in the XSLT stylesheet, pass it as a stylesheet parameter.
<i>Fields marked with (*) are required.</i>
<form action="/chap6/personalData" method="post">
The template finally produces a table so that all of the headings and text fields are properly aligned. As in earlier stylesheet examples, this template creates the table, while another template creates each row in the table:
<table border="0" cellpadding="2" cellspacing="0">
<!-- Select all immediate children, such as firstName, lastName, daytimePhone, etc... -->
<xsl:apply-templates/>
</table>
<div align="center">
<hr/>
<input type="submit" name="submitBtn" value="Submit"/>
</div>
</form>
</xsl:template>
Since this particular instance of <xsl:apply-templates/> does not utilize the select attribute, all child elements will be selected. The next template is designed to match each of the possible types of elements that can appear and will be instantiated once for each occurrence of
<firstName>, <lastName>, etc.:
<xsl:template
match="firstName|lastName|daytimePhone|eveningPhone|email">
This template first produces a <tr> element. If this particular element has a required="true"
attribute, the XML data contains <requiredFieldsMissing/>. The value of this element is an empty string, the font is changed to bold and red. This indicates to the user that a required field was missing. The font weight and color are inserted as the style attribute on the <tr> element as follows:
<tr>
<xsl:if test="@required='true'
and ../../requiredFieldsMissing and .=''">
<xsl:attribute name="style">
<xsl:text>color:red; font-weight:bold;</xsl:text>
</xsl:attribute>
</xsl:if>
The template then produces its first <td> tag, which contains the caption for the current field. It would be nice if XSLT offered a lookup table mechanism for situations such as this, but
<xsl:choose> does get the job done:
<td>
<xsl:choose>
<xsl:when test="name( )='firstName'">
First Name:</xsl:when>
<xsl:when test="name( )='lastName'">
Last Name:</xsl:when>
<xsl:when test="name( )='dayt imePhone'">
Daytime Phone:</xsl:when>
<xsl:when test="name( )='eveningPhone'">
Evening Phone:</xsl:when>
<xsl:when test="name( )='email'">
Email:</xsl:when>
</xsl:choose>
</td>
This is still better than hardcoding the captions into the XML or servlet because we can make changes to the stylesheet without recompiling anything. You can even change the captions to a foreign language without affecting any of the Java code, offering remarkable flexibility to web page designers.