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

2

11

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!

Fearfully answered 16/2, 2012 at 3:27 Comment(1)
Wont "Initial State Saving to server & on session timeout transfer state to client for all time responsiveness" be a better solution to this rather than all these hacky things? I am wondering why JSF doesn't already provide this & if I can somehow implement this.Sakhuja
M
19

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);
Maki answered 16/2, 2012 at 13:15 Comment(3)
TSo I'm guessing the optimal route is to write a filter that sits in front of the Shiro filter. When Shiro sends a redirect, my filter will intercept it and send the XML. I have a feeling I can't do this inside the faces app because Shiro will be blocking the request.Fearfully
Your best bet is then indeed a filter in front of Shiro filter.Maki
For your information, I blogged about exactly this case and provided a complete ready-to-use filter for Shiro: balusc.blogspot.com/2013/01/…Maki
B
4

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.

Bartholemy answered 16/2, 2012 at 4:59 Comment(1)
Wont "Initial State Saving to server & on session timeout transfer state to client for all time responsiveness" be a better solution to this rather than all these hacky things? I am wondering why JSF doesn't already provide this & if I can somehow implement this.Sakhuja

© 2022 - 2024 — McMap. All rights reserved.