Exception handler no vraptor3

eu reabri a issue pra gente dar uma olhada nela

lucascs, por enquanto está funcionando de forma quase satisfatória., pois detectei um problema nele.

Se você notar bem eu “seto” os dados nesse map como String mesmo, já que vem do HttpServletRequest.getParameter. Enfim, o EL funciona direitinho com todos tipos, seja ele um boolean, double… porém apenas com Date ele não funciona, pois eu adiciono no Map como String. Isso é uma coisa que preciso rever.

java.lang.IllegalArgumentException: Cannot convert 11/11/1111 of type class java.lang.String to class java.util.Date at com.sun.el.lang.ELSupport.coerceToType(ELSupport.java:381) at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:209) at org.apache.jasper.runtime.ExpressionEvaluatorImpl.evaluate(ExpressionEvaluatorImpl.java:137) at org.codehaus.waffle.taglib.form.DateTag.evaluateDate(DateTag.java:120) at org.codehaus.waffle.taglib.form.DateTag.start(DateTag.java:105) at org.codehaus.waffle.taglib.form.FormElement.doStartTag(FormElement.java:73) at org.codehaus.waffle.taglib.form.DateTag.doStartTag(DateTag.java:90)

Um potencial erro, que na verdade não sei se vai ou não acontecer, é quando eu já possuo o bean como atributo de request. Note que eu trato tudo como se fosse um Map com as propriedades já que o JSTL trabalha bem assim. Nesse caso se eu tiver já um atributo com esse nome e for do tipo do objeto eu vou ganhar um ClassCastException. Vou fazer mais uns testes nisso.

Abraços

é… a gente faz quase isso… mas passa um RequestOutjectMap com o toString sobrescrito… mais feio :stuck_out_tongue: sua implementação parece bem melhor =)

Lucas, estive analisando que esse objeto só é usado para as validações e no meu caso para o exception-handler. O problema que eu tenho seria do parse de valores como date e afins, que as EL não conseguem trabalhar.

No meu caso eu faço um forward para um outro logic, ou seja, penso que eu poderia reinjetar os parametros da requisição de novo no método via ONGL, como é feito quando eu acesso um URL qualquer. Isso resolveria nosso problema, pois aí ele chamaria os converters que estão registrados e tudo ficaria bem.

Porém teriamos um problema caso alguém for fazer um redirect direto para um JSP e não para um logic. Aí nesse caso essa solução com o Map faz mais sentido.

O que você acha?

Então… mesmo que seja um forward pra logica, as lógicas que vão pra formulários de adição geralmente não recebem parâmetro nenhum, ou pelo menos não o objeto que estamos tentando criar com o formulário…

infelizmente o OGNL não vai ajudar, acho que vai ter que ser o mapa ou algo do tipo mesmo =(

Olá a todos !

Estive tendo o mesmo problema, e depois de muito penar, finalmente entendi qual o problema no meu caso. No meu JSP eu tenho:

[code]<form action="<c:url value="/notasFiscaisEntrada/gravar"/>" method=“post”>

Data Entrada:
<input type=“text” id=“dataEntrada” name=“notaFiscalEntrada.dataEntrada"
value=”<fmt:formatDate pattern=“dd/MM/yyyy” value="${notaFiscalEntrada.dataEntrada}" />"/>

[/code]

Quando executo pela primeira vez não há problema, já que notaFiscalEntrada.dataEntrada é do tipo java.util.Date.

Porem quando retorno a esta página após um erro, ela quebra com o erro

Bem, o que esta acontecendo ? No vRaptor (3.1.0) temos o DefaultValidator:

public class DefaultValidator implements Validator { ... public <T extends View> T onErrorUse(Class<T> view) { if (!hasErrors()) { return new MockResult().use(view); //ignore anything, no errors occurred } result.include("errors", errors); outjector.outjectRequestMap(); return viewsFactory.instanceFor(view, errors); } ... }

Na chamada outjector.outjectRequestMap() todos os campos são transformador em String. E a tag fmt:formatDate necessita de um um value do tipo java.util.Date.

Como resolver ? No meu caso, criei uma taglib de formatação que pode aceitar tanto String quanto Date

<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"> <tlib-version>1.0</tlib-version> <short-name>xxx</short-name> <uri>/WEB-INF/tlds/xxxTagLibrary</uri> <tag> <name>formatDate</name> <tag-class>com.xxx.ui.util.tag.FormatDateTagHandler</tag-class> <body-content>empty</body-content> <attribute> <name>pattern</name> <rtexprvalue>true</rtexprvalue> <type>java.lang.String</type> </attribute> <attribute> <name>value</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>Object</type> </attribute> </tag> </taglib>

e a classe:

[code]public class FormatDateTagHandler extends SimpleTagSupport {

private String pattern;
private Object value;

@Override
public void doTag() throws JspException {
    JspWriter out = getJspContext().getOut();

    try {
        JspFragment f = getJspBody();
        if (f != null) {
            f.invoke(out);
        }

        if (value == null) {
            out.print("");
        } else if (value instanceof Date) {
            out.print(getDateFormat().format((Date) value));
        } else {
           // aqui tratamos na segunda chamada depois de um erro, quando a campo esta como String
            out.print(value);
        }
    } catch (java.io.IOException ex) {
        throw new JspException("Error in FormatDateTagHandler tag", ex);
    }

}

private DateFormat getDateFormat() {
    if (pattern == null) {
        return new SimpleDateFormat("dd/MM/yyyy");
    } else {
        return new SimpleDateFormat(pattern);
    }
}

public void setPattern(String pattern) {
    this.pattern = pattern;
}

public void setValue(Object value) {
    this.value = value;
}

}[/code]

o JSP ficou quase o mesmo:

<%@ taglib prefix="xxx" uri="/WEB-INF/tlds/xxxTagLibrary" %> ... <label for="dataEntrada">Data Entrada:</label> <input type="text" id="dataEntrada" name="notaFiscalEntrada.dataEntrada" value="<xxx:formatDate pattern="dd/MM/yyyy" value="${notaFiscalEntrada.dataEntrada}" />"/><br />

e pronto, não tive mais problemas. Ainda precisa de alguns ajustes finos, mas a idéia geral é esta.

Espero que ajude !

Felipe T. Farias

[quote=garcia-jj]… lança uma exception de negócio, estou com problemas ao buscar os dados na tela.

Nesse caso no controller roda tudo bem. …[/quote](Seguindo o conselho do Garcia…)
VRaptor forks, contribuidores e entusiastas,
Resumo da Opera: Qual é a abordagem + profissional para propagar tanto as Mensagens de [color=red]Erro de Negócio, [/color]como as Mensagens de [color=darkblue]Sucesso[/color] até a Visão :?: ?!
Obs.: qq contribuição é de suma importância! :!:

Derlon, a abordagem mais profissional eu não sei bem qual seria, mas vou te explicar como eu uso. Obviamente vou resumir aqui porque já falei muito sobre isso, e se você der uma olhada no post http://guj.com.br/posts/list/30/136307.java#789504 vai entender melhor como eu uso. Não sei se é o mais correto, porém foi o mais prático que achei, já que todos meus projetos são muito grandes (em quantidade de classes mesmo).

Basicamente tenho um exception handler que quando der um erro ele volta para alguma tela que eu disser para ele ir. Sempre que der um erro, ele tenta pegar a mensagem da root exception e retorna para tal dela que eu especifiquei exibindo a mensagem. Basicamente algo como abaixo:

[code] public void list(Paging paging) {
result.onExceptionUse(UserSpaceController.class).dashboard();

    result.include("branchList", branchService.find(paging));
}

public void edit(Long id) {
    result.onExceptionUse(getClass()).list(null);

    if (id != null) {
        result.include("branch", branchService.findById(id));
    }
}

[/code]

Nesse caso se você entrar no método list e der algum erro qualquer e que você não trate via try and catch o exception handler irá atuar redirecionando para a dashboard. E caso eu entrar no edit e der um erro, o exception handler irá fazer um forward para o list.

Enfim, essa é a forma que eu uso. Como o vraptor ainda não tem esse exception handler você pode fazer o processo mais manual ou então usar esse cara na tua aplicação. Caso você queira fazer manual, usando try and catch, você pode fazer algo conforme meu código abaixo. No frigir dos ovos o resultado é o mesmo, a vantagem do exception handler é você não precisar fazer try and catch, validator.add e validator.onErrorUse a cada método do controller.

try { result.include("branchList", branchService.find(paging)); } catch (Exception e) { validator.add(new ValidationMessage(e.getMessage(), "error")); validator.onErrorUse(Results.logic()).forwardTo(UserSpaceController.class).dashboard(); }

Se você não entendeu algo, me avise.

Abraços

[quote=Paulo Silveira]… Sobre essa parte de tratamento de excecoes, nao estou vendo ainda a melhor solucao. Acho melhor esperarmos, ja que da pra contonar isso atraves de interceptadores ou mesmo do mecanismo padrao do web.xml …[/quote] Bem, então, por enquanto…

[quote=garcia-jj]…
Basicamente tenho um exception handler que quando der um erro ele volta para alguma tela que eu disser para ele ir. Sempre que der um erro, ele tenta pegar a mensagem da root exception e retorna para tal dela que eu especifiquei exibindo a mensagem…
[/quote]O “try and catch o exception handler”, na verdade, não TÃO [color=red]Harded[/color]-Coded, mas a sua abordagem permite 1 redução muito boa de código!!! :thumbup: Só ficou 1 pouco místico p/ mim: como é q faz p/s MSGsErro chegarem na View??! :?:

[quote=garcia-jj] ... validator.add(new ValidationMessage(e.getMessage(), "error")); ...
[/quote] só num achei muito bacana misturar ‘Falhas de Validação’ com Mensagen (de Erro) de Negócio (ou ainda de Infra).
Mas, em termos gerais (p/ Apps com 1 n° considerável de UserCases) ficou muito elegante sua abordágem!!8) Valews mesmo!

[quote=derlon][quote=garcia-jj] ... validator.add(new ValidationMessage(e.getMessage(), "error")); ...
[/quote] só num achei muito bacana misturar ‘Falhas de Validação’ com Mensagen (de Erro) de Negócio (ou ainda de Infra).
Mas, em termos gerais (p/ Apps com 1 n° considerável de UserCases) ficou muito elegante sua abordágem!!8) Valews mesmo! [/quote]

Pois é, o código que coloquei aqui era uma sugestão do inicio de tudo. Como estava com pouco tempo coloquei isso para mexer logo mais que a idéia evoluísse. Você tem uma idéia melhor? Usei o “validator.addMessage” porque o vraptor não oferece nenhum outro local para jogar mensagens automaticas para a tela sem usar o result.include. Mas acho que a partir dessa minha idéia inicial podemos evoluir, o que você acha?

Essa foi a principal motivação, já que o código se torna bem cansativo com tantos try and catch.

http://guj.com.br/posts/list/204039.java

[quote=garcia-jj]… Você tem uma idéia melhor? Usei o “validator.addMessage” porque o vraptor não oferece nenhum outro local para jogar mensagens automaticas para a tela sem usar o result.include. Mas acho que a partir dessa minha idéia inicial podemos evoluir, o que você acha?..[/quote] Bem, na verdade acabei seguindo a dica do Lucas p/ usar o Padrão do V|Raptor ‘result.include(“message”, “…”);’. E, somado a isto, adotei a Convenção’result.include(“msgFalha”, “…”);’ p/ Erros de Negócio/Infra e ‘result.include(“msgSucesso”, “…”);’ obvamente p/ MSGs de Sucesso!
E até modifiquei o LogOn, q anteriormente tinha colocado a lógica de Autenticação bem dentro da Validação,: mudando p/ a minha famosa Classe ‘SessaoDoUsuario’. Inicialmente, cheguei pensar em incluir a mensagem de “‘Nome de Acesso’ e/ou ‘Senha’ Inválidos” via “msgAutentic”, mas dpois eu pensei: não, tenho q ser simples; então passei ela pela “msgFalha”, q já fazia parte da minha Convenção!! :mrgreen:

Semana passada passei pro Lucas aquela alteração que tinhamos sugerido do Bean Validator, e o Lucas já publicou no master no Vraptor. POR ENQUANTO as validações estão caindo no objeto Validator.add(Message) sendo que a chave é o nome do campo.

public void validate(Object object) { if (beanValidators == null || beanValidators.isEmpty()) { logger.warn("has no validators registered"); } else { for (BeanValidator validator : beanValidators) { addAll(validator.validate(object)); } } }

Em uma versão de testes minhas coloquei diferente. Ao invés de retornar para o mesmo saco das mensagens de validação eu retorno em outro objeto para a tela, com o nome beanmessages. Assim sabe o que eu posso fazer? Quando imprime a tela ele mostra a mensagem de erro ao lado do campo.

public void validate(Object object) { List<Message> beanMessages = new LinkedList<Message>(); if (beanValidators == null || beanValidators.isEmpty()) { logger.warn("has no validators registered"); } else { for (BeanValidator validator : beanValidators) { beanMessages.add(validator.validate(object)); } result.include("beanMessages", beanMessages); super.add(new ValidationMessgage("Ocorreram erros de validação.", "error")); } }

Enfim, é uma abordagem diferente. Acho que você está correto de separar todo, no caso mensagens de validação em um lado, erros do sistema em outro e mensagens de confirmação em outro.

Porém optei nesse meu teste separar as mensagens do beanValidator para poder pintar os campos com erro e exibir uma mensagem ao lado.

Lucas, como vocês estão fazendo nos projetos da Caelum com Vraptor para exibir mensagens, digamos, mais inteligentes para esses casos (confirmação, erros de infra, erros validação, etc)?

[quote=garcia-jj]
Lucas, como vocês estão fazendo nos projetos da Caelum com Vraptor para exibir mensagens, digamos, mais inteligentes para esses casos (confirmação, erros de infra, erros validação, etc)?[/quote]
Depende dos casos…
a gente geralmente não usa ejbs, e previne erros de banco de dados com validações… as validações a gente sempre mostra em cima da página, todas juntas num lugar só…
as validações que ficam do lado do campo geralmente são feitas com javascript, pra não chegar a fazer requisições “inválidas”