Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSF 2.1 Redirect Preserving error message

Tags:

jsf

I have the following commandButton action method handler:

public String reject()
{
  //Do something
  addMessage(null, "rejectAmountInvalid", FacesMessage.SEVERITY_ERROR);

  redirectToPortlet("/xxx/inbox?source=pendingActions#pendingApproval");
}

public static void addMessage(String clientId, String key, Severity level, Object... objArr)
{
    FacesContext context = FacesContext.getCurrentInstance();
    FacesMessage message = null;
    String msg = getTextFromResourceBundle(key);
    if (objArr != null && objArr.length > 0)
        msg = MessageFormat.format(msg, objArr);
    message = new FacesMessage(msg);

    message.setSeverity(level);
    context.addMessage(clientId, message);
}

public static void redirectToPortlet(String urlToRedirect)
{
    FacesContext context = FacesContext.getCurrentInstance();
    ExternalContext externalContext = context.getExternalContext();

    try
    {
        PortletRequest portletRequest = (PortletRequest) externalContext.getRequest();
        ThemeDisplay themeDisplay = (ThemeDisplay) portletRequest.getAttribute("THEME_DISPLAY");
        String portalURL = themeDisplay.getPortalURL();
        String redirect = portalURL + urlToRedirect;
        externalContext.redirect(redirect);
    }
    catch (Throwable e)
    {
        logger.log("Exception in redirectToPortlet to the URL: " + urlToRedirect, VLevel.ERROR, e);
    }
}

When the page is redirected to "/xxx/inbox?source=pendingActions#pendingApproval", the error message I added is lost. Is there a way to preserve the error message in JSF 2.1?

Thanks Sri

like image 713
Sri Avatar asked Aug 14 '12 15:08

Sri


2 Answers

You can use a PhaseListener to save the messages that weren't displayed for the next request.

I've been using for a while one from Lincoln Baxter's blog post Persist and pass FacesMessages over multiple page redirects, you just copy the class to some package and register on your faces-config.xml.

It's not mentioned explicitly in the blog post, but I'm assuming the code is public domain, so I am posting here for a more self-contained answer:

package com.yoursite.jsf;

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.
 *
 * After each phase where messages may be added, this moves the messages
 * from the page-scoped FacesContext to the session-scoped session map.
 *
 * Before messages are rendered, this moves the messages from the
 * session-scoped session map back to the page-scoped FacesContext.
 *
 * Only global messages, not associated with a particular component, are
 * moved. Component messages cannot be rendered on pages other than the one on
 * which they were added.
 *
 * To enable multi-page messages support, 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.
 *
 * @author Jesse Wilson jesse[AT]odel.on.ca
 * @secondaryAuthor Lincoln Baxter III lincoln[AT]ocpsoft.com 
 */
public class MultiPageMessagesSupport implements PhaseListener
{

    private static final long serialVersionUID = 1250469273857785274L;
    private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT";

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

    /*
     * Check to see if we are "naturally" in the RENDER_RESPONSE phase. If we
     * have arrived here and the response is already complete, then the page is
     * not going to show up: don't display messages yet.
     */
    // TODO: Blog this (MultiPageMessagesSupport)
    public void beforePhase(final PhaseEvent event)
    {
        FacesContext facesContext = event.getFacesContext();
        this.saveMessages(facesContext);

        if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            if (!facesContext.getResponseComplete())
            {
                this.restoreMessages(facesContext);
            }
        }
    }

    /*
     * Save messages into the session after every phase.
     */
    public void afterPhase(final PhaseEvent event)
    {
        if (!PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            FacesContext facesContext = event.getFacesContext();
            this.saveMessages(facesContext);
        }
    }

    @SuppressWarnings("unchecked")
    private int saveMessages(final FacesContext facesContext)
    {
        List<FacesMessage> messages = new ArrayList<FacesMessage>();
        for (Iterator<FacesMessage> iter = facesContext.getMessages(null); iter.hasNext();)
        {
            messages.add(iter.next());
            iter.remove();
        }

        if (messages.size() == 0)
        {
            return 0;
        }

        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> existingMessages = (List<FacesMessage>) sessionMap.get(sessionToken);
        if (existingMessages != null)
        {
            existingMessages.addAll(messages);
        }
        else
        {
            sessionMap.put(sessionToken, messages);
        }
        return messages.size();
    }

    @SuppressWarnings("unchecked")
    private int restoreMessages(final FacesContext facesContext)
    {
        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> messages = (List<FacesMessage>) sessionMap.remove(sessionToken);

        if (messages == null)
        {
            return 0;
        }

        int restoredCount = messages.size();
        for (Object element : messages)
        {
            facesContext.addMessage(null, (FacesMessage) element);
        }
        return restoredCount;
    }
}

And then, on your faces-config.xml:

<phase-listener>com.yoursite.jsf.MultiPageMessagesSupport</phase-listener>
like image 150
Elias Dorneles Avatar answered Nov 02 '22 23:11

Elias Dorneles


If the redirect is to the same path, you could just use Flash#setKeepMessages().

context.getExternalContext().getFlash().setKeepMessages(true);

This way the messages are persisted in the flash scope which lives effectively as long as a single subsequent GET request (as occurs during a redirect).

like image 22
BalusC Avatar answered Nov 02 '22 23:11

BalusC