Bug VRaptor 3.1 - e não fui eu quem disse

23 respostas
bronx

Buenas a todos.

Semana passada atualizei a versão do VRaptor para a 3.1 (usava a 3.0.2).

Durante os testes que estava fazendo, obtive a seguite exception:

java.lang.IllegalStateException: Some request parameter has the same name as a request attribute. It shouldn't happen, please report this bug.
	br.com.caelum.vraptor.validator.DefaultOutjector.castMap(DefaultOutjector.java:86)
	br.com.caelum.vraptor.validator.DefaultOutjector.outjectRequestMap(DefaultOutjector.java:56)
	br.com.caelum.vraptor.validator.DefaultValidator.onErrorUse(DefaultValidator.java:62)
	br.com.myapp.admin.controller.UsuarioController.salvar(UsuarioController.java:88)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	java.lang.reflect.Method.invoke(Unknown Source)
	br.com.caelum.vraptor.interceptor.ExecuteMethodInterceptor.intercept(ExecuteMethodInterceptor.java:50)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:48)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:77)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:42)
	br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.myapp.interceptor.AccessControllerInterceptor.intercept(AccessControllerInterceptor.java:53)
	br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.myapp.interceptor.ModulosInterceptor.intercept(ModulosInterceptor.java:39)
	br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.caelum.vraptor.util.hibernate.HibernateTransactionInterceptor.intercept(HibernateTransactionInterceptor.java:45)
	br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:46)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:80)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:48)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:62)
	br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91)
	br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:55)
	br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88)

Como a mensagem diz, não pode haver atributo e parâmetro com o mesmo nome dentro do request (não me perguntem o motivo dessa restrição).

Esse método (salvar) recebe um parâmetro do tipo Usuario, chamado “usuario”.
Logo, na JSP, eu uso: “usuario.id”, “usuario.nome” etc.

O que pega é que, caso ocorra um erro de validação, eu volto para a mesma JSP, enviando como atributo um objeto da classe Usuario, coincidentemente chamado “usuario”, pois meus inputs estão assim:

<input type="text" name="usuario.nome" value="${usuario.nome}"/>

Dei uma olhada no código da classe DefaultOutjector, mas não compreendi muito bem seu funcionamento, o que limita minha “ajuda” apenas à detecção do problema.

Para contornar o problema, simplesmente alterei o nome do atributo que passo no result.include(…), e não ocorre mais a exception!
O estranho é que, mesmo sem alterar a JSP (o atributo “value” dos meus inputs ainda estão com “${usuario.nome}”), ao retornar para a página os campos são carregados normalmente, como se eu ainda estivesse fazendo:

this.result.include("usuario", usuario);

sendo que na realidade, estou fazendo:

this.result.include("usuarioA", usuario);

Sinistro… :shock:

23 Respostas

bronx

Exactly!!

Minha classe Usuario não é um componente. realmente não sei o que rola.

bronx

seufagner,

Saca só o que estou fazendo:

public void salvar(Usuario usuario) throws NoSuchAlgorithmException {

		if(this.usuarioDao.getByEmail(usuario.getEmail()) != null){
			this.validator.add(new ValidationMessage("Já existe um usuário com este email.", "usuario.email"));
			this.result.include("usuarioA", usuario);
			this.result.include("perfisAcesso", this.perfilDao.listPerfisByEmpresa(usuario.getEmpresa()));
		}

		this.validator.onErrorUse(Results.logic()).forwardTo(UsuarioController.class).editar();

		this.usuarioDao.save(usuario);

		this.result.use(Results.logic()).redirectTo(UsuarioController.class).listar();
	}

Se já existir usuário com o email informado, deve voltar para a tela de edição, passando o Usuario que recebeu, para poder preencher novamente os campos.
Got it?

bronx

Nada! :frowning:

O VRaptor desvia o fluxo imediatamente ao encontrar o “onErrorUse” (caso haja erro). Não chega no “return usuario” (mas os dados ainda assim voltam para a JSP…=S)

bronx

Hehehe!

Quando for assim não edite não, para quem ler entender o que rolou. ^^

Dá próxima, lance uma “errata”. hehehe

Mas o problema persiste…¬¬

bronx

Pode crer.

De qualquer forma, é um comportamento anormal (presumo). Fico cabreiro de deixar a parada assim. rs

G

Esse caso do Outjector é uma lenda. Eu já havia reportado um bug (esqueci qual issue) sobre esse componente, oportunidade na qual o Lucas refez esse componente. Há dois tópicos relacionados a discuçao: http://guj.com.br/posts/list/143727.java e http://guj.com.br/posts/list/146543.java.

O que esse componente faz basicamente é pegar os atributos que estão como parametros e joga no request para que você possa recuperar na tela. Se você ler meus tópicos que passei é muito útil quando você faz uma validação, e quando dá erros você retorna para a tela anterior com os dados todos preenchidos.

Eu havia comentado com o Lucas (acho que por email) sobre a complexidade de você tratar quando você tem já um objeto no request, como no teu caso usuário. Como seus parametros são algo como usuario.nome, o que o outjector faz é criar um map com esses valores. Porém quando você tem um POJO já lá como fazer? No componente original que eu mandei para o pessoal do vraptor eu fazia um set nesse objeto via beanutils. Solução muito feia, mas foi o que consegui fazer com o curto tempo que eu tinha.

Nesse caso, não sei qual a viabilidade já que conheço pouco a implementação do vraptor, é tentar jogar os dados direto nos métodos do controller, ao invés de fazer um setAttribute dos parametros no request.

Abraços

Lucas_Cavalcanti

a implementação desse outjector mudou (de novo) por causa desse bug…

na proxima versão do vraptor esse bug está corrigido… enqto ela não sai vc pode criar essa classe que resolve o problema:

@Component
public class ReplicatorOutjector implements Outjector {

    private final Result result;
	private final MethodInfo method;
	private final ParameterNameProvider provider;

	public ReplicatorOutjector(Result result, MethodInfo method, ParameterNameProvider provider) {
		this.result = result;
		this.method = method;
		this.provider = provider;
    }

    public void outjectRequestMap() {
          String[] names = provider.parameterNamesFor(method.getResourceMethod().getMethod());
          for (int i = 0; i < names.length; i++) {
               result.include(names[i], method.getParameters()[i]);
          }
    }

}
bronx

garcia-jj e Lucas,

Valeu pelo help.

Vou usar (paliativamente) a solução do Lucas.

Mas confesso que ainda não compreendi o que o Outjector faz, e consequentemente, o problema em si.

Se tiverem um tempo para explicar melhor, agradeço! :wink:

E Lucas, tem previsão de release de nova versão?

Estou prestes a fazer uma alteração simples no DefaultValidator.
Queria te mostrar para ver se é interessante ou não adicionar ao VRaptor.
Mas isso é coisa para outro tópico. rs

Abs.

G

Lucas, agora que passou o mal tempo no trabalho (muitos projetos entrando em produção) quero voltar a poder colaborar como antes. Dá para explicar bem resumidamente o que o Replicator faz? Ele trabalha junto com o DefaultOutjector ou sobrescreve?

bronx, eu expliquei acima para que ele serve? O que você não entendeu? Ou melhor, desculpe que as vezes estou fazendo várias coisas ao mesmo tempo e acabo enrolando a explicação.

Abraços

Lucas_Cavalcanti

O outjector serve pra, qdo acontecer algum erro de validação, replicar todos os parâmetros da requisição atual para a próxima… desse jeito é possível deixar os forms preenchidos qdo der erro de validação…

@garcia-jj
o Replicator é exatamente o código que eu passei… Ele só pega os parâmetros populados do método do controller e coloca de volta no result… Não dá mais class cast, mas em compensação não vai aceitar dados inválidos (tipo abc num campo numérico)

G

Lucas Cavalcanti:
@garcia-jj
o Replicator é exatamente o código que eu passei… Ele só pega os parâmetros populados do método do controller e coloca de volta no result… Não dá mais class cast, mas em compensação não vai aceitar dados inválidos (tipo abc num campo numérico)

Ou seja, o DefaultOutjector morre e fica esse no lugar?

Lucas_Cavalcanti

morrer não, mas talvez não seja mais o padrão… Vc ainda vai poder voltar a implementação pra ele, se vc quiser

G

Lucas, fiz uns testes naquela minha aplicação grande, que estava desatualizada com o 3.0.2. Migrei tudo para o 3.1.1 e coloquei essa nova implementação de outjector.

Não sei se você lembra, mas eu havia criado meu proprio ExecuteMethodInterceptor porque eu precisei criar uma implementação minha de Result que contém os métodos CustomResult.onErrorUse (aquele meu caso do exception-handler).

Apenas injetei o Outjector nessa classe e ao chamar o método Outjector.outjectRequestMap() tudo funcionou direitinho, sem aqueles erros de cast nem mesmo de falha de conversão com a waffle. Ficou muito bom mesmo.

Esse componente entra em qual versão do vraptor?

Abraços, e obrigado.

Lucas_Cavalcanti

o componente de erros? provavelmente na próxima… adiantamos a versão 3.1.1 por causa do maven, então não deu tempo de implementar…

G

Lucas, estava me referindo ao ReplicatorOutjector, que ainda não está no vraptor. Tive que criar essa classe manualmente por enquanto e funcionou muito bem.

O componente de erros que você fala é aquela minha sugestão do exception handler? O Paulo me disse que iria ficar em standby por um tempo. Você já começou a fazer algo sobre isso?

ABraços

Lucas_Cavalcanti

o ReplicatorOutjector já está na versão 3.1.1… (ou pelo menos deveria estar)…

não comecei a fazer ainda o exception handler…

G

Oops, estou com a 3.1.0 :oops:

seufagner

Qual o escopo do teu da entidade Usuario ?

Bem, teoricamente se esta chegando com os valores que foram atribuidos antes do request, teu usuario nao esta “morrendo” ao retornar.

seufagner

Ah, você está retornando um Usuario no metodo do Controller?

Porque voce gostaria de indicar, na unha, que deseja colocar tal Usuario no request, visto que o VRaptor ja faz isto ?

Abs

seufagner

bronx

vc pode postar a classe controller?

de qualquer forma, tenta retornar Usuario metodo. Acredito que funcione.

seufagner

ps. ops, dei mole… rs

realmente não entendi, ja que nao é modificado o escopo do Usuario ate porque , claro, ele nao e um componente

seufagner

bronx:
Nada! :frowning:

O VRaptor desvia o fluxo imediatamente ao encontrar o “onErrorUse” (caso haja erro). Não chega no “return usuario” (mas os dados ainda assim voltam para a JSP…=S)

aham, prestei atencao depois… por isso editei rs

seufagner

po cara, se ele ja esta no request entao nao da um include no result e leva tua vida numa boa… quando alguem esclarecer vc vai e refatora se necessario… rs

Criado 4 de fevereiro de 2010
Ultima resposta 4 de fev. de 2010
Respostas 23
Participantes 4