One tricky thing I’ve seen people have trouble with (and at one point had some trouble with myself..) is the way events fired by listeners are processed in JSF - specifically, how exceptions are handled. The symptom is easy to see - exceptions are being thrown in methods called by these listeners, but aren’t being propagated back. In fact, the whole lifecycle continues on as though nothing has happened. This causes confusion, and in some cases, even worse problems. One potential solution is to use an Action instead of an ActionListener, but what about in cases where you actually _need_ to use an ActionListener (or ValueChangeListener)?

First, what is the root of this problem? If you look at UIViewRoot, around line 446, you see:

try {
source.broadcast(event);
} catch(AbortProcessingException e){
; //A "return" here would abort remaining events too
}

As you can see, this behavior is actually happening by design. An exception in one event will not abort processing of subsequent events. But what if we want this to happen? After all, if an exception occurs in one of our listeners, it may well be fatal enough we want to stop doing anything else and display an error page. How do we do that? We could manually catch exceptions in each method - that would work but would be kind of time consuming. The way I ended up dealing with this is adding in some AOP code (via Spring) to handle exceptions thrown from these methods by logging them and redirecting the user to an error page. Here is the implementation I ended up with:

@Aspect
public class ListenerExceptionHandler{
@AfterThrowing(pointcut=“execution(* *(javax.faces.event.ValueChangeEvent)) || execution(* *(javax.faces.event.ActionEvent))”, throwing=“ex”)
public void handleListenerException(Exception ex) throws IOException{
ExceptionLogger.logException(ex);
FacesContext.getCurrentInstance().getExternalContext().dispatch(“/errorPage.jsf”);
FacesContext.getCurrentInstance().responseComplete();
}
}

This code will catch any exception thrown in a listener method, log it (in our case, it also emails the exception), and then pass the user onto an error page. Pretty simple to implement :)