Entre para a LISTA VIP da Black Friday

00

DIAS

00

HORAS

00

MIN

00

SEG

Clique para saber mais

Micro Profile JavaEE com Wildfly Swarm

Micro Profile JavaEE com Wildfly Swarm
fernando.furtado
fernando.furtado

Compartilhe

Foi-se o tempo em que desenvolver uma aplicação JavaEE era algo extremamente burocrático e que tínhamos um servidor de aplicação inchado e com um consumo excessivo de recursos.

Hoje temos servidores que foram pensado na reutilização dos recursos e ainda temos a possibilidade de escolher quais módulos vamos inicializar junto com o servidor de aplicação, os chamados profiles. Mas e quando pensamos em micro-serviço? Como podemos trabalhar com micro-serviços no JavaEE?

A ideia do micro-serviço é de ter serviços isolados e independentes. Até aí tudo bem, podemos criar aplicações JavaEE separadas(isto é cada uma com seu .war). Mas como podemos fazer o deploy dessas aplicações?

Banner da promoção da black friday, com os dizeres: A Black Friday Alura está chegando. Faça parte da Lista VIP, receba o maior desconto do ano em primeira mão e garanta bônus exclusivos. Quero ser VIP

Podemos ter um único contêiner (servidor de aplicação) para onde efetuamos o deploy de todas as aplicações. Mas com isso estamos indo de frente com a ideia de micro-serviço. Pois estamos gerando um ponto único de falha (SPOF).

Em outras palavras se precisarmos por alguma razão interromper o contêiner, todas as aplicações penduradas nele cairão também.

Outra alternativa seria cada serviço ter seu próprio contêiner, mas dessa forma estaríamos subutilizando o próprio contêiner. Pois nele temos diversos serviços rodando e que nossas aplicações não iriam precisar.

![Fonte: http://wildfly-swarm.io](assets/micro-profile-javaee-com-wildfly-swarm/monolithic-as-300x73.png) Fonte: http://wildfly-swarm.io 

Se não tivessemos usando servidor de aplicação poderiamos usar um contêiner embutido na aplicação (Tomcat, Jetty, Undertown e etc..). Porém dessa forma perderiamos as facilidades que o servidor de aplicação nos proporcionam.

E aí que entra uma ferramenta nova para nos ajudar, Wildfly-Swarm.

Com ele podemos declarar quais "módulos" da plataforma JavaEE queremos utilizar.

A partir daí é desenvolver sua aplicação JavaEE normalmente e ao empacotar nossa aplicação, o wildfly-swarm pegará nosso pacote .war e irá embrulhar em um pacote dele com um contêiner micro para rodar nossa aplicação. Daí o nome de Micro-Profile.

Esse artefato final (o pacote que foi gerado a partir do nosso .war) é um arquivo .jar e podemos executar com um simples comando java -jar no nosso terminal.

Wildfly-Swarm usa o conceito de UberJar para gerar o artefato final. Que nada mais é do que um arquivo .jar que contém além do seu .war todas as dependências necessárias para que o wildfly-swarm consiga rodar.

Logo um UberJar é um arquivo .jar um pouco mais inchado mas somente com o necessário para rodar a aplicação.

![Fonte: http://wildfly-swarm.io](assets/micro-profile-javaee-com-wildfly-swarm/swarm-uberjar-300x300.png) Fonte: http://wildfly-swarm.io

O mínimo necessário para rodar o wildfly-swarm é JDK8 e Maven ou Gradle.

No wildfly-swarm temos o conceito de fraction, que nada mais é do que uma funcionalidade/configuração do servidor de aplicação. Na maioria das vezes um fraction pode ser comparado (mapeado) com um subsystem do servidor de aplicação (ex.: Datasource, Driver, Pool, socket-binding e etc...), temos outros casos em que um fraction é uma funcionalidade que antes não tínhamos (nativamente) no servidor de aplicação (Ex.: Jolokia, Spring, NetflixOSS ).

Para usar o wildfly precisamos importar um pom.xml do wildfly-swarm no nosso projeto, e adicionar um plugin que fará a geração do UberJar.

Nesse post vamos construir uma simples aplicação rest usando o wildfly-swarm. Vamos lá!

Com o projeto (web) maven criado precisamos importar o pom.xml do wildfly-swarm ao nosso projeto. Esse pom.xml é importado declarando uma denpendencia gerenciada e declarando que o escopo da mesma será import.

Para não ficar espalhando a versão do wildfly-swarm estamos utilizando por todo nosso pom.xml vamos declarar uma propriedade com ela. Além disso já vamos definir a versão do Java que iremos utilizar, e como se trata de um projeto web teoricamente precisariamos de um arquivo web.xml porém não é necessário para nosso caso. Então vamos definir que não deve ser gerado um erro caso não exista o arquivo web.xml.

 <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <failOnMissingWebXml>false</failOnMissingWebXml> <version.wildfly.swarm>2016.9</version.wildfly.swarm> </properties> 

Agora vamos importar o arquivo pom.xml do wildfly-swarm que tem o nome de BOM (Bill of Materials).

 <dependencyManagement> <dependencies> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>bom</artifactId> <version>${version.wildfly.swarm}</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> 

Além disso precisamos adicionar o plugin que irá gerar o UberJar baseado no nosso war.

 <plugin> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-plugin</artifactId> <version>${version.wildfly.swarm}</version> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin> 

Precisamos também adicionar a dependência referente à api do JavaEE e esta será provida pelo wildfly-swarm.

 <!--Java EE Api --> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> 

E esse é o setup para utilizar o wildfly-swarm, ao final teriamos o nosso arquivo pom.xml mais ou menos da seguinte maneira:

 <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>br.com.caelum</groupId> <artifactId>livraria</artifactId> <version>1.0</version> <packaging>war</packaging>

<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <failOnMissingWebXml>false</failOnMissingWebXml> <version.wildfly.swarm>2016.9</version.wildfly.swarm> </properties>

<dependencyManagement> <dependencies> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>bom</artifactId> <version>${version.wildfly.swarm}</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement>

<dependencies>

<!--Java EE Api --> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency>

</dependencies>

<build> <finalName>livraria</finalName> <plugins> <plugin> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-plugin</artifactId> <version>${version.wildfly.swarm}</version> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin> </plugins> </build>

</project> 

Agora vamos adicionar as dependências que queremos do servidor de aplicação, para o nosso caso vamos adicionar, jax-rs, cdi, ejb, jpa, datasources.


<!--Swarm Dependencies -->

<dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>jaxrs-cdi</artifactId> </dependency>

<dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>ejb</artifactId> </dependency>

<dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>jpa</artifactId> </dependency>

<dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>datasources</artifactId> </dependency>

Vamos começar a "codar" nosso projeto de livraria.

Vamos iniciar criando nossas classes de domínio Livro e Autor e seus respectivos DAOs

 @Entity public class Livro { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String titulo; private String descricao; @ManyToMany private List<Autor> autores; @Deprecated private Livro(){} public Livro(Long id, String titulo, String descricao, List<Autor> autores) { this.id = id; this.titulo = titulo; this.descricao = descricao; this.autores = autores; } public Long getId() { return id; } public String getTitulo() { return titulo; } public String getDescricao() { return descricao; }

public List<Autor> getAutores() { return autores; }

public void adicionaAutor(Autor autor) { this.autores.add(autor); } } 
 @Entity public class Autor { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String nome; @Deprecated private Autor(){} public Autor(Long id, String nome) { this.id = id; this.nome = nome; }

public Long getId() { return id; }

public String getNome() { return nome; } } 
 public class LivroDAO {

@PersistenceContext private EntityManager manager;

public Livro buscaLivroComId(Long id) {

Livro livro = manager.createQuery("select l from Livro l left join fetch l.autores a where l.id = :id", Livro.class) .setParameter("id", id).getSingleResult();

return livro; }

public List<Livro> listaTodos() { return manager.createQuery("select l from Livro l left join fetch l.autores a", Livro.class).getResultList(); }

public void adicionar(Livro livro) { manager.persist(livro); }

public void atualiza(Livro livro) { manager.merge(livro); }

} 
 public class AutorDAO {

@PersistenceContext private EntityManager manager;

public Autor buscaAutorComId(Long id) { return manager.find(Autor.class, id); }

public void adiciona(Autor autor) { manager.persist(autor); } } 

Vamos criar uma classe para configurar o JAX-RS para receber requisições a partir da raiz:

 import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application;

@ApplicationPath("/") public class JaxRsConfiguration extends Application{ } 

Agora que já temos como receber requisições vamos criar nossos recursos que serão expostos pela nossa api LivroResource e AutorResource além disso já vamos marcar-los como EJB Stateless para que já tenhamos transação entre outras coisas disponíveis.

 @Path("livros") @Stateless public class LivroResource {

@Inject private LivroDAO livroDao;

@Path("{id:\\\\d+}") @GET @Produces(MediaType.APPLICATION\_JSON) public Livro buscaPorId(@PathParam("id") Long id){ return livroDao.buscaLivroComId(id); } @GET @Produces(MediaType.APPLICATION\_JSON) public List<Livro> listaDeLivros(){ return livroDao.listaTodos(); } @POST @Consumes(MediaType.APPLICATION\_JSON) public Response adicionaLivro(Livro livro){ livroDao.adicionar(livro); return Response .created(URI.create("/livros/" + livro.getId())) .entity(livro) .type(MediaType.APPLICATION\_JSON) .build(); } } 

Nesse recurso temos as seguintes URIs

  • /livros - GET (Retorna uma lista com todos os livros)
  • /livros/id - GET (Retorna o livro com ID especificado)
  • /livros - POST (Cria um novo livro)
 @Path("livros/{livroId:\\\\d+}/autores") @Stateless public class AutorResource { @Inject private LivroDAO livroDao; @Inject private AutorDAO autorDao; @GET @Produces(MediaType.APPLICATION\_JSON) public List<Autor> listaDeAutoresDoLivro(@PathParam("livroId") Long livroId){ return livroDao.buscaLivroComId(livroId).getAutores(); } @GET @Path("{autorId:\\\\d+}") @Produces(MediaType.APPLICATION\_JSON) public Autor autorDoLivro(@PathParam("autorId") Long autorId){ return autorDao.buscaAutorComId(autorId); } @POST @Consumes(MediaType.APPLICATION\_JSON) public Response adicionaAutor(@PathParam("livroId") Long livroId, Autor autor){ Livro livro = livroDao.buscaLivroComId(livroId); autorDao.adiciona(autor); livro.adicionaAutor(autor); livroDao.atualiza(livro); return Response .created(URI.create("/livros/" + livroId + "/autores/" + autor.getId()) ) .entity(autor) .type(MediaType.APPLICATION\_JSON) .build(); }

} 

Nesse recurso temos as seguintes URIs

  • /livros/idDoLivro/autores - GET (retorna todos os autores de um livro especifico)
  • /livros/idDoLivro/autores/idDoAutor - GET (retorna um autor especifico de um livro especifico)
  • /livros/idDoLivro/autores - POST (cria um novo autor associado ao livro)

Agora que temos nosso projeto pronto temos que registrar um datasource e associa-lo no nosso arquivo persistence.xml . Para o nosso caso, esta configuração será feita em uma classe com um método main.

Porém para configurar o datasource precisamos dizer qual o driver, e no nosso caso será um driver para mysql. para não precisarmos registrar um driver, podemos adicionar a dependência para esse driver diretamente no pom.xml

 <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>mysql</artifactId> <version>${version.wildfly.swarm}</version> </dependency> 

Para que seja adicionado corretamente o driver para mysql precisamos excluir o driver default que vem quando adicionamos a dependência para JPA que é o H2. Para isso vamos alterar a dependência de JPA e remover o H2.

 <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>jpa</artifactId> <exclusions> <exclusion> <groupId>org.wildfly.swarm</groupId> <artifactId>h2</artifactId> </exclusion> </exclusions> </dependency> 

(Se não fizermos essa configuração ao subirmos o wildfly-swarm iremos receber um erro, informando que esta rolando um conflito entre essas dependências H2, MYSQL).

Pronto agora vamos registrar nosso datasource dentro de um método main:

 public class Boot { public static void main(String\[\] args) throws Exception { Swarm swarm = new Swarm(args); swarm.fraction( new DatasourcesFraction() .dataSource("swarmDs", (ds) -> { ds.driverName("mysql"); ds.connectionUrl("jdbc:mysql://localhost/livraria\_swarm?createDatabaseIfNotExist=true&&useSSL=false"); ds.userName("root"); })); swarm.start(); } } 

Como estamos sobrescrevendo o comportamento default do wildfly-swarm precisamos ensinar ele como ele deve fazer o deploy da nossa aplicação (ou seja dentro do nosso .war quais classes devem estar disponíveis, quais arquivos e etc...).

Para fazer isso iremos usar uma biblioteca chamada ShrinkWrap da própria JBoss que serve para criar um pacote programaticamente, ela é muito utilizada quando estamos usando o Arquillian para testes de aceitação/integração.

Com ele podemos criar JARArchive (.jar) e WARArchive (.war). Além desses temos outros tipos mais especificos por exemplo RibbonArchive um archive especifico que pode ser registrar em aplicações baseadas em Ribbon, Secured que já injeta o arquivo keycloak.json e já configura a parte de seguraça.

Temos também um outro tipo especifico que é JAXRSArchive que é um archive que já configura o jax-rs e faz o binding para classe de configuração Application/@ApplicationPath. Vamos utilizar esse archive.

Além disso precisamos dizer que ao fazer o deploy da aplicação seja levado os arquivos persistence.xml e beans.xml necessários para o funcionamento da JPA e CDI.

O arquivo beans.xml deve estar dentro do diretório WEB-INF a partir do classpath e o arquivo persistence.xml, deve estar em META-INF a partir do classpath também. E precisaremos configurar isso também.

 public class Boot { public static void main(String\[\] args) throws Exception { Swarm swarm = new Swarm(args); swarm.fraction( new DatasourcesFraction() .dataSource("swarmDs", (ds) -> { ds.driverName("mysql"); ds.connectionUrl("jdbc:mysql://localhost/livraria\_swarm?createDatabaseIfNotExist=true&&useSSL=false"); ds.userName("root"); })); swarm.start(); JAXRSArchive deployment = ShrinkWrap.create(JAXRSArchive.class); ClassLoader classLoader = Boot.class.getClassLoader(); deployment.addAsWebInfResource(classLoader.getResource("beans.xml"),"beans.xml"); deployment.addAsWebInfResource(classLoader.getResource("persistence.xml"),"classes/META-INF/persistence.xml"); deployment.addPackages(true, Package.getPackage("br.com.caelum.livraria")); deployment.addAllDependencies(); swarm.deploy(deployment); } } 

Como ultima configuração precisamos indicar ao plugin do wildfly-swarm que ele não deve usar sua classe padrão para iniciar. Ele deve usar nossa classe Boot e com isso alterar o arquivo de manifesto para usar essa classe como ponto inical da nossa aplicação. Vamos alterar a declaração do plugin no arquivo pom.xml e adicionar a configuração ``.

 <plugin> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-plugin</artifactId> <version>${version.wildfly.swarm}</version> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> <configuration> <mainClass>br.com.caelum.livraria.Boot</mainClass> </configuration> </plugin> 

Para que seja gerado o UberJar devemos executar o goal package do maven (Ex.: mvn package). Ao termino dessa execução no diretório target teremos um arquivo livraria.war e um livraria-swarm.jar (entre outros arquivos).

O arquivo livraria-swarm.jar é o nosso UberJar.

Para executarmos podemos faze-lo pelo plugin do maven através de mvn wildfly-swarm:run ou executando o comando java -jar livraria-swarm.jar.

Dessa forma temos uma aplicação JavaEE somente com o mínimo necessário e um contêiner bem mais leve para rodar a mesma.

Aprendendo mais

Quer aprender mais sobre como utilizar a especificação JavaEE?

Não deixe de conferir nosso curso Plataforma JavaEE, nele abordamos jax-rs, jpa, cdi, ejb entre outros recursos da especificação JavaEE.

E aí o que você achou do wildfly-swarm?

Veja outros artigos sobre Programação