Tratamento e manipulação de exceções

Gostaria de saber da experiência e da opinião de vocês a respeito da maneira como lidam com exceções nas suas aplicações.

Em um projeto recente defini duas formas de tratar as exceções da aplicação, uma baseada em (checked) Exception e outra em RuntimeException, direta ou indiretamente.

Todas as exceções de negócio ou que demandam tratamento esperado deveria ser subclasse de Exception.

As exceções técnicas ou inesperadas seriam subclasse de RuntimeException.

Exemplos:

Exceção de negócio (checked)

public void cadastrarFornecedor( Fornecedor f ) throws ValidacaoException { if( !validaCnpj( f.getCnpj() ) ) { throw new CNPJInvalidoException( f.getCnpj() ); } }

Exceção inesperada

public Fornecedor buscarFonecedor( String cnpj ) { try { // faz a busca do fornecedor pelo CNPJ via JPA } catch( Exception e ) { throw new DAOException(e); } }

Em um ponto centralizado do sistema eu trato as exceções inesperadas, fazendo logging e exibindo página de erro.

Se o caso de uso prevê tratamento específico em caso de erro técnico/inesperado, isso é feito no código mesmo:

public void consolidarInformacoes() { fazerCalculos(); salvarAlgunsDados(); try { atualizarInfoFornecedores(); } catch( RuntimeException e ) { // nao preciso fazer rollback, apenas logging log.warn(e); } consiladacaoFinal(); }

Ou seja, no código acima eu teria uma transação completa, mas o fato do método atualizarInfoFornecedores() lançar alguma exceção, não compromete o processo todo.

E ai, como costumam fazer?

Quero principalmente evitar os Anti-patterns “Coding by Exception”, “Error hiding” e “Expection handling”.

Não lembro se é muito diferente do que eu sempre fiz/faço.

Sempre tratei exceção como checked, apesar de alguns runtimes eu encapsular como checked só pra passar pra frente.

até hoje não tive nenhum problema, ou retrabalho gigante por causa disso.

e sim, no final sempre tem o reponsável por fazer logging de determinados erros…

por isso eu disse que não é tão diferente do que vc mostrou aqui.

Minha preocupação em sempre usar checked exception é de transformar a aplicação inteira em um fluxo de exceções e ter de me preocupar com detalhes não-funcionais, por exemplo.

Então Destro, como você sabe eu gosto muito do Struts, então adotei uma solução para ele.

Eu trabalho, basicamente, com dois tipos de exceção: a de negócio e a inesperada.

Utilizo o tratamento de exceções do Struts, bem, na verdade eu estendo a classe ExceptionHandler dele. Para cada tipo de exceção eu crio um handler diferenciado.

Pois bem, a exceção de negócio é lançada da seguinte maneira:

throw new BusinessException(String msgCode, Object[] bindValues, String redirectPage);

Onde msgCode é a entrada no meu properties que representa qual a mensagem que deverá ser apresentada na tela e os outros parâmetros são opcionais. Os valores do bindValues são os binds para as mensagens e redirectPage é o caminho para o qual o usuário será direcionado. Estes dois últimos são opcionais, pois pode não haver valores de bind para a mensagem e o usuário pode ser direcionado para a página de erro default configurada no struts-config.xml:

  <global-forwards> 
    <forward name="errorTiles" path="error.page"></forward> 
  </global-forwards>  

No meu caso uso quase sempre tiles, mas pode ser qualquer página. Ah, bom dizer que no caso do tiles o redirectPage também pode ser uma entrada do tiles-defs.xml

As exceções inesperadas são capturadas pelo UnexpectedExceptionHandler e o usuário é sempre direcionado para a página de erro default. Eu nunca trato nenhum tipo de exceção inesperada, nunca.

Olá!

Não curto muito checked exceptions, acho que deviam ser sempre uncheckeds (como em C#). No final das contas eu sempre encapsulo mesmo numa exceção unchecked, exatamente onde ela deveria ser capturada originalmente. Em outras palavras, eu nunca utilizo um “throws” na assinatura do método, pois acho chato ter que ficar tratando exceções nas camadas superiores. No catch eu já dou um “throw” numa exceção unchecked, o que elimina o tratamento acima, deixo no caso o container (ou mesmo alguma classe de controle) identificar e redirecionar a página. O esquema de exceções não esperadas é esse mesmo que o Feijão falou, log, redireciona pra página de erro genérica (“Erro interno do servidor”) e pronto.

O que da pra fazer com exceções de negócio é personalizar o erro passando parâmetros que serão exibidos em uma página, com um botão voltar (history.back()) ou (se você estiver trabalhando com ajax), devolver para um javascript (previamente preparado) um xml (ou json) de erro, dai o javascript pode dinamicamente (innerHTML, ou algo assim) montar uma div com css, exibir na tela, num campo escondido, sei la.

Abraços

Daniel,
A forma como trabalho não é muito diferente da sua.
Usamos Struts 1.x e JSF, em ambos temos um ponto central, chamado de handler pra exibir uma página de erro padrão e fazer log das exceções inesperadas - RuntimeException.
As exceções de negócio são tratadas sempre nas actions ou nos Managedbeans elas são subclasses de NegocioException que é subclasse de Exception, assim no throws dos métodos eu posso declarar só NegocioException ao invés de uma lista grande de exceções.
Quando estamos em uma transação e as exceções lançadas por algum método podem ser ignoradas colocamos em um try.catch separado, semelhante ao exemplo que você postou, geralmente feito no business object.
Eu prefiro tratar as exceções de negócio em camadas especificas, do que ficar criando novas exceções para encapsular outras exceções.

try{ fazAlgumaCoisa(); }catch(UmaException e){ throw new OutraException(e); }

Daniel,

pra não me repetir, minhas opiniões sobre exceptions do Java estão escritas nesse tópico - http://www.guj.com.br/posts/list/116363.java

[]s

Concordo plenamente com Emerson, checked exceptions nao tem serventia pra mim.

Mas como ele mesmo disse, há muita gente, inclusive entre os grandes autores que trabalham com java, que discordam de mim.