Using JSF 2.0 / Facelets, is there a way to attach a global listener to all AJAX calls?

Is there a way to attach a global listener to all AJAX calls in JSF? Maybe through a phase listener or something?

Yes, a PhaseListener can do it. A SystemEventListener also. A Filter also.

If you’re inside JSF context, then you can check as follows whether the current request is an ajax request or not.

if (FacesContext.getCurrentInstance().getPartialViewContext().isAjaxRequest()) {
    // It's an ajax request.
}

If you’re not inside JSF context, e.g. inside a Filter, then you can check as follows whether the current request is a JSF ajax request or not.

if ("partial/ajax".equals(request.getHeader("Faces-Request"))) {
    // It's a JSF ajax request.
}

Here is the conundrum… Lets say you’re using f:ajax tags and something like apache shiro and you let your session expire. Then you come back and click a button that has an f:ajax attached to it. The server will respond with a 302 redirect to the login page.

The user sees nothing. They can repeatedly click and invoke the ajax call, but to them the app is just “dead.”

Forcing a redirect on an ajax request requires a special XML response. When you’re inside JSF context, then ExternalContext#redirect() already takes this implicitly into account. All you need to do is to write this:

FacesContext.getCurrentInstance().getExternalContext().redirect(url);

If you’re not inside JSF context, e.g. inside a Filter, then you’d need to write the whole XML response yourself. E.g.

response.setContentType("text/xml");
response.getWriter()
    .append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
    .printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>", url);

Leave a Comment