JAX-RS (Java API for RESTful Web Services) ou JSR 311 é a especificação da linguagem Java para criar WebServices RESTful.
Existe varias implementações desta especificação tais como Jersey, Apache CXF, JBoss RESTEasy, Restlet…
Neste artigo vou utilizar Jersey a implementação da Sun/Oracle para esta especificação.
Com JAX-RS é possível criar WS através de Annotations de forma bastante simples e rápida, que podem ser distribuídos através de Web Containers como Tomcat, Jetty, JBoss etc…
Neste caso vou utilizar o framework Grizzly que permite criar um web container embedded excluindo assim a necessidade de um web container externo à aplicação.
Comecemos pelo serviço que permite o calculo de fibonacci
package com.wordpress.magician.jaxrs.ws;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
/**
* Calcula o resultado a sequencia de fibonacci
* @author magician
*
*/
@Path("/fib/{number}")
public class Fibonacci {
/**
* Devolve uma resposta em plain text com o resultado do calculo.
* @param number numero a calcular
* @return resultado do calculo
*/
@GET
@Produces("text/plain")
public String fibText(@PathParam("number") int number){
return String.format("%d",fibonacci(number));
}
/**
* Calcula a sequência de fibonacci de forma recursiva
* @param n numero actual do processo recursivo
* @return resultado actual do processo recursivo
*/
private long fibonacci(int n){
if(n <= 1){
return n;
}else{
return fibonacci(n-1) + fibonacci(n-2);
}
}
}
Fibonacci.java
Como se pode ver é muito simples, na header da classe utilizamos a annotation @Path que permite definir o caminho para o serviço. Neste caso o path será /fib/{number} onde o {number} é utilizado como parâmetro para método fibText onde é associado através da annotation @PathParam.
A annotation @GET indica que o serviço deve ser acedido por HTTP GET e a annotation @Produces define o mime type do conteúdo gerado como resposta pelo WS. Em seguida temos o serviço Auth, este serviço recebe um username e uma password sob forma de String e caso o username seja igual à password ele devolve um long representando a session id que o utilizador deve usar. Caso o username seja diferente da password o serviço devolve -1 ou seja sem session id.
package com.wordpress.magician.jaxrs.ws;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
/**
* Simula a autenticação de um user/pass
* @author magician
*
*/
@Path("/auth")
public class Auth {
/**
* Devolve uma resposta em plain text com o resultado da autenticação.
* @param user String com o username
* @param pass String com a password
* @return String com a resposta plain text
*/
@POST
@Produces("text/plain")
public String authText(@FormParam("user") String user, @FormParam("pass") String pass){
return String.format("%d", session(user, pass));
}
/**
* Devolve uma resposta em XML com o resultado da autenticação.
* @param String com o user username
* @param String com a pass password
* @return String com a resposta em XML
*/
@POST
@Produces("text/xml")
public String authXML(@FormParam("user") String user, @FormParam("pass") String pass){
return String.format("<?xml version=\"1.0\" encoding=\"UTF-8\"?><auth username=\"%s\">%d</auth>", user, session(user, pass));
}
/**
* Caso o user seja igual à pass a autenticação é feita
* com sucesso
* @param user String com o username
* @param pass String com a password
* @return -1 se a auth falha com long caso contrario
*/
private long session(String user, String pass){
System.out.println(user);
System.out.println(pass);
if(user.equals(pass)){
return System.nanoTime();
}
return -1;
}
}
Auth.java
Este serviço tem duas coisas distintas do anterior, primeiro ele apenas deve ser acedido por HTTP POST e não por HTTP GET como era o caso anterior. Isto pode ser definido pela annotation @POST que é utilizada nas linhas 22 e 34, e utilizamos a annotation @FormParam para ir buscar os valores user e pass que devem vir no request POST.
A outra diferença está no tipo de conteúdo produzido pelo serviço neste serviço é gerado dois tipos de conteúdo Plain Text e XML. Desta forma quando o request vem limitado a resposta XML é o método authXML que é executado, caso o request aceite qualquer tipo de conteúdo ou plain text então é o método authText que é executado.
O terceiro serviço permite obter imagens através do seu nome, basicamente enviamos o nome da imagem no url e o WS envia como resposta o conteúdo do ficheiro da imagem.
package com.wordpress.magician.jaxrs.ws;
import java.io.File;
import javax.activation.MimetypesFileTypeMap;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
/**
* Procura a imagem pelo seu nome e devolve como resposta.
* @author magician
*
*/
@Path("/images/{image}")
public class GetFile {
/**
* Devolve a imagem com o mime type do ficheiro ou 404 caso
* o ficheiro não seja encontrada.
* @param image nome da imagem a procurar
* @return imagem com o mime type da imagem fonte.
*/
@GET
@Produces("image/*")
public Response getImage(@PathParam("image") String image){
String tmp = System.getProperty("java.io.tmpdir");
File target = new File(tmp + image);
System.out.println(target.getAbsolutePath());
if(!target.exists()){
throw new WebApplicationException(404);
}
String mt = new MimetypesFileTypeMap().getContentType(target);
return Response.ok(target, mt).build();
}
}
GetFile.java
A novidade neste serviço é a utilização da Classe Response, esta classe permite gerar uma resposta que deve ser devolvida pelo serviço. No serviço GetFile é gerada uma response OK e irá conter o conteúdo do ficheiro com o mime type desse mesmo ficheiro.
A classe Response permite retornar respostas de erro ou redirect com Response.status ou Response.temporaryRedirect.
Por fim temos o servidor que irá export por HTTP o WS com os serviços descritos acima, como referido foi utilizado o framework Grizzly.
package com.wordpress.magician.jaxrs;
import java.util.HashMap;
import java.util.Map;
import com.sun.grizzly.http.SelectorThread;
import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;
/**
* Servidor HTTP para o WebService utilizando o WebContainer
* do framework Grizzly
* @author magician
*
*/
public class Server {
public static void main(String[] args) throws Exception {
Map<String, String> initParams = new HashMap<String, String>();
initParams.put("com.sun.jersey.config.property.packages","com.wordpress.magician.jaxrs.ws");
SelectorThread container = GrizzlyWebContainerFactory.create("http://0.0.0.0:8080/", initParams);
try{
container.initEndpoint();
container.startEndpoint();
}finally{
if(container.isRunning()){
container.stopEndpoint();
}
}
}
}
Server.java
Como se pode ver a configuração é muito simples basta definir um URL (linha 22) e indicar o root package onde se encontram as implementações do serviços (linha 20). Por fim ao executar o nosso projecto
passamos a ter o serviço exposto no URI http://localhost:8080/
Acedendo ao URI http://localhost:8080/application.wadl recebemos a seguinte resposta
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<application xmlns="http://research.sun.com/wadl/2006/10">
<doc xmlns:jersey="http://jersey.dev.java.net/" jersey:generatedBy="Jersey: 1.5 01/14/2011 12:36 PM"/>
<resources base="http://localhost:8080/">
<resource path="/images/{image}">
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="template" name="image"/>
<method name="GET" id="getImage">
<response>
<representation mediaType="image/*"/>
</response>
</method>
</resource>
<resource path="/fib/{number}">
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:int" style="template" name="number"/>
<method name="GET" id="fibText">
<response>
<representation mediaType="text/plain"/>
</response>
</method>
</resource>
<resource path="/auth">
<method name="POST" id="authText">
<request>
<representation mediaType="application/x-www-form-urlencoded">
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="user"/>
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="pass"/>
</representation>
</request>
<response>
<representation mediaType="text/plain"/>
</response>
</method>
<method name="POST" id="authXML">
<request>
<representation mediaType="application/x-www-form-urlencoded">
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="user"/>
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="pass"/>
</representation>
</request>
<response>
<representation mediaType="text/xml"/>
</response>
</method>
</resource>
</resources>
</application>
Este XML é o utilizado para descrever o WS, tem um propósito semelhante ao WSDL dos WS SOAP mas neste caso para RESTful e bastante mais simples que o WSDL. Com ele é possível saber que serviços estão disponíveis, dado os paths, métodos de request, tipo de dados que devem ser passados ou que são devolvidos, etc…
Referencias:
http://en.wikipedia.org/wiki/JAX-RS
http://www.suryasuravarapu.com/2009/02/rest-jersey-configuration-on-tomcat.html

Cara,
Gostei do seu blog. Também tenho um relacionado a programação, tem poucos codigos java, mas pretendo colocar mais. Depois visita lá…quem sabe a gente não compartilha alguns codigos.
http://brunoagt.wordpress.com/
Abraços
Comentário por brunoagt — 30 Março, 2011 @ 22:38 |
Parabéns mais uma vez, o blog está sempre atualizado
Comentário por Tocar Violão — 1 Junho, 2011 @ 22:18 |