Recipe3.18.Defeating Browser Caching


Recipe 3.18. Defeating Browser Caching

Problem

You want to force the browser to display an up-to-date JSP page instead of showing the page from the browser's cache.

Solution

Set the nocache attribute to true for the controller element in your struts-config.xml file.

<controller nocache="true"/>

Discussion

To speed processing, browsers frequently keep a copy of a visited page on the client's local system. If an identical URL for the original page is requested and that page hasn't expired, the browser may display the page from the local cache instead of issuing a new request. This caching reduces network traffic and improves the user experience significantly. However, this can cause problems for dynamically generated pages. Consider a JSP page that renders data retrieved from the HTTP session. If data stored in the session changes, the browser won't be aware of the change. When the browser receives a new request for the page, it serves up the old page instead.

The easiest means of solving this problem for a Struts application is to configure the Struts RequestProcessor to generate a nocache header entry for every generated HTTP response. Set the nocache attribute to true on the controller element, as shown in the Solution. If the nocache attribute is not specified, the default value is false.

While this solves the problem, a consequence of its use is that every page accessed through a Struts Action results in a new request being sent to the server, even if the data haven't changed. One alternative solution is to generate a dummy request parameter with some unique value, such as the current time in milliseconds. This guarantees that the browser issues a new request. This technique works when the request uses the HTTP GET method; however, it may fail if the URL is accessed via an HTTP POST, or the URL is the result of a servlet forward to the URL.

Given these conditions, it would be nice if there were a way to indicate selectively which actions should be cached or not. You can delve into this by taking a look at how the Struts RequestProcessor handles the nocache attribute. The code below shows the actual implementation of the processNocache( ) method from the org.apache.struts.action.RequestProcessor class:

/**  * Set the no-cache headers for all responses, if requested.  * <strong>NOTE</strong> - This header will be overridden  * automatically if a <code>RequestDispatcher.forward( )</code> call is  * ultimately invoked.  *  * @param request The servlet request we are processing  * @param response The servlet response we are creating  */ protected void processNoCache(HttpServletRequest request,                               HttpServletResponse response) {     if (moduleConfig.getControllerConfig( ).getNocache( )) {         response.setHeader("Pragma", "No-cache");         response.setHeader("Cache-Control", "no-cache");         response.setDateHeader("Expires", 1);     } }

You can provide a similar response handling on a per-Action basis by utilizing a custom action mapping. The custom action mapping, shown in Example 3-23, allows specification of the nocache attribute on a per-action basis.

Example 3-23. A custom action mapping for avoiding browser caching
package com.oreilly.strutsckbk; import org.apache.struts.action.ActionMapping; public class NocacheActionMapping extends ActionMapping {     private String nocache;     private boolean nocacheEnabled = false;          public String getNocache( ) {         return nocache;     }     public void setNocache(String nocache) {         this.nocache = nocache;         nocacheEnabled = new Boolean(nocache).booleanValue( );     }          public boolean isNocacheEnabled( ) {         return nocacheEnabled;     } }

A custom RequestProcessor evaluates the nocache setting for each request. Example 3-24 shows the custom RequestProcessor that checks the caching property of the custom ActionMapping, setting the HTTP response headers as appropriate.

Example 3-24. Nocache request processor
package com.oreilly.strutsckbk; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.RequestProcessor; public class NocacheRequestProcessor extends RequestProcessor {          protected ActionForward processActionPerform(HttpServletRequest             request,            HttpServletResponse response, Action action,ActionForm form,              ActionMapping mapping) throws IOException, ServletException {       ActionForward forward = null;       if (mapping instanceof NocacheActionMapping) {         NocacheActionMapping customMapping =                    (NocacheActionMapping) mapping;         if (customMapping.isNocacheEnabled( )) {           response.setHeader("Pragma", "No-cache");           response.setHeader("Cache-Control", "no-cache");           response.setDateHeader("Expires", 1);         }       }       forward = super.processActionPerform( request, response,                                              action, form, mapping);       return forward;     } }

To complete the solution, you need to define the custom action mapping and request processor in your struts-config.xml file (see Example 3-25).

Example 3-25. Struts Config for action-based response caching
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC           "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"           "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config>   <action-mappings type="com.oreilly.strutsckbk.NocacheActionMapping">     <action   path="/main"               type="org.apache.struts.actions.ForwardAction"           parameter="/index.jsp">       <set-property property="nocache" value="true"/>     </action>   </action-mappings>   <controller      processor/> </struts-config>

See Also

The Struts documentation for the controller element can be found at http://jakarta.apache.org/struts/userGuide/configuration.html#controller_config. Recipe 2.8 demonstrates another way that you can use custom action mappings.



    Jakarta Struts Cookbook
    Jakarta Struts Cookbook
    ISBN: 059600771X
    EAN: 2147483647
    Year: 2005
    Pages: 200

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net