Implementing Flash Scope in Java Web Applications

Steve Neal Development, Java Programming 6 Comments

While working recently on a Spring MVC project I found myself wishing it supported flash scope. I hunted around for a simple solution but couldn’t find anything that didn’t rely on having to import large framework libraries. After a little thought I came up with the following simple and lightweight solution that has worked really well for me. I’ve included the code for this below so feel free to try it out.

What is Flash Scope

Flash scope is a useful part of many web frameworks. It allows the use of the post/redirect/get design pattern to alleviate many of the problems associated with handling multiple submits or resubmission of data in browser requests to the server.

Flash scope is an additional scope to those provided in a standard Java Web application (page, request, session and application). Any attributes held in flash scope will be available for the duration of the current request, and the subsequent request too.

My Implementation

To implement flash scope in my application, I added a servlet filter that checks for request attribute names starting with ‘flash.‘. When it finds such an attribute, it ensures that it is made available in the subsequent request by temporarily storing it in the user’s session, and then reinstating it when the next request is received.

For example, to add a message to flash scope, just use the regular syntax for adding a request scoped attribute and let the fiter take care of it:

request.setAttribute("flash.message", "Here is the news...");

The above attribute can then be accessed in the redirect target JSP using EL or scriptlet syntax (omitting the ‘flash.‘ prefix):

${message}
or
<%= request.getAttribute("message") %>

Here’s the code for the filter:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * Ensures that any request parameters whose names start
 * with 'flash.' are available for the next request too. 
 */
public class FlashScopeFilter implements Filter {

    private static final String FLASH_SESSION_KEY = "FLASH_SESSION_KEY";

    @SuppressWarnings("unchecked")
    public void doFilter(ServletRequest request, ServletResponse response, 
					FilterChain chain) throws IOException, ServletException {
					
        //reinstate any flash scoped params from the users session 
		//and clear the session
        if (request instanceof HttpServletRequest) {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            HttpSession session = httpRequest.getSession(false);
            if (session != null) {
                Map<String, Object> flashParams = (Map<String, Object>) 
									session.getAttribute(FLASH_SESSION_KEY);
                if (flashParams != null) {
                    for (Map.Entry<String, Object> flashEntry : flashParams.entrySet()) {
                        request.setAttribute(flashEntry.getKey(), flashEntry.getValue());
                    }
                    session.removeAttribute(FLASH_SESSION_KEY);
                }
            }
        }

        //process the chain
        chain.doFilter(request, response);

        //store any flash scoped params in the user's session for the 
		//next request
        if (request instanceof HttpServletRequest) {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            Map<String, Object> flashParams = new HashMap();
			Enumeration e = httpRequest.getAttributeNames();
            while (e.hasMoreElements()) {
                String paramName = (String) e.nextElement();
                if (paramName.startsWith("flash.")) {
                    Object value = request.getAttribute(paramName);
                    paramName = paramName.substring(6, paramName.length());
                    flashParams.put(paramName, value);
                }
            }
            if (flashParams.size() > 0) {
                HttpSession session = httpRequest.getSession(false);
                session.setAttribute(FLASH_SESSION_KEY, flashParams);
            }
        }
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        //no-op
    }

    public void destroy() {
        //no-op
    }
}

You can see from the listing, that the filter stores the flash scoped parameters in a session scoped map called FLASH_SESSION_KEY. Before the request is processed by the filter chain, the flash attributes are retrieved from the session and added to request scope; after the chain has finished, any new flash scoped parameters are stored in a new session scoped map.

Note that the attributes have the ‘flash.‘ prefix removed when added to the session scoped map – this not only ensures that they can be accessed using their ‘short name’, but that they are only kept in the session for one extra request.

This approach is not Spring specific and should work with and Java Web framework. I’ve omitted the configuration for the filter as there are loads of examples of how to do this on the Internet already.

Comments 6

  1. Joris Roovers

    Just wanted to point out that I needed to access flash variable on the jsp page using ${flash["message"]} instead of only ${message}.

    For reference, I added the following to web.xml in order to use the filter:

    flashScope
    web.FlashScopeFilter

    flashScope
    /*

    Thanks for the nice post :-)
    Note: Implementation of flash scope functionality will be added to Spring 3.1 (see https://jira.springsource.org/browse/SPR-6464)

  2. Steve Neal

    Thanks for spotting that Joris!

    I’ve updated the post to show how to correctly handle the flash scoped parameters in the filter.

    Rather than just adding the map directly to the request (which results in you needing the syntax you mentioned), the individual entries in the map are now added instead. This is how I use it in my applications and it works very well for me.

    FYI: the lines of code I’ve changed are: the loop on line 30, and the adding of each map entry on line 31.

  3. Synthomat

    One should point out, that a session must be created once somewhere, so that the filter can access it :-)

    I just changed

    HttpSession session = httpRequest.getSession(false)
    -> true at the top

    Don’t know if it is technically right or whether it is applicable for all.

Leave a Reply

Your email address will not be published. Required fields are marked *