Microserviços no JavaEE
A iniciativa Eclipse MicroProfile surgiu em 2016 e reúne grupos de usuários e as empresas que distribuem os principais containers JavaEE como WildFly (Red Hat), WebSphere (IBM), TomEE (TomiTribe) e Payara (antigo GlassFish). O objetivo da iniciativa é otimizar a plataforma JavaEE para o uso da arquitetura de microserviços, criando novos recursos e funcionalidades para isso.
A especificação Eclipse MicroProfile (atualmente na versão 2.0) define uma plataforma de tamanho reduzido para desenvolver microserviços utilizando as principais APIs JavaEE. Portabilidade, menos configuração e simplificação do processo de deploy também são um requisito. Todos os containers microprofile devem conter as seguintes especificações JavaEE:
- JAX-RS 2.0
- CDI 1.2
- JSON-P 1.0
Neste artigo exploramos o Payara MicroProfile, o qual, como vimos, é compatível com a especificação Eclipse MicroProfile. Não confunda o Payara Micro com o Payara Server Full, que é o container tradiconal e maior, contendo toda API JavaEE bem como o console de administração.
O Payara Micro compõe um arquivo .jar de 70MB, não é necessário instalação, configuração, nem nenhuma escrita de código. Para subir o Payra Micro apenas execute o arquivo .jar passando como parâmetro o arquivo .war do serviço. Você pode baixá-lo aqui.
Como exemplo, vamos criar um serviço chamado BookService. Esse serviço acessa a base de dados e fornece uma lista de livros disponíveis para outra aplicação que acessa o serviço. Você pode escolher o banco de dados que mais lhe convier, neste exemplo utilizamos o MySQL. A IDE utilizada é o Eclipse JavaEE Photon.
O código fonte do projeto completo está disponível no github.
O código fonte do projeto completo está disponível no github.
Crie um banco de dados chamado Livros.
Abra o Eclipse e crie um novo projeto MAVEN com o nome de BookService. No Arquivo pom.xml adicionamos as dependencias do mysql-connector e javaEE 8 API como segue:
<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>BookService</groupId>
<artifactId>BookService</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
</dependencies>
</project>
Agora clique com o botão direito na pasta raiz do projeto. Navegue por Java EE Tools e depois selecione Generate Web Deployment Descriptor:A importância do arquivo web.xml neste projeto será crucial. Tradicionalmente, aplicações Java EE definem o datasource no próprio servidor de aplicação seja via console admin ou por algum cliente em linha de comando. Com microserviços, convém utilizar uma outra variante. Vamos definir o datasource na própria aplicação e, mais ainda, podemos disponibilizar esse datasource para outros serviços. Isso é possível desde que usemos um JNDI válido para o datasource contendo o namespace java:global. Portanto nosso web.xml fica como segue:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<data-source>
<name>java:global/livrosDataSource</name>
<class-name>com.mysql.cj.jdbc.MysqlXADataSource</class-name>
<server-name>localhost</server-name>
<port-number>3306</port-number>
<database-name>Livros</database-name>
<user>root</user>
<password>km02h5</password>
<property>
<name>serverTimezone</name>
<value>UTC</value>
</property>
<property>
<name>useTimezone</name>
<value>true</value>
</property>
<property>
<name>useSSL</name>
<value>true</value>
</property>
<property>
<name>fish.payara.slow-query-threshold-in-seconds</name>
<value>5</value>
</property>
<property>
<name>fish.payara.log-jdbc-calls</name>
<value>true</value>
</property>
<property>
<name>fish.payara.is-connection-validation-required</name>
<value>true</value>
</property>
<property>
<name>fish.payara.connection-validation-method</name>
<value>custom-validation</value>
</property>
<property>
<name>fish.payara.validation-classname</name>
<value>org.glassfish.api.jdbc.validation.MySQLConnectionValidation</value>
</property>
</data-source>
</web-app>
Como vamos utilizar as APIs Java Persistence (JPA) e Java Transaction (JTA), precisamos criar o arquivo persistence.xml e informar o nome do datasource criado no web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="livrosDataSourcePU" transaction-type="JTA">
<jta-data-source>java:global/livrosDataSource</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property
name="javax.persistence.schema-generation.database.action"
value="create" />
</properties>
</persistence-unit>
</persistence>
Crie agora 2 pacotes chamados com.app e outro com.entidades. No pacote entidades crie a classe Livro
//imports omitidos...
@Entity
@Table(name = "LIVROS")
@XmlRootElement
@NamedQuery(name = "Livro.buscarTodos", query = "SELECT l FROM Livro l")
public class Livro implements Serializable{
private static final long serialVersionUID = 1L;
@Id @Basic @NotNull
private Long id;
private String nome;
private int numeroPaginas;
private double preco;
@Temporal(TemporalType.DATE)
private Date publicacao;
//getters, setters, equals e hashCode omitidos...
}
No pacote app configuramos o web service REST. Primeiro criamos a classe AplicationConfig. Nela definimos que os serviços poderão ser invocados quando houver a palavra rest na URL do browser:
package com.app;
import java.util.Set;
import javax.ws.rs.core.Application;
@javax.ws.rs.ApplicationPath("rest")
public class ApplicationConfig extends Application{
@Override
public Set> getClasses() {
Set> resources = new java.util.HashSet<>();
resources.add(LivrosREST.class);
return resources;
}
}
E agora crie a classe LivrosREST, a qual será o web service propriamente dito, que retorna ao cliente os livros disponíveis:
//imports e package omitidos...
@Stateless
@Path("livros")
public class LivrosREST {
@PersistenceContext(unitName = "livrosDataSourcePU")
private EntityManager em;
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public List findAll() {
List livros = null;
try {
TypedQuery query = em.createNamedQuery("Livro.buscarTodos", Livro.class);
livros = query.getResultList();
}
catch (NoResultException e) {
}
return livros;
}
}
Nosso projeto já está pronto. Clique com o botão direito do mouse na pasta raiz do projeto, navegue pelas opções run/run as/maven install:
Após executar esse comando, repare que no log do eclipse aparece o caminho completo para onde foi gerado o arquivo .war. Selecione e copie esse caminho:
Agora vamos publicar o serviço. Vá para a pasta onde você salvou o arquivo jar do Payara Micro. Como foi dito no começo deste artigo, execute como um arquivo jar qualquer, e utilizamos o comando --deploy passando como parâmetro o arquivo .war que acabamos de gerar. Abra o prompt de comando e execute o seguinte comando:
java -jar payara-micro-5.182.jar --deploy /home/rafael/eclipse-workspace/BookService/target/BookService-1.0.war
Ao final do log do servidor, aparece a URL do serviço publicado. A porta default é 8080:Como a tabela do banco está vazia, ao acessar essa URL não teremos nenhum retorno. Como teste execute o seguinte script sql no seu banco de dados:
insert into LIVROS values (1, 'Pro Spring 5', 600, 25.99, '2010-05-25');
insert into LIVROS values (2, 'Java How to Program', 1200, 10.50, '1999-06-30');
insert into LIVROS values (3, 'JSP Design Patterns', 450, 1.99, '2001-11-28');
insert into LIVROS values (4, 'OCP Certification Guide', 900, 25.99, '2012-05-05');
insert into LIVROS values (5, 'Java EE 7', 600, 5.00, '2016-01-02');
Agora sim. Acesse novamente a URL http://localhost:8080/BookService-1.0/rest/livros e veremos a resposta sob a forma de um arquivo xml:Na segunda parte deste post criamos uma aplicação cliente que acessa esse serviço.