Aplicações com arquitetura REST (Representational State Transfer) para a criação de serviços web interoperáveis estão cada vez mais comuns. Uma das grandes vantagens da arquitetura REST é a facilidade de desenvolver os serviços e os clientes. Um dos principais frameworks para desenvolver aplicações REST em Java é o Jersey, que é um framework que implementa todas as características da arquitetura REST.
Uma aplicação é chamada de RESTful se ela segue todos os padrões da arquitetura REST. Para mostrar o desenvolvimento de uma aplicação RESTful, neste exemplo será desenvolvido uma aplicação servidor com três operações (GET, PUT e DELETE), e uma aplicação cliente para testar os serviços criados. Também será utilizado um banco de dados MySQL para persistir os dados trocados entre os serviços e clientes. Uma aplicação REST tem que ser executada em um servidor, a aplicação desenvolvida neste exemplo foi testada no Apache Tomcat 8.0.12.
Configurando o Projeto
Para exemplificar o uso do Jersey, será construída duas aplicações, um servidor que será executado no Tomcat, e uma aplicação cliente que será executada em console. O servidor implementara as funções GET, PUT e DELETE, que atualizara uma tabela em um banco de dados MySQL. A Listagem 1 mostra como configurar a aplicação servidor no Maven Framework com todas as dependências necessárias para o projeto. As principais dependências do projeto servidor são o jar jersey-server que contém as classes para a implementação do servidor REST e o jar do conector JDBC do MySQL.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.devmedia.rest</groupId>
<artifactId>server</artifactId>
<packaging>war</packaging>
<version>0.0.1</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.1</version>
</dependency>
</dependencies>
<build>
<finalName>server</finalName>
</build>
</project>
O projeto cliente terá uma classe para testar cada um dos serviços disponibilizados pelas interfaces do REST. A Listagem 2 mostra como configurar a aplicação cliente para criar um cliente REST com o Jersey. A principal dependência desse projeto é o jar jersey-client, que contém as classes para a implementação de um cliente REST.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.devmedia</groupId>
<artifactId>sclient</artifactId>
<version>0.0.1</version>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.1</version>
</dependency>
</dependencies>
</project>
A aplicação servidor é uma aplicação web que será executada no Tomcat, para isso é preciso criar o arquivo web.xml. Nesse arquivo é registrado o Servlet do Jersey, que deve interceptar todas as chamadas para os serviços, por isso foi definido que a chamada aos serviços será feita com o prefixo /rest. Também é necessário registar onde as classes Java com os serviços serão colocadas, no caso desse exemplo, no pacote com.devmedia.server. A Listagem 3 mostra o arquivo web.xml do projeto servidor.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>com.devmedia.server</display-name>
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages
</param-name>
<param-value>com.devmedia.server</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
Implementação da Aplicação Servidor
A primeira coisa para começar a implementar a aplicação é criar o banco de dados. A Listagem 4 mostra o script para a criação do banco de dados DEVMEDIA, e da tabela USER. Na tabela USER serão armazenados os dados referentes a um usuário, as colunas dessa tabela são id, que representa um identificador para um usuário, user_name que armazenara o nome de usuário, password, que armazena uma senha para o usuário, address, que representa o endereço do usuário e email, que armazena o e-mail do usuário. Nessa aplicação será utilizado o banco de dados MySQL, mas em aplicações RESTful com o Jersey, pode ser utilizado qualquer banco de dados que tenha um driver JDBC.
CREATE DATABASE devmedia;
CREATE TABLE user (
id INT PRIMARY KEY,
user_name VARCHAR(30),
password VARCHAR(10),
address VARCHAR(50),
email VARCHAR(30)
);
Depois de criado o banco de dados, é iniciado a implementação das classes Java, a primeira a ser criada, é a classe User, que segue o modelo do banco de dados, e que também representa um usuário, e tem como atributos os mesmos dados das colunas da tabela User do banco de dados. A Listagem 5 mostra o código dessa classe.
package com.santana.easy.monitor.model;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = -3842844978617110554L;
private int id;
private String userName;
private String password;
private String address;
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
O próximo passo, é a criação da classe que faz a conexão com o banco de dados, para isso foi criada a classe Connect, que implementa uma conexão básica com JDBC, nesse exemplo, o foco é mais na implementação dos serviços REST, por isso, não foi feito nada muito complexo na camada de dados, mas seria possível utilizar Hibernate ou qualquer outro framework de persistência de dados.
A Listagem 6 mostra o código da classe Connect, essa classe tem um atributo chamado com, que armazena a conexão com o banco de dados. No construtor é feita a conexão, utilizando a string de conexão jdbc:mysql://localhos:3306/devmedia, que é o banco de dados que foi criado anteriormente. O método closeConnection, apenas fecha a conexão criada. Além disso, existem três métodos, que são os métodos que fazem a manipulação dos dados dos usuários, que são insertUser, getUser e deleteUser.
package com.devmedia.database;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.devmedia.model.User;
public class Connect {
Connection con = null;
public Connect() throws SQLException, InstantiationException,
IllegalAccessException, ClassNotFoundException {
String url = "jdbc:mysql://localhost:3306/user";
String user = "root";
String password = "eduardo73";
Class.forName("com.mysql.jdbc.Driver").newInstance();
con = DriverManager.getConnection(url, user, password);
}
public void closeConnection() throws SQLException {
con.close();
}
public void insertUser(User user) {
try {
PreparedStatement preparedStatement = con
.prepareStatement("insert user
(id, user_name, password, address, email) values(?,?,?,?,?)");
preparedStatement.setInt(1, user.getId());
preparedStatement.setString(2, user.getUserName());
preparedStatement.setString(3, user.getPassword());
preparedStatement.setString(4, user.getAddress());
preparedStatement.setString(5, user.getEmail());
preparedStatement.execute();
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(Connect.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
public User getUser(int id) {
User user = null;
try {
PreparedStatement preparedStatement = con
.prepareStatement("select * from user where id = ?");
preparedStatement.setInt(1, id);
ResultSet rs = preparedStatement.executeQuery();
if (rs.next()) {
user = new User();
user.setId(rs.getInt(1));
user.setUserName(rs.getString(2));
user.setPassword(rs.getString(3));
user.setAddress(rs.getString(4));
user.setEmail(rs.getString(5));
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(Connect.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
return user;
}
public void deleteUser(int id) {
try {
PreparedStatement preparedStatement = con
.prepareStatement("delete from user where id = ?");
preparedStatement.setInt(1, id);
preparedStatement.execute();
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(Connect.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
A Listagem 7 mostra a implementação da classe que expõe os métodos como serviços REST. A anotação @Path definida na classe, indica o endereço que o serviço será acessada. O método getUserHTML recebe como parâmetro um id de um usuário, e enviar como resposta um HTML com o nome do usuário. Se o serviço for acessado via um browse, esse método será executado, porque esse método responde um HTML. Além desse método existe outro método que responde com um objeto do tipo User, o método getUser. Para acessar esse método, é necessário indicar que se deseja acessar o método com tipo de resposta APPLICATION_JSON.
Além dos dois métodos GET, existe o método PUT, createUser, que recebe como parâmetro um objeto do tipo User, e salva esse usuário na base de dados e o método DELETE, deleteUser, que recebe como parâmetro um id, e exclui da base de dados o usuário com esse id.
package com.devmedia.server;
import java.sql.SQLException;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.devmedia.database.Connect;
import com.devmedia.model.User;
@Path("/user")
public class RestUser {
@GET
@Produces(MediaType.TEXT_HTML)
public String getUserHTML(@QueryParam("id") int id)
throws SQLException, InstantiationException, IllegalAccessException,
ClassNotFoundException {
Connect connect = new Connect();
User user = connect.getUser(id);
connect.closeConnection();
return "<HTML><BODY>" + user.getUserName() +
"</BODY></HTML>";
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public User getUser(@QueryParam("id") int id) throws SQLException,
InstantiationException, IllegalAccessException, ClassNotFoundException {
Connect connect = new Connect();
User user = connect.getUser(id);
connect.closeConnection();
return user;
}
@PUT
public Response createUser(User user) throws SQLException,
InstantiationException, IllegalAccessException,
ClassNotFoundException {
Connect connect = new Connect();
connect.insertUser(user);
connect.closeConnection();
return Response.status(Status.OK).build();
}
@DELETE
public Response deleteUser(@QueryParam("id") int id)
throws SQLException, InstantiationException, IllegalAccessException,
ClassNotFoundException {
Connect connect = new Connect();
connect.deleteUser(id);
connect.closeConnection();
return Response.status(Status.OK).build();
}
}
A Listagem 8 mostra a criação do cliente que acessa o serviço GET do servidor desenvolvido anteriormente. Para executar o cliente, o servidor de aplicação com o servidor deve estar sendo executado. O primeiro passo no desenvolvimento do cliente, é criar um objeto do tipo Client, depois é definido o path da requisição, no caso desse cliente, o endereço http://localhost:8080/server/rest/user. Por ultimo é definido os parâmetros da requisição, no caso desse chamado, o parâmetro id, e por fim é feito a chamada get.
package com.santana.easy.manager.client;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
import com.devmedia.model.User;
public class MainClientGet {
public static void main(String args[]) {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://localhost:8080/server");
WebTarget resourceWebTarget = webTarget.path("rest");
WebTarget pathdWebTarget = resourceWebTarget.path("user");
WebTarget pathdWebTargetQuery = pathdWebTarget.queryParam("id", 1);
Invocation.Builder invocationBuilder =
pathdWebTargetQuery.request
(MediaType.APPLICATION_JSON_TYPE);
Response response = invocationBuilder.get();
System.out.println(response.getStatus());
User user = response.readEntity(User.class);
System.out.println(user.getId());
System.out.println(user.getUserName());
System.out.println(user.getPassword());
System.out.println(user.getAddress());
System.out.println(user.getEmail());
}
}
A Listagem 9 mostra a criação do cliente que acessa o serviço PUT do servidor desenvolvido anteriormente. O código é bastante parecido com o cliente anterior, a diferença é que o parâmetro é um objeto do tipo User.
package com.santana.easy.manager.client;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.devmedia.model.User;
public class MainClientPut {
public static void main(String args[]) {
User user = new User();
user.setId(1);
user.setUserName("eduardo");
user.setPassword("1234");
user.setAddress("Rua abc");
user.setEmail("abc@abc.com");
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://localhost:8080/server");
WebTarget resourceWebTarget = webTarget.path("rest");
WebTarget deleteWeb = resourceWebTarget.path("user");
Invocation.Builder deleteInvocationBuilder = deleteWeb.request();
Response putResponse = deleteInvocationBuilder.put
(Entity.entity(user, MediaType.APPLICATION_JSON_TYPE));
System.out.println(putResponse.getStatus());
System.out.println(putResponse.readEntity(String.class));
}
}
A Listagem 10 mostra a criação do cliente que acessa o serviço DELETE do servidor desenvolvido anteriormente. O código é bastante parecido com os dois clientes anteriores.
package com.santana.easy.manager.client;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
public class MainClientDelete {
public static void main(String args[]) {
ClientConfig clientConfig = new ClientConfig();
Client client = ClientBuilder.newClient(clientConfig);
WebTarget webTarget = client.target("http://localhost:8080/server");
WebTarget resourceWebTarget = webTarget.path("rest");
WebTarget deleteWebTarget = resourceWebTarget.path("user");
WebTarget deleteWebTargetQuery = deleteWebTarget.queryParam("id", 1);
Invocation.Builder invocationBuilder = deleteWebTargetQuery.request();
Response response = invocationBuilder.delete();
System.out.println(response.getStatus());
}
}
Este artigo mostrou como desenvolver uma aplicação RESTful com o framework Jersey em Java. Para isso foi criado um servidor com dois métodos GET, um método PUT e um DELETE. Para testar os serviços foi criado um cliente que acessa os serviços criados.