6.4. Stylesheet Caching Revisited
We
have seen two approaches that eliminate the need to hardcode the
absolute pathname of XSLT stylesheets in your servlet code. In the
first approach, the ServletContext was used to
load resources from the web application using a relative pathname. In
the second approach, the location was specified as a context
initialization parameter.
This takes care of compilation
changes, but now we have the issue of dynamic loading. In the
PersonalDataServlet class, the two XSLT
stylesheets are located and "compiled" into instances of
the javax.xml.transform.Templates interface.
Although this offers high performance for transformations, the two
stylesheets are never flushed from memory. If changes are made to the
XSLT stylesheets on disk, the servlet must be stopped and started
again.
6.4.1. Integration with the Stylesheet Cache
In Chapter 5, "XSLT Processing with Java", a
stylesheet cache was implemented. In this next example,
PersonalDataServlet is modified to use the cache
instead of Templates directly. This will offer
virtually the same runtime performance. However, you will be able to
modify the stylesheets and immediately see those changes in your web
browser. Each time a stylesheet is requested, the cache will check
its timestamp on the file system. If the file has been modified, a
new Templates instance is instantiated without
bringing down the servlet.
Fortunately, integration with the cache actually makes the
PersonalDataServlet simpler to implement. Example 6-10 contains the modified listing, and all
modified lines are emphasized.
Example 6-10. Modified PersonalDataServlet.java with stylesheet cache
package chap6;
import com.oreilly.javaxslt.util.StylesheetCache;
import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
/**
* A modification of PersonalDataServlet that uses the
* com.oreilly.javaxslt.util.StylesheetCache class.
*/
public class PersonalDataServlet extends HttpServlet {
private PersonalDataXML personalDataXML = new PersonalDataXML( );
private String editXSLTFileName;
private String thanksXSLTFileName;
/**
* One-time initialization of this Servlet.
*/
public void init( ) throws UnavailableException {
this.editXSLTFileName = getServletContext( ).getRealPath(
"/WEB-INF/xslt/editPersonalData.xslt");
this.thanksXSLTFileName = getServletContext( ).getRealPath(
"/WEB-INF/xslt/confirmPersonalData.xslt");
}
/**
* Handles HTTP GET requests, such as when the user types in
* a URL into his or her browser or clicks on a hyperlink.
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException,
ServletException {
PersonalData personalData = getPersonalData(request);
// the third parameter, 'false', indicates that error
// messages should not be displayed when showing the page.
showPage(response, personalData, false, this.editXSLTFileName);
}
/**
* Handles HTTP POST requests, such as when the user clicks on
* a Submit button to update his or her personal data.
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException,
ServletException {
// locate the personal data object and update it with
// the information the user just submitted.
PersonalData pd = getPersonalData(request);
pd.setFirstName(request.getParameter("firstName"));
pd.setLastName(request.getParameter("lastName"));
pd.setDaytimePhone(request.getParameter("daytimePhone"));
pd.setEveningPhone(request.getParameter("eveningPhone"));
pd.setEmail(request.getParameter("email"));
if (!pd.isValid( )) {
// show the 'Edit' page with an error message
showPage(response, pd, true, this.editXSLTFileName);
} else {
// show a confirmation page
showPage(response, pd, false, this.thanksXSLTFileName);
}
}
/**
* A helper method that sends the personal data to the client
* browser as HTML. It does this by applying an XSLT stylesheet
* to the DOM tree.
*/
private void showPage(HttpServletResponse response,
PersonalData personalData, boolean includeErrors,
String xsltFileName) throws IOException, ServletException {
try {
org.w3c.dom.Document domDoc =
this.personalDataXML.produceDOMDocument(
personalData, includeErrors);
Transformer trans =
StylesheetCache.newTransformer(xsltFileName);
response.setContentType("text/html");
PrintWriter writer = response.getWriter( );
trans.transform(new DOMSource(domDoc), new StreamResult(writer));
} catch (Exception ex) {
showErrorPage(response, ex);
}
}
/**
* If any exceptions occur, this method can be showed to display
* the stack trace in the browser window.
*/
private void showErrorPage(HttpServletResponse response,
Throwable throwable) throws IOException {
PrintWriter pw = response.getWriter( );
pw.println("<html><body><h1>An Error Has Occurred</h1><pre>");
throwable.printStackTrace(pw);
pw.println("</pre></body></html>");
}
/**
* A helper method that retrieves the PersonalData object from
* the HttpSession.
*/
private PersonalData getPersonalData(HttpServletRequest request) {
HttpSession session = request.getSession(true);
PersonalData pd = (PersonalData) session.getAttribute(
"chap6.PersonalData");
if (pd == null) {
pd = new PersonalData( );
session.setAttribute("chap6.PersonalData", pd);
}
return pd;
}
}
One key difference in this example is its reliance on the
com.oreilly.javaxslt.util.StylesheetCache class.
This will, of course, require that you add
StylesheetCache.class to your WAR file in the
appropriate directory. Another option is to place the stylesheet
cache into a JAR file, and place that JAR file into the
TOMCAT_HOME/lib directory. This approach is
taken when you download the example code for this book.
The biggest code savings occur in the init( )
method because the filenames for the stylesheets are stored instead
of Templates instances. This is because the
stylesheet cache requires filenames as inputs and will create its own
instances of Templates, which accounts for a
majority of the simple changes throughout the servlet.
Once you get this example up and running, testing the stylesheet
reloading capability is a snap. As before,
chap6.war is copied to the
TOMCAT_HOME/webapps directory. After you run the
servlet the first time, you will notice that the WAR file is expanded
into the TOMCAT_HOME/webapps/chap6 directory.
Simply go into the
TOMCAT_HOME/webapps/chap6/WEB-INF/xslt directory
and edit one of the stylesheets. Then click on the Refresh button on
your web browser, and you should see the results of the edits that
were just made.
If you don't see the changes, there might be some leftover
files from earlier examples in this chapter. Be sure to shut down
Tomcat and remove both chap6.war and the
chap6 directory from Tomcat's
webapps directory. Then re-deploy and try
again.
 |  |  | | 6.3. Another Servlet Example |  | 6.5. Servlet Threading Issues |
Copyright © 2002 O'Reilly & Associates. All rights reserved.
|
|