VRaptor 3 - tratamento de erro nas aplicações

82 respostas
galmeida

Recentemente conversei com o Guilherme Silveira a respeito de tratamento de erros em aplicações escritas para o VRaptor.
Minha idéia era escrever um Interceptor que nao deixasse vazar nenhuma exception para o usuário, até aí muito simples, basta fazer algo +/- assim:

public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance) {
        String message = null;
        boolean redirect = false;
        try {
            stack.next(method, resourceInstance);
        } catch (Throwable e) {
           ....
            this.result.include("error", messageAmigavel);
            this.result.use(Results.page()).forward("/jsp/error.jsp");
        }
    }

até aí sem problemas.

Agora vamos melhorar um pouco para o usuário, no lugar de jogar o cara para uma página genérica de erro eu quero “manter o cara na pagina que ele está” e adicionar uma msg de erro, eu poderia fazer algo como this.result.use(Results.page()).redirect(requset.getHeader(“Referer”)), mas nesse caso como o redirecionamento é client-side, a mensagem de erro nao mais estará dísponível, nem os outros valores que eventualmente se queira “manter”.

Depois de comentar isso com o Guilherme a resposta dele foi: “o lucas e o kung tinham uma conversa sobre isso, mas com certeza posta!”

Como vcs estão fazendo isso por aí? Alguma sugestão?

[]s
galmeida

82 Respostas

galmeida

aparentemente um this.result.use(Results.page()).forward() resolve parte do problema.

Lucas_Cavalcanti

sim,…

uma outra saída é adicionar um erro de validacao:

validator.add(new ValidationMessage());
validator.validate();

daí vai pra página de erro padrão da validação…

por enquanto o validator está obrigando a especificar qual é a página de validação…
mas provavelmente a gente vá tentar redirecionar pro referer com a mensagem de validação, mesmo…

wariows

lucascs:
sim,…

uma outra saída é adicionar um erro de validacao:

validator.add(new ValidationMessage());
validator.validate();

daí vai pra página de erro padrão da validação…

por enquanto o validator está obrigando a especificar qual é a página de validação…
mas provavelmente a gente vá tentar redirecionar pro referer com a mensagem de validação, mesmo…

Falar nisso, fiz:

this.validator.onError().goTo(MyController.class).myLogic();

e mais a frente:

this.validator.add(new ValidationMessage("erro","message"));

mas ele não me redirecionou pra MyController.myLogic, me redirecionou para o jsp padrão: /WEB-INF/jsp/my/myLogic.jsp

Preciso fazer alguma coisa a mais pra fazer isso funcionar?

[]'s

Lucas_Cavalcanti

o validator executa a lógica que você pediu (execução de método java mesmo) e redireciona pra view dela…
teoricamente você estaria redirecionando pra página de formulário que você já está…

o que você queria fazer? mudar a url do browser e executar a lógica tudo de novo?

wariows

lucascs:
o validator executa a lógica que você pediu (execução de método java mesmo) e redireciona pra view dela…
teoricamente você estaria redirecionando pra página de formulário que você já está…

o que você queria fazer? mudar a url do browser e executar a lógica tudo de novo?

Estou redirecionando para outra lógica, mas percebi o erro, estava faltando o validator.validate(), mas não tem nada sobre isso na doc. Adiciona lá :wink:

Lucas_Cavalcanti

coloquei na documentação, obrigado =)

Paulo_Silveira

lucas, sera que nao era melhor dar um WARN caso um validator injetado nao tivesse seu .validate() invocado ate o final da execucao?

galmeida

acho uma ótima idéia

galmeida

Hoje recebi uma mensagem privada a respeito desse tópico, e para enriquecer a discussão e contar com a ajuda dos mais experientes no vraptor estou trazendo a discussão p/ cá

monike:
OIe, boa tarde!

Vi uma dúvida no forum e tb gostaria que pudesse me explicar como fez.É a primeira vez que uso VRAPTOR.
Como faço para validar caso ocorra o erro?
Eu to usando hibernate…como mostrar na página “atual” pro usuário o erro , como direcionar o erro para esta página?
Tenho que mexer no meu validateAdd, no meu metodo void add, criar um interceptor
Se vc tiver um exemplo ai te agradeceria muito!!!

obrigada,

Monike.

Olá Monike,

se o seu problema for a validação aí vão algumas informações útis p/ vc:

  • no site do vraptor tem a documentação de como fazer validação: http://vraptor.caelum.com.br/documentacao/validacao/
  • aparentemente vc precisa chamar [color=darkblue]validator.validate()[/color] para iniciar a validação, a documentação não menciona isso ([color=darkred]isso está certo?[/color])
  • usando os validators o método documentado para definir a página que será exibida quando houver erro de validação é: [color=darkblue]validator.onError().goTo(SeuController.class).metodo()[/color] (ver na documentação)

mas pelo que vc escreveu parece que o que vc quer fazer é “pegar” exceções dentro do controller, criar a mensagem de erro e exibi-la na página de “origem” (referer) do usuário, correto? Esse é exatamente o problema que eu tentava resolver nesse tópico. Infelizmente ainda não consegui uma solução boa, provavelmente voltarei a trabalhar nisso amanhã. (algum dos experts consegue nos ajudar?)

[]s
galmeida

wariows

Sim, está certo. Mas você usar o modo fluente não precisa chamar o [color=darkblue]validator.validate()[/color]

Se você sempre utilizar uma action/method para renderizar a pagina, então vc pode redirecionar pra tela de formulario do controller com o [color=darkblue]validator.onError().goTo(SeuController.class).metodo()[/color]

Se não me engano, uma das próximas issues é redirecionar para o referer caso dê erro de validação (lucas?)
Aproveitando a deixa, to querendo fazer um ‘forward after login’, alguém tem alguma ideia sobre isso?

N

Olá, criei um interceptor para o tratamento de exceções, pois foi a forma que achei melhor, fiz do seguinte modo:

public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance) throws InterceptionException { try{ stack.next(method, resourceInstance); }catch(Exception exception){ this.result.include("error", exception.getMessage()); this.result.use(Results.page()).forward(); } }

E possuo um método que lança uma exception do seguinte modo

throw new Exception("mensagem de erro");

Porém, em vez do vraptor receber uma exceção no método itercept com a mensagem “mensagem de erro”, ele está recebendo uma mensagem “java.lang.Exception: mensagem de erro”, acho que em algum ponto ela está sendo renomeada entre o controller e o interceptor, alguém teve um problema parecido?

Obrigado.

Paulo_Silveira

ola nicko

voce nao esta chamando o toString da exception ou concatenando a referência com alguma string? por default, o toString de um exception mostra o noma da classe da exception seguido pelo valor do getMessage().

onde isto esta aparecendo? no jsp na hora que voce faz ${error}?

sergiolopes

só pra registrar a todos os interessados nas validacoes: o ultimo beta do vraptor alterou o esquema de validacao.
agora o validate nao existe mais e vc chama o goTo depois de ter feito o checking (ou os add(Message)). o goTo na pratica chama o validate antigo (que agora é private)

tambem implementamos um esquema de validacao nos setters dos seus beans.
quem nao gosta de espalhar codigo de validacao no controller (eu por exemplo prefiro validar no setter do bean) pode usar isso agora q o vraptor valida.

N

Paulo Silveira:
ola nicko

voce nao esta chamando o toString da exception ou concatenando a referência com alguma string? por default, o toString de um exception mostra o noma da classe da exception seguido pelo valor do getMessage().

onde isto esta aparecendo? no jsp na hora que voce faz ${error}?

Não estou usando o toString, estou usando como mostrei ali, dando inspect no objeto quando ele está no interceptor o valor já está lá com o “java.lang.Exception:”, no jsp mostra o “java.lang.Exception:”, porém é por que ele já está sendo mandado assim. Quando crio a exceção a mensagem está correta, quando cai no interceptor já está com o prefixo. Estou usando exatamente como mostrei, alguma idéia?

Obrigado.

Paulo_Silveira

oi nicko!

o inspect dos debuggers vao chamar o toString do objeto! por isso esta aparecendo aquela mensagem! se voce deixar rodar o programae e usar ${erro} no JSP, vai vir so a mensagem de erro.

lembre-se que nao é muito bom usar a classe Exception diretamente, e sim uma das suas filhas, pra sempre trabalhar com algo mais especifico.

N

Paulo Silveira:
oi nicko!

o inspect dos debuggers vao chamar o toString do objeto! por isso esta aparecendo aquela mensagem! se voce deixar rodar o programae e usar ${erro} no JSP, vai vir so a mensagem de erro.

lembre-se que nao é muito bom usar a classe Exception diretamente, e sim uma das suas filhas, pra sempre trabalhar com algo mais especifico.

Olá, desculpe, acho que não me expressei direito, dou um inspect no “exception.getMessage()”, que é a mensagem, se desse inspect no objeto ele mostraria o nome da classe como prefixo, que não é o problema. No meu jsp tenho:

<fmt:message key="${error}"/> e ele exibe “???java.lang.Exception: mensagem de erro???”.

Esse era um teste, pretendo usar uma exception precisa para manipular os erros que espero. Novamente, muito obrigado pela ajuda.

wariows

Te certifica de sempre logar a stack trace da exception, senão a complexidade vai crescendo, dai um erro acontece e esse deu catch vai pegar tudo e engolir a stack trace…

G

Esses controles de erro do vraptor3 funcionam com exceptions lançadas pela minha aplicação (business exceptions por exemplo) ou apenas trata erros de validação?

wariows

Não funcionam com business exception (embora vc possa fazer uma gambiarra e usar os erros de validação como erros de aplicação, mas não é aconselhável). Eu estou implementando um handler pra business exceptions com AOP, assim que tiver pronto posto aqui e mando pra galera do vraptor dar uma olhada.

[]'s

sergiolopes

Se voce tiver validacoes no setter do seu modelo que sera chamado pelo VRaptor, você pode lançar excecoes de negocio dentro do setter e eles serao considerados erros de validacao (o Validator entra em acao). Pra isso, basta que a sua exception de negocio seja anotada com @ValidateException.

G

Ségio, na verdade penso que o vraptor cuidaria disso, hehe. Algo semelhante ao que ocorria com o struts, que tinha um bom exception handler.

Eu achei que nesse caso eu fazendo isso no meu controller:

@Post @Path("/security/user/") public void store(UserDTO user) { validator.onErrorUse(Results.page()).of(getClass()).edit(user.getId()); userService.store(user); }

Quando no meu EJB desse um erro, por exemplo new MyBusinessException(“A data do aniversário está errada”), o vraptor iria me direcionar para o método edit. Por enquanto eu estou usando o pseudo exception-handler do web.xml direcionando a um servlet que então vê o referer e devolve para ele. É uma boa solução até, mas penso que seria interessante ficar dentro do vraptor, assim posso, como no caso do validator, direcionar de forma mais elegante como com o Result.use().

Aliás, aproveitando, notei que eu devo primeiro adicionar as mensagens de erro, e depois chamar o validator.onErrorUse(). Caso eu fazer o validator.onErrorUse e depois adicionar as mensagens via validator.add() ele me lança a exception:

br.com.caelum.vraptor.InterceptionException: There are validation errors and you forgot to specify where to go. Please add in your method something like: validator.onErrorUse(page()).of(AnyController.class).anyMethod(); or any view that you like. at br.com.caelum.vraptor.interceptor.ExecuteMethodInterceptor.intercept(ExecuteMethodInterceptor.java:64) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71) at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:93) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71) at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:54) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:51) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71) at br.com.caelum.vraptor.interceptor.multipart.MultipartInterceptor.intercept(MultipartInterceptor.java:58) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)

G

Estava lendo agora a documentação sobre validações em http://vraptor.caelum.com.br/documentacao/validacao/, mas não achei como imprimir isso no jsp. Nesse caso é exportada em algum atributo no request? Ou preciso de algum tratamento especial via interceptor?

wariows

Você precisa adicionar as mensagens antes e por ultimo (para indicar o fim da validação) chamar o onErrorUse…
As mensagens são adicionadas no result com o identificador ‘errors’ no jsp basta fazer:

<c:forEach var="error" items="errors"> ${error.category} ${error.message} </c:forEach>

G

Sérgio, me dê uma sugestão… minha aplicação é distribuida, sendo o módulo EJB remoto. Como faço para anotar minhas service-exceptions com @ValidateException se o módulo EJB não conhece a camada web?

Eu teria que fazer alguma wapper dela chegando na camada web?

sergiolopes

na minha resposta eu estava imaginando as suas classes de modelo sendo populadas nos argumentos do metodo do vraptor.
no caso de vc invocar um EJB dentro do metodo, acho que o melhor eh fazer um try/catch mesmo e usar o Validator

G

Sergio Lopes:
na minha resposta eu estava imaginando as suas classes de modelo sendo populadas nos argumentos do metodo do vraptor.
no caso de vc invocar um EJB dentro do metodo, acho que o melhor eh fazer um try/catch mesmo e usar o Validator

Foi o que eu fiz por enquanto. Vou repensar em uma forma de não precisar ficar no try-and-catch.

Abraços

G

Fiz um interceptor bem simples por enquanto para testes de um futuro exception-handler.

try {
     stack.next(method, resourceInstance);
} catch (Exception e) {
    try {
        ValidationMessage m = new ValidationMessage(ExceptionUtils.getRootCauseMessage(e), "error");
        validator.add(m);

        Class<UserController> whoCalls = (Class<UserController>) method.getMethod().getDeclaringClass();
        validator.onErrorUse(Results.logic()).forwardTo(whoCalls).list();
    } catch (Exception ex) {
        throw new InterceptionException(ex);
    }
}

Minha duvida agora é... na linha 8 e 9 eu fiz uma gambiarra para indentificar o controller e redirecionar para um método qualquer. Pensei em duas hipóteses: 1) quando der erro voltar para o referer ou 2) redirecionar para uma página especificada no método que está sendo executando quando deu erro.

No caso 1 o controller é facil saber (via method.getMethod().getDeclaredClass()), porém como eu posso descobrir para onde redirecionar? Por exemplo, pelo referer "/user/edit" eu descobrir a rota (qual método do controller chamar) ou algo assim. Eu tenho acesso a tabela de rotas via injeção no construtor?

Abraços

D

Estou exibindo as mensages de erro utilizando o seguinte loop no JSP:

<c:forEach items="${errors}" var="error">
	<li class="${error.category}">${error.message}</li>
</c:forEach>

Mas as mensagens aparecem com “???” antes e depois da mensagem. Por ex.: “usuário inválido” aparece no JSP como “???usuário inválido???”.

Alguma luz?

T+

D

Fiz um debug rápido e percebi que o problema é que ele usa a mensagem como chave para um resource-bundle. Não existe uma forma mais prática, utilizando a mensagem de erro diretamente??? Aliás, nem na documentação nem na matéria da MundoJava #38 é mencionada a necessidade de um ResourceBundle, e eu particularmente prefiro evitar ter que fazer esse tipo de amarração em um arquivo externo.

Observando um pouco mais, tenho a impressão que se utilizar a validação clássica isso não ocorre (há controle na criação do ValidationMessage), enquanto na validação fluente ele sempre tenta utilizar o resource bundle. No caso vejo duas alternativas:

  • ativar o uso do ResourceBundle ou não no Validations via um parâmetro no web.xml
  • adicionar uma variante do método that() que receba um ValidationMessage. Nesse caso, algo que ache o que poderia ser legal era adicionar um método add(String category, String message) no Validator, com o mesmo comportamento do that() que recebe categoria e mensagem. Talvez alterar o nome do parâmetro de “message” para “resourceKey” criaria algo mais fácil de compreender (que será usado um resourceBundle).

T+

G

Hmm, muito estranho. Não sei se é porque no meu caso todas as validações ficam no módulo EJB, mas as mensagens para mim estão vindo corretamente. Como você está usando? Pode passar o pedaço do JSP e do controller?

No meu caso eu faço:

if (result.getErrorMethod() == null) {
                throw new RuntimeException(ExceptionUtils.getRootCause(e));
            }

            final List<Message> messages = Lists.newLinkedList();
            Throwable cause = ExceptionUtils.getRootCause(e);

            if (cause instanceof InvalidStateException) {
                for (InvalidValue v : ((InvalidStateException) cause).getInvalidValues())
                    messages.add(new ValidationMessage(v.toString(), "error"));
            } else {
                messages.add(new ValidationMessage(ExceptionUtils.getMessage(cause), "error"));
            }
Lucas_Cavalcanti

duron, abre uma issue sobre isso que vc quer fazer por favor (se possível em ingles)?

por padrão a forma clássica não é internacionalizada e a forma fluente é. Você pode colocar sua mensagem de erro no campo category, que não é internacionalizado, se vc quiser.

[]'s

G

Havia no struts há algum tempo uma gambiarra que verificava se a mensagem existia no resource-bundle. Caso sim mostrava a mensagem, caso contrário exibia assim como ela era especificada. :smiley:

Lucas_Cavalcanti

Obrigado garcia-jj, vc me deu uma solução “boa” pra isso:

Crie a classe:

public class NoResourceBundle extends ResourceBundle {
	@Override
	public Enumeration<String> getKeys() {
		return Collections.enumeration(Arrays.<String>asList());
	}

	@Override
	protected Object handleGetObject(String key) {
		return key;
	}
}

e quando for usar a validação fluente:

validator.checking(new Validations(new NoResourceBundle()) {
    that(...);
});

isso deve funcionar… você pode até cachear essa instância do NoResourceBundle em algum lugar
e usar import estático pra ficar mais legível…

[]'s

rafaelunp

Amigo,

De acordo com o que li neste post, implementei um interceptor que deverá exibir a mesma pagina caso seja lancada alguma exceção. Assim sendo ficou:

@Intercepts
@RequestScoped
public class ExceptionInterceptor implements Interceptor{

	Logger log = Logger.getLogger(getClass());

	private final Result result;

	public ExceptionInterceptor(Result result) {
		this.result = result;
	}

	/*
     * Este interceptor deve interceptar todos os métodos.
     * method.getMethod() retorna o método java que está sendo executado
     * method.getResourceClass().getType() retorna a classe que está sendo executada
     */
	public boolean accepts(ResourceMethod method) {
		return true;
	}

	public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance) throws InterceptionException {
		try {

			log.debug("Antes");
			stack.next(method, resourceInstance);
			log.debug("Depois");

		} catch(Exception exception){
			log.debug("exception: "+exception.getMessage());
			try {
				this.result.include("error", exception.getMessage());
				this.result.use(Results.page()).forward();
			} catch (Exception e) {
				log.error("Erro no Interceptor: "+e.getMessage());
				e.printStackTrace();
			}
		}
	}

}

Sendo que quando é lancada a exceção no controller, por exemplo, e o interceptor "pega" essa exceção, ele está dando um nullpointer nesta linha:

this.result.use(Results.page()).forward();

Meu controller está assim:

@Resource
public class ClienteController {

	private final ClienteDAO dao;
	private final Result result;
	private final Validator validator;

	public ClienteController(ClienteDAO dao, Result result, Validator validator) {
		this.dao = dao;
		this.result = result;
		this.validator = validator;
	}
...
}

Estou fazendo certo?
Lembrando que a ideia é permanecer na página que originou o erro.

Abs,

G

Creio que não, hehe. Eu não sei o que faz o forward() vazio (preguiça de ler a documentação, hehe), mas creio que você precise especificar para onde você quer ir.

Caso você queira, assim como eu, voltar para a tela anterior, precisa elaborar umas coisinhas a mais. No meu caso eu alterei uma série de coisas com a ajuda do Lucas, fazendo assim um exception-handler. http://guj.com.br/posts/list/143727.java

Lucas e Paulo, o que vocês acham de colocar esse exception handler no core do vraptor3?

Lucas_Cavalcanti

Então… nunca é bom dar try…catch em Exception no Interceptor… Faz isso pra uma Exception mais específica…

o result.use(page()).forward() vai pra página padrão do método atual

o NullPointer está acontecendo no result, nessa linha?

this.result.use(Results.page()).forward();

ou em algo dentro da chamada forward? (não faz sentido dar exception nesse ponto…)

@garcia-jj acho que a gente pode pensar em algo do tipo sim, abre uma issue lá no github por favor?

sergiolopes

e lembrando que se vc quer apenas um redirect generico qdo alguma exception vazar, nada de maracutaias! use o error-page no web.xml :slight_smile:

rafaelunp

Lucas,

Acho que o null pointer está no forward. Debuguei e percebi que o result nao estã nulo (inclusive, na linha acima ele é utilizado sem problemas (this.result.incluide…).
Segue abaixo o log do erro:

SEVERE: Servlet.service() for servlet jsp threw exception java.lang.NullPointerException at br.com.caelum.vraptor.interceptor.OutjectResult.accepts(OutjectResult.java:47) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:45) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:62) at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91) at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:49) at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646) at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436) at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374) at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302) at br.com.caelum.vraptor.view.DefaultPageResult.forward(DefaultPageResult.java:72) at br.com.lojavirtual.controller.ExceptionInterceptor.intercept(ExceptionInterceptor.java:51) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.util.hibernate.HibernateTransactionInterceptor.intercept(HibernateTransactionInterceptor.java:45) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:46) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:80) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:62) at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91) at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:55) at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454) at java.lang.Thread.run(Thread.java:595) 03/12/2009 - 15:34:58 [br.com.lojavirtual.controller.ExceptionInterceptor] ERROR - Erro no Interceptor: null java.lang.NullPointerException at br.com.caelum.vraptor.interceptor.OutjectResult.accepts(OutjectResult.java:47) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:45) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:62) at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91) at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:49) at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646) at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436) at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374) at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302) at br.com.caelum.vraptor.view.DefaultPageResult.forward(DefaultPageResult.java:72) at br.com.lojavirtual.controller.ExceptionInterceptor.intercept(ExceptionInterceptor.java:51) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.util.hibernate.HibernateTransactionInterceptor.intercept(HibernateTransactionInterceptor.java:45) at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:46) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:80) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67) at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46) at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59) at br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:62) at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91) at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:55) at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454) at java.lang.Thread.run(Thread.java:595)

Lucas_Cavalcanti

bom… isso é um bug que foi corrigido já, mas não foi lançado ainda…

ele acontece pois a página padrão da sua lógica não existe… e o VRaptor se perdia com isso…

você pode atualizar a versão do repositório do VRaptor, esperar o próximo release, ou não fazer essa lógica que vc está fazendo…

ela é ruim pq nem sempre suas lógicas vão para a página padrão…

se dentro de uma lógica vc estiver fazendo:

result.use(...)....

o result.use(page()).forward() do seu interceptor não vai fazer mais sentido…

o melhor a fazer quando dar uma exception é ir para uma página fixa (usando o error-page como o Sergio disse), ou no máximo ir pro referer (que não é sempre garantido):

result.use(Results.referer()).forward();

http://vraptor.caelum.com.br/cookbook/usando-o-header-referer-para-fazer-redirecionamentos/

G

Lucas, o problema de usar referer é que se der um erro e eu ir pro referer funciona da primeira vez. Mas se der um erro de novo meu referer é a própria página de erro, assim você entra em loop infinito.

A solução do Sérgio é boa para erros que você não consegue mais recuperar a aplicação, exemplo, banco fora do ar e afins.

Mas quando dá erros de regra de negócio ou até mesmo o estouro de uma constraint, o que você faz? Exibir uma página padrão é muito ruim, pois você tem que fazer o usuário clicar em voltar e você ainda precisa cuidar para trazer os dados de novo. Em muitos casos history.back não serve. O mais ideal, na minha visão, é você voltar para alguma tela e exibir alguma mensagem que explique o que aconteceu.

Exemplo: estou na tela de edição do usuário, então quando eu vou salvar o objeto ocorre um erro de lock otimista. O que eu vou fazer? Volto para a tela de edição do usuário, exibo a mensagem “Versão do objeto inválida”. Assim ao mesmo tempo eu mostro a mensagem do erro e o usuário já está na própria tela de edição. Imagina que saco você mandar o usuário para uma página em branco dizendo “Versão do objeto inválida” e apenas isso? Aí ele tem que voltar por conta própria, além de você ter um processamento de tela e requisição a mais.

Obvio que você pode fazer try and catch para tratar os erros que vêm da regra de negócio, mas pense o quão trabalhoso seria fazer isso em uma aplicação do tamanho da que eu tenho, que são aproximadamente 350 controllers.

Achei que nesse ponto ficou um espaço vazio no vraptor. Por isso há algum tempo eu tenho corrido atrás de implementar isso, que consegui com a tua ajuda. FIz algo baseado no Struts 1x (que eu gostava muito), onde você diz antes de tudo para onde ir caso der um erro, e se der erro ele vai para tal tela.

Eu estarei muito ocupado até os dias do sun tech days, mas logo após isso vou passar um gist com meu exception handlers e te passo. Há uns detalhes que ainda estou revendo nesse exception handler, mas tudo culpa do waffle :slight_smile:

Lucas_Cavalcanti

issue criada =)

vamos pensar em algo legal, talvez usando interceptors e uma interface que vc pode implementar e funcionar automaticamente…

G

Lucas, o que eu fiz foi assim, baseado nas tuas sugestões em outro tópico:

Criei uma classe CustomResult que implementa Result. Essa classe possui um método chamado onErrorUse tem o mesmo comportamento que o Result.use.

public <T> T onErrorUse(Class<T> controller) {
	return proxifier.proxify(controller, new MethodInvocation<T>() {

		@Override
		public Object intercept(T proxy, Method method, Object[] args, SuperMethod superMethod) {
			CustomResult.this.method = method;
			CustomResult.this.args = args;
			return null;
		}
	});
}

Depois criei uma classe copiando o conteúdo de ExecuteMethodInterceptor, alterei o try and catch para apenas um catch com meu handler.

public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance)
	throws InterceptionException {
	try {
		Method reflectionMethod = method.getMethod();
		Object[] parameters = this.info.getParameters();
		Object result = reflectionMethod.invoke(resourceInstance, parameters);
		if (validator.hasErrors()) { // method should have thrown
			// ValidationError
			throw new InterceptionException("There are validation errors and you forgot to specify where to go.");
		}

		if (reflectionMethod.getReturnType().equals(Void.TYPE)) {
			// vraptor2 compatibility
			this.info.setResult("ok");
		} else {
			this.info.setResult(result);
		}
		stack.next(method, resourceInstance);
	} catch (Exception e) {
		// if onErrorUse is null, throws the exception
		if (result.getErrorMethod() == null) {
			throw new InterceptionException(ExceptionUtils.getRootCause(e));
		}

		// get the cause
		Throwable cause = ExceptionUtils.getRootCause(e);

		// if is validation exceptions
		if (cause instanceof InvalidStateException) {
			for (InvalidValue v : ((InvalidStateException) cause).getInvalidValues())
				validator.add(new ValidationMessage(v.toString(), "error"));
		} else {
			validator.add(new ValidationMessage(ExceptionUtils.getMessage(cause), "error"));
		}

		Method errorMethod = result.getErrorMethod();
		Object[] args = result.getErrorArgs();
		Object target = result.use(Results.logic()).forwardTo(errorMethod.getDeclaringClass());

		try {
			errorMethod.invoke(target, args);
		} catch (Exception e0) {
			throw new InterceptionException(e0);
		}
	}
}

Além disso coloquei no web.xml a página de erros para cuidar das exceptions que não consigo recuperar a aplicação:

<error-page>
	<exception-type>java.lang.Exception</exception-type>
	<location>/WEB-INF/jspx/error.jspx</location>
</error-page>

Para usar basta fazer algo como isso aqui:

public void store(Registration registration) {
	result.onErrorUse(getClass()).edit(registration.getId());

	registrationService.store(registration);
	result.use(Results.logic()).redirectTo(getClass()).list(Paging.first());
}

Há um problema nisso tudo. Quando eu volto para a tela de edição os dados não estão vindos preenchidos. Há um bug que já foi aberto para corrigir o OutjectResult. Eu fiz um componente bem gambiarrento para isso, mas tenho vergonha de postar o código, hahahahhaha.

That's all :D

G

Complementando, se vocês acharem interessante colocar esse exception handler no vraptor, que o onErrorUse fique no próprio Result, assim como temos o Result.use.

Uma outra idéia é colocar umas constantes no ValidationMessage para os valores mais usados como ValidationMessage.ERROR, ValidationMessage.WARNING e afins.

Meus dois pitacos :slight_smile:

Lucas_Cavalcanti

obrigado garcia…

vou dar uma olhada nesse código e ver se coloco algo bem parecido no core do vraptor =)

G

lucascs:
obrigado garcia…

vou dar uma olhada nesse código e ver se coloco algo bem parecido no core do vraptor =)

Lucas, essa semana estava revisando as issues lá no github para ver no que eu poderia ajudar. E notei que essa issue do exception handler e a do outjector estão abertas.

Fiquei preocupado em ver que novas funcionalidades foram colocadas como prioridade maior que correção de bugs. No caso do outjector é um bug que impacta até mesmo no validator, que é uma coisa tão trivial de usarmos. Já o exception handler é uma feature nova, embora eu pense que é uma feature muito importante.

Não sei como anda o tempo de vocês, mas se de alguma forma eu puder ajudar fico a disposição.

Lucas_Cavalcanti

Desculpa a demora em responder (estava de férias, sem internet)…

realmente nesse último mes estivemos sem tempo, e não conseguimos resolver muitas issues…

vou tentar resolver essas issues rapidamente nos proximos dias

[]'s

G

Lucas, fez bem, pois computadores não combinam com férias, haha.

Será que sai na 3.1? Se precisar de alguma coisa, me avise. Abraços

Lucas_Cavalcanti

então… ainda não sei mto bem como fazer isso…

colocar um onError no Result não parece mto legal… talvez a gente possa usar o próprio validator pra isso:

validator.onExceptionUse(...)

ou melhor:

validator.onException(SqlException.class).use(...);

mas o que seria melhor mesmo, é criar uma interface que cuida disso, que acha?

public interface ExceptionHandler<T extends Exception> {
       void handle(T exception, ResourceMethod method);
}

daí vc faz o que quiser dentro desse metodo…

G

Lucas, na verdade a idéia de usar o onErrorUse foi você que me deu :slight_smile: http://guj.com.br/posts/list/143727.java

Eu não usei nada no validator porque sinto que validação é uma coisa, erros da aplicação é outra. Você pode ter erros causados pela validação, porém pode acontecer erros que não são de validações.

Mas acho que sua idéia da interface é muito boa. Pelo que notei seria igual ao comportamento do Results, certo?

Lucas_Cavalcanti

Não… o comportamento seria algo do tipo:

se a exceção é SQLException e existe um ExceptionHandler então executa ele…

Se SQLException extends IOException e existir um ExceptionHandler executa ele

se não existir nenhum Handler da exceção, executa o comportamento padrão (que é o que está hoje)

G

:smiley: Mas assim fica mais do que bom, hehe.

G

lucascs, agora lembrei de uma coisa. Minha sugestão para o exception handler é que a cada ação você possa definir para onde ir. Um exemplo, se você está na página de salvar e dá um erro você vai para a tela de edição. Dessa forma que você sugeriu o handler fica por excessão. Só que sendo genérico assim dá para fazer isso?

Lucas_Cavalcanti

dá se ele receber o ResourceMethod… aí a partir do resourceMethod vc consegue saber…

mas o controle fica no handler, não no meio do código da lógica…

Lucas_Cavalcanti

mas é aquela coisa: excessões são coisas que não deveriam acontecer… não é bom vc ficar se prevenindo delas toda hora…

por exemplo vc que usa ejbs, qdo acontecer uma RemoteException (não sei se eh essa mesmo) vc pode tentar se recuperar… mas tem q ser meio genérico

G

Lucas, mas se você coloca o código do tratamento dentro do handler você não consegue ter algo especifico sobre para onde ir. Da forma proposta fica como no web.xml, ou seja, vai para uma tela genérica.

Há um post meu aqui explicando o quão trabalhoso fica ter essa visão de que exceptions não deveriam acontecer: http://guj.com.br/posts/list/30/136307.java#789504

Normalmente quando é lançado um erro de regra de negócio o ideal é voltar para a tela de edição e resolver o erro. Porém se eu estou em uma classe genérica não tenho como saber onde é a tela de edição, ou qualquer outra tela que eu queria voltar. Aí a gente cai no problema que falei de ter que ficar fazendo try and catch a cada chamada do EJB. Isso se torna improdutivo para os projetos que eu tenho usado Vraptor, já que um deles tem aproximadamente 350 controllers.

Por isso eu havia colocado o Result.onErrorUse, pois assim eu posso ter um controle melhor para onde vou de forma bem especifica. Exemplo de como o código fica bem simples:

public void edit(Long id) {
        if (id != null) {
            result.include("degree", coreService.loadDegree(id));
        }
    }

    public void store(DegreeDTO degree) {
        result.onErrorUse(getClass()).edit(degree.getId());

        coreService.storeDegree(degree);
        result.use(Results.logic()).redirectTo(getClass()).list(null);
    }

    public void delete(final Long id) {
        result.onErrorUse(getClass()).edit(id);

        coreService.deleteDegree(id);
        result.use(Results.logic()).redirectTo(getClass()).list(null);
    }

Senão eu teria de fazer isso:

public void delete(final Long id) {
    try {
        coreService.deleteDegree(id);
        result.use(Results.logic()).redirectTo(getClass()).list(null);
    } catch(MyBusinessException e) {
        ... adiciona as mensagens
        result.use(getClass()).edit(id);
    }
}

Se for genérico separado por tipos de exceptions eu não consigo saber para onde mandar meu fluxo.

Enfim, é apenas uma sugestão minha para deixar o vraptor melhor. A decisão final é de vocês da Caelum.

Lucas_Cavalcanti

minha preocupação em fazer um onErrorUse, sem passar um tipo de exceção específico, é que isso vai mascarar os problemas que acontecerem…

se der uma nullPointer, vai voltar pra tela de edição e vc nem vai ficar sabendo, qdo vc só queria se recuperar da sua MyBusinessException…

é como se vc estivesse dando um try{…}catch (Throwable t) {…} que é uma péssima prática de programação…

e outra coisa: o que você manda pro cliente qdo dá uma exception nesse caso?

G

lucascs:
minha preocupação em fazer um onErrorUse, sem passar um tipo de exceção específico, é que isso vai mascarar os problemas que acontecerem…

se der uma nullPointer, vai voltar pra tela de edição e vc nem vai ficar sabendo, qdo vc só queria se recuperar da sua MyBusinessException…

é como se vc estivesse dando um try{…}catch (Throwable t) {…} que é uma péssima prática de programação…

e outra coisa: o que você manda pro cliente qdo dá uma exception nesse caso?

Ahh, Lucas, entendi sua posição. No meu caso o onErrorUse faz quase um try/catch para Exception, não Throwable. Mas mesmo assim não fica bom, confesso.

Será que é possível fazer algo como abaixo? Minha preocupação é apenas eu poder excolher para onde eu quero ir.

result.onExceptionUse(MyException.class).forwardTo(MyController.class).edit();
result.onExceptionUse(MyBusiness.class).forwardTo(OutraCoisa.class).displayError();
Paulo_Silveira

Oi Garcia!

A parte dos outjectors o lucas ja fez.

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 de configurar as error pages, correto? ai poderiamos debater um pouco mais

G

Paulo e Lucas, perfeito.

Muito obrigado por enquanto, tanto pela implementação quanto pela oportunidade de opinar sobre isso.

R

Chegaram a alguma conclusão sobre qual seria a melhor maneira de tratar uma exceção no VRaptor?

Lucas_Cavalcanti

depende da exceção…

se é uma exceção inesperada (geralmente as runtime) mostrar uma página de erro genérica mesmo (aquela configurada no web.xml)

se é uma exceção que faz parte da sua lógica de negócios, você pode mostrar uma página diferente pro usuário, ou incluir uma mensagem de validação.

R

Lucas,

Eu trabalho com o VRaptor em uma aplicação com o Flex.
Então, a exceção precisa ser disparada para chegar no RemoteObject como faultEvent. Eu não tenho como redirecionar para um página do navegador, ou usar a página de erro do web.xml

Se eu usar o result.onErrorUse ele vai me redirecionar para outra página ou tenho como disparar um outro erro com uma mensagem?

Exemplo:

result.onError(Exception.class).throws(FlexException.class).withMessage(“Ocorreu um erro não reconhecido”);

ou

result.onError(Exception.class).throws(new FlexException(“Ocorreu um erro não reconhcido”);

Lucas_Cavalcanti

dá pra fazer isso sim… crie a classe/interface:

interface ExceptionView extends View {
    void doThrow(Exception e);
}
// implementação anotada com @Component

e vc pode fazer:

result.onError(Exception.class).use(ExceptionView.class).doThrow(...);
R

Preciso criar uma “ExceptionView” para cada tipo de exceção que capturar? Ou crio uma vez só?

Lucas_Cavalcanti

uma só.

R

Essa classe não poderia fazer parte do projeto? (só uma sugestão)

Lucas_Cavalcanti

do VRaptor, você diz? A gente pode criar um plugin pra isso.

R

é… se é a mesma classe para todo projeto… penso que ela poderia fazer parte do projeto vraptor-core

Lucas_Cavalcanti

na verdade, se você vai fazer isso genericamente eu colocaria esse código num interceptor, com try catch mesmo.

para ir para lugares diferentes já existe o result.onError(), ou transformar exceções em erros de validação.

R

ok vou fazer alguns testes aqui.

vou usar um interceptor para as exceções genéricas e o result para as específicas.
acho que vai funcionar como o esperado…

valeu!

R
package br.com.xxxx.infra;

import br.com.caelum.vraptor.View;

public interface ExceptionView extends View
{
    void doThrow(Exception e);
}

Como ficaria a implementação?

@Component
public class ClasseQualquer implements ExceptionView
{

       @Override
	public void doThrow(Exception e) 
	{
		//o que vai aqui??
	}

}
Lucas_Cavalcanti

aí vc coloca o que vc quer que aconteça… pelo nome do método chutaria algo como:

throw e;
R

Mas ai eu só posso disparar RuntimeException se eu quiser pegar no Interceptor igual você me sugeriu.
Se for Exception eu vou precisar tratar o throws do método doThrow, certo?
Ela não vai chegar até a camada Flex.

Interceptor:

public void intercept(InterceptorStack stack, ResourceMethod method, Object instance) { Transaction transaction = null; try { transaction = sessao.beginTransaction(); stack.next(method, instance); transaction.commit(); } catch(GenericJDBCException ex) { result.on(GenericJDBCException.class).use(ExceptionView.class).doThrow(new FlexException("xxx", ex)); } catch(Exception ex) { result.on(Exception.class).use(ExceptionView.class).doThrow(new FlexException("xxxx", ex)); } finally { if (transaction != null && transaction.isActive()) { transaction.rollback(); } } }

R

Desculpa, agora que eu vi, posso fazer um throw new no Interceptor?

public void intercept(InterceptorStack stack, ResourceMethod method, Object instance) { Transaction transaction = null; try { transaction = sessao.beginTransaction(); stack.next(method, instance); transaction.commit(); } catch(GenericJDBCException ex) { throw new FlexException("xxxx.", ex); } catch(Exception ex) { throw new FlexException("xxxx", ex); } finally { if (transaction != null && transaction.isActive()) { transaction.rollback(); } } }

Lucas_Cavalcanti

Pois é, essa era a minha sugestão :wink: Pode fazer isso sim.

kalilmvp

Pessoal…estou apanhando muito aqui, estou tentando fazer basicamente a mesma coisa, só que NUNCAAA cai no meu catch do Interceptor…

Dou um throw new Exception, ou uma exceção que eu criei, throw new BusinessException, mas de forma alguma cai no catch…

Só lança no meu console a exceção e a mensagem - java.lang.Exception: mensagem…

Alguém sabe como deve ser feito???

Muito obrigado!!

Lucas_Cavalcanti

o try…catch está em volta do stack.next?

o interceptor está anotado com @Intercepts?

R

O erro deve estar acontecendo, pois se uma exceção é pega durante um intercept o VRaptor dispara um exceção personalizada, envelopando a exceção principal. Se eu não me engano é VRaptorInterceptorException…

Faz um catch mais generico Exception e ve qual o tipo de exceção que está sendo disparada.

F

Oi gente, será que vocês podem me dar uma luz nesse erro?

HTTP Status 500 - Error creating bean with name 'discoController': Unsatisfied dependency expressed through constructor argument with index 0 of type [br.com.caelum.vraptor.blank.RepositorioDeDisco]: : No matching bean of type [br.com.caelum.vraptor.blank.RepositorioDeDisco] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [br.com.caelum.vraptor.blank.RepositorioDeDisco] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

as minhas classes são

DiscoController

package br.com.caelum.vraptor.blank;

import java.util.List;

import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Resource;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.Validator;

@Resource
public class DiscoController {
	
	private RepositorioDeDisco discos;
	private Validator validator;
	private Result result;

	public DiscoController(RepositorioDeDisco discos, Result result, Validator validator){
		this.discos = discos;
		this.result = result;
		this.validator = validator;
	}
	
	/*
	 * http://localhost:8080/vraptor-discos/disco/formulario
	 */
	public void formulario(){}
	
	@Post
	public void adicionar(final Disco disco){
	}
	
	/*
	 * http://localhost:8080/vraptor-produtos/disco/lista
	 */
	public List<Disco> lista(){
		return null;
	}
	
	/*
	 * http://localhost:8080/vraptor-produtos/disco/exibe
	 */
	public void exibe(Long id){}
	
	public void telaAnteriorAltera(Long id){}
	
}

a DiscoDAO

package br.com.caelum.vraptor.blank;

import java.util.ArrayList;
import java.util.List;

public class DiscoDAO implements RepositorioDeDisco {
	
	private static List<Disco> DISCOS = new ArrayList<Disco>();
	
	static {
		popinicia();
	}
	
	public List<Disco> lista(){
		return DISCOS;
	}
	
	private static void popinicia(){
		DISCOS.add(new Disco("Syncronicity"));
		DISCOS.add(new Disco("Bessies Blues"));
		DISCOS.add(new Disco("Wes Montgomery"));
		DISCOS.add(new Disco("mine"));
	}

	public void salva(Disco disco) {
		DISCOS.add(disco);
	}

	public List<Disco> pegaTodos() {
		return DISCOS;
	}

	public void remove(Disco disco) {
		DISCOS.remove(disco);
	}

	public Disco pegaPorId(Long id) {
		for(Disco disco : DISCOS) {
			DISCOS.remove(id);
			if(disco.getId().equals(id)) return disco;
		}
		return null;
	}

	public void altera(Disco disco) {
		DISCOS.add(disco);
	}
}

RepositorioDeDisco

package br.com.caelum.vraptor.blank;

import java.util.List;

public interface RepositorioDeDisco {
	
	void salva(Disco disco);
	List<Disco> pegaTodos();
	void remove(Disco disco);
	Disco pegaPorId(Long id);
	void altera(Disco disco);
	
}
package br.com.caelum.vraptor.blank;

public class Disco {
	
	private static int i;	
	private Integer id;
	private String nome;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}
	
	public Disco(String nome){
		i++;
		this.id = i;
		this.nome = nome;
	}
}

Será que os senhores podem me ajudar?

Lucas_Cavalcanti

faltou um @Component no DiscoDAO

fabioducarmo

Pessoal,

Estou tentando validar a minha aplicação, eu tenho uma página em que são enviados três arquivos, os nomes desses arquivos tem um regra envolvida, dai criei uma classe que será responsável por esse upload, e nela tenho as regras para os nomes dos meus arquivos. A partir do controller eu invoco o método que inicia o processo de upload(valida o nome, cria os diretórios e envia os arquivos).

Problema:
Implementei a funcionalidade utilizando exceções("IllegalArgumentsException") para informar os problemas na minha regra de negócios. a classe subia as exceções até o controller e no controller eu mostrava elas na tela. Porém caso o erro estivesse no nome do primeiro arquivo, como eu lancei uma exceção, o fluxo é interrompido e não valido os demais arquivos.

Questionamentos:
Teria uma forma melhor em fazer isso? Preciso validar todos os arquivos e depois mostrar todas as mensagens de erro na tela.
Vi muitas discussões a respeito de ExceptionHandler, isso já foi incorporado na versão 3.5.1?

[b]Classe responsável pela validação e upload.

@Component
public class UploadArquivosContraChequeBKP {

	public void salvarArquivos(List<UploadedFile> arquivos, String path)
			throws Exception {
		if (!arquivos.isEmpty()) {
			criaDiretorio(path);
			for (Iterator<UploadedFile> iterator = arquivos.iterator(); iterator
					.hasNext();) {
				UploadedFile uploadedFile = (UploadedFile) iterator.next();
				String fileName = uploadedFile.getFileName();
				if (validaNomeArquivo(fileName)) {
					try {
						IOUtils.copyLarge(uploadedFile.getFile(),
								new FileOutputStream(new File(path + fileName)));
					} catch (FileNotFoundException e) {
						throw new FileNotFoundException(
								"Problemas ao enviar arquivo!" + e.getMessage());
					}
				} else {
					throw new IllegalArgumentException(
							"Problemas ao enviar arquivo!");
				}
			}
		}
	}

	private void criaDiretorio(String novoDiretorio)
			throws FileNotFoundException {
		String nomeDiretorio = null;
		nomeDiretorio = novoDiretorio;
		if (!new File(nomeDiretorio).exists()) {
			(new File(nomeDiretorio)).mkdirs();
		}
	}

	private boolean validaNomeArquivo(String nomeArquivo) throws Exception {
		List<String> subStrings = separaString(nomeArquivo);
		if (subStrings.size() != 3) {
			throw new IllegalArgumentException(
					"Formato do nome do arquivo inválido! Arquivo: "
							+ nomeArquivo);
		} else {
			for (int i = 0; i < subStrings.size(); i++) {
				if (i == 0) {
					validaTipo(nomeArquivo, subStrings, i);
				} else if (i == 1) {
					validaPeriodo(nomeArquivo, subStrings, i);
				} else if (i == 2) {
					validaExtensao(nomeArquivo, subStrings, i);
				}
			}
		}
		return true;
	}

	public List<String> separaString(String nomeArquivo) {
		List<String> parametros = new ArrayList<String>();
		if (nomeArquivo.contains(".")) {
			String[] arrayPonto = nomeArquivo.split("\\.");
			for (int i = 0; i < arrayPonto.length; i++) {
				String str = arrayPonto[i];
				if (str.contains("-")) {
					String[] arrayHifen = str.split("-");
					for (int j = 0; j < arrayHifen.length; j++) {
						parametros.add(arrayHifen[j]);
					}
				} else {
					parametros.add(str);
				}
			}
		}
		return parametros;
	}

	private boolean validaTipo(String nomeArquivo, List<String> subStrings,
			int i) {
		String tipo = subStrings.get(i);
		if (!(tipo.equalsIgnoreCase("cabecalho"))
				&& !(tipo.equalsIgnoreCase("repeticao"))
				&& !(tipo.equalsIgnoreCase("totais"))) {
			throw new IllegalArgumentException(
					"Campo tipo na nomeação do arquivo é incompatível com padrão estabelecido! Nome do Arquivo: "
							+ nomeArquivo + " Formato não permitido: " + tipo);
		}
		return true;
	}

	private boolean validaPeriodo(String nomeArquivo, List<String> subStrings,
			int i) {
		String periodo = subStrings.get(i);
		if (periodo.length() == 6) {
			if (periodo.matches("[0-9]+")) {
				String mes = periodo.substring(0, 2);
				String ano = periodo.substring(2, 6);
				if (Integer.parseInt(mes) <= 0 || Integer.parseInt(mes) > 12) {
					throw new IllegalArgumentException(
							"Mês informado no nome do arquivo é inválido! Nome do Arquivo: "
									+ nomeArquivo + ", Mês: " + mes);
				}
				if (Integer.parseInt(ano) < 1971) {
					throw new IllegalArgumentException(
							"Ano informado no nome do arquivo inválido! Nome do Arquivo: "
									+ nomeArquivo + ", Ano: " + ano
									+ " É menor que o menor ano permitido.");
				} else if (Integer.parseInt(ano) > new GregorianCalendar()
						.get(GregorianCalendar.YEAR)) {
					throw new IllegalArgumentException(
							"Ano informado no nome do arquivo é inválido! Nome do Arquivo: "
									+ nomeArquivo + ", Ano: " + ano
									+ " É maior que o maior ano permitido.");
				}
			} else {
				throw new IllegalArgumentException(
						"Período informado não tem somente digítos! Período:"
								+ periodo);
			}
		} else {
			throw new IllegalArgumentException(
					"Periodo informado inválido! Período: " + periodo);
		}
		return true;
	}

	private boolean validaExtensao(String nomeArquivo, List<String> subStrings,
			int i) {
		String extensao = subStrings.get(i);
		if (!extensao.equalsIgnoreCase("txt")) {
			throw new IllegalArgumentException(
					"Arquivo enviado não é um txt! Arquivo: " + nomeArquivo);
		}
		return true;
	}
}
G

Adicione tudo em uma lista e depois lance a exception mostrando todos os erros da lista.

Criado 27 de agosto de 2009
Ultima resposta 6 de jun. de 2013
Respostas 82
Participantes 13