VRaptor controller usando interfaces

15 respostas
andre_udi

Olá pessoal,

vou tentar explicar a minha situação. Hoje utilizamos o vraptor na nossa “camada de serviços” http.
São serviços simples, que fazem a integração com diversos serviços legados da empresa.
Em um novo projeto, que usará vraptor também para a parte web, surgiram algumas idéias na equipe para facilitar o desenvolvimento.
Uma delas é extrair uma interface dos controladores e, no caso do cliente ser também java, utilizar essa interface para construir um proxy que faça a chamada real, via http.
Começamos a experimentar essa idéia, pois em tese o cliente ficaria + “simples”, como por exemplo:

//acessa o resource cliente e o salva:
clients.save( newClient )

Tivemos alguns problemas:

Com isso, o vraptor não conseguue extrair informações dos métodos. Alteramos as annotations para que possam ser herdadas.
Os testes passaram, mas existe algum motivo para que elas não sejam passíveis de serem herdadas?

  • O vraptor registra a interface como controlador.

O vraptor encontra a interface. No nosso caso, estamos utilizamos spring. Quando o vraptor tenta obter o controlador para processar a request,
o spring não consegue instanciar a interface e lança uma exceção.
Ao fazer scanner o vraptor usa uma AnnotationDB, que já retorna a interface com as annotations.

Pra resolver, registro a implementação do controller no spring com um @Component e alterei a a implementação do SpringBasedContainer de:

Map<String, T> beans = parentContext.getBeansOfType(type);
for (Entry<String, T> def : beans.entrySet()) {
   BeanDefinition definition = parentContext.getBeanFactory().getBeanDefinition(def.getKey());
   if (isPrimary(definition) || hasGreaterRoleThanInfrastructure(definition)) {
        return def.getValue();
}

para:

String[] beanNamesForType = parentContext.getBeanNamesForType(type);
for( String beanName : beanNamesForType ){
    BeanDefinition definition = parentContext.getBeanFactory().getBeanDefinition(beanName);
    if (notAnInterface(beanName) && isPrimary(definition) || hasGreaterRoleThanInfrastructure(definition)) {
        return (T) parentContext.getBean(beanName);
    }
}

Desta forma, quando o vraptor depara com uma interface, procura por uma implementação que a satisfaça.

Seria essa a melhor forma de atingir esse objetivo?

  • Vraptor não consegue obter nomes de parâmetros.
    Tivemos que utilizar a @Named nos parâmetros da interface.

Claramente, esta abordagem de utilização tem também pontos fracos, como:

[list]Acomplamento do projeto cliente com as interfaces;[/list]
[list]Código “mágico” realizando as requisições - apesar de bem testado e com boas mensagens de erro, pode ser ruim debugar ou procurar erros;[/list]
[list]Se utilizarmos objetos como parâmetros dos controladores (o que acho muito bacana no vraptor), o cliente também deverá conhecê-los[/list]

O que vocês acham dessa abordagem? A simplicidade de uso compensa as situações citadas acima?

Mais uma vez, parábens pelo excelente projeto e obrigado pela ajuda.

15 Respostas

Lucas_Cavalcanti

a motivação pra criar a interface é pra chamar métodos da sua aplicação a partir de outro sistema? ou seja, pra criar wrappers http pra chamar os métodos?

O que vc pode fazer:

  • colocar o @Resource na implementação, e não na interface, daí não precisa mudar nada no spring
  • mudar o PathAnnotationsRoutesParser pra procurar anotações na interface (vc deve ter feito algo do tipo)

o acoplamento com a interface é sempre fraco (vc pode até evoluir o servidor acrescentando métodos, que o cliente não vai quebrar)

quanto a receber objetos nos métodos do controlador, o cliente só precisa ter uma classe com os dados que ela quer passar, não precisa ser exatamente a mesma, ou seja, vc pode passar uma versão da classe, e se vc evoluir essa classe não vai necessariamente quebrar o cliente.

qto a esse código mágico que vc está citando, isso acontece com qqer tecnologia remota, como EJB, WS, RMI, etc… Na verdade o que vc quer fazer seria mais fácil fazer com EJBs, integrando com o VRaptor. assim vc teria uma camada de serviços feitos em EJB, e o VRaptor seria só a frente web dessa camada, delegando tudo pros ejbs. Desvantagem é que vc precisa de um appserver (JBoss por exemplo). O garcia-jj já fez um projeto grande assim

vc pode compartilhar esse código num http://gist.github.com pra eu dar uns pitacos?

andre_udi

Olá Lucas,

obrigado pela resposta =)

Sim, a idéia é criar um wrapper que facilite a chamada de métodos.
Vou alterar a PathAnnotationsRoutesParser entaão =).

O acoplamento com interfaces eh fraco sim. Mas como a ideia é reutilizar a interface, acaba q é necessário conhecer os mesmos objetos que o controlador recebe.
Justamente para q o cliente consiga montar a requisicao utilizando as anotacoes do vraptor.

Qual trecho vc quer q eu crie um gist?

Ainda hoje, assim que puder, coloco o projeto no github e posto o link aqui. Pitacos são sempre bem vindos =)

Lucas_Cavalcanti

o link do github ja serve =)

andre_udi

Olá Lucas,

ficou muito simples implementando um route parser customizado.

segue o gist com a implementação:

depois posto o cliente.

[]'s

Lucas_Cavalcanti

muito bom mesmo =)

usar o @Resource na implementação funcionou?

andre_udi

Funcionou perfeito.
Só não consigo escapar do @Named na interface, com os parametros.
Alguma idéia?

Lucas_Cavalcanti

tem outro componente, o ParameterNamesProvider. O default é o ParanamerNamesProvider, tenta estender ele e ver quais valores ele tá retornando… acho que o problema é q os nomes dos parâmetros não ficam na interface, só na implementação.

o que vc pode tentar fazer também é ao inves de retornar o ResourceMethod dos métodos da interface, dar um jeito de fazer isso com os métodos da implementação.

andre_udi

Olá Lucas,

consegui fazer uma implementação que, quando solicita o nome pra um método de interface, traz a implementação do mesmo.

Tá aqui ó:

Só que, no lado do cliente, eu n consigo obter os nomes dos parâmetros dos métodos, logo, pra idéia inicial, não ajuda muito.

[]'s

Lucas_Cavalcanti

vc tá usando o paranamer no cliente?

andre_udi

Sim.

Mas a paranamer não encontra os nomes dos parâmetros, e fica solicitando compilação com -g. Pelo que pesquisei, mesmo com -g, os nomes não são preservados na
interface.

Lucas_Cavalcanti

=(
só com o @Named mesmo, então…

andre_udi

Uma versão inicial do cliente já tá no github:

https://github.com/andrerigon/vraptor-client

só tá funcionando com @Path e @Get, e ainda tem várias coisas pra melhorar. Mas já dá pra ter uma idéia =D

D

Isso é interessante, fazendo mais alguns ajustes dá até para usar com GWT, usando RPC. Não que não daria para usar vraptor com gwt, é que trabalhar com json com GWT não achei muito interessante.
Vou continuar acompanhando a discussão.

jayrmotta

Davis,

Sem querer perder o foco da discussão, mas fiquei curioso em relação ao seu comentário. Porque você diz que não acha interessante trabalhar com JSON e GWT? Na minha cabeça a implementação ideal para uma aplicação web é aquela onde não se trafega html entre o cliente e o servidor ao decorrer de diversas requisições, preferencialmente só na primeira, o resto é manipulado por javascript e em casos onde se justifica, requisitar novos módulos (conceito do GWT). Eu sei que existem formas de se trabalhar com JSON no GWT, apesar de dizerem que o serializador/deserializador dele ser excelente.

A questão é, o vraptor-client é excelente pra quem usa o vraptor para implementar serviços REST pois permite que um cliente conheça os serviços sem conhecer a implementação, garante a verificação de tipos de forma estática e etc. Estamos usando ele aqui e estamos muito felizes com o resultado, acredito que da pra evoluir ele em diversos aspectos, pra ficar mais fiél ao vraptor, usem ele e cadastrem issues no github, será bom para a evolução dele! :slight_smile:

D

Também gosto muito de trabalhar com json, quando não estou num ambiente GWT, até agora usei o GWT somente para protótipos e testes.

A questão é que eu ainda não achei um serializer/deserializer json para GWT o bom o suficiente para me convencer, então trabalhar com RPC ainda é o mais simples. Agora tem o RequestFactory que eu ainda não cheguei estudar/testar, então não sei dizer a respeito.

Mais realmente, a idéia do projeto é muito boa.

Criado 16 de agosto de 2011
Ultima resposta 21 de set. de 2011
Respostas 15
Participantes 4