FlashScope implementation for Java Beans


package dk.dalsgaarddata.lifecycle;

import java.util.Map;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;

/**
 * @author jda
 * 
 * 2013.10.02 - John Dalsgaard, Dalsgaard Data A/S, www.dalgaard-data.eu
 * 
 * An XPages (JSF) lifecycle controller to implement flashScope beans in XPages
 * Inspired by: http://www.bleedyellow.com/blogs/andyc/entry/a_flash_scope_for_xpages?lang=en
 * 
 * This Controller class must be added to the JSF lifecycle to be active:
 * 
 * <lifecycle>
 * 		<phase-listener>dk.dalsgaarddata.lifecycle.Controller</phase-listener>
 * </lifecycle>
 * 
 */
public class Controller implements PhaseListener {
	private static final long serialVersionUID = 1L;

	public PhaseId getPhaseId() {
		return PhaseId.RENDER_RESPONSE;
	}

	public void beforePhase(PhaseEvent event) {
		System.out.println("START PHASE " + event.getPhaseId());
	}

	@SuppressWarnings("unchecked")
	public void afterPhase(PhaseEvent event) {
		System.out.println("END PHASE " + event.getPhaseId());
		/**
		 * Implementation of a flashScope for XPages
		 * 
		 * 1. Create a bean 
		 * 2. Put your bean in a package that contains "flashscope", e.g. dk.dalsgaarddata.dk.bean.flashscope 
		 * 3. Add your bean to faces-config.xml - with session scope
		 * 
		 *    <managed-bean> 
		 *       <managed-bean-name>Entry</managed-bean-name>
		 *       <managed-bean-class>dk.dalsgaarddata.bean.flashscope.EntryBean</managed-bean-class>
		 *       <managed-bean-scope>session</managed-bean-scope>
		 *    </managed-bean>
		 * 
		 * This is enough for the code below to identify the bean as a "flashScope" bean. The logic simply removes the bean
		 * from the sessionScope - unless this is a POST. It will then be instantiated again on first reference :-)
		 * 
		 * Now you can use your bean on an XPage just using Expression Language (EL) to bind all fields to bean properties. 
		 * No need for SSJS :-)
		 * 
		 * 
		 * NB. A little known fact (and not seen in examples) is that you MUST ALWAYS surround your PhaseListener code with 
		 * try-catch. Because if you don't the calling process will. This is what happens:
		 * 
		 *    try { 
		 *       yourListener.beforePhase(Phase);
		 *    } catch (Throwable t) {
		 *       remove(yourListener); 
		 *    }
		 * 
		 * So any phase listener method that bubble up an exception gets removed from the list of listeners - silently....!!
		 * 
		 * Thanks to Nathan Freeman for pointing this out ;-)
		 */
		try {
			FacesContext facesContext = event.getFacesContext();
			ExternalContext externalContext = facesContext.getExternalContext();
			HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
			if (!request.getMethod().equals("POST")) {
				Map<String, Object> sessionScope = externalContext.getSessionMap();
				for (Map.Entry<String, Object> entry : sessionScope.entrySet()) {
					Object value = entry.getValue();
					if (null != value) {
						if (value.getClass().getCanonicalName().indexOf(".flashscope.") > 0) {
							// System.out.println("Reset flashScope bean: " + value.getClass().getCanonicalName());
							sessionScope.remove(entry.getKey());
						}
					}
				}
			}
		} catch (Exception e) {
			// Use proper error logging. Here I just throw it to the console...
			System.err.println("ERROR in " + this.getClass().getSimpleName() + ": in afterPhase");
			e.printStackTrace();
		}
	}
}
All code submitted to OpenNTF XSnippets, whether submitted as a "Snippet" or in the body of a Comment, is provided under the Apache License Version 2.0. See Terms of Use for full details.
No comments yetLogin first to comment...