Web Services REST

Embed Size (px)

Citation preview

Web Services RESTpor Bruno Luiz Pereira da Silva2 2 0Email5

IntroduoEste artigo fala sobre web services REST. Esta linha de web services vem se tornando cada vez mais popular e uma excelente opo para integrao este sistemas de diferentes plataformas. Neste artigo abordarei os principais conceitos deste tipo de servios, utilizarei um exemplo de leilo do Mercado Livre para ilustrar o problema, e mostrarei detalhes da arquitetura e implementao.

DefinioREpresentation State Transfer Estilo de arquitetura de software para sistemas distribudos Termo proposto por Roy Fielding em sua tese de doutorado Web services com a arquitetura da internet Explorao extensa dos recursos do HTTP

SurgimentoRoy Fielding um dos principais autores do protocolo HTTP, e ele props em sua tese de doutorado um estilo de arquitetura que faz extenso uso dos recursos oferecidos por este protocolo. Enquanto nos servios WS-I os recursos do HTTP so muito pouco explorados (inclusive porque o SOAP independente de transporte), nos servios REST umas das principais

caractersticas a utilizao de muitos recursos do HTTP para elaborar um protocolo de comunicao conciso e claro.

REST ? TCP/IP - WS-I ? OSIWS-I: Muitas especificaes antes das implementaes Muitos documentos e complexidade para definir as implementaes Modelo semelhante a waterfall/cascata REST: Conjunto de regras simples Especificaes criadas aps uso maduro Especificaes por grupos de estudo do IETF Modelo incremental de desenvolvimento dos padres/boas prticas

Motivao Por que implementar servios REST?Protocolos menos complexos Mais poder e flexibilidade nas comunicaes Arquitetura amplamente disponvel nas empresas Menos overhead de protocolo

Quando NO implementar servios REST?Integraes com produtos fechados WS-* Quando WS-Transaction fizer sentido Quando WSSecurity fizer sentido Quando no houver API HTTP razovel no servidor e/ou clientes-alvo

ArquiteturaA arquitetura dos web services WS-* se baseia em um protocolo bem definido, com regras precisas quanto ao formato dos dados trafegados e seguindo padres acordados em consrcios de grandes corporaes. Contrastando com isso, arquitetura dos web services REST radicalmente diferente. Poderamos ilustrar as filosofias de projeto de servios WS-* e REST com as sentenas a seguir. WS-*: J temos o protocolo e os padres, devemos definir os servios que vamos oferecer e os documentos que desejamos trocar entre as partes. REST: Vamos identificar os recursos envolvidos e utilizar extensamente os recursos do HTTP para definir um bom protocolo de interao com estes recursos.

Estilos de acesso aos servios

REST: Clientes interagem com os Recursos atravs de requisies HTTP GET, PUT, POST e DELETE WS-*: Clientes invocam diferentes operaes, com conjuntos variados de parmetros de entrada e sada

Estilo Declarativo x Imperativo

A URI deve indicar o que voc est manipulando e o mtodo (ou verbo) HTTP indicar como voc est manipulando. Neste exemplo, a URI /usuario/123456 nos indica que estamos manipulando um usurio especfico. Sabendo que estamos usando o mtodo HTTP GET, temos a clara indicao de que estamos buscando os dados deste usurio. Este estilo de invocao de servios pode ser considerado Declarativo. Em bons protocolos REST, a juno do mtodo HTTP com a URI do recurso j nos indica na maioria dos casos qual a operao sendo realizada. Assim, com um pequeno trecho do cabealho HTTP j somos capazes de compreender a comunicao.

Nos web services WS-*, a informao da operao que est sendo realizada fica encapsulada no corpo da requisio. Mesmo quando a camada de transporte das mensagens SOAP HTTP, a URI no esclarece de forma alguma a operao envolvida. A informao dos servios disponveis fica descrita por elementos operation de um documento WSDL, geralmente em um formato fazerEssaOperacao. Esta maneira de desenvolver web services classificada como Imperativa.

Modelagem dos servios

Nesta seo falaremos da modelagem e desenvolvimento dos servios utilizando REST. Para ilustrar bem os cenrios abordados, trabalharemos em cima de um problema proposto: um processo de leilo do Mercado Livre.

Apresentao do problemaO problema que buscaremos resolver envolve servios referentes a um processo de leilo do Mercado Livre. Num leilo tpico, um usurio cadastrado no site coloca para venda um produto (novo ou usado), definindo um valor para o lance inicial e ento aguarda pelas ofertas de compra por parte de outros usurios interessados no produto. Aps receber algumas ofertas pelo produto, em um determinado momento o vendedor decide aceitar a melhor oferta recebida, e ento vende o produto para o comprador que fez esta oferta, encerrando neste momento o leilo. Em seguida venda, ocorrem os trmites de pagamento e entrega do produto (que no trataremos aqui) e no final, vendedor e comprador avaliam um ao outro, o que essencial para os usurios sentirem maior segurana ao realizar negociaes futuras deste gnero. Na modelagem desta aplicao, quatro entidades sero utilizadas: Usurio, Item, Oferta e Avaliao. Modelagem com Recursos O ponto de partida do desenvolvimento com REST definir quais so os recursos envolvidos, com base nos requisitos do sistema e nos servios que se deseja oferecer. No nosso exemplo, esta etapa no complexa. Os recursos que manipularemos so: Usuario, Item, Oferta e Avaliacao. Embora esta identificao tenha sido trivial no domnio que definimos, em alguns casos este processo pode ser um dos mais complexos na modelagem da aplicao. De uma maneira geral, quanto mais a aplicao se aproxima de um CRUD, mais fcil a identificao dos recursos. Para este processo de leilo, os servios que disponibilizaremos sero os da Tabela 1: Servio Anunciar item Buscar itens do vendedor Cadastrar usurio Realizar oferta Retirar oferta Buscar ofertas do item Descrio Permite que um usurio coloque um produto venda. Pesquisa os itens venda de um vendedor.

Realiza o cadastro de um novo usurio no site. Permite que um comprador faa uma oferta por um produto. Permite a remoo de uma oferta por parte do comprador. Pesquisa por todas as ofertas feitas sobre um produto.

Buscar melhor oferta Aceitar melhor oferta Avaliar usurio Buscar avaliaes do usurio

Busca a melhor oferta feita at o momento sobre um produto. Permite que um vendedor aceite a melhor oferta feita sobre o seu produto, e com isso encerre o leilo do mesmo. Realiza a avaliao de um usurio por parte de outro usurio, aps o trmino do processo de compra. Pesquisa por todas as avaliaes recebidas por um usurio.

Tabela 1. Servios oferecidos para interao com o processo de leilo.

Protocolo de comunicao RESTTendo definido os recursos e os servios que precisamos oferecer, necessrio definir as manipulaes possveis sobre os recursos existentes. Esta etapa a traduo de operaes de negcio em interaes diretas sobre usurios, tens, ofertas e avaliaes. Esta traduo feita explorando os recursos do HTTP nos levar a um conjunto de URIs que a aplicao oferece para os clientes. Para elaborar um bom protocolo de comunicao REST, devemos pensar em algumas questes importantes, como estas: Quais so os recursos? Quais so as URIs? Quais so os formatos manipulados? Que mtodos HTTP so aceitos em cada URI? Que status HTTP deve ser retornado em cada situao? Que cabealhos HTTP so relevantes em cada situao? A definio das URIs e dos mtodos HTTP aceitos primordial para um protocolo REST com clareza e de fcil utilizao e extenso. Ao ler as URIs j devemos ser capazes de entender quais so os recursos presentes nas mesmas, e o casamento delas com os mtodos HTTP deve ser intuitivo. A Tabela 2 descreve todas as URIs disponveis na aplicao e quais mtodos HTTP podem ser invocados em cada uma delas. Alm disso, so especificados os recursos manipulados por cada requisio e o efeito que uma determinada chamada exerce sobre os recursos existentes. URI Mtodo GET /item/{id} PUT Item Coleo de ofertas Oferta Oferta Atualiza um item. Busca ofertas feitas sobre um item. Formato Item Efeito Busca um item.

GET /item/{id}/ofertas POST /oferta/{id} GET

Adiciona oferta a um item. Busca uma oferta.

PUT DELETE /usuario POST GET /usuario/{id} PUT

Oferta Usuario Usuario Usuario Coleo de avaliaes Coleo de tens Item Avaliao Avaliao Coleo de URIs

Atualiza uma oferta. Remove uma oferta. Cadastra um usurio. Busca um usurio. Atualiza um usurio. Busca as avaliaes recebidas por um usurio. Busca os tens anunciados por um determinado usurio. Usurio coloca novo item venda. Busca uma determinada avaliao. Realizao da avaliao de um usurio sobre outro. Consulta URIs e mtodos HTTP disponveis para acesso.

/usuario/{id}/avaliacoes

GET

GET /usuario/{id}/itens POST /avaliacao/{id} /avaliacao/de/{id}/para/{id} GET POST

/services

GET

Tabela 2. URIs de acesso a recursos e mtodos HTTP aceitos por cada uma delas

Implementao com a JAX-RS (Java API for RESTFul Web Services)Para oferecer melhor suporte a servios REST em Java, foi criada a JSR-311. Veja os objetivos desta JSR. Com a JAX-RS, um recurso web implementado como uma classe Recurso e as requisies so tratadas por mtodos da mesma. Uma classe Recurso simplesmente um POJO contendo anotaes da JAX-RS para indicar os mapeamentos e operaes existentes.

Ciclo de vida e ambientePor padro, uma nova instncia da classe Recurso criada para cada requisio quele Recurso. Inicialmente o construtor invocado, dependncias necessrias so injetadas, e ento o mtodo adequado executado. Aps estas etapas, o objeto fica disponvel para o coletor de lixo. As classes Recurso so instanciadas pelo runtime JAX-RS e devem possuir pelo menos um construtor pblico. Um construtor pblico pode incluir parmetros com uma das seguintes anotaes: @Context, @HeaderParam, @CookieParam, @MatrixParam, @QueryParam e

@PathParam. Estas anotaes realizam injeo de dependncias relativas a servios REST, e so apresentadas na Tabela 3. Anotao Descrio

@Context

Injeta uma instncia de recursos como UriInfo, HttpHeaders, ServletConfig, ServletContext, HttpServletRequest e HttpServletResponse. Outros recursos de Java EE podem ser opcionalmente oferecidos por uma implementao desta JSR.

@HeaderParam

Extrai o valor de um cabealho da requisio HTTP.

@CookieParam

Extrai o valor de um cookie presente na requisio.

@MatrixParam

Extrai o valor de parmetros enviados no formato chave=valor dentro de um segmento da URI. Exemplo: /usurio/123/itens;categoria=eletronicos;limitePreco=1000

@QueryParam

Extrai o valor de um parmetro fornecido na query string da requisio.

@PathParam

Extrai o valor de um parmetro enviado dentro da URI.

Tabela 3. Anotaes da JAX-RS para injeo de dependncias Com exceo do Context, estes parmetros so enviados dentro de URIs, query strings, cabealhos HTTP e cookies. Sendo assim, sua representao na camada de transporte como String. Entretanto, podemos colocar estas anotaes sobre parmetros que no so String, para j recebermos os dados convertidos em um formato mais adequado para nossa manipulao. Tipos de parmetros que podem ser marcados com estas anotaes so: Tipos primitivos Classes que possuam um construtor tendo uma nica String como parmetro Classes que possuam um mtodo esttico valueOf() recebendo uma String como parmetro; List, Set ou SortedSet, onde T satisfaz a condio 2 ou a 3. A seguir um exemplo de uso destas anotaes: @GET @Path("{usuarioId}") public Response buscarUsuario(@PathParam("usuarioId") String usuarioId) { Usuario usuario = usuarioService.buscar(usuarioId); Response resposta = Response.ok(usuario).build(); return resposta; } Este exemplo mostra como poderia ser um mtodo de busca de usurio recebendo uma URI /usuario/{usuarioId}, como /usuario/123. O parmetro usuarioId do mtodo poderia ser int ou Integer, caso o ID do usurio fosse um nmero inteiro. A implementao da API faria a converso do parmetro enviado na URI para o tipo especificado no mtodo.

Requisies aos mtodos de RecursosUm mtodo de Recurso uma operao exposta como um servio REST. Estes mtodos ficam em uma classe Recurso e so anotados com o mtodo HTTP associado operao em questo. O conjunto de anotaes que define os mtodos HTTP que podem ser utilizados nas operaes : @GET, @POST, @PUT, @DELETE, @HEAD e @OPTIONS. Mtodos de Recursos que sero expostos para os clientes devem ser pblicos. Implementaes da JSR devem alertar os desenvolvedores caso encontrem mtodos nopblicos que sejam marcados com alguma destas anotaes de mtodo HTTP. Os parmetros dos mtodos so convertidos da requisio de acordo com as anotaes apresentadas na Tabela 3. Um mtodo de Recurso pode ter no mximo um parmetro noanotado. Este parmetro no-anotado ser obtido do corpo da requisio. A obteno de parmetros do corpo da requisio s faz sentido quando estamos falando de requisies POST e PUT. Estes so os nicos mtodos HTTP que possuem um corpo, e quase sempre so utilizados em operaes de criao e atualizao de Recursos, respectivamente. O trecho a seguir apresenta um exemplo de mtodo que recebe requisies POST para cadastro de usurios. @POST public Response cadastrarUsuario(Usuario usuario) { usuario = usuarioService.cadastrar(usuario); try { return Response.created(new URI(usuario.getCodUsuario())).build(); } catch (URISyntaxException e) { throw new RuntimeException(e); } }

Respostas dos mtodos de RecursosAs respostas aos mtodos de Recursos podem ser declaradas como void, Response ou qualquer outra classe Java. Retornar void implica em enviar uma resposta com corpo vazio, o que mapeado em um status HTTP 204 (No Content). Este status utilizado pela JSR para indicar que a requisio teve sucesso, e a resposta no possui corpo. Colocar uma classe Java como tipo de resposta far com que o objeto retornado seja colocado no corpo da resposta. Um objeto nulo enviado na resposta implicar em status HTTP 204 e um objeto no-nulo implicar no status HTTP 200. A classe Response pode ser colocada como tipo de retorno dos mtodos caso desejemos ter mais controle sobre a resposta. Com esta forma de retorno, conseguimos especificar cabealhos, corpo, status, cookies e mais algumas informaes da resposta enviada. At este momento no mencionamos nada a respeito do formato dos Recursos manipulados. Mostramos exemplos de busca e cadastro de usurios, mas ficou explcita nos exemplos apenas a manipulao de objetos Java. A questo dos formatos muito importante e coberta na seo Manipulao de diferentes formatos.

Tratamento de erros e exceesComo padro, quando ocorre uma exceo durante uma chamada REST, o cliente recebe um status HTTP 500 (Internal Server Error). Em muitas situaes este status pode no ser satisfatrio, pois no fornece muita informao sobre o erro que ocorreu no servidor. Para

resolver este problema a JSR-311 permite que a resposta seja personalizada em caso de excees. Podemos fazer este controle de duas formas. A primeira forma com uma exceo especial. Basta disparar uma unchecked exception javax.ws.rs.WebApplicationException. Criando esta exceo podemos passar o status HTTP e um objeto Response, permitindo total controle da resposta que ser enviada para o usurio. Porm, em uma aplicao grande muito comum termos o lanamento de outras excees em camadas inferiores. Para esta situao a JSR-311 permite que seja criada uma classe para mapear a resposta correspondente a cada exceo. Este mapeamento seria conhecido apenas pela camada de servios REST. A classe de mapeamento deve implementar a interface javax.ws.rs.ext.ExceptionMapper e ser anotada com @Provider. A partir da, quando a exceo especificada for disparada o controle vai passar para o mtodo toResponse() desta classe. Este mtodo poder construir a resposta de acordo com a exceo, que passada como parmetro. Sobre a segunda forma de tratar excees vale citar que ela ainda est sendo implementada na verso 0.8 da especificao, e est sujeita a modificaes at a finalizao da JSR. O trecho a seguir apresenta um exemplo de mapeamento de exceo para uma resposta com status HTTP customizado. @Provider public class ItemJaVendidoExceptionMapper implements ExceptionMapper { public Response toResponse(ItemJaVendidoException e) { return Response.status(Response.Status.GONE).build(); } }

Manipulao de diferentes formatosUma das capacidades mais interessantes que temos na JSR-311 a de tratar facilmente diferentes formatos nos nossos servios. Somos capazes de receber e gerar dados em diferentes tipos de contedo, sem nus para os desenvolvedores. Os formatos XML e JSON j esto disponveis no Jersey (implementao de referncia da JSR). Alm disso, a JSR permite que seja oferecido o suporte a qualquer formato, atravs de classes Provider. Classes Provider so desenvolvidas para permitir a leitura e/ou escrita de determinados tipos de contedo (Content-Types). Uma classe Provider implementa as interfaces MessageBodyReader e MessageBodyWriter para oferecer suporte ao tipo de contedo que se prope. Poderamos ter, por exemplo, um Provider que soubesse manipular recursos com formato mp3 em uma aplicao multimdia. Classes Recurso podem usar as anotaes @ConsumeMime e @ProduceMime para declarar quais so os tipos de contedo que elas geram e os tipos de contedo que elas aceitam receber, respectivamente. Caso nenhum tipo de contedo seja declarado, assumese que qualquer tipo de contedo (*/*) aceito.

O trecho a seguir apresenta um exemplo de uso destas anotaes na classe UsuarioResource. Neste exemplo, declaramos que a classe aceita e gera contedo nos formatos text/xml e application/json. @ConsumeMime( { "text/xml", "application/json" }) @ProduceMime( { "text/xml", "application/json" }) public class UsuarioResource {} Neste exemplo, colocamos as anotaes sobre a classe. Estas anotaes tambm podem ser colocadas sobre mtodos. Caso as anotaes sejam colocadas sobre a classe e tambm sobre um mtodo, a anotao sobre o mtodo a que vale. O trecho abaixo mostra um exemplo de anotaes sobre a classe e sobre um mtodo. Neste exemplo, declaramos que a classe aceita os tipos de contedo text/xml e application/json em todos os seus mtodos, com exceo do mtodo buscarUsuario(). Neste mtodo declaramos que produzimos apenas contedo em formato text/xml. O mtodo atualizarUsuario() herda as declaraes feitas na classe, portanto aceita contedo em text/xml e application/json. @Path("usuario") @ConsumeMime( { "text/xml", "application/json" }) @ProduceMime( { "text/xml", "application/json" }) public class UsuarioResource { @GET @Path("{usuarioId}") @ProduceMime("text/xml") public Response buscarUsuario(@PathParam("usuarioId") String usuarioId) { Usuario usuario = usuarioService.buscar(usuarioId); Response resposta = Response.ok(usuario).build(); return resposta; } @PUT @Path("{usuarioId}") public Response atualizarUsuario(Usuario usuario) { usuarioService.atualizar(usuario); return Response.ok().build(); } } importante mencionar como funciona este tratamento dos tipos de contedo. Quando um cliente faz uma requisio HTTP, ele pode especificar o cabealho Accept. Este cabealho informa ao servidor quais so os tipos de contedo que o cliente aceita receber. Se o cliente no especificar este cabealho, o valor assumido do mesmo */*, o que indica que o cliente aceita qualquer tipo de contedo. Ao receber uma requisio HTTP, o runtime JAX-RS ir comparar a lista de tipos enviados no cabealho Accept com a lista de tipos de contedo registrados para o mtodo invocado. Nesta comparao, as seguintes regras sero aplicadas: Caso exista apenas um tipo de contedo em comum entre as duas listas, este ser o tipo de contedo enviado; Caso exista mais de um tipo de contedo em comum entre as listas, o

contedo ser enviado no formato que aparecer primeiro na lista registrada no servidor. Se o mtodo declarou gerao de text/xml e application/json (nesta ordem) e o cliente aceita ambos os tipos, o cliente receber text/xml; Se a lista de tipos de contedo oferecida pelo servidor no contiver nenhum dos tipos que o cliente afirmou aceitar, o runtime JAX-RS envia uma resposta de falha com status HTTP 415 (Unsupported Media Type). Com estes exemplos, mostramos a capacidade de gerar diferentes formatos em nossos servios, sem que seja necessrio tratar isso explicitamente pela nossa aplicao. A declarao dos tipos de contedo atravs das anotaes @ConsumeMime e @ProduceMime suficiente para que o runtime JAX-RS faa o tratamento correto. Uma ltima questo que precisamos abordar neste contexto como indicar para a JAX-RS como deve ser feito o mapeamento de nossas classes em XMLs. As implementaes da JSR so obrigadas a suportar o uso de JAXB (Java Architecture for XML Binding) na converso de Java para XML e de XML para Java. O JAXB um dos componentes do Java EE 5 e foi includo tambm na verso 6 do Java SE. Ele a forma padro de mapeamento entre classes Java e documentos XML na JSR-311. Para utilizar o JAXB no mapeamento de nossas classes, a opo mais simples utilizar a anotao @XmlRootElement sobre as mesmas. Ao fazer isso, o JAXB far a converso da classe e dos seus atributos em um documento XML cujo elemento root ser o nome da classe (comeando por minscula). Os elementos filhos sero os atributos da classe, seguindo a nomenclatura da mesma. Caso seja desejado, possvel especificar na anotao @XmlRootElement um nome de elemento XML diferente do nome da classe. Podemos tambm modificar os elementos XML dos atributos da classe usando a anotao @XmlElement. O JAXB nos permite customizar bastante os mapeamentos realizados, se assim quisermos. O trecho abaixo apresenta um exemplo no qual mapeamos a classe Avaliacao para um XML com elementos em ingls. No entraremos em mais detalhes sobre o JAXB neste momento, pois isto fugiria do foco do artigo. @XmlRootElement(name = "feedback") public class Avaliacao {XmlElement(name = "feedbackCode") private String codAvaliacao; @XmlElement(name = "rater") private Usuario avaliador; @XmlElement(name = "positive") private boolean positiva; @XmlElement(name = "comment") private String comentario; }

Mapeamento de URIs e mtodos HTTP em classes Recurso e seus mtodosUma das principais vantagens no uso da JSR-311/Jersey eliminar do nosso desenvolvimento a validao de requisies e o mapeamento das mesmas em classes e mtodos que devem process-las. Isto implica em obter a URI e o mtodo HTTP das solicitaes e conferir se existe algum dos nossos servios que saiba trat-la. Alm disso,

antes de invocar os servios em questo, precisamos extrair das URIs os parmetros que tenham sido enviados nas mesmas. A anotao @Path pode ser colocada em classes e mtodos. Quando a colocamos sobre uma classe, estamos associando a classe a um prefixo de URI. Esta anotao foi utilizada anteriormente no artigo para associar o prefixo /usuario da nossa aplicao classe UsuarioResource. Colocamos a anotao @Path sobre mtodos para formar o caminho completo dos nossos servios. Juntando os valores da anotao @Path sobre os mtodos com o valor da anotao sobre a classe, temos a lista de URIs disponveis na classe Recurso em questo. A colocao das anotaes @GET, @POST, @PUT, @DELETE, @HEAD e @OPTIONS complementa o mapeamento de URIs. Com estas duas informaes sobre cada mtodo, conseguimos mapear precisamente cada solicitao na classe e no mtodo que devem trat-la. Mostraremos na prxima seo como fica o uso da JSR-311 no desenvolvimento dos servios de leilo.

Aplicando a JSR-311 e o Jersey nos servios de leiloNesta seo mostraremos como usar o Jersey para implementar os servios do processo de leilo. Devido s limitaes de espao, escolhemos apenas uma parte dos servios, mas de forma que seja possvel ilustrar com clareza as diferenas. A primeira etapa necessria a configurao do Jersey no projeto. A distribuio binria estvel mais recente no momento da escrita deste artigo a 0.7. Esta distribuio pode ser obtida no site do projeto. Devemos mapear todos os prefixos de URIs dos nossos servios para um Servlet do Jersey. O trecho abaixo mostra um web.xml configurado com este mapeamento. Na nossa aplicao, como todas as URIs so de servios REST, mapeamos toda a aplicao (/*) para o Servlet do Jersey. Jersey Web Application com.sun.ws.rest.spi.container.servlet.ServletContainer com.sun.ws.rest.config.feature.Redirect true com.sun.ws.rest.config.feature.ImplicitViewables true 1

Jersey Web Application /* Alm da configurao do Servlet, precisamos adicionar algumas bibliotecas para utilizar o Jersey. O conjunto mnimo de bibliotecas que devem ser colocadas na aplicao inclui o jersey.jar, jsr311-api.jar e asm.jar. Estas bibliotecas e mais algumas dependncias esto presentes no diretrio /lib da distribuio binria do Jersey. Se no estiver usando Java SE 6 ou Java EE 5, voc tambm precisar adicionar o JAXB ao projeto. Utilizando um servidor de aplicaes Java EE 5 e as bibliotecas presentes na distribuio binria do projeto, voc tem a garantia de ter todas as dependncias necessrias.

Desenvolvimento dos serviosMostraremos agora como pode ser feita a implementao de alguns servios do processo de leilo com o uso do Jersey. Somente um subconjunto dos servios ser apresentado neste artigo, mas a implementao completa pode ser vista no cdigo fonte. Comearemos pelos servios correspondentes ao prefixo /usuario e depois falaremos tambm sobre os servios do prefixo /avaliacao. A Tabela 4 lista os servios que apresentaremos neste artigo. URI Mtodo Formato Efeito

/usuario

POST

Usuario

Cadastra um usurio.

GET /usuario/{id} PUT

Usuario

Busca um usurio.

Usuario

Atualiza um usurio.

/usuario/{id}/avaliacoes

GET

Coleo de avaliaes

Busca as avaliaes recebidas por um usurio.

GET /usuario/{id}/itens POST

Coleo de itens

Busca os itens anunciados por um determinado usurio.

Item

Usurio coloca novo item venda.

/avaliacao/{id}

GET

Avaliao

Busca uma determinada avaliao.

/avaliacao/de/{id}/para/{id}

POST

Avaliao

Realizao da avaliao de um usurio sobre outro.

A listagem abaixo apresenta a classe UsuarioResource. Esta uma das classes Recurso da nossa aplicao e nela esto todos os servios do prefixo /usuario. A classe foi anotada com @Path(usuario), o que faz a associao da mesma com o prefixo citado. Alm disso, a classe possui as anotaes @ConsumeMime e @ProduceMime, que neste caso declaram que os servios da mesma so capazes de consumir e gerar contedo nos formatos text/xml e application/json. @Path("usuario") @ConsumeMime( { "text/xml", "application/json" }) @ProduceMime( { "text/xml", "application/json" }) public class UsuarioResource { private ItemService itemService; private UsuarioService usuarioService; private AvaliacaoService avaliacaoService; public UsuarioResource() { this.itemService = ServiceFactory.getItemService(); this.usuarioService = ServiceFactory.getUsuarioService(); this.avaliacaoService = ServiceFactory.getAvaliacaoService(); } @GET @Path("{usuarioId}") public Response buscarUsuario(@PathParam("usuarioId") String usuarioId) { Usuario usuario = usuarioService.buscar(usuarioId); if(usuario == null){ return Response.status(HttpServletResponse.SC_NOT_FOUND).build(); } Response resposta = Response.ok(usuario).build(); return resposta; } @POST public Response cadastrarUsuario(Usuario usuario) { usuario = usuarioService.cadastrar(usuario); try { return Response.created(new URI(usuario.getCodUsuario())).build(); } catch (URISyntaxException e) { throw new RuntimeException(e); } } @PUT

@Path("{usuarioId}") public Response atualizarUsuario(Usuario usuario) { usuarioService.atualizar(usuario); return Response.ok().build(); } @POST @Path("{usuarioId}/itens") public Response cadastrarItem(@Context UriInfo uriInfo, @PathParam("usuarioId") String usuarioId, Item item) throws URISyntaxException { Usuario usuario = new Usuario(usuarioId); item = itemService.cadastrar(item, usuario); URI uriItem = new URI(uriInfo.getBaseUri() + "item/" + item.getCodItem()); return Response.created(uriItem).build(); } @GET @Path("{usuarioId}/itens") public Response buscarItensDoUsuario(@PathParam("usuarioId") String usuarioId) { // Verifica se o usurio existe if (this.usuarioService.buscar(usuarioId) == null) { return Response.status(Status.NOT_FOUND).build(); } List itens = itemService.buscarPorVendedor(new Usuario(usuarioId)); return Response.ok(new ItensUsuario(itens)).build(); } @GET @Path("{usuarioId}/avaliacoes") public AvaliacoesUsuario buscarAvaliacoesDoUsuario(@PathParam("usuarioId")String usuarioId){ Usuario usuario = new Usuario(usuarioId); List avaliacoes = avaliacaoService.buscarPorUsuario(usuario); return new AvaliacoesUsuario(avaliacoes); } } O primeiro servio o de busca de usurio. Este mtodo foi anotado com @Path({usuarioId}). O casamento da anotao sobre o mtodo com a anotao sobre a classe especifica que este mtodo responde a requisies para a URI /usuario/{usuarioId}. Como o mtodo tambm foi anotado com @GET, sabemos que as solicitaes HTTP GET para /usuario/{usuarioId} sero tratadas por este mtodo. Importante reparar no uso da anotao @PathParam para injetar no parmetro usuarioId o valor que veio na URI.

Na resposta a esta solicitao, retornamos o status HTTP 200 (OK) e os dados do usurio no corpo da resposta. Caso o usurio no tenha sido encontrado, retornamos status 404 (Not Found). A listagem abaixo mostra a classe Usuario, que manipulada por alguns servios na classe UsuarioResource. Por simplicidade mostramos apenas a declarao da classe com os atributos. @XmlRootElement public class Usuario { private private private private String String String String codUsuario; nome; login; email;

private Item[] items; private Oferta[] ofertas; private Avaliacao[] avaliacoes; } O segundo servio presente em UsuarioResource o de cadastro de usurio. Como no colocamos nenhuma anotao @Path sobre este mtodo, ele est associado URI da classe (/usuario). O mtodo foi anotado com @POST, ento ele responde s solicitaes POST na URI citada. O parmetro contendo os dados do usurio no recebeu nenhuma anotao, o que significa que ele obtido do corpo da solicitao. Como o mtodo de cadastro de usurio no tem as anotaes @ConsumeMime e @ProduceMime, ele herda as declaraes feitas sobre a classe. Sendo assim, podemos cadastrar usurios usando text/xml ou application/json. Na resposta criao do usurio ns enviamos o status HTTP 201 (Created), colocando no header Location a URI do novo usurio. O mtodo de atualizao de usurio recebe solicitaes PUT em /usuario/{usuarioId}. Os dados do usurio tambm so consumidos do corpo da solicitao, e as operaes com sucesso resultam no envio do status HTTP 200. No mtodo de cadastrar itens, usamos a anotao @Path para associar o servio URI /usuario/{usuarioId}/itens. Usamos a anotao @PathParam para extrair da URI o ID do usurio envolvido. Usamos tambm a anotao @Context para injetar a classe UriInfo, que nos fornece informaes sobre a URI de acesso aos servios. No final, usamos a UriInfo para colocar no header Location o caminho absoluto de acesso ao item recm-criado. A listagem abaixo mostra a classe Item, manipulada neste servio. @XmlRootElement public class Item { private String codItem; private String nome; private private private private String descricao; BigDecimal valorInicial; boolean novo; boolean vendido;

} No mtodo de buscar itens do usurio temos o primeiro servio que manipula colees. Este mtodo trata de solicitaes GET URI /usuario/{usuarioId}/itens. Para retornar a lista de itens do usurio foi criada a classe ItensUsuario, que simplesmente contm a lista. A listagem a seguir apresenta a declarao desta classe. @XmlRootElement public class ItensUsuario {

private List item; public ItensUsuario() {} public ItensUsuario(List itens) { this.item = itens; } } O mtodo de buscar avaliaes do usurio estruturalmente semelhante ao de buscar itens. Foi criada a classe AvaliacoesUsuario para retornar a lista de avaliaes. Como esta muito semelhante ItensUsuario, ela ser omitida. Para implementar os servios do prefixo /avaliacao foi criada a classe AvaliacaoResource. Esta classe pode ser vista na listagem a seguir. Temos a anotao @Path registrando a URI desejada e tambm as anotaes @ConsumeMime e @ProduceMime declarando que manipulamos text/xml e application/json. @Path("avaliacao") @ConsumeMime( { "text/xml", "application/json" }) @ProduceMime( { "text/xml", "application/json" }) public class AvaliacaoResource { private AvaliacaoService avaliacaoService; public AvaliacaoResource() { this.avaliacaoService = ServiceFactory.getAvaliacaoService(); } @GET @Path("{avaliacaoId}") public Response buscarAvaliacao(@PathParam("avaliacaoId") String avaliacaoId) { Avaliacao avaliacao = avaliacaoService.buscar(avaliacaoId); if(avaliacao == null){ return Response.status(HttpServletResponse.SC_NOT_FOUND).build(); } return Response.ok(avaliacao).build(); } @POST @Path("de/{avaliador}/para/{avaliado}") public Response avaliarUsuario(@Context UriInfo uriInfo, @PathParam("avaliador") String avaliador, @PathParam("avaliado") String avaliado, Avaliacao avaliacao) throws URISyntaxException { Usuario usuarioAvaliado = new Usuario(avaliado); avaliacao = avaliacaoService.cadastrar(avaliacao, usuarioAvaliado); URI uri = new URI(uriInfo.getBaseUri() + "avaliacao/" + avaliacao.getCodAvaliacao()); return Response.created(uri).build(); } }

O primeiro servio desta classe o de busca de avaliao, que muito semelhante ao servio de busca de usurio que vimos anteriormente. Este servio ficou mapeado em /avaliacao/{avaliacaoId}, recebendo solicitaes GET. O servio de avaliar usurio mais interessante. Extramos dois parmetros da URI e consumimos um recurso do corpo da solicitao. A URI /avaliacao/de/{avaliador}/para/ {avaliado} um bom exemplo da liberdade que temos na definio das URIs. Podemos mold-las para aumentar a clareza das operaes. Isto facilita a aproximao dos servios com o nosso domnio da aplicao.

Criao de clientes Java para os servios RESTExpusemos com bom nvel de detalhe a implementao de servios REST do lado do servidor. Para permitir uma viso completa da comunicao, muito importante falar tambm de clientes RESTful. Uma vez que j temos o protocolo estabelecido, o papel do cliente manipular as solicitaes e respostas no formato acordado com o servidor. Para exemplificar como pode ser feito isso do lado do cliente, apresentamos na listagem abaixo a classe OfertaTestREST, que realiza uma solicitao para cadastro de uma oferta sobre um item. O cdigo deste teste foi feito para facilitar a ilustrao do que est sendo feito. Este o motivo de imprimir os cabealhos e corpo tanto da requisio como da resposta HTTP. public class OfertaTestREST extends TestCase { public void testCadastro() throws HttpException, IOException{ // Criamos o usurio Usuario usuario = new Usuario(); usuario.setNome("Usuario Artigo"); usuario.setEmail("[email protected]"); usuario.setLogin("artigo" + System.currentTimeMillis()); UsuarioService usuarioService = ServiceFactory.getUsuarioService(); usuario = usuarioService.cadastrar(usuario); // Criamos o item Item item = new Item(); item.setDescricao("Item teste Artigo " + System.currentTimeMillis()); item.setNome("Iphone"); item.setNovo(true); item.setValorInicial(new BigDecimal(0)); item.setVendido(false); ItemService itemService = ServiceFactory.getItemService(); item = itemService.cadastrar(item, usuario); // E agora a criao da Oferta de maneira RESTFul Oferta oferta = new Oferta(); oferta.setDataModificacao(new Date()); oferta.setItem(item); oferta.setOfertante(usuario); oferta.setValor(new BigDecimal(111.22).setScale(2, RoundingMode.HALF_UP));

// Montando requisio com o commons-http-client HttpClient client = new HttpClient(); PostMethod method = new PostMethod("http://localhost:8080/item/" + item.getCodItem() + "/ofertas"); // Gerao de XMLs com o XStream XStream xstream = new XStream(); xstream.alias("oferta", Oferta.class); xstream.alias("ofertante", Usuario.class); xstream.alias("item", Item.class); String ofertaXml = xstream.toXML(oferta); System.out.println(ofertaXml); // Definindo corpo da requisio StringRequestEntity requestEntity = new StringRequestEntity(ofertaXml, "text/xml", "UTF-8"); method.setRequestEntity(requestEntity); // Here it goes... int statusCode = client.executeMethod(method); // Status HTTP deve ser 201 - Created assertEquals(HttpServletResponse.SC_CREATED, statusCode); System.out.println("\n#### REQUISIO ####\n"); System.out.println(method.getName() + " " + method.getPath()); Header[] headersRequest = method.getRequestHeaders(); for(Header header : headersRequest){ System.out.println(header.getName() + " : " + header.getValue()); } method.getRequestEntity().writeRequest(System.out); System.out.println("\n\n#### RESPOSTA ####\n"); System.out.println(method.getStatusLine().getHttpVersion() + " " + method.getStatusLine().getStatusCode()); Header[] headersResponse = method.getResponseHeaders(); for(Header header : headersResponse){ System.out.println(header.getName() + " : " + header.getValue()); } System.out.println(method.getResponseBodyAsString()); } }

O formato da requisio HTTP gerada por esta classe pode ser visto na primeira listagem abaixo. Na listagem seguinte pode ser conferido o formato da resposta HTTP a esta solicitao. A terceira listagem desta sequncia mostra o formato da resposta busca de ofertas de um determinado item. #### REQUISIO #### POST /item/13c017ba-7c01-44aa-9a0b-b815a9ea298f/ofertas User-Agent : Jakarta Commons-HttpClient/3.0.1 Host : localhost:8080 Content-Length : 596 Content-Type : text/xml; charset=UTF-8 111.22 2008-09-28 10:36:54.642 BRT 13c017ba-7c01-44aa-9a0b-b815a9ea298f Iphone Item teste Tech Talk 1222609014628 0 true false fc6104ad-b9a5-4b2d-9085-a186083b9c2d Usuario Tech Talk techtalk1222609014475 [email protected] false #### RESPOSTA #### HTTP/1.1 201 Server : Apache-Coyote/1.1 Location : http://localhost:8080/item/13c017ba-7c01-44aa-9a0bb815a9ea298f/ofertas Content-Length : 0 Date : Sun, 28 Sep 2008 13:36:55 GMT A implementao das solicitaes HTTP em Java pode ser feita com uso da biblioteca commons-http-client. Esta API permite que montemos requisies HTTP e recebamos suas respostas correspondentes, da mesma forma que ocorreria com um browser simples. No existe a capacidade de executar cdigo javascript e tambm no existem equivalentes para os plugins dos browsers completos. Mesmo sem estes recursos, o commons-http-client nos d o poder de fazer praticamente qualquer operao padro HTTP do lado cliente. Isto torna esta biblioteca um componente de fundamental importncia para implementaes RESTful feitas em Java.

Clientes Ajax/JSON

Quando falamos em web services a primeira idia sobre o formato para troca das informaes o XML. Porm, outros formatos para troca de dados existem e podem atender melhor a alguns casos particulares. Um destes formatos o JSON. Ele tem se difundido bastante, especialmente como uma alternativa ao XML em AJAX, pois um formato nativamente suportado por qualquer interpretador javascript. Outro motivo que um formato mais enxuto, gerando documentos menores e mais fceis consumir. Por estas vantagens, tem sido comum o uso de JSON para criar clientes AJAX de servios REST. Na listagem abaixo apresentamos um exemplo de funo javascript que busca itens de um usurio num servio REST e apresenta os mesmos em uma tabela. Mostramos apenas os trechos mais relevantes. O leitor pode obter o cdigo completo dos exemplos de clientes ajax no site da revista. A listagem seguinte apresenta a definio de uma funo que faz uma requisio HTTP GET assncrona a um servio REST. function mostrarItens() { // Faz a chamada rest var url = "/usuario/" + document.getElementById("cod_usuario").value + "/itens"; var tabela = document.getElementById("itens"); var response = RESTFul.get(url); if (response.status != 200) { tabela.innerHTML = "Usurio no encontrado!"; return; } var itensUsuario = response.getObject(); var stringitens = ""; stringitens += "codigodescrionomenovo" +"valor inicialvendido"; for (i in itensUsuario.itensUsuario.item) { var item = itensUsuario.itensUsuario.item[i]; stringitens += " // requisio Ajax para HTTP GET get: function (url, request) { // cria um XmlHttpRequest xmlhttp = getXTR(); // constri um request default if (!request) {request = new RESTFul.Request();} xmlhttp.open("GET",url,false); // requisio GET assncrona xmlhttp.setRequestHeader("Accept","application/json"); xmlhttp.send(null); // requisio sem corpo while (xmlhttp.readyState != 4); var response = new RESTFul.Response(xmlhttp.status, xmlhttp.statusText, xmlhttp.getAllResponseHeaders(), xmlhttp.responseText);

return response; }

Web Application Description Language (WADL)H muita discusso a respeito de interfaces de descrio de servios REST. H quem julgue que elas no so necessrias. Outros acham interessante ter um equivalente do WSDL para REST. Consideramos que conveniente oferecer uma interface simples de consulta dos servios disponveis, mas sem tantos detalhes como o WSDL. Uma das opes disponveis para isso o WADL. O WADL informa quais so as URIs disponveis, os mtodos permitidos em cada uma delas, e os parmetros de entrada e sada dos servios. O Jersey gera automaticamente um WADL dos nossos servios a partir de nossas classes Recurso. Consideramos este documento bom o suficiente para no valer a pena gerar algo semelhante de forma customizada. Mais detalhes sobre os servios podem ser colocados numa Wiki ou pgina semelhante. A gerao do WADL uma funcionalidade especfica do Jersey, e no est presente na JSR-311. Para acessar o WADL correspondente aos nossos recursos, devemos fazer uma solicitao HTTP GET URI /application.wadl, na raiz da nossa aplicao. Isto conveniente por permitir a fcil visualizao com um browser. Veja o documento WADL dos servios do processo de leilo.

Suporte de ferramentasUm dos pontos positivos dos web services WS-* que j existe um amplo conjunto de ferramentas para facilitar o trabalho com esta linha de servios. Geradores de clientes e esqueletos de servio esto disponveis para vrias plataformas e linguagens. Os web services REST tiveram que esperar muito mais tempo para terem as primeiras ferramentas de desenvolvimento. No devemos ver isso como um ponto negativo. Se existem muitos produtos para melhorar o trabalho com WS-* porque as tecnologias envolvidas exigem isso. Os servios REST so essencialmente mais simples e concentram seus detalhes principais em torno de HTTP. Como as tecnologias utilizadas com REST j so muito maduras e conhecidas, indiretamente j havia um timo suporte a este desenvolvimento. Recentemente o NetBeans introduziu plugins focados em servios REST, e estes contribuem com um ganho de produtividade no comeo do desenvolvimento. Somos capazes de gerar classes Recurso a partir de entidades JPA e tambm gerar clientes Java e Javascript para nossos servios. Estas funcionalidades ajudam na rpida criao de prottipos, e oferecem um bom ponto de partida na implementao. Detalhes sobre o suporte do NetBeans ficam fora do escopo deste artigo, mas recomendamos que os leitores avaliem os benefcios que este IDE traz.

ConclusoO objetivo deste artigo foi partir de um exemplo rico o suficiente de arquitetura orientada a servios e ento fazer a modelagem e desenvolvimento dos mesmos com uso de uma

abordagem RESTful. Atravs do exemplo do leilo do Mercado Livre, fomos capazes de percorrer todas as etapas envolvidas na implementao de web services REST. Entre estas etapas podemos destacar a identificao dos recursos, mapeamento de URIs, definio do protocolo de comunicao e formas de mapeamento de Java para XML e XML para Java. Foi possvel ilustrar bem como o bom uso dos recursos do HTTP podem ajudar na definio de um protocolo de comunicao conciso e claro. Elementos j conhecidos como os status e mtodos HTTP, URIs e Content-Types passam a ser utilizados para comunicaes bem mais diversas do que a transferncia simples de HTML. Com o amadurecimento das implementaes REST, estamos caminhando na direo de solues poderosas e interoperveis. At alguns anos atrs, a adoo de web services representava uma troca de performance por interoperabilidade. Piorar a performance na comunicao com a mesma plataforma e linguagem para ser capaz de falar com qualquer outro servio. Isto felizmente est deixando de ser verdade. Estamos conquistando poder suficiente para ter ao mesmo tempo alta performance e interoperabilidade. A JSR-311 e o Jersey trazem benefcios interessantes, e no nos tiram o poder do REST. A introduo destes componentes simplifica o desenvolvimento. Alm disso, ganhamos funcionalidades que seriam muito trabalhosas de implementar de forma customizada. A capacidade de manipular mltiplos formatos e a gerao do WADL so bons exemplos disso. Esperamos que este artigo tenha contribudo com novas idias para os leitores no desenvolvimento de servios REST. Esta linha de servios vem amadurecendo progressivamente e j se apresenta como uma opo poderosa para integrao entre aplicaes. Pretendo publicar vrios outros artigos na rea de web services, integrao e tecnologias relacionadas, ento se voc gostou deste artigo, acompanhe sempre as novidades por aqui

Bruno Luiz Pereira da Silva - Visite o site do autor: http://brunopereira.org/.

Engenheiro eletrnico e de computao de 26 anos que se frustrou com a eletrnica e se apaixonou por software. Trabalha com software desde 2003 (sem contar o perodo acadmico) e com Java desde o comeo de 2004. Se interessa bastante por diversas reas distintas, como linguagens de programao, open source, sistemas operacionais, processo de desenvolvimento, computao distribuda, SOA e Java em geral. Trabalha h 2 anos e meio na Concrete Solutions, atuando na Globo.com (mais precisamente na rea do ISP) desde minha chegada empresa. Atualmente tambm colunista da Java Magazine, com foco principal em Java EE e Web Services em geral.