quinta-feira, 28 de novembro de 2013

Logar e Relançar - Antipattern de exceção


Quando se trata uma exceção, o correto é:
  • Ou envolver a exceção em uma nova exceção, passando mais informações;
  • Ou tratá-la, logando a exceção e exibindo ao usuário, quando for o caso. 
Mas nunca os dois (relançar e logar). Considere o Servlet abaixo. Neste exemplo, este é o nível mais alto da operação, então o papel do tratamento de exceção é informar ao usuário (logar e enviar uma mensagem para o Response).

public class ClienteServlet ...
    ...
    public void gravarCliente(ServletRequest req, ServletResponse res) {
        try {
            DAOCliente.gravar(criaClienteVo(req));
        } catch (RuntimeException e) {
            log.error("Erro ao salvar cliente", e);
            res.setAttribute("error", "Erro. Contate suporte.");
        } catch (BusinessException e) {
            log.error("Erro de negócio ao salvar cliente", e);
            res.setAttribute("error", "Erro ao salvar cliente " 
                    + e.getMessage());
        }
    }
}    

Exemplo de Antipattern 1

No caso de um código que loga e relança, como o código abaixo, teremos a saída mostrada na sequência. Note que a exceção é mostrada 2 vezes, mas de maneiras diferentes. 

public class DAOCliente ...
    ...
    public void gravar(ClienteVo cliente) throws BusinessException {
        try {
            ...
            ... COMANDO INSERT
            ...
        } catch (SQLException e) {
            log.error("Erro ao salvar cliente", e);
            throw new BusinessException("Erro no banco", e);
        }
    }
}

13:40:02 [ERROR] Erro ao salvar cliente
java.sql.SQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)
13:40:02 [ERROR] Erro de negócio ao salvar cliente
BusinessException: Erro no banco
    at DAOCliente.gravar(DAOCliente.java:63)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)
Caused by: java.sql.SQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    ... 2 more

Exemplo de Antipattern 2

Ou ainda como o código abaixo, exatamente a mesma exceção é logada 2 vezes.

public class DAOCliente ...
    ...
    public void gravar(ClienteVo cliente) {
        try {
            ...
            ... COMANDO INSERT
            ...
        } catch (SQLException e) {
            log.error("Erro ao salvar cliente, e);
            throw new e;
        }
    }
}

13:40:02 [ERROR] Erro ao salvar cliente
java.sqlSQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)
13:40:02 [ERROR] Erro de negócio ao salvar cliente
java.sql.SQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)

Código esperado

O código deve OU relançar uma exceção (caso seja um nível intermediário ou inferior) OU logar e informar ao usuário (caso seja o nível superior). No exemplo abaixo, como não é a classe de nível superior, ela deve apenas relançar a exceção. Com isso, o log fica com apenas uma única ocorrência.

public class DAOCliente ...
    ...
    public void gravar(ClienteVo cliente) {
        try {
            ...
            ... COMANDO INSERT
            ...
        } catch (SQLException e) {
            throw new BusinessException("Erro no banco", e);
        }
    }
}

13:40:02 [ERROR] Erro de negócio ao salvar cliente
BusinessException: Erro no banco
    at DAOCliente.gravar(DAOCliente.java:63)
    at ClienteServlet.gravarCliente(ClienteServlet.java:85)
Caused by: java.sql.SQLException: ORA-00001: unique constraint ...
    at oracle.driver...SomeOracleDriverClass()
    at DAOCliente.gravar(DAOCliente.java:50)
    ... 2 more

Nenhum comentário:

Postar um comentário