VRaptor com Beans atraves de Interface e ComponentFactory

28 respostas
jurado

Olá.

Tenho uma situação em que o bean que deve ser populado pelo parâmetro do controller na verdade é um objeto que implementa uma determinada interface “bean like”. Porém, o beam não é registrado pelo vraptor e, consequentemente, não é preenchido pela view. Este é o exemplo:

Interface do bean

public interface MeuBean {
	String getValor();
	
	void setValor(String valor);
}

Implementação do bean (note q é package private):

class MeuBeanImpl implements MeuBean{
	private String valor;
	
	public String getValor() {
		return valor;
	}

	public void setValor(String valor) {
		this.valor = valor;
	}
	
}

BeanCreator:

@Component
public class MeuBeanCreator implements ComponentFactory<MeuBean> {
	private final MeuBean meuBean;

	public MeuBeanCreator() {
		this.meuBean = new MeuBeanImpl();
	}

	@Override
	public MeuBean getInstance() {
		return meuBean;
	}

}

Esta é a view:

<html>
<body>
	<form action="add" method="post">
		<input type="text" name="meuBean.valor">
		<input type="submit">
	</form>
</body>
</html>

E o controller:

@Resource
public class BeanController {
	private final Result result;

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

	public void adiciona() {

	}

	public void add(MeuBean meuBean) {
		// SEMPRE NULL
		System.out.println("valor: " + meuBean.getValor());
		result.redirectTo(this).adiciona();
	}
}

Obviamente este problema não acontece se eu tornar MeuBeanImpl uma classe pública e fizer a referência direta a ela no método add().

Um outro problema que encontrei foi, caso eu defina um construtor customizado ao Bean (por exemplo, passando o valor do field “valor”), o vraptor não utiliza o ComponentFactory para instanciação (e passagem de um parâmetro que eu defini). Ele EXIGE que o bean tenha um construtor sem parâmetros. Este comportamento é esperado?

Obrigado pela atenção!

Att,

Daniel Jurado

28 Respostas

Lucas_Cavalcanti

Olá Daniel,

o VRaptor não popula parâmetros que são interface (ele não tem como adivinhar qual é a implementação).

o ComponentFactory funciona para injeção de dependências, não para população de parâmetros.

qto ao problema do construtor, se vc habilitar o IOGI no VRaptor resolve esse problema (http://vraptor.caelum.com.br/documentacao/componentes-utilitarios-opcionais/ parte de instanciador de parametros imutáveis)

jurado

Olá, Lucas.

Obrigado pelas dicas. Mas fica uma pergunta: É necessário saber qual a implementação para chamar o setter?

Estou evitando ter que criar uma classe intermediária (DTO) só pra fazer a transmissão dos valores…

Meu projeto tem utiliza um DAO que mascara toda implementação dos objetos do banco. Ele faz isso pois dependendo de alguns parametros persiste no Hibernate, em Memória, em disco, em lugar nenhum, etc.

Basicamente somente tenho acesso às interfaces com injeção das implementacoes via Guice.

Via componentfactory o vRaptor instancia corretamente o objeto que quero no parametro do controller, mas simplesmente não chama o setter com o valor da view. Tem outra forma de obter isso sem ter q criar algo intermediário gerando duplicação?

Lucas_Cavalcanti

jurado:
Olá, Lucas.

Obrigado pelas dicas. Mas fica uma pergunta: É necessário saber qual a implementação para chamar o setter?


Não, mas para dar new precisa :wink:

Isso é uma funcionalidade nova do VRaptor 3.4.0. Se vc recebe uma Interface como parâmetro ele vai usar injeção de dependências e desabilitar a população de parâmetros, por motivos de segurança.
(senão um usuário poderia conseguir mudar os componentes da sua aplicação com parâmetros da requisição, o que geralmente não é mto legal)

o que vc pode fazer é criar um Converter para a sua interface (e não um ComponentFactory):

@Convert(MeuBean.class)
public class MeuBeanConverter implements Converter<MeuBean> { // converter do VRaptor
   
     public MeuBean convert(String value, ....) {
            return new MeuBeanImpl(...);
     }
}

daí na requisição precisa ter um parâmetro direto pra MeuBean (mesmo que não tenha valor):

public void metodoDoController(MeuBean bean) {...}

//na request precisa de um parâmetro chamado bean, com algum valor que será passado no value do converter
jurado

Nesse abordagem eu consigo injetar @Components (ou outras coisas registradas no injector) dentro do converter?

Pergunto isso pois meus objetos serão criados através de factories cujo bind foi feito nas modules do Guice que registrei via GuiceProvider.

E como ficariam os nomes dos parametros na view? O mesmo nome dos params que eu colocar no converter?

Por ex:

@Convert(MeuBean.class)
public class MeuBeanConverter implements Converter<MeuBean> {

     // injetado pelo Guice
     private final MeuBeanFactory meuBeanFactory;
     public MeuBeanConverter(MeuBeanFactory meuBeanFactory){
            this.meuBeanFactory = meuBeanFactory;
     }

     public MeuBean convert(String parametroQueEuQueroNoConstrutor) {
            return meuBeanFactory.create(parametroQueEuQueroNoConstrutor);
     }
}

e na view

<html>
<body>
	<form action="add" method="post">
		<input type="text" name="parametroQueEuQueroNoConstrutor">
		<input type="submit">
	</form>
</body>
</html>

É isso?

Testarei amanha!

Lucas_Cavalcanti

vc consegue receber dependências no converter sim.

Se vc só vai receber um valor no construtor, vc consegue fazer do jeito que vc falou sim, mas o nome não é o nome que está no converter, e sim o nome que vc colocou no método do controller.

jurado

Acho q minha abordagem vai ser mais simples: sem nenhum parametro no construtor.

Fiz um teste rápido aqui mas o parâmetro veio null. Tentei um converter assim:

@Convert(MeuBean.class)
public class MeuBeanConverter implements Converter<MeuBean> {
	@Override
	public MeuBean convert(String value, Class<? extends MeuBean> type,
			ResourceBundle bundle) {
		return new MeuBeanImpl();
	}

}

um controller assim:

public void add(MeuBean bean) {
		System.out.println("valor: " + bean.getValor());
	}

e uma view assim:

<html>
<body>
	<form action="add">
		<input type="text" name="bean.valor">
		<input type="submit">
	</form>
</body>
</html>

Era essa a idéia?

Depurei o converter e o vraptor sequer chamou-o… :frowning:

Lucas_Cavalcanti

vc precisa chamar o parâmetro de “bean”… assim, como o VRaptor não consegue instanciar sozinho um bean, ele procura o converter pra fazer isso… se vc usa bean.valor ele tenta fazer new Bean() e depois bean.setValor()

jurado

Na view, certo?

Tentei também desta forma e o bean veio como null.

<form action="add">
		<input type="text" name="bean">
		<input type="submit">
	</form>

Desculpe a encheção de saco a esta hora da noite, heheheh… :oops:

Lucas_Cavalcanti

estranho… ele pelo menos chamou o bean?

o bean veio null ou o valor veio null?

na verdade vc precisaria fazer o seguinte:

<form action="add">  
        <input type="hidden" name="bean" value="qqerCoisa, soh pra chamar o converter" />
        <input type="text" name="bean.valor">  ==> para setar o valor dentro do bean.
        <input type="submit">  
    </form>

de qqer forma, deveria chamar o converter

jurado

Funcionou com o hidden!

Estranho, q sequer chamou o Converter quando somente usei o type=“text” com name=“bean”. O parametro do controller veio null.

Mas já ajudou bastante a evitar a duplicação!

Idéia: Será q se anotássemos os component factories com alguma @ específica (ou até mesmo criar um conceito de ObjectFactory) que permita que os parametros das actions fossem instanciados por terceiros pra entao serem populados pelo vraptor não resolveriam melhor este problema? Pode ser um preciosismo meu, mas os projetos que estamos desenvolvendo por aqui ficaram mais bem resolvidos e desacoplados quando as entidades principais ficaram ocultadas pelas suas interfaces.

Valeu, Lucas!

Lucas_Cavalcanti

não é muito comum usar interfaces para as classes do modelo. O ganho de desacoplamento geralmente não paga a complexidade do código final. Distribuir um jar com as classes de modelo geralmente é mais simples

Cuidado com o overengineering :wink:

celsodantas

Galera, eu estou com um problema semelhante: estou tentando popular uma interface como parametro pro controller.

A diferença de meu problema é, eu tenho um objeto Order (classe concreta) que possui uma lista de Buyables (classe abstrata). As classes que extendem de Buyable são: Component e Computer.

Implementei o BuyableConverter, acredito eu que corretamente. Ele é chamado normalmente se eu passar ele sozinho por parametro:

void metodoDoController(Buyable buyable) {
    ...
}

url: …?buyable=component&buyable.ukey=123

@Convert(Buyable.class)
@ApplicationScoped
public class BuyableConverter implements Converter<Buyable> {

	@Override
	public Buyable convert(String value, Class<? extends Buyable> type, ResourceBundle bundle) {
		
		if (value.equals("component")) {
			return new Component();
		} else if (value.equals("computer")) {
			return new Computer();
		}
		return null;
	}

}

Porém o converter não é chamado quando eu tento popular o Buyable dentro da Order.

public class Order {
	private List<Buyable> items;
}
void metodoDoController(Order order) {
    // quero acessar: order.items
    ...
}

url: …?order.item[0]=component&order.item[0].ukey=123

Alguém tem alguma noção e como resolver isso? O problema é justamente mandar a lista de interface dentro do objeto Order, se eu mandar a lista sozinha, funciona normalmente.
Alguma luz?

Lucas_Cavalcanti

se o atributo se chama items (e tem o getter/setter), vc deveria fazer order.items[0], order.items[1], etc

celsodantas

perdão, escrevi a url errada:
url: …?order.items[0]=component&order.items[0].ukey=123

recebo isso:

java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.WildcardTypeImpl cannot be cast to java.lang.Class

br.com.caelum.vraptor.http.ognl.ListAccessor.getActualType(ListAccessor.java:102)

br.com.caelum.vraptor.http.ognl.ListAccessor.setProperty(ListAccessor.java:81)

ognl.OgnlRuntime.setProperty(OgnlRuntime.java:2225)

ognl.ASTProperty.setValueBody(ASTProperty.java:127)

ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)

ognl.SimpleNode.setValue(SimpleNode.java:279)

ognl.ASTChain.setValueBody(ASTChain.java:227)

ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)

ognl.SimpleNode.setValue(SimpleNode.java:279)

ognl.Ognl.setValue(Ognl.java:737)


Lucas_Cavalcanti

WildcardTypeImpl

isso acontece qdo vc tem ? extends Algo, ou algo do tipo… o seu getter/setter está assim?

celsodantas

Você sabe de mais! haha

então, é exatamente isso. Não posso usar o <? extends Buyable>, não?

removi aqui e de fato funcinou.
Acontece que tenho em diversos momentos do meu código algo como:

List components;

order.setItems(components);

Lucas_Cavalcanti

precisa ser setItems? você pode criar outro método para isso, algo como addItems ou changeItems, daí vc pode usar a assinatura diferente.

de qqer forma, vc não consegue colocar um a List<? extends Buyable> dentro de uma List, certo? teve que fazer um casting bizarro pra funcionar?

celsodantas

continuei com os testes e deixando como

<blockquote>class Order {

List items;

}</blockquote>

eu tinha testado acessando a url:
…?order.items[0]=component

e funcionou. Porém quando começo a setar os valores pra dentro do objeto:

…?order.items[0]=component&order.items[0].ukey

recebo o erro:

net.vidageek.mirror.exception.ReflectionProviderException: could not invoke constructor public br.com.models.dtos.Buyable() on class br.com.models.dtos.Buyable

net.vidageek.mirror.provider.java.PureJavaConstructorReflectionProvider.instantiate(PureJavaConstructorReflectionProvider.java:40)

net.vidageek.mirror.invoke.ConstructorHandlerByConstructor.withArgs(ConstructorHandlerByConstructor.java:46)

net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withArgs(ConstructorHandlerByArgs.java:36)

net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withoutArgs(ConstructorHandlerByArgs.java:32)

br.com.caelum.vraptor.http.ognl.ListNullHandler.instantiate(ListNullHandler.java:50)

br.com.caelum.vraptor.http.ognl.ReflectionBasedNullHandler.nullPropertyValue(ReflectionBasedNullHandler.java:70)

ognl.ASTProperty.getValueBody(ASTProperty.java:118)

ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)

ognl.SimpleNode.getValue(SimpleNode.java:236)

ognl.ASTChain.setValueBody(ASTChain.java:222)

ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)

ognl.SimpleNode.setValue(SimpleNode.java:279)

ognl.Ognl.setValue(Ognl.java:737)

ognl.Ognl.setValue(Ognl.java:783)


Tem solução?
E eu posso tentar dar um jeito de tirar o wildcard (adoraria não precisar) sim.

Lucas_Cavalcanti

você está com a última versão do VRaptor? teoricamente setar atributos depois de converter funciona

pra não precisar tirar o wildcard vc precisaria sobrescrever o ListNullHandler do VRaptor… Talvez até dê pra fazer isso (na última versão), sobrescrevendo o OgnlFacade, e tentando extrair o tipo do wildcard… quer tentar fazer isso?

celsodantas

estou usando a versão 3.4.0

e vou adiantar usando sem o wildcard. assim que tiver tudo funcionando, eu tento resolver isso. um problema de cada vez. =]

Acredito ter achado um bug:

Dentro de meu objeto que extende de Buyable:

class Component extends Buyable {

private Integer quantity;

}

eu tenho o “quantity” que é Integer. E só passou quando coloquei tipo primitivo: “int”. Tava dando o erro de construtor:

net.vidageek.mirror.exception.ReflectionProviderException: could not invoke constructor public br.com.models.dtos.Buyable() on class br.com.models.dtos.Buyable

net.vidageek.mirror.provider.java.PureJavaConstructorReflectionProvider.instantiate(PureJavaConstructorReflectionProvider.java:40)

net.vidageek.mirror.invoke.ConstructorHandlerByConstructor.withArgs(ConstructorHandlerByConstructor.java:46)

net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withArgs(ConstructorHandlerByArgs.java:36)

net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withoutArgs(ConstructorHandlerByArgs.java:32)

br.com.caelum.vraptor.http.ognl.ListNullHandler.instantiate(ListNullHandler.java:50)

br.com.caelum.vraptor.http.ognl.ReflectionBasedNullHandler.nullPropertyValue(ReflectionBasedNullHandler.java:59)

ognl.ASTProperty.getValueBody(ASTProperty.java:118)

ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)

ognl.SimpleNode.getValue(SimpleNode.java:236)

ognl.ASTChain.setValueBody(ASTChain.java:222)

ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)

ognl.SimpleNode.setValue(SimpleNode.java:279)

ognl.Ognl.setValue(Ognl.java:737)

ognl.Ognl.setValue(Ognl.java:783)


celsodantas

Vou corrigir minha colocação:

Eu tinha isso:

abstract class Buyable {

public abstract Integer getQuantity();

}
class Component {

public Integer getQuantity(){ return quantity; }

}

e tava dando erro. fui refatorar o código, jogando os atributos e implementações do metodo de get/set de quantity na abstract class e joguei pra Integer (objeto) de novo e funcionou.

Lucas_Cavalcanti

não basta ter só o getter, precisa ter o setter tb, pra conseguir setar

celsodantas

eu tenho os get/set, só tirei pra simplificar a explicação do problema.

Cavando um pouco mais o problema, descobri que ao mandar muitos parametros (12 ou mais parametros seja via GET ou POST) o VRaptor esquece de chamar o @Convert de Buyable e da o seguinte erro:

net.vidageek.mirror.exception.ReflectionProviderException: could not invoke constructor public br.com.models.dtos.Buyable() on class br.com.models.dtos.Buyable
net.vidageek.mirror.provider.java.PureJavaConstructorReflectionProvider.instantiate(PureJavaConstructorReflectionProvider.java:40)
net.vidageek.mirror.invoke.ConstructorHandlerByConstructor.withArgs(ConstructorHandlerByConstructor.java:46)
net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withArgs(ConstructorHandlerByArgs.java:36)
net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withoutArgs(ConstructorHandlerByArgs.java:32)
br.com.caelum.vraptor.http.ognl.ListNullHandler.instantiate(ListNullHandler.java:50)
br.com.caelum.vraptor.http.ognl.ReflectionBasedNullHandler.nullPropertyValue(ReflectionBasedNullHandler.java:59)
ognl.ASTProperty.getValueBody(ASTProperty.java:118)
ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
ognl.SimpleNode.getValue(SimpleNode.java:236)
ognl.ASTChain.setValueBody(ASTChain.java:222)
ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)
ognl.SimpleNode.setValue(SimpleNode.java:279)
ognl.Ognl.setValue(Ognl.java:737)
ognl.Ognl.setValue(Ognl.java:783)
br.com.caelum.vraptor.http.ognl.OgnlFacade.setValue(OgnlFacade.java:100)
br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.setProperty(OgnlParametersProvider.java:161)
br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createParameter(OgnlParametersProvider.java:133)
br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.getParametersFor(OgnlParametersProvider.java:85)
br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.getParametersFor(ParametersInstantiatorInterceptor.java:105)
br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:77)
br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.interceptors.AuthorizationInterceptor.intercept(AuthorizationInterceptor.java:39)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:48)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.interceptor.ExceptionHandlerInterceptor.intercept(ExceptionHandlerInterceptor.java:71)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:83)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:69)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.core.EnhancedRequestExecution.execute(EnhancedRequestExecution.java:44)
br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58)
br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)
root cause

java.lang.InstantiationException
sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(Unknown Source)
java.lang.reflect.Constructor.newInstance(Unknown Source)
net.vidageek.mirror.provider.java.PureJavaConstructorReflectionProvider.instantiate(PureJavaConstructorReflectionProvider.java:34)
net.vidageek.mirror.invoke.ConstructorHandlerByConstructor.withArgs(ConstructorHandlerByConstructor.java:46)
net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withArgs(ConstructorHandlerByArgs.java:36)
net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withoutArgs(ConstructorHandlerByArgs.java:32)
br.com.caelum.vraptor.http.ognl.ListNullHandler.instantiate(ListNullHandler.java:50)
br.com.caelum.vraptor.http.ognl.ReflectionBasedNullHandler.nullPropertyValue(ReflectionBasedNullHandler.java:59)
ognl.ASTProperty.getValueBody(ASTProperty.java:118)
ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
ognl.SimpleNode.getValue(SimpleNode.java:236)
ognl.ASTChain.setValueBody(ASTChain.java:222)
ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)
ognl.SimpleNode.setValue(SimpleNode.java:279)
ognl.Ognl.setValue(Ognl.java:737)
ognl.Ognl.setValue(Ognl.java:783)
br.com.caelum.vraptor.http.ognl.OgnlFacade.setValue(OgnlFacade.java:100)
br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.setProperty(OgnlParametersProvider.java:161)
br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createParameter(OgnlParametersProvider.java:133)
br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.getParametersFor(OgnlParametersProvider.java:85)
br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.getParametersFor(ParametersInstantiatorInterceptor.java:105)
br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:77)
br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.interceptors.AuthorizationInterceptor.intercept(AuthorizationInterceptor.java:39)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:48)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.interceptor.ExceptionHandlerInterceptor.intercept(ExceptionHandlerInterceptor.java:71)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:83)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:69)
br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
br.com.caelum.vraptor.core.EnhancedRequestExecution.execute(EnhancedRequestExecution.java:44)
br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58)
br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)

--
é esse URL q estou acessando:
http://localhost:8080/ws/orders/save?order.client.ukey=2323332323U&order.vendor.ukey=2323235&order.paymentCondition=2323233PKV&order.status=EmAndamento&order.shippingCost=3333.33&items[0]=component&items[0].quantity=33&items[0].ukey=20100111OZR9I90N0JNK&items[0].D50ukey=d50ukey_bla&items[0].D52ukey=d52ukey_bla&items[0].price=33.33&token=123&_format=json

Ah! E como pode ver pela URI eu não estou passando o Buyable pra dentro de Order e sim como um parametro solto. o metodo do controller está assim:

@Post
	@Path(value="/")
	public void save(Order order, List<Buyable> items) {
		
		if (order != null && items != null) {
			order.setItems(items);
			order = orderBO.save(order);
		}
		result.use(Results.representation()).from(order).recursive().serialize();
	}
Lucas_Cavalcanti

o problema não é ele esquecer, e sim a ordem em que os parâmetros são tratados…

se ele tratar o items[0].quantity=33 antes de tratar o itens[0]=component, ele vai tentar instanciar o Buyable via construtor (antes de converter)

isso é um bug, abre uma issue por favor:

dá pra tentar fazer um workaround por enquanto. Crie um Filtro (servlet filter):

public class GambiFilter implements Filter {
    //init e destroy vazios

   public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {

          chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest)req) {
             @Override
             public Map getParameterMap() {
                  return new TreeMap(super.getParameterMap());
             }
          }, res);
   }
}

registre esse filtro antes do filtro do VRaptor.

o que eu fiz aqui foi ordenar os parâmetros do request, assim ele trata os converters antes de popular os parametros (items[0] vem antes de items[0].quantity)

tenta isso plz e me fala se funcionou

celsodantas

Valeu pela resposta, Lucas! Mas infelizmente não funcionou.

Coloquei alguns sysouts no GambiFilter e observei como o VRaptor está setando os atributos passando por parametro e a ordem dos parametros não parece ter muita alteração. Da a impressão que o VRaptor está ignorando ou reordenando os parametros internamente.

Como ficou meu GambiFilter:

public class GambiFilter implements Filter {
        // destroy e init vazios

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {

		try {
			chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) req) {
				@Override
				public Map getParameterMap() {
					System.out.println(new TreeMap(super.getParameterMap()));
					return new TreeMap(super.getParameterMap());
				}
			}, res);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ServletException e) {
			e.printStackTrace();
		}
	}
}

A url que acesso:
http://localhost:8080/ws/orders/save?order.id=123&order.client.username=66&order.vendor.ukey=66666&order.vendor.username=blabla&order.vendor.email=[email removido]&order.client.ukey=66&order.client.name=66&items[0]=component&items[0].ukey=1234&items[0].imageUrl=1234&items[0].deffault=true&token=123&_format=json

Console:

{_format=[Ljava.lang.String;@5e9a94, items[0]=[Ljava.lang.String;@f66abc, items[0].deffault=[Ljava.lang.String;@1d97efc, items[0].imageUrl=[Ljava.lang.String;@1ff323, items[0].ukey=[Ljava.lang.String;@193b022, order.client.name=[Ljava.lang.String;@110b205, order.client.ukey=[Ljava.lang.String;@1178281, order.client.username=[Ljava.lang.String;@78f83a, order.id=[Ljava.lang.String;@1f0cbfb, order.vendor.email=[Ljava.lang.String;@1dfe453, order.vendor.ukey=[Ljava.lang.String;@73b72, order.vendor.username=[Ljava.lang.String;@cfefc0, token=[Ljava.lang.String;@18526aa}
10:27:59,684 DEBUG [OgnlParametersProvider] Applying id with [123]
10:27:59,685 DEBUG [OgnlParametersProvider] Applying vendor.email with [[email removido]]
10:27:59,685 DEBUG [OgnlParametersProvider] Applying vendor.username with [blabla]
10:27:59,686 DEBUG [OgnlParametersProvider] Applying client.name with [66]
10:27:59,686 DEBUG [OgnlParametersProvider] Applying client.ukey with [66]
10:27:59,686 DEBUG [OgnlParametersProvider] Applying vendor.ukey with [66666]
10:27:59,687 DEBUG [OgnlParametersProvider] Applying client.username with [66]
{_format=[Ljava.lang.String;@5e9a94, items[0]=[Ljava.lang.String;@f66abc, items[0].deffault=[Ljava.lang.String;@1d97efc, items[0].imageUrl=[Ljava.lang.String;@1ff323, items[0].ukey=[Ljava.lang.String;@193b022, order.client.name=[Ljava.lang.String;@110b205, order.client.ukey=[Ljava.lang.String;@1178281, order.client.username=[Ljava.lang.String;@78f83a, order.id=[Ljava.lang.String;@1f0cbfb, order.vendor.email=[Ljava.lang.String;@1dfe453, order.vendor.ukey=[Ljava.lang.String;@73b72, order.vendor.username=[Ljava.lang.String;@cfefc0, token=[Ljava.lang.String;@18526aa}
10:27:59,688 DEBUG [OgnlParametersProvider] Applying [0].deffault with [true]
07/12/2011 10:27:59 org.apache.catalina.core.StandardWrapperValve invoke
GRAVE: Servlet.service() for servlet [default] in context with path [/ws] threw exception
net.vidageek.mirror.exception.ReflectionProviderException: could not invoke constructor public br.com.models.dtos.Buyable() on class br.com.models.dtos.Buyable
at net.vidageek.mirror.provider.java.PureJavaConstructorReflectionProvider.instantiate(PureJavaConstructorReflectionProvider.java:40)
at net.vidageek.mirror.invoke.ConstructorHandlerByConstructor.withArgs(ConstructorHandlerByConstructor.java:46)
at net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withArgs(ConstructorHandlerByArgs.java:36)
at net.vidageek.mirror.invoke.ConstructorHandlerByArgs.withoutArgs(ConstructorHandlerByArgs.java:32)
at br.com.caelum.vraptor.http.ognl.ListNullHandler.instantiate(ListNullHandler.java:50)
at br.com.caelum.vraptor.http.ognl.ReflectionBasedNullHandler.nullPropertyValue(ReflectionBasedNullHandler.java:59)
at ognl.ASTProperty.getValueBody(ASTProperty.java:118)
at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at ognl.SimpleNode.getValue(SimpleNode.java:236)
at ognl.ASTChain.setValueBody(ASTChain.java:222)
at ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)
at ognl.SimpleNode.setValue(SimpleNode.java:279)
at ognl.Ognl.setValue(Ognl.java:737)
at ognl.Ognl.setValue(Ognl.java:783)
at br.com.caelum.vraptor.http.ognl.OgnlFacade.setValue(OgnlFacade.java:100)
at br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.setProperty(OgnlParametersProvider.java:161)
at br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createParameter(OgnlParametersProvider.java:133)
at br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.getParametersFor(OgnlParametersProvider.java:85)
at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.getParametersFor(ParametersInstantiatorInterceptor.java:105)
at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:77)
at br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.interceptors.AuthorizationInterceptor.intercept(AuthorizationInterceptor.java:39)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:48)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.interceptor.ExceptionHandlerInterceptor.intercept(ExceptionHandlerInterceptor.java:71)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:83)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:69)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.core.EnhancedRequestExecution.execute(EnhancedRequestExecution.java:44)
at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58)
at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at br.com.filter.GambiFilter.doFilter(GambiFilter.java:25)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:964)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.InstantiationException
at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at net.vidageek.mirror.provider.java.PureJavaConstructorReflectionProvider.instantiate(PureJavaConstructorReflectionProvider.java:34)
... 61 more

Lucas_Cavalcanti

ok, tentativa número 2 :wink:

remova o filtro

copie essa classe para a sua aplicação, qqer lugar:

anote a classe com @Component

primeiro veja se o mapa requestNames está ordenado. Se não estiver, mude o método parametersThatStartWith mudando a última linha pra:

return new TreeMap<String, String[]>(requestNames);

se estiver mude no método createParameter o for que itera em entries, tente fazer algo do tipo:

for (Entry<String, String[]> parameter : new TreeSet<Entry<String, String[]>(requestNames.entrySet())) {

acho que isso vai funcionar, daí a gente passa essa solução pro vraptor original

celsodantas

Resolvido! :smiley:

De fato, tava vindo desarrumado, e colocando isso:

private Map<String, String[]> parametersThatStartWith(String name) { Map<String, String[]> requestNames = filterKeys(request.getParameterMap(), containsPattern("^" + name)); return new TreeMap<String, String[]>(requestNames); }

resolveu.

e ai? eu faço o pullrequest ou vc atualiza lá?
por sinal, muito obrigado pela ajuda e disponibilidade!

Lucas_Cavalcanti

faz um pull request, por favor

Abraços.

Criado 9 de outubro de 2011
Ultima resposta 7 de dez. de 2011
Respostas 28
Participantes 3