I have a servlet filter that handles errors for both vanilla servlets and JSF pages.
If an error is detected the user is redirected to an error page where he can give feedback. Then I try to read the value in the ErrorBean object. However, sometimes the error is not present – there is a 50-50 chance of the error being present.
When I use
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
it sometimes returns a map with 1 entry and sometimes an empty map. In every case, the id is passed at http level.
I can’t really reproduce what is causing this. Here is the relevant code (helper methods + empty implementations omitted). The ErrorFilter is mapped at /* and the ErrorBean is a session scope bean managed by JSF.
ErrorFilter
public class ErrorFilter implements Filter
{
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException
{
HttpServletRequest hreq = (HttpServletRequest) req;
HttpServletResponse hres = (HttpServletResponse) resp;
try
{
chain.doFilter(req, resp);
}
catch (IOException e)
{
handleError(e, hreq, hres);
}
catch (ServletException e)
{
handleError(e, hreq, hres);
}
catch (RuntimeException e)
{
handleError(e, hreq, hres);
}
}
private static void handleError(Throwable e, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
final RequestInfo requestInfo = new RequestInfo(getUri(req), req.getSession().getId(), null, null, UserFactory.getUser(), InetAddress.getByName(req.getRemoteAddr()));
String token = new DecimalFormat("00000000").format(Math.abs(RANDOM.nextInt() % Integer.MAX_VALUE));
//log msg
//send mail in a different thread
if (!req.getRequestURI().startsWith("/faces/error.jsp"))
{
resp.sendRedirect("/faces/error.jsp?token=" + token);
}
else
{
//log that an infite loop occurred
throw new ServletException(crapMsg, e);
}
}
}
ErrorBean
public class ErrorBean implements Serializable
{
private String feedback;
private String lastToken;
public String getLastErrorCode()
{
return "your token: " + getToken();
}
private String getToken()
{
final String token = (String) FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("token");
//here the "token" returns null although it is sent via get.
if (token != null)
{
if (!token.equals(lastToken))
{
// reset data on token change.
feedback = null;
}
lastToken = token;
}
return lastToken;
}
public void setFeedback(String feedback)
{
this.feedback = feedback;
}
public String getFeedback()
{
if (feedback == null)
{
feedback = getDefaultMessage();
}
return feedback;
}
public void send()
{
sendMail(lastToken,feedback);
}
}
From my humble point of view, The faces Context should see the parameter the vary first time when request is already redirected on :
resp.sendRedirect("/faces/error.jsp?token=" + token);But when you make some other request, Faces do manipulation with it’s Navigation system and Redirect the page, and obviously that the Filter is doing
chain.doFilter(req,resp);that time, which authorize the JSF Navigation model to execute and thus removing your request parameter.My suggestion is that you can add it to another session value, It’ll be easier to manipulate then.