When an error occurs in a Java program it usually results
in an exception being thrown. Java exception handling enables Java applications
to handle errors and this is an important aspect in order to write robust and reliable
Java applications or components. So, how we choose to throw, catch and handle
these exceptions is always an aspect that we should carefully consider. JSF has
its own system of treating exceptions. First it defines a super-exception,
named FacesException. This class
represents general JSF exceptions and usually when you get a FacesException, it wraps the real cause
of the exception inside of it - for this we have FacesException#getCause(). As a note here,
this is the reason for what OmniFaces comes with a set of utilities in its org.omnifaces.util.Exceptions class meant to provide
general utility methods with respect to working with exceptions - for example,
we can unwrap a caught FacesException until a non-FacesException is found. Beside the FacesException, JSF provides
several sub-classes of it specially dedicated for some JSF common exceptions,
such as AbortProcessingException, ConverterException, EvaluationException, FaceletException, ProtectedViewException, UpdateModelException, ValidatorException, ViewExpiredException. Probably, the most well
known from here we have ConverterException, ValidatorException and ViewExpiredException.
Obviously, when an JSF application causes an exception,
JSF is responsible for handle it. With other words, JSF decide how and what to
do when an exception occurs. It decide if the exception must be thrown as it
is, must be reported in a special case (e.g. with a custom message), must be
simply ignored (swallowed) etc. Basically, the unhandled exceptions are queued
via ExceptionQueuedEvent, and they are handled by an exception handler. For example, Oracle Mojarra
implementation provide two handler exceptions, one is com.sun.faces.context.ExceptionHandlerImpl and another one is com.sun.faces.context.AjaxExceptionHandlerImpl. Basically, both of them (and any other exception
handler) acts the same in two simple steps:
- override the handle() method
- override the handle() method
- iterate over the unhandled exceptions (one by one) and handle them
accordingly
Note that
OmniFaces comes with two exception handler implementations, FullAjaxExceptionHandler (handle exceptions
occurred during a JSF AJAX request) and FacesMessageExceptionHandler
(adds every exception as a global FATAL faces message). Both of them are
dissected in detail in "Mastering
OmniFaces" book.
Now, as you can see, we can have multiple exception
handlers, so JSF has decide which one(s) to use? Here is where the ExceptionHandlerFactory enters in the
scene. This class is responsible to serve instances of exception handlers. For
example, in Oracle Mojarra the implementation of ExceptionHandlerFactory is com.sun.faces.context.ExceptionHandlerFactoryImpl. There is true that here we have a little trick in JSF,
because since there are two exception handlers, JSF group them in com.sun.faces.context.AjaxNoAjaxExceptionHandler. It is a simple and very intuitive class, just check out its code line by
line:
// Source code from Oracle Mojarra 2.2.9
package com.sun.faces.context;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
public class AjaxNoAjaxExceptionHandler extends ExceptionHandlerWrapper {
private AjaxExceptionHandlerImpl
ajaxExceptionHandlerImpl;
private ExceptionHandlerImpl
exceptionHandlerImpl;
public
AjaxNoAjaxExceptionHandler(AjaxExceptionHandlerImpl ajaxExceptionHandlerImpl,
ExceptionHandlerImpl exceptionHandlerImpl) {
this.ajaxExceptionHandlerImpl =
ajaxExceptionHandlerImpl;
this.exceptionHandlerImpl =
exceptionHandlerImpl;
}
@Override
public ExceptionHandler getWrapped()
{
FacesContext fc =
FacesContext.getCurrentInstance();
if (null != fc &&
fc.getPartialViewContext().isAjaxRequest()) {
return
ajaxExceptionHandlerImpl;
}
return exceptionHandlerImpl;
}
}
And the ExceptionHandlerFactoryImpl uses like this:
// Source code from Oracle Mojarra 2.2.9
public ExceptionHandler getExceptionHandler() {
FacesContext fc =
FacesContext.getCurrentInstance();
ApplicationAssociate myAssociate =
getAssociate(fc);
ExceptionHandler result = new
AjaxNoAjaxExceptionHandler(
new AjaxExceptionHandlerImpl(new
ExceptionHandlerImpl(Boolean.TRUE)),
new ExceptionHandlerImpl(((myAssociate !=
null) ? myAssociate.isErrorPagePresent() : Boolean.TRUE)));
return result;
}
We also can have a quick look over the ExceptionHandlerImpl. Especially on the
ExceptionHandlerImpl#handle()method:
// Source code from Oracle Mojarra 2.2.9
public void handle() throws FacesException {
for
(Iterator<ExceptionQueuedEvent> i =
getUnhandledExceptionQueuedEvents().iterator(); i.hasNext(); ) {
ExceptionQueuedEvent event =
i.next();
ExceptionQueuedEventContext
context = (ExceptionQueuedEventContext) event.getSource();
try {
Throwable t =
context.getException();
...
}
So, mainly if we think to write a custom exception handler, we have to
think to the flow from figure below:
Here it is a custom exception handler factory:
public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory
{
private ExceptionHandlerFactory
exceptionHandlerFactory;
public
CustomExceptionHandlerFactory(){
}
public CustomExceptionHandlerFactory(ExceptionHandlerFactory
exceptionHandlerFactory) {
this.exceptionHandlerFactory =
exceptionHandlerFactory;
}
@Override
public ExceptionHandler
getExceptionHandler() {
ExceptionHandler handler = new CustomExceptionHandler(exceptionHandlerFactory.getExceptionHandler());
return handler;
}
}
And, the custom exception handler skeleton is:
public class CustomExceptionHandler extends ExceptionHandlerWrapper {
private static final Logger LOG =
Logger.getLogger(CustomExceptionHandler.class.getName());
private ExceptionHandler
exceptionHandler;
CustomExceptionHandler(ExceptionHandler
exceptionHandler) {
this.exceptionHandler =
exceptionHandler;
LOG.info("JSF uses the
CustomExceptionHandler ...");
}
@Override
public ExceptionHandler getWrapped()
{
return exceptionHandler;
}
@Override
public void handle() throws
FacesException {
LOG.info("JSF invoked
CustomExceptionHandler#handle() method ...");
final
Iterator<ExceptionQueuedEvent> queue =
getUnhandledExceptionQueuedEvents().iterator();
// If there's no unhandled
exception simply return.
if (!queue.hasNext()) {
return;
}
/////////////////////////////////////
// HANDLE THE FIRST EXCEPTION ONLY
//
/////////////////////////////////////
Throwable throwable =
queue.next().getContext().getException();
// Handle the exception, throwable.
LOG.log(Level.INFO, "Handling
exception: {0}", throwable);
// Let JSF handle exceptions of
type AbortProcessingException.
if (throwable instanceof
AbortProcessingException) {
return;
}
// Remove the exception from the
queue.
queue.remove();
// in order to find the exception root cause,
maybe use here
// the org.omnifaces.util.Exceptions utilities
such as:
// Exceptions#is() or/and Exceptions#unwrap()
methods
//... HANDLE THE EXCEPTION ...
// Remove all remaining exceptions if you want
to fix only the first.
while (queue.hasNext()) {
queue.next();
queue.remove();
}
// Give control to the default (or
other) exception handler.
getWrapped().handle();
//////////////////////////////////////
// HANDLE ALL OR A CERTAIN
EXCEPTION //
///////////////////////////////////////
while (queue.hasNext()) {
ExceptionQueuedEvent item =
queue.next();
ExceptionQueuedEventContext
exceptionQueuedEventContext = (ExceptionQueuedEventContext) item.getSource();
// Get the excpetion from
current iteration.
Throwable throwable_item =
exceptionQueuedEventContext.getException();
// If you know that you want
to handle all exceptions from queue
// then you can remove the
exception from this iteration from the queue.
queue.remove();
// Handle the exception at
this iteration, throwable_item.
LOG.log(Level.INFO,
"Handling exception: {0}", throwable_item);
// In order to find the
exception root cause, maybe use here
// the
org.omnifaces.util.Exceptions utilities such as:
// Exceptions#is() or/and
Exceptions#unwrap() methods
//...
// If you want to handle a
certain exception place here the conditions to
// identify it. Also, remove
from the queue only this exception
// (pay attention to not
remove unhandled exceptions, only if you intend to do so):
queue.remove();
//... HANDLE THE EXCEPTION
OF THIS ITERATION ...
// Eventually, if you don't
want to handle more exception then break the loop.
break;
}
// Give control to the default (or
other) exception handler.
getWrapped().handle();
}
}
You can find this skeleton here.
Niciun comentariu :
Trimiteți un comentariu