VRaptor + Restfulie [Resolvido]

24 respostas
D

Chegou a necessidade de consumir os controllers que estão como restful no vraptor, se eu acesso a uri no navegador, me aparece o documento xml correto, agora se na hora em que vou consumir esse webservice esta dando esse erro:

Exception in thread "main" com.thoughtworks.xstream.mapper.CannotResolveClassException: categoriaveicular : categoriaveicular at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:68) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:71) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.PackageAliasingMapper.realClass(PackageAliasingMapper.java:88) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:86) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:96) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38) at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:52) at com.thoughtworks.xstream.core.util.HierarchicalStreams.readClassType(HierarchicalStreams.java:31) at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:136) at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:33) at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:923) at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:909) at com.thoughtworks.xstream.XStream.fromXML(XStream.java:853) at com.thoughtworks.xstream.XStream.fromXML(XStream.java:845) at br.com.caelum.restfulie.XStreamDeserializer.fromXml(XStreamDeserializer.java:32) at br.com.caelum.restfulie.http.DefaultResponse.getResource(DefaultResponse.java:61) at br.com.caelum.restfulie.EntryPointService.get(EntryPointService.java:107) at com.teste.principal.Principal.main(Principal.java:13)

24 Respostas

D

Como o cliente será uma aplicação mobile, o client restifulie foi descartado.

A minha pergunta agora seria, como já existe aplicações usando SOAP como webservice, tem como através de SOAP fazer chamadas REST?
Dessa forma eu utilizo o Restifulie no server-side.

Alex_Basto

davisnog:
Como o cliente será uma aplicação mobile, o client restifulie foi descartado.

A minha pergunta agora seria, como já existe aplicações usando SOAP como webservice, tem como através de SOAP fazer chamadas REST?
Dessa forma eu utilizo o Restifulie no server-side.

Na sua pergunta eu não consigo entender a que conceitos você quer dizer sobre SOAP para REST, você quer empregar invocações para que especificação ? Na sua observação o Client Restifulie fora descarto “Implementação a que ?”.

G

Não sei como você está gerando o webservice, porém pelo erro é quando você esquece de colocar um alias para o bean. No caso o erro não parece ser no webservice, mas sim no seu cliente que consome o xml.

Quando você declara suas classes java precisa apontar quais atributos serão convertidos em qual classe. No caso o xstream usa ou o nome completo da classe ou então você precisa definir o alias manual, seja via código ou annotation.

D

Alex Basto seguinte, hoje eu gero os webservices com o restifulie, biblioteca disponibilizada pelo pessoal da caelum, junto com o vraptor, nesse caso os metodos de meus controllers são consumidos via webservice REST.

Mais, as aplicações mobile que temos hoje, consomem webservices no padrão SOAP, a minha pergunta era, seria possivel consumir os REST através de SOAP?

D

garcia-jj, as minhas classes model quanto a minha classe cliente, nos dois casos CategoriaVeicular, estão com alias do xstream.
Mais como eu havia dito, pra mobile ficou inviavel utilizar o restifulie como client, no caso será utilizado somente no server.

D

Aproveitando a oportunidade.

Hoje tenho minha classe model no projeto web, com as annotations do hibernate, por exemplo CategoriaVeicular.

No outro projeto mobile tem mesma classe, mais sem annotations, pois ela é utilizada para receber os dados do webservice.

Nesse caso eu tenho nos dois projetos com a mesma classe CategoriaVeicular, a diferença é que no projeto web eu tenho as dependências do hibernate e no mobile não.

Minha pergunta seria: Tem alguma sugestão para que eu possa gerar um jar das classes model e disponibilizar para as duas aplicações, nesse caso as alterações seriam todas feitas no projeto web, e dessa forma qualquer alteração necessária seria feita num unico projeto.

G

Hmm, você pode nos mostrar como está seu cliente? Apenas a classe que consome.

D

Codigo no client, essa é uma aplicação console que esta consumindo o webservice.

@XStreamAlias("categoriaveicular")
public class CategoriaVeicular {

	private int codigo; 
	private String descricao;
	private Situacao situacao;
//gets e sets removidos
}
public class PrincipalTeste{
        public static void main(String[] args) throws URISyntaxException{
		CategoriaVeicular categoriaVeicular = Restfulie.resource("http://localhost:8080/categoriaveicular/1").get();
	}
}

No servidor a diferença é que tem as annotations do hibernate.

G

davisnog:
Aproveitando a oportunidade.

Hoje tenho minha classe model no projeto web, com as annotations do hibernate, por exemplo CategoriaVeicular.

No outro projeto mobile tem mesma classe, mais sem annotations, pois ela é utilizada para receber os dados do webservice.

Nesse caso eu tenho nos dois projetos com a mesma classe CategoriaVeicular, a diferença é que no projeto web eu tenho as dependências do hibernate e no mobile não.

Minha pergunta seria: Tem alguma sugestão para que eu possa gerar um jar das classes model e disponibilizar para as duas aplicações, nesse caso as alterações seriam todas feitas no projeto web, e dessa forma qualquer alteração necessária seria feita num unico projeto.

Davi, você pode usar algum gerador que sempre que você altere a classe ele crie para você a classe do projeto mobile. Mas minha sugestão é fazer manual mesmo, pois essa coisa de classe persistente ser espelho de classe de transferencia de dados não me parece bom. Obvio que isso varia de projeto a projeto, mas não curto muito (minha opinião).

Penso assim porque nem sempre você tem as classes exatamente iguais. Uma coisa é a sua estrutura de tabelas e outra é o que você precisa trafegar e exibir para o cliente.

Lucas_Cavalcanti

Então… mesmo definindo os Alias via anotações, vc precisa registrar as classes no XStream… tem algo como xstream.readAnnotations(CategoriaVeicular.class) ou coisa do tipo…

qto a pergunta se tem como consumir REST via SOAP, acho que não dá… o formato das msgs soap é bem diferente (tem cabeçalho, corpo e tal)

G

davisnog:
Codigo no client, essa é uma aplicação console que esta consumindo o webservice.
[…]
No servidor a diferença é que tem as annotations do hibernate.

Davi, me parece normal sua classe mesmo. Mande o envelope do XML para comparar?

Abraços

G

Não, não dá mesmo. Rest além de você possuir aqueles estados que o SOAP também tem, no rest você não possui um contrato formal, portanto seu XML é assim como você quiser, por exemplo:

<caelum-projetos> <projeto name="vraptor3" /> <projeto name="restfulie" /> </caelum-projetos>

Já no SOAP você tem contratos fortes entre o serviço e o consumidor. Todo SOAP possui um WSDL que define esse contrato e garante que todo mundo saiba como consumir esse webservice. O webservice possui um envelope com uma série de informações além do seu próprio XML de retorno.

D

Bom a classe envelope se eu entedi é essa:

@Component
@RequestScoped
public class XmlSerializer extends RestfulSerialization {

	public XmlSerializer(HttpServletResponse response,
			TypeNameExtractor extractor, Restfulie restfulie,
			Configuration config) {
		super(response, extractor, restfulie, config);
	}
	
	@Override
	protected XStream getXStream() {
		XStream instance = super.getXStream();
		instance.processAnnotations(CategoriaVeicular.class);
		return instance;
	}
}
Guilherme_Silveira

Bom dia Davi, Garcia,

Realmente a exception parece não estar usando o XStream com a configuração que voce citou. Mas como no cliente não poderá usar o restfulie por ser celular, deixamos de lado. Se aparecer o problema novamente, poste o código do cliente por aqui?

Sobre SOAP consumir REST, como as mensagens em REST geradas pelo serializador não está envelopada em soap por padrão, não funcionará.
Mas a questão da arquitetura REST não possuir um contrato formal é um pouco diferente. Você pode - e muitos dizem que deve - criar xsd’s ou utilizar o schematron para fazer a validação de sua estrutura xml e REST não te proibe de faze-lo, basta adicionar o cabeçalho com link para seu schema:

<!-- cabecalho para seu xsd aqui -->
 <caelum-projetos>  
     <projeto name="vraptor3" />  
     <projeto name="restfulie" />  
 </caelum-projetos>

Tem um post aqui sobre o assuntO: http://guilhermesilveira.wordpress.com/2009/12/08/hypermedia-making-it-easier-to-create-dynamic-contracts/

O REST também possui as informações (metadados) que o soap define em diversos momentos, mas utiliza cabeçalhos http (o protocolo em si) para passar parte dessas informações.

Na parte de criar uma classe anotada/não anotada para ser usada nos dois lugares, algumas pessoas vão recomendar não usar a mesma classe mesmo: se você disponibiliza a mesma classe para os dois lados, você aumenta o acoplamento entre os dois lados. Permitindo cada lado ter uma representação diferente (com os campos que interessa para cada um) diminui o acoplamento, mas isso só faz sentido se você for disponibilizar para outros clientes diferentes.

Abraço

D

ainda sobre a classe anotada/não anotada. Somente o projeto web poderá fazer alterações, o mobile somente vai ter essas classes para receber os dados do webservice.

Como o Garcia falou de um gerador, alguem conhece algum?

D

Fazendo uns testes aqui, lembrei que posso utilizar Results.representation(), mais dai eu não consegui utilizar as url como eu usaria se não precisasse passar _format=xml, antes eu fazia assim http://localhost:8080/Testes/categoriaveicular/1, com eu preciso passar o formato eu tenho que fazer assim http://localhost:8080/Testes/categoriaveicular.buscar?_format=xml&categoriaVeicular.codigo=1.

Tem uma forma de fazer isso usando o metodo padrão de url?

Eu pensei em algo assim:

http://localhost:8080/Testes/categoria-veicular/formato/parametro, no caso se não tiver o formato seria html normal.

M

davisnog:
Fazendo uns testes aqui, lembrei que posso utilizar Results.representation(), mais dai eu não consegui utilizar as url como eu usaria se não precisasse passar _format=xml, antes eu fazia assim http://localhost:8080/Testes/categoriaveicular/1, com eu preciso passar o formato eu tenho que fazer assim http://localhost:8080/Testes/categoriaveicular.buscar?_format=xml&categoriaVeicular.codigo=1.

Tem uma forma de fazer isso usando o metodo padrão de url?

Eu pensei em algo assim:

http://localhost:8080/Testes/categoria-veicular/formato/parametro, no caso se não tiver o formato seria html normal.

Só fica difícil chamar isso de REST depois.

Falando nisso, qual o ganho em adicionar o framework restfulie ao vraptor neste caso, com tantas limitações no lado cliente?

D

O Restfulie seria utilizado somente do server, o ganho seria com o controle de transação no rest, mais eu acho que isso já é feito pelo proprio vraptor, sem a necessidade do restfulie.

Guilherme_Silveira

O controle de transacao dentro de uma requisicao realmente é interno e nao depende do Restfulie.
A vantagem do restfulie entra na parte do hypermedia… se nao for necessario ele, so o representation deve ser suficiente!

Abraco

D

Nessa altura do campeonato acho que já não haveria a necessidade do Restifulie, pois o ganho mesmo seria no client, mais isso não será o caso.

Ainda não gostei da forma que tenho que fazer para usar o representation, eu gostaria de poder utilizar o mesmo metodo para as mais de uma situação, por exemplo na tela eu poderia utilizar json para ajax, xml como webservice, ou somente utilizar um include(“meuObjeto”, meuObjeto); e utilizar isso numa tabela por exemplo.

Lucas_Cavalcanti

davisnog:
Fazendo uns testes aqui, lembrei que posso utilizar Results.representation(), mais dai eu não consegui utilizar as url como eu usaria se não precisasse passar _format=xml, antes eu fazia assim http://localhost:8080/Testes/categoriaveicular/1, com eu preciso passar o formato eu tenho que fazer assim http://localhost:8080/Testes/categoriaveicular.buscar?_format=xml&categoriaVeicular.codigo=1.

pq vc precisa mudar tanto sua url soh pra incluir o formato?
http://localhost:8080/Testes/categoriaveicular/1?_format=xml já é o suficiente…

e se vc tiver a possibilidade, nem precisa colocar o _format… vc pode colocar na requisição o header:
Accept: application/xml

usando a URI padrão: http://localhost:8080/Testes/categoriaveicular/1

D

Perfeito Lucas, funcionou, com Accept: application/xml.

antes eu tinha testado e imprimia o conteudo no console para teste, e retirando essa impressão do console, o xml é gerado somente com o proxy da classe, não aparecendo os dados. Agora se eu imprimo qualquer informação desse objeto no console, dessa forma os dados no xml é gerado normalmente.
Pelo que eu pude perceber, eu tenho que chamar algum metodo do meu objeto antes de gerar o xml.

somente mais uma duvida, para eu utilizar outros metodos delete, e put, seria dessa forma:

http://localhost:8080/Testes/categoriaveicular/1?_method=DELETE

Lembrando que essa chamada é feita pelo cliente do webservice.

Testando essa url no navegador me aparece esse erro:

java.lang.IllegalArgumentException: You can't use _method parameter on a GET request. Use POST instead. br.com.caelum.vraptor.resource.HttpMethod.of(HttpMethod.java:51) br.com.caelum.vraptor.http.DefaultResourceTranslator.translate(DefaultResourceTranslator.java:52) br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:64) 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.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)

Lucas_Cavalcanti

se vc tá fazendo a requisição via um cliente http (que não seja um browser), vc pode setar o httpMethod diretamente pra PUT ou DELETE…
senão vc tem que fazer uma requisição POST com o parametro _method setado pra PUT ou DELETE

D

perfeito Lucas, agora achei um client Restful http://rest-client.googlecode.com/ para testar minha aplicação. Value mesmo.

Criado 14 de dezembro de 2009
Ultima resposta 16 de dez. de 2009
Respostas 24
Participantes 6