Vraptor3: Forçando instanciação de objeto via converter

22 respostas
G

Tenho um converter para um objeto de paginação. Esse meu objeto de paginação nada mais é que um VO com os dados da página atual.

public class Paging {
    private int page;
}

Meu converter:

public Paging convert(String value, Class<? extends Paging> type, ResourceBundle bundle) {
    if (value == null || value.trim().isEmpty()) {
        return Paging.getInstance(); // create an empty paging
    }

    // create a paging with current page value
    return Paging.getInstance(Integer.valueOf(value));
}

No meu controller eu uso basicamente assim:

@Path( { "/admin/user/", "/admin/user/page-{paging}/" })
public void list(Paging paging) {
    [...]
}

Porém quando o URI for /admin/user/ esse objeto vem sempre nulo. Atualmente eu fico fazendo if para evitar nulos, mas eu quero poder inicializar esse objeto sempre, mesmo que o valor original seja nulo. Há como?

22 Respostas

Lucas_Cavalcanti

tem como sim… =) (pergunta difícil dessa vez heim garcia :P)

Tem um projeto paralelo que instancia os parâmetros… pra funcionar vc precisa criar o arquivo (ou colocar no seu customProvider):

@Component
public class CustomProvider extends SpringProvider {

	@Override
	protected void registerCustomComponents(ComponentRegistry registry) {
		registry.register(ParametersProvider.class, IogiParametersProvider.class);
		registry.register(Instantiator.class, VRaptorInstantiator.class);
	}
}

registrar isso no web.xml e colocar o iogi.jar no sistema (tá na pasta lib/optional do vraptor-core)

esse Iogi é bem melhor que o ognl em vários sentidos, mas ele não está totalmente estável. A gente usa em 2 projetos grandes e ainda não deu problemas… com ele os parâmetros são sempre instanciados…

outro jeito de fazer isso é descobrir um jeito de hackear o Ognl pra ele sempre instanciar os parâmetros… com o iogi é mais fácil :wink:

G

Lucas, acabou o round de perguntas difíceis, hehehe.

Hmm, eu tinha visto uma issue sobre o Iogi. Ele instancia tudo por padrão? Aí fico com uma dúvida… no caso se eu tiver um parametro Customer e no meu JSP uma propriedade customer.name ele sempre vai instanciar customer mesmo que name seja null? E no caso se eu nem sequer tiver o customer na tela mas tiver como parametro ele vai instanciar?

Abraços

Lucas_Cavalcanti

sem problemas, pode fazer perguntas difíceis :wink: hehehe
ele faz a mesma coisa que o Ognl, mas usa uma estratégia melhor… Teoricamente ele tem o mesmo comportamento que o Ognl (tem vários testes no VRaptor pra garantir isso ;))

então sim… isso vai funcionar… caso não funcione me fale =)

G

Não achei o Instantiator.class. Estou com o vraptor baixado sexta-feira do github. 3.0.3-snapshot.

Lucas_Cavalcanti

tá no iogi.jar… vc precisa colocar ele no seu WEB-INF/lib

G

Lucas, fiz os testes aqui e foi melhor que o esperado.

Notei que ele instancia automaticamente mesmo os parametros, e sempre chamando os converters se houverem. No caso do meu converter que passei no primeiro post ele chega com value == null, assim eu posso tratar da forma que eu quiser. No meu caso eu instancio a primeira página.

Quando chamei meu método edit(Long) passando o parametro como null ele então passou como nulo corretamente. Fiquei com medo dele passar um new Long(0) ou algo assim. Era o que eu esperava.

Só fiquei com dúvida se ele instancia sempre o objeto ou ele tenta apenas invocar o converter se existir. No caso, explicando melhor, se eu tiver um método store(Customer) onde customer for null, o Iogi mantém ele null ou instancia um new Customer()?

G

Ao tentar salvar um objeto estou tomando uma exception. Fazendo debug isso ocorre entre o post da página e antes de entrar no método.

As classes envolvidas são essas abaixo. Noto que em quase todas telas tudo funciona bem, porém essa aqui dá esse erro. Lucas, será que o Iogi está se perdendo em setar propriedades aninhadas?

@Resource
public class MessageCenterController {

    public void send(MessageDTO message) {
        result.onErrorUse(getClass()).editNew();

        messageService.sendMessage(message);
        result.use(Results.logic()).redirectTo(getClass()).list(MessageFolder.INBOX, null);
    }
}
public class MessageDTO implements Serializable {
    private Long id;
    private UserDTO from;
    private UserDTO to;
    private UserDTO owner;
    private String subject;
    private String content;
    private Date sentDate;
    private boolean read;
    private MessageDTO related;
}
SEVERE: StandardWrapperValve[default]: PWC1406: Servlet.service() for servlet default threw exception
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [java.lang.Long] is defined: no bean for this type registered
	at br.com.caelum.vraptor.ioc.spring.VRaptorApplicationContext.getBean(VRaptorApplicationContext.java:217)
	at br.com.caelum.vraptor.ioc.spring.SpringBasedContainer.instanceFor(SpringBasedContainer.java:61)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator$VRaptorDependencyProvider.canProvide(VRaptorInstantiator.java:99)
	at br.com.caelum.iogi.DependenciesInjector.canObtainDependenciesFor(DependenciesInjector.java:20)
	at br.com.caelum.iogi.reflection.Target.canInstantiateOrInject(Target.java:111)
	at br.com.caelum.iogi.reflection.Target.compatibleConstructors(Target.java:101)
	at br.com.caelum.iogi.ObjectInstantiator.instantiate(ObjectInstantiator.java:44)
	at br.com.caelum.iogi.MultiInstantiator.instantiate(MultiInstantiator.java:21)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:64)
	at br.com.caelum.vraptor.http.iogi.IogiParametersProvider.instantiateOrAddError(IogiParametersProvider.java:65)
	at br.com.caelum.vraptor.http.iogi.IogiParametersProvider.instantiateParameters(IogiParametersProvider.java:58)
	at br.com.caelum.vraptor.http.iogi.IogiParametersProvider.getParametersFor(IogiParametersProvider.java:45)
	at br.com.caelum.vraptor.interceptor.
ParametersInstantiatorInterceptor.getParametersFor(ParametersInstantiatorInterceptor.java:83)
	at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:68)
	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.InstantiateInterceptor.intercept(InstantiateInterceptor.java:42)
	at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at esim.web.interceptor.NoCacheInterceptor.intercept(NoCacheInterceptor.java:49)
	at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at esim.web.security.SecurityUserSession.intercept(SecurityUserSession.java:105)
	at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41)
	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 esim.web.interceptor.CustomRequestExecution.execute(CustomRequestExecution.java:41)
	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:246)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
	at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)
	at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:288)
	at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:647)
	at com.sun.enterprise.web.connector.grizzly.ssl.SSLReadTask.process(SSLReadTask.java:440)
	at com.sun.enterprise.web.connector.grizzly.ssl.SSLReadTask.doTask(SSLReadTask.java:228)
	at com.sun.enterprise.web.connector.grizzly.ssl.SSLWorkerThread.run(SSLWorkerThread.java:106)
Lucas_Cavalcanti

então… o Iogi é uma sigla pra Immutable Object Graph Instantiator, ou seja, ele foi feito pra instanciar
objetos imutáveis. A consequencia disso é que ele tenta usar o construtor da sua classe que tem mais
argumentos (ele é espertinho e consegue descobrir como popular pelos parametros da requisição…)

E ele usa injeção de dependências nos seus parâmetros tb (vc pode receber daos nos seus parâmetros
e implementar um Active Record!)

E por enqto, qdo ele não consegue achar o parâmetro na requisição, ele tenta instanciar pelo container
do VRaptor… não achou a propriedade que é Long na requisição, e fez um container.instanceFor(Long.class)
que claramente não funciona…

pra corrigir isso, por enqto, vc tem que tirar os construtores, ou diminuir a visibilidade deles… (feio, eu sei =()
mas se for soh esse caso acho que não vai ter mto problema… vou tentar corrigir isso aqui assim que
possível…

G

Lucas, vou então seguir usando o provider tradicional por enquanto.

Abraços

Lucas_Cavalcanti

=( ok… a gente só não colocou o Iogi como padrão pq ele tá meio instável… mas em breve ele vai estar estável e a gente vai fazer isso.

[]'s

G

Lucas, ressucitando o tópico… agora preciso mesmo converter os parametros mesmo que seja null. Como estão os trabalhos com o iogi, já dá para usar sem aquele erro?

Lucas Cavalcanti:
pra corrigir isso, por enqto, vc tem que tirar os construtores, ou diminuir a visibilidade deles… (feio, eu sei =()
mas se for soh esse caso acho que não vai ter mto problema… vou tentar corrigir isso aqui assim que
possível…

Então quer dizer que eu posso alterar meus DTOs e remover o contrutor que tem com o parametro Long, deixando apenas o default?

Lucas_Cavalcanti

acho que esse erro ainda não foi corrigido no iogi =(

sem o construtor que recebe o long, ou adicionando um construtor padrão talvez funciona…

G

Lucas, eu não achei o site do projeto IOGI. Onde eu encontro ele?

Abraços

Lucas_Cavalcanti

acho q o único site sobre o iogi por enquanto é esse:

ander.parra

Lucas, trocamos o OGNL pelo IOGI, estava indo tudo bem, precisamos corrigir algumas gambis que haviam sido criadas que o OGNL permitia.

Agora estamos com um problema.

Existe um parametro opcional no form que está sendo enviada como null e o IOGI dá erro porque não aceita null.

Isso é um BUG ou tem alguma forma de contornar?

Lucas_Cavalcanti

qual é o tipo do parâmetro? vc está enviando o value “null”?

ander.parra

Tenho uma estrutura semelhante a esse exemplo:

public class Aluno {
   private Cpf cpf;
   
   // get e set 
}

public class Cpf {
  private String valor;

  public Cpf(String cpf) {
    valor = cpf;
  }
  
  // get
}

No request está sendo enviado aluno.cpf=""

O Cpf do aluno é opcional.

Lucas_Cavalcanti

vc criou um converter pra cpf?

se não, tente trocar por aluno.cpf.cpf, que o iogi vai usar o construtor pra popular

G

O IOGI trabalha com os construtores (método padrão). Então você precisa criar os construtores com as possibilidades de campos que você tem na tela.

Ele tem um fallback que usa sets, porém quando você tem construtores com argumentos ele tenta usar esses construtores.

Mais ou menos assim. No meu caso eu fiz a primeira opção.

ander.parra

Então, meu objeto Cpf é imutável mas o aluno possui um Cpf opcional.

Se eu criar um contratutor default (ou um setter) para me adaptar a tela, meu objeto não vai ser mais imutável, o OGNL resolveria meu problema e não precisaria migrar para o IOGI, certo?

Lucas_Cavalcanti

vou tentar reproduzir o seu problema aqui, peraí

Lucas_Cavalcanti

nos meus testes aqui:

pessoa.cpf = 123232 => cpf null
pessoa.cpf.cpf = 1231313 => cpf 1231313
pessoa.cpf= => cpf null
pessoa.cpf.cpf= => cpf com valor null

dá pra fazer duas coisas:

  • criar um converter pra cpf
  • hackear o Iogi para ignorar os parâmetros vazios

o que vc prefere fazer?

Criado 10 de dezembro de 2009
Ultima resposta 24 de ago. de 2011
Respostas 22
Participantes 3