package com.hantsylabs.example.spring.faces; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; /** * Enables messages to be rendered on different pages from which they were set. * To produce this behaviour, this class acts as a <code>PhaseListener</code>. * * This is performed by moving the FacesMessage objects: * <li>After each phase where messages may be added, this moves the messages from * the page-scoped FacesContext to the session-scoped session map. *</li> <li>Before messages are rendered, this moves the messages from the session-scoped * session map back to the page-scoped FacesContext. *</li> * Only messages that are not associated with a particular component are ever * moved. These are the only messages that can be rendered on a page that is different * from where they originated. * * To enable this behaviour, add a <code>lifecycle</code> block to your * faces-config.xml file. That block should contain a single <code>phase-listener</code> * block containing the fully-qualified classname of this file. * * EDIT: This code was minimally modified by Max Kuipers to address some of the Java 1.6 * compiler warnings. All code was originally written by Jesse Wilson. * * @author <a href="mailto:jesse@odel.on.ca">Jesse Wilson</a> * @author <a href="mailto:mkuipers@sourceallies.com">Max Kuipers</a> */ public class MessageHandler implements PhaseListener { private static final long serialVersionUID = 1L; /** * a name to save messages in the session under */ private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT"; /** * Return the identifier of the request processing phase during which this * listener is interested in processing PhaseEvent events. */ public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } /** * Handle a notification that the processing for a particular phase of the * request processing lifecycle is about to begin. */ public void beforePhase(PhaseEvent event) { if(event.getPhaseId() == PhaseId.RENDER_RESPONSE) { FacesContext facesContext = event.getFacesContext(); restoreMessages(facesContext); } } /** * Handle a notification that the processing for a particular phase has just * been completed. */ public void afterPhase(PhaseEvent event) { if(event.getPhaseId() == PhaseId.APPLY_REQUEST_VALUES || event.getPhaseId() == PhaseId.PROCESS_VALIDATIONS || event.getPhaseId() == PhaseId.UPDATE_MODEL_VALUES || event.getPhaseId() == PhaseId.INVOKE_APPLICATION) { FacesContext facesContext = event.getFacesContext(); saveMessages(facesContext); } } /** * Remove the messages that are not associated with any particular component * from the faces context and store them to the user's session. * * @return the number of removed messages. */ private int saveMessages(FacesContext facesContext) { // remove messages from the context List<FacesMessage> messages = new ArrayList<FacesMessage>(); for(Iterator<FacesMessage> i = facesContext.getMessages(null); i.hasNext(); ) { messages.add(i.next()); i.remove(); } // store them in the session if(messages.size() == 0) { return 0; } Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap(); // if there already are messages @SuppressWarnings("unchecked") List<FacesMessage> existingMessages = (List<FacesMessage>) sessionMap.get(sessionToken); if(existingMessages != null) { existingMessages.addAll(messages); } else { sessionMap.put(sessionToken, messages); // if these are the first messages } return messages.size(); } /** * Remove the messages that are not associated with any particular component * from the user's session and add them to the faces context. * * @return the number of removed messages. */ private int restoreMessages(FacesContext facesContext) { // remove messages from the session Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap(); @SuppressWarnings("unchecked") List<FacesMessage> messages = (List<FacesMessage>)sessionMap.remove(sessionToken); // store them in the context if(messages == null) { return 0; } int restoredCount = messages.size(); for(Iterator<FacesMessage> i = messages.iterator(); i.hasNext(); ) { facesContext.addMessage(null, i.next()); } return restoredCount; } }