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(); } } }