Book HomeJava and XSLTSearch this book

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.



Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.