Uma das funcionalidades mais simples e legais introduzidas pela especificação 2.0 do JSF é a possibilidade de se criar um manipulador global de exceções. Trechos de código como esse aqui abaixo tratando as exceções previstas uma a uma no web.xml se tornaram desnecessários.

<error-page>
  <error-code>404</error-code>
  <location>/404.xhtml</location>
</error-page>

Ao invés da solução acima, o JavaServer Faces nos permite implementar um manipulador global de exceções de forma relativamente simples para todas as exceções que podem ocorrer na aplicação. Para isso, você precisa apenas de criar duas classes que estendam as classes:
ExceptionHandlerWrapper – Que fornece uma implementação simplificada da ExceptionHandler permitindo por exemplo que os desenvolvedores possam fornecer um comportamento especializado para uma instância ExceptionHandler.
ExceptionHandlerFactory – Essa classe por sua vez atua como uma Factory responsável por criar e retornar, quando necessário, uma nova instância de ExceptionHandler.
Por fim você precisa apenas algumas linhas ao arquivo “faces.config.xml” responsáveis por registrar a classe de tratamento no FacesServlet.

	
<factory>
  <exception-handler-factory>
     br.com.semeru.exceptions.CustomExceptionHandlerFactory
  </exception-handler-factory>
</factory>

No trecho de código abaixo temos a classe “CustomExceptionHandlerFactory” que é responsável por fabricar uma instância da classe “CustomExceptionHandler” que é responsável por capturar e tratar a exceção.

package br.com.semeru.exceptions;

import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerFactory;

public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {
    private ExceptionHandlerFactory parent;

    public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) {
        this.parent = parent;
    }

    @Override
    public ExceptionHandler getExceptionHandler() {
        ExceptionHandler handler = new CustomExceptionHandler(parent.getExceptionHandler());
        return handler;
    }

}

Já no trecho abaixo temos a classe “CustomExceptionHandler” que pode tratar a exceção da forma que você julgar mais apropriada. Você pode simplesmente imprimir a StackTrace e retornar uma página de erros, ou tratar as exceções que mais ocorrem e retornar uma página personalizada para cada uma delas. Outra coisa que pode ser interessante é enviar a StackTrace via e-mail para a equipe de desenvolvimento.

package br.com.semeru.exceptions;

//import java.io.PrintWriter;
//import java.io.StringWriter;
import java.util.Iterator;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.application.FacesMessage;
import javax.faces.application.NavigationHandler;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;

//Inicialmente devemos implementar a classe CustomExceptionHandler que extende a classe ExceptionHandlerWrapper
public class CustomExceptionHandler extends ExceptionHandlerWrapper {

    private ExceptionHandler wrapped;

    //Obtém uma instância do FacesContext
    final FacesContext facesContext = FacesContext.getCurrentInstance();

    //Obtém um mapa do FacesContext
    final Map requestMap = facesContext.getExternalContext().getRequestMap();

    //Obtém o estado atual da navegação entre páginas do JSF
    final NavigationHandler navigationHandler = facesContext.getApplication().getNavigationHandler();

    //Declara o construtor que recebe uma exceptio do tipo ExceptionHandler como parâmetro
    CustomExceptionHandler(ExceptionHandler exception) {
        this.wrapped = exception;
    }

    //Sobrescreve o método ExceptionHandler que retorna a "pilha" de exceções
    @Override
    public ExceptionHandler getWrapped() {
        return wrapped;
    }

    //Sobrescreve o método handle que é responsável por manipular as exceções do JSF
    @Override
    public void handle() throws FacesException {

        final Iterator iterator = getUnhandledExceptionQueuedEvents().iterator();
        while (iterator.hasNext()) {
            ExceptionQueuedEvent event = iterator.next();
            ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();

            // Recupera a exceção do contexto
            Throwable exception = context.getException();

            // Aqui tentamos tratar a exeção
            try {

//                // Aqui você poderia por exemploinstanciar as classes StringWriter e PrintWriter
//                StringWriter stringWriter = new StringWriter();
//                // PrintWriter printWriter = new PrintWriter(stringWriter);
//                // exception.printStackTrace(printWriter);
//                // Por fim você pode converter a pilha de exceções em uma String
//                String message = stringWriter.toString();
//
//                // Aqui você poderia enviar um email com a StackTrace
//                // em anexo para a equipe de desenvolvimento
//
//                // e depois imprimir a stacktrace no log
//                exception.printStackTrace();

                // Coloca uma mensagem de exceção no mapa da request
                requestMap.put("exceptionMessage", exception.getMessage());

                // Avisa o usuário do erro
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage
                    (FacesMessage.SEVERITY_ERROR, "O sistema se recuperou de um erro inesperado.", ""));

                // Tranquiliza o usuário para que ele continue usando o sistema
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage
                    (FacesMessage.SEVERITY_INFO, "Você pode continuar usando o sistema normalmente!", ""));

                // Seta a navegação para uma página padrão.
                navigationHandler.handleNavigation(facesContext, null, "/restrict/home.faces");

                // Renderiza a pagina de erro e exibe as mensagens
                facesContext.renderResponse();
            } finally {
                // Remove a exeção da fila
                iterator.remove();
            }
        }
        // Manipula o erro
        getWrapped().handle();
    }
}

No bloco try-finally você pode converter o Throwable em qualquer exceção específica e dar um tratamento especial a cada uma delas. Um simples problema de ViewExpiredException, por exemplo, pode ser redirecionado para uma página informando que a “sessão expirou” já, no caso de uma exceção do tipo NullPointerException podemos usar uma página padrão apenas dizendo algo como “Ocorreu um erro inesperado, por favor, tente novamente mais tarde.”
Um detalhe muito importante a se lembrar é que ao lidar com exceções em desenvolvimento web, você deve garantir que não está compartilhando informações indesejadas e/ou sensíveis ao retornar o erro para a página web.

Bom é isso e espero que gostem do post.

Editado para responder ao comentário do André

Olá gravei uma vídeo aula explicando de forma clara como adotar o exception handler em um projeto JSF.

Tags: , , , , ,