views:

624

answers:

3

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 ErroBean it is there - SOMETIMES. :( about 50% of the time.

FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()

sometimes returns a map with 1 entry and sometimes an empty map. in every case, id is passed at http level.

i can't really reproduce what is causing this. here are the relevant codes (helper methods + emptly implementations omitted) :

the ErrorFilter is mapped at /* and the ErrorBean is a session scope bean managed by JSF

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

}

}

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

}

A: 

I don't think the FacesContext is available in a Servlet Filter. Doesn't a filter run before hitting the FacesServlet (which creates the FacesContext)?

Try reading: http://www.thoughtsabout.net/blog/archives/000033.html

GreenieMeanie
I believe he means that the error page is a JSF page which uses a managed bean to look up a HTTP parameter set by the filter (in which case, the context would be available).
McDowell
He has the comment "create errorInfo object with an "int" token". Without seeing this object and how it used, it is hard to tell. The .getToken() on that object could be returning null for all we know.
GreenieMeanie
the token is just a random string generated like this: this.token = new DecimalFormat("00000000").format(Math.abs(RANDOM.nextInt() % Integer.MAX_VALUE));
Andreas Petersson
note that the facescontext is not used at all in the filter. it is used in the ErrorBean to obtain the parameters
Andreas Petersson
A: 

Error pages aren't generally good candidates for JSF in the context of anything larger than JSF itself (meaning for example, from the webapp framerwork). And yes, the FaceContext isn't something you can depend on being available whenever you want it. It's created by the Faces Servlet to handle a JSF request, and it's discarded when the request is complete. It has no longer-term persistence from request to request.

Also, URLs have a completely different significance in JSF than they do in most web applications. They're more like a handle used to track the conversation than an absolute indication of what page is being requested or displayed.

Tim H
+1  A: 

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.

agawish
i did not figure out exactly why it is failing SOMETIMES.. the solution you propose worked well, I implemented it as a workaround that way. storing the token attribute in session. this works robust.
Andreas Petersson