9.22.2006

The simplest thing to do...

I have been living in the Java world for a long time now. In this world full of patterns, complicated API and well intentionend flexible frameworks, we do not always follow the simplest path to a solution.

I'm currently working on adding some functionnality to an application that we wrote for a client last year. This application written in Java using JSF/Facelets/Spring/Hibernate and running on Tomcat.

For the new functionnality, I wish to move information from page to page but I don't want them to stay in the session for long. I also don't want to explicitely have to remove explicitely these object from the session. Now I could use request scope attributes to do that, but then if I choose to do a redirect instead of a forward, I will loose the information. I could also go with a more complicated option of using Spring WebFlow (or something similar). But I don't want to another layer and more configuration files.

In the weeks before starting this new project, I wrote some application using Ruby on Rails. The philosophy behind this framework is very different than the philosophy behind most Java frameworks. Here the DRY (Don't Repeat Yourself) principle is king. This contrast a lot with the stack I'm using as I have to define information about my controller, pages, beans in multiple places.

One of the concept I found useful in Rails is the Flash. The Flash is a hash structure like the session but it retains objects up to the end of the next request. It's a bit like the request scope with the added benefit of surving redirects.

So I thought, why not implement this in Java. For my Java implementation, I choose not to implement the "now" operation that is present in Rails as I did not really needed it. I also use a filter to do the management of the Flash.

This simple solution allow me to pass objects from page to page without having to polute the session to much. It has the benefit of allowing request-like scope to survive redirects. The current implementation may not work well with Ajax requests (I use Ajax4JSF and I have yet to find a way to differenciate Ajax request from normal requests, so the objects will be purge when doing an Ajax request unless I explicitely retain them when processing the Ajax request).

So here is the code (the two classes must be in the same package). Note that you can customize the Filter mapping to suits your particular needs.

Flash.java
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class Flash extends HashMap<String, Object> implements Serializable {
    private Map<String, Integer> time2live= new HashMap<String,    Integer>();
   
    @Override
    public Object put(String key, Object value) {
        time2live.put (key, 1);
        return super.put(key, value);
    }
   
    public Flash keep(String key) {
        if (containsKey(key)) {
            time2live.put(key, 1);
        }
        return this;
    }
   
    public Flash keep() {
        for (Map.Entry<String, Integer> entry : time2live.entrySet()) {
            entry.setValue(1);
        }
        return this;
    }
   
    public Flash discard(String key) {
        if (containsKey(key)) {
            time2live.put(key, 0);
        }
        return this;
    }
   
    public Flash discard() {
        for (Map.Entry<String, Integer> entry : time2live.entrySet()) {
            entry.setValue(0);
        }
        return this;
    }
   
    void manageFlash()  {
        for (Iterator<Map.Entry<String, Integer>> entries= time2live.entrySet().iterator(); entries.hasNext();) {
            Map.Entry<String, Integer> entry= entries.next();
            if (entry.getValue() == 0) {
                entries.remove();
                remove(entry.getKey ());
            } else {
                entry.setValue(entry.getValue()-1);
            }
        }
    }
}

FlashFilter.java
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest ;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
 * @web.filter name="FlashFilter" display-name="Flash Filter"
 * @web.filter-mapping url-pattern="*.html"
*/
public class FlashFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest= (HttpServletRequest)request;
       
        Flash flash= (Flash) httpRequest.getSession(true).getAttribute("flash");
        if (flash == null) {
            flash= new Flash();
            httpRequest.getSession().setAttribute("flash", flash);
        }
        flash.manageFlash();
       
        chain.doFilter(request, response);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
    }
}


AdSense Links