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
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>
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With