Search code examples
javaajaxhttp-redirectjsf-2listener

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?

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."

So, my though is, is there a way to attach a listener to all ajax calls in JSF? If so, what I'd like to do is monitoring the response code. If it's a redirect, use a window.navigate to send them along their way.

I'm always open to hear how others have solved this problem!


Solution

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