JSF 2 Global exception handling, navigation to error page not happening - jsf

I am developing a JSF 2.0 based web application. I am trying to implement a global exception handler which will redirect the user to a generic error page whenever any exception occurs (e.g. NullPointerException,ServletException,ViewExpiredException etc.)
Whenever a NPE occurs in my app, My customnavhandler breakpoint is hit and NavigationHandler code is executed, but somehow redirection to error page is not happening, the requested page remains partially rendered. Any idea what could be wrong here ? One info is that I am throwing an NPE deliberately on the requested page (which was partiallyu rendered after NPE)
My faces-config.xml entry
<factory>
<exception-handler-factory>
com.common.exceptions.CustomExceptionHandlerFactory
</exception-handler-factory>
</factory>
My CustomNavHandler
public class CustomExceptionHandler extends ExceptionHandlerWrapper {
private static final Logger logger = Logger.getLogger("com.gbdreports.common.exception.CustomExceptionHandler");
private final ExceptionHandler wrapped;
public CustomExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped() {
return this.wrapped;
}
public void handle() throws FacesException {
final Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator();
while (i.hasNext()) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context =
(ExceptionQueuedEventContext) event.getSource();
// get the exception from context
Throwable t = context.getException();
final FacesContext fc = FacesContext.getCurrentInstance();
final ExternalContext externalContext = fc.getExternalContext();
final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
final ConfigurableNavigationHandler nav = (ConfigurableNavigationHandler) fc.getApplication().getNavigationHandler();
//here you do what ever you want with exception
try {
//log error ?
logger.error("Severe Exception Occured");
//log.log(Level.SEVERE, "Critical Exception!", t);
//redirect error page
requestMap.put("exceptionMessage", t.getMessage());
nav.performNavigation("/TestPRoject/error.xhtml");
fc.renderResponse();
// remove the comment below if you want to report the error in a jsf error message
//JsfUtil.addErrorMessage(t.getMessage());
}
finally {
//remove it from queue
i.remove(); }
}
//parent hanle
getWrapped().handle();
}
}
My customNavhandler factory
public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
#Override
public ExceptionHandler getExceptionHandler() {
return new CustomExceptionHandler (parent.getExceptionHandler());
}
}

It's most likely because the current request is an ajax (asynchronous) request. The exception handler which you've there is designed for regular (synchronous) requests.
The proper way to change the view in case of an ajax exception is as follows:
String viewId = "/error.xhtml";
ViewHandler viewHandler = context.getApplication().getViewHandler();
context.setViewRoot(viewHandler.createView(context, viewId));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
This is however somewhat naive. This won't work if the ajax exception is been thrown in midst of rendering of a ajax response.
I'd suggest to not reinvent the wheel. The JSF utility library OmniFaces has a complete working solution in flavor of FullAjaxExceptionHandler. You can find the full source code here and the showcase example here. It makes use of standard servlet API <error-page> declarations in web.xml. This way the error pages are also reusable for synchronous requests, with a little help of FacesExceptionFilter, also provided by OmniFaces.
See also:
using ExternalContext.dispatch in JSF error handler causes corrupt page rendering
What is the correct way to deal with JSF 2.0 exceptions for AJAXified components?

Unified way to handle both ajax and non ajax requests exception could be done simplifying your code. Instead of
requestMap.put("exceptionMessage", t.getMessage());
nav.performNavigation("/TestPRoject/error.xhtml");
fc.renderResponse();
is enough to use:
fc.getExternalContext().redirect("/TestPRoject/error.xhtml");

Related

Capture and log / notify unhandled exceptions in a JSF based application

I would like to check and log all unhandled exceptions in my JSF web application using log4j. I read this post Log runtime Exceptions in Java using log4j and add a class that implements Thread.UncaughtExceptionHandler. but the method is not fired.
What is the correct way to achieve this?
add a class that implements Thread.UncaughtExceptionHandler. but the method is not fired
There's usually no means of an uncaught exception in the HTTP request thread managed by a Java EE web application as the server ultimately catches any exception coming from the web application and displays it in case of synchronous HTTP requests in a HTTP 500 error page (in case of asynchronous (ajax) requests, this is in turn framework-dependent). Moreover, if there was really an uncaught exception which escaped the server's attention, it would have killed the server's runtime thread causing the whole server to stop. This is clearly not an ideal real world scenario.
JSF offers you the ExceptionHandler API to get hand of those exceptions and if necessary control them.
Here's a kickoff example:
public class YourExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
public YourExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public void handle() throws FacesException {
FacesContext facesContext = FacesContext.getCurrentInstance();
for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents().iterator(); iter.hasNext();) {
Throwable exception = iter.next().getContext().getException(); // There it is!
// Now do your thing with it. As per the question, you want to log it using log4j.
logger.error("An exception occurred!", exception);
}
getWrapped().handle();
}
#Override
public ExceptionHandler getWrapped() {
return wrapped;
}
}
In order to get it to run, create a custom ExceptionHandlerFactory as follows:
public class YourExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
public YourExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
#Override
public ExceptionHandler getExceptionHandler() {
return new YourExceptionHandler(parent.getExceptionHandler());
}
}
Which needs to be registered in faces-config.xml as follows:
<factory>
<exception-handler-factory>com.example.YourExceptionHandlerFactory</exception-handler-factory>
</factory>
See also:
Exception handling in JSF ajax requests

AbortProcessingException leaves stack trace in server log, how to disable this?

There is a commandButton on a page that asynchronously invokes some service, and when user clicks a button I want to verify service availability by checking special config from database. This config can be updated every minute. So if the service is not available, action of commandButton should not be executed.
So, as per Differences between action and actionListener, I'm checking service availability in actionListener and throw AbortProcessingException in case service is not available. So action will be skipped.
This works fine, but it leaves a stack trace in server's log. I don't want such behavior. Is it possible to handle the exception in such way that this will not leave mark in server logs, just like when ValidatorException is thrown? I'm using OmniFaces FullAjaxExceptionHandler, if that's relevant.
You can suppress the logging with a custom exception handler. As you're currently using OmniFaces FullAjaxExceptionHandler, you'd better extend it. In the handle() method, check if there's an exception and if it's an instance of AbortProcessingException. If so, then just ignore it and return directly from the exception handler.
public class YourExceptionHandler extends FullAjaxExceptionHandler {
public YourExceptionHandler(ExceptionHandler wrapped) {
super(wrapped);
}
#Override
public void handle() throws FacesException {
Iterator<ExceptionQueuedEvent> events = getUnhandledExceptionQueuedEvents().iterator();
if (events.hasNext() && events.next().getContext().getException() instanceof AbortProcessingException) {
return; // Ignore (and don't log).
}
super.handle(); // Continue to FullAjaxExceptionHandler.
}
}
Create a factory around it:
public class YourExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory wrapped;
public YourExceptionHandlerFactory(ExceptionHandlerFactory wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getExceptionHandler() {
return new YourExceptionHandler(getWrapped().getExceptionHandler());
}
#Override
public ExceptionHandlerFactory getWrapped() {
return wrapped;
}
}
In order to get this to run, register it as factory in faces-config.xml the usual way, replacing the FullAjaxExceptionHandlerFactory:
<factory>
<exception-handler-factory>com.example.YourExceptionHandlerFactory</exception-handler-factory>
</factory>

How to display Primefaces dialog (dialog framework) as part of CustomExceptionHandler

I have a CustomExceptionHandler from which I want to display a modal dialog to get some information from the user, using Primefaces dialog framework.
The CustomExceptionHandler#handle method is being called as expected when I deliberately cause a Null Pointer exception. And I can display and interact with the dialog's .xhtml page if I directly invoke it from a p:commandButton's actionListener on a xhtml page (both cases with ajax enabled and disabled).
When I invoke a p:commandButton (with ajax disabled to eliminate a possible ajax error from complicating the scenario) on a page that leads to the NPE, both before/after log messages are generated from CustomExceptionHandler#handle, but the dialog page is not displayed. The window flashes like it's being refreshed, however.
From CustomExceptoinHandler.java:
public class CustomExceptionHandler
extends ExceptionHandlerWrapper
{
private ExceptionHandler wrapped;
public CustomExceptionHandler(ExceptionHandler wrapped)
{
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped()
{
return wrapped;
}
#Override
public void handle()
throws FacesException
{
Iterator<ExceptionQueuedEvent> iterator = getUnhandledExceptionQueuedEvents().iterator();
while (iterator.hasNext()) {
boolean removeError = false;
Throwable t = iterator.next().getContext().getException();
while (t.getCause() != null)
t = t.getCause();
if (t instanceof NullPointerException) {
Logging.devLogger.warn("NPE exception handler before dialog");
Map<String, Object> options = new HashMap<>();
options.put("modal", true);
String page = "/home/dialogs/exceptionDialog.xhtml";
RequestContext.getCurrentInstance().openDialog(page, options, null);
Logging.devLogger.warn("NPE exception handler after dialog");
removeError = true;
} // TBD: other cases and refactoring ....
if (removeError) iterator.remove();
}
getWrapped().handle();
}
}
The RequestContext....openDialog() invocation is the same when handling the exception and when the dialog is directly opened via a commandButton. I've determined that the backing bean for the exception dialog (view scoped) gets constructed when the dialog is opened outside the exception situation, but does not get constructed when opened while handling the exception (#PostConstruct method not called).
How would I change the way the exception is handled so the dialog behaves as expected? I'm using Mojarra 2.2.5, Primefaces 4.0, Glassfish 4.0.
I'm woring with PrimeFaces 5.2, i've solved this problem in other way.
To show the dialog, i've created the error dialog in my template named "errorDialog" and te show it by my CustomExceptionHandler i've used, this command
RequestContext.getCurrentInstance().execute("PF('errorDialog').show();");
You can't use the command below, becose there is already an exception, so you can rendered the dialog.
RequestContext.getCurrentInstance().openDialog(page, options, null);
Concerning the exception handling by #PostConstruct method, i've used :
#PostConstruct
private void init() {
try {
initController();
} catch (Exception ex) {
logger.error(ex, null);
//Show the error dialog
MyUtil.openErrorDialog();
}
}
Note : I have still a problem, when an exception (Error 500) is occurred and handled by the App Server and not my CustomHandlerClass, i can't show the dialog becose there is already a redirection by the App Server.

Redirect in session bean #PostConstruct

I have a JSF 2.0 app that has a session bean that is accessed from home.xhtml, as follows:
#Named(value = "home")
#SessionScoped
public class Home implements Serializable{
#PostConstruct
public void init() {
// Retrieve database data here
try {
} catch (Exception ex) {
System.out.println("EXCEPTION");
}
}
}
What I want to do is, if the database retrieval fails, redirect to error.xhtml page. How is this done in the init method above?
You can use External Context's redirect(java.lang.String) method.
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
externalContext.redirect("page2.xhtml");
No need to manually mess with redirecting. Just throw the exception outright.
#PostConstruct
puclic void init() throws Exception { // Please be more specific, e.g. SQLException.
// Retrieve database data here without try/catch block.
}
It will already end up in a HTTP 500 error page whose location (and thus the look'n'feel) can be customized by web.xml:
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/errorpages/500.xhtml</location>
</error-page>
In case you'd like to also cover exceptions on ajax requests, head to this answer: What is the correct way to deal with JSF 2.0 exceptions for AJAXified components?

Why use a JSF ExceptionHandlerFactory instead of <error-page> redirection?

All of the ExceptionHandlerFactory examples I have come across so far redirect a user to a viewExpired.jsf page in the event that a ViewExpiredException is caught:
public class ViewExpiredExceptionExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
public ViewExpiredExceptionExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped() {
return this.wrapped;
}
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
if (t instanceof ViewExpiredException) {
ViewExpiredException vee = (ViewExpiredException) t;
FacesContext facesContext = FacesContext.getCurrentInstance();
Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
NavigationHandler navigationHandler = facesContext.getApplication().getNavigationHandler();
try {
// Push some useful stuff to the request scope for use in the page
requestMap.put("currentViewId", vee.getViewId());
navigationHandler.handleNavigation(facesContext, null, "/viewExpired");
facesContext.renderResponse();
} finally {
i.remove();
}
}
}
// At this point, the queue will not contain any ViewExpiredEvents. Therefore, let the parent handle them.
getWrapped().handle();
}
}
It seems to me that the following simple web.xml configuration is fundamentally the same and a lot simpler:
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/viewExpired.jsf</location>
</error-page>
This prompts the question - why would one use an ExceptionHandlerFactory?
The particular example does only one useful thing: it saves the view ID as a request attribute so that you can use for example
<h:link value="Go back to previous page" outcome="#{currentViewId}" />
But this is not tremendously useful as the raw request URI is already available by the <error-page>'s default request attribute javax.servlet.error.request_uri.
<h:outputLink value="#{requestScope['javax.servlet.error.request_uri']}">Go back to previous page</h:outputLink>
However one thing what a custom ExceptionHandler is really useful for is that it allows you to deal with exceptions during ajax requests. By default they have namely no single form of helpful feedback in the client side. Only in Mojarra with project stage set to "Development" you'll see a bare JavaScript alert message with the exception message. But that's it. There is no single form of feedback in "Production" stage. With a custom ExceptionHandler you would be able to parse the web.xml to find the error page locations, create a new UIViewRoot with it and force JSF to set ajax rendering to #all.
So, basically:
String errorPageLocation = "/WEB-INF/errorpages/500.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
See also this related question: What is the correct way to deal with JSF 2.0 exceptions for AJAXified components? and this blog: Full Ajax Exception Handler.
It depends what do you want to do when you recive ViewExpiredException.
If you just want to display to a user error page you can do it like you said.
This post show you how to programmatically intercept the
ViewExpiredException and do something nice with it.

Resources