[Resolvido]Como popular lista de objetos imutáveis com VRaptor?

16 respostas
Rafael_Guerreiro

Boa tarde pessoal!

Na verdade os objetos não são completamente imutáveis.
Existem 2 atributos somente que podem ser alterados… Esse objeto tem um ID que é passado para a view e, da view, esse ID é devolvido com os 2 valores mutáveis preenchidos.

Somente com o ID, eu carrego os outros atributos imutáveis…

Minha dúvida está exatamente em como fazer isso…

Eu tentei criar um construtor para essa classe pegando somente o ID… Mas aí acontece uma exception no instanciador do VRaptor (IOGI)

br.com.caelum.vraptor.http.InvalidParameterException: Exception when trying to instantiate Target(name=objs, type=interface java.util.List)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.handleException(VRaptorInstantiator.java:95)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.handleException(VRaptorInstantiator.java:97)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:87)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:80)
	at br.com.caelum.vraptor.http.iogi.IogiParametersProvider.instantiateOrAddError(IogiParametersProvider.java:80)
	at br.com.caelum.vraptor.http.iogi.IogiParametersProvider.instantiateParameters(IogiParametersProvider.java:73)
	at br.com.caelum.vraptor.http.iogi.IogiParametersProvider.getParametersFor(IogiParametersProvider.java:63)
	at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.getParametersFor(ParametersInstantiatorInterceptor.java:126)
	at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:83)
	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.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.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.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.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
	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:91)
	at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58)
	at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88)
	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:225)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
	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:98)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1001)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:579)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:680)
Caused by: java.lang.NullPointerException
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator$VRaptorTypeConverter.setPropertiesAfterConversions(VRaptorInstantiator.java:143)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator$VRaptorTypeConverter.instantiate(VRaptorInstantiator.java:134)
	at br.com.caelum.iogi.MultiInstantiator.instantiate(MultiInstantiator.java:20)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:85)
	at br.com.caelum.iogi.collections.IndexedListInstantiator.instantiate(IndexedListInstantiator.java:34)
	at br.com.caelum.iogi.collections.ListInstantiator.instantiate(ListInstantiator.java:25)
	at br.com.caelum.iogi.collections.ListInstantiator.instantiate(ListInstantiator.java:10)
	at br.com.caelum.vraptor.http.iogi.NullDecorator.instantiate(NullDecorator.java:25)
	at br.com.caelum.iogi.MultiInstantiator.instantiate(MultiInstantiator.java:20)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:85)
	... 42 more

Se ficar dificil de entender meu problema, me avisa ai…

16 Respostas

DaniloAndrade

Boa Tarde Rafael,

coloca o codigo do seu Controller e seu jsp pra gente da uma olhada

Lucas_Cavalcanti

quais são os nomes dos parâmetros que vc tá passando na requisição?

Rafael_Guerreiro
Eu pensei em fazer algo desse tipo na view.
<c:forEach items="${fields}" var="obj" varStatus="status">
	<input type="hidden" name="objs[${status.index}]" value="${obj.id}"><!-- //para pegar o id da classe imutavel. Eu também tentei passando name="objs[${status.index}].id", mas não funcionou. -->
	<label for="${obj.id}" class="label-customer">${obj.label}</label>
<!-- //baseado no ID dessa classe, ela seria populada com os outros valores que estão em memória  no servidor -->
	<input type="text" id="${obj.id}" name="objs[${status.index}].value" class="component"
		style="width: ${obj.width}px;" maxlength="${obj.length > 0 ? obj.length : ''}" value="${obj.value}">
<!-- //Mas com os atributos mutáveis preenchidos pelo usuário... -->
</c:forEach>
Minha action:
@Post("/report/save")
	public void saveOrUpdate(List<InputField> objs) {
		logger.debug(objs.toString()); //  para ver se está vindo certo.
		result.use(Results.status()).accepted(); // Retorna 202 para o AJAX.
	}
Minha classe meio imutavel:
public class InputField implements Comparable<InputField> {

	private final String id;
	private final int index;
	private final String label;
	private final String name;
	private final int width;
	private final SearchField searchField;
	private final boolean insertable;
	private final boolean updatable;
	private final boolean nullable;
	private final int length;
	private final int precision;
	private final int scale;
	private final String datePattern;
	private final InputType inputType;

	private String value; // Mutavel por setter
	private String display; // Mutavel por setter

	public InputField(String id) { // Eu não quero ter esse construtor aqui, só criei para ver se o erro passava...
		this(id, 0, null, null, 0, null, false, false, false, 0, 0, 0, null,
				null);
	}

	public InputField(String id, int index, String label, String name,
			int width, SearchField searchField, boolean insertable,
			boolean updatable, boolean nullable, int length, int precision,
			int scale, String datePattern, InputType inputType) {
		// So atribui e verifica integridade
	}
	// getters
}

é isso.
Eu também pensei em criar um converter que recebia a classe preenchida com o ID e os valores mutáveis e ai eu pegaria a classe com o mesmo hash em memória e setaria os dois valores mutáveis...

existe alguma melhor forma de fazer isso?

Lucas_Cavalcanti

se vc está usando o iogi, ele consegue preencher os parâmetros do construtor… só passar inputs com names terminando com os nomes dos parâmetros do construtor, como se fossem setters.

Rafael_Guerreiro

Eu não configurei nada. Mas acredito que eu esteja usando IOGI sim… Pois o erro aconteceu no IOGI…

Então eu teria que ter todos esses atributos setados?

private final int index;  
    private final String label;  
    private final String name;  
    private final int width;  
    private final SearchField searchField;  
    private final boolean insertable;  
    private final boolean updatable;  
    private final boolean nullable;  
    private final int length;  
    private final int precision;  
    private final int scale;  
    private final String datePattern;  
    private final InputType inputType;

Não tem uma forma de eu setar alguns deles e, no java, eu seto esses, pois eles não fazem parte dessa requisição…

Lucas_Cavalcanti

então seu objeto não é imutável :wink: não deveriam estar no construtor

coloque no construtor só os objetos que são obrigatórios… e os outros vc preenche por setters

Rafael_Guerreiro

Hmm… Eu precisava que eles fossem imutáveis…
Portanto coloquei todos na requisição e funcionou bem demais…

Muito obrigado!

Lucas_Cavalcanti

vc pode colocar mais construtores, mas isso deixa o modelo um pouco complicado…

não seria interessante vc agrupar alguns desses fields em classes que fazem sentido?

Rafael_Guerreiro

Na verdade todos eles fazem sentido dentro da classe em que estão…
Essa classe (que terá o nome refatorado) contém as configurações de uma coluna no banco de dados.
O que eu queria, na verdade, era minimizar essa requisição em específico…

Se eu criar uma outra classe somente com ID, Display e Value e dessa eu converto para a correta, me parece gambiarra…

Lucas_Cavalcanti

Não foi isso que eu sugeri… foi modificar a classe InputField:

public class InputField implements Comparable<InputField> {  
  
    private final String id;  

    // criar uma classe Constraints com isso
    private final int index;
    private final boolean insertable;  
    private final boolean updatable;  
    private final boolean nullable;  

    //classe FormConfig com isso
    private final InputType inputType;  
    private final String label;  
    private final String name;    
    private final SearchField searchField;  

    // classe TypeConfig com isso
    private final int width;
    private final int length;  
    private final int precision;  
    private final int scale;  
    private final String datePattern;

e um construtor que recebe 3 coisas, ao invés de 15… daí vc conseguiria fazer os construtores sem um dos caras passando o valor pros outros de um jeito mais fácil
mas a InputField continuaria tendo como atributo essas classes novas, sem precisar de conversão pra nada

Rafael_Guerreiro

Não, aquilo lá foi uma das soluções que eu pensei. Mas é muito feia.

Eu entendi essa sua modificação na classe, achei até interessante, mas não entendi como que eu faria para resolver o problema de ter que passar todos os atibutos…
Eu teria que fazer assim: objs[${status.index}].constraints.index para poder setar o index em constraint…

Pela organização, realmente é melhor e eu vou fazer isso.

  • EDIT -
    Eu não entendi essa parte:
    daí vc conseguiria fazer os construtores sem um dos caras passando o valor pros outros de um jeito mais fácil
    mas a InputField continuaria tendo como atributo essas classes novas, sem precisar de conversão pra nada
Lucas_Cavalcanti

ao invés de ter um:

public InputField(<5 params>) {
     this(<15 valores default>);
}

vc faria:

public InputField(Constraints constraints) {
     this(constraints, new FormConfig(), new TypeConfig());
}

e em cada uma das classes vc faria um construtor que tem os valores default…

talvez seja melhor nem ser um construtor e sim uma static factory tipo: FormConfig.withDefaultValues();

Rafael_Guerreiro

Então, mas o problema é que não existem valores Default… Pois essa classe é a representação de uma coluna no banco, então o sistema inspeciona o banco e gera essas classes.
Essas classes precisam ser populadas com o que o sistema gerou para elas.

Eu tentei fazer com o construtor somente com o ID da classe para que eu pudesse resgatar o ID e popular os atributos mutáveis… Não deu certo…

Mas fazer isso que eu tentei é uma prática ruim? Teria como eu fazer um converter para ela e no converter eu recebo o ID e pego a classe que contém esse ID e depois eu configuro os 2 atributos mutáveis?

Lucas_Cavalcanti

dá sim… cria um converter pra classe e na hora de passar o parâmetro na requisição vc faz sem o .id no final

Rafael_Guerreiro

Então, eu tinha feito isso… Mas ai lançou aquela exception lá em cima. E ele nem chega no converter…

Lucas_Cavalcanti

o nullpointer pode ter sido em outra coisa…

tenta fazer um teste:

public void teste(InputField input) {...}
public void teste2(List<InputField> inputs) {...}

e mandar na requisição:

/teste
input = id do maluco  

/teste2
inputs[0] = um id
inputs[1] = outro id

e vê se ainda dá o nullpointer

Criado 27 de julho de 2012
Ultima resposta 30 de jul. de 2012
Respostas 16
Participantes 3