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

Solution 1:

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

Solution 2:

To redirect a jsf ajax request you need xml as follows

<?xml version="1.0" encoding="UTF-8"?>   
<partial-response>  
      <redirect url="XXX">  
      </redirect>  
</partial-response>

Here XXX is url you want redirect to happen.

On ajax call redirect sent is not as above hence no redirect.

To get the desired result have a filter for all jsf request except few pages(login page) and check session is valid and if it is really jsf ajax call by checking header "Faces-Request", its value should be "partial/ajax". If session has expired and is ajax request send above xml as response.

It should work.