Olá a todos neste blog, e aqui está o quarto post da série sobre Quarkus!

foi sobre como Quarkus combina MicroProfile e Spring. Vale lembrar que se posiciona como "Java subatômica super rápida", também conhecida como "pilha Java voltada para Kubernetes, otimizada para GraalVM e OpenJDK HotSpot e construída a partir das melhores bibliotecas e padrões". Hoje, vamos mostrar como modernizar aplicações Java existentes, utilizando os recursos do Quarkus, exemplificado pelo , que utiliza tecnologias suportadas pelo Quarkus como CDI e Servlet 3.
É importante observar que tanto o Quarkus quanto o JBoss EAP focam em utilizar ferramentas que são amplamente baseadas em padrões. Você não tem uma aplicação rodando no JBoss EAP? Sem problemas, é fácil migrar de seu servidor de aplicações atual para o JBoss EAP com o . Após isso, a versão final e funcional do código modernizado estará disponível no repositório , no módulo .
Na redação deste post, foram utilizados , principalmente e Building a .
Obtenha o código
Primeiro, vamos criar um clone local do repositório :
$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Clonando para 'jboss-eap-quickstarts'...
remoto: Enumerando objetos: 148133, feito.
remoto: Total 148133 (delta 0), reutilizado 0 (delta 0), pack-reused 148133
Recebendo objetos: 100% (148133/148133), 59.90 MiB | 7.62 MiB/s, feito.
Resolvendo deltas: 100% (66476/66476), feito.
$ cd jboss-eap-quickstarts/helloworld/
Vamos ver como funciona o helloworld original
Bom, a essência deste aplicativo é clara pelo nome, mas iremos modernizar seu código de forma rigorosa. Portanto, primeiro, vamos dar uma olhada neste aplicativo em seu estado original.
Implantando o helloworld
1. Abra o terminal e navegue até a pasta raiz do JBoss EAP (você pode baixá-lo ), ou seja, na pasta EAP_HOME.
2. Inicie o servidor JBoss EAP com o perfil padrão:
$ EAP_HOME/bin/standalone.sh
Nota: no Windows, para iniciar, use o script EAP_HOMEbinstandalone.bat.
Após alguns segundos, o log deve exibir algo como:
[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) iniciado em 3315ms - Iniciados 306 de 527 serviços (321 serviços estão atrasados, passivos ou sob demanda)
3. Abra no navegador :8080 e você verá isto:

Fig. 1. Página inicial do JBoss EAP.
4. Siga as instruções no guia : implantamos o helloworld e executamos (a partir da pasta raiz do projeto) o seguinte comando:
$ mvn clean install wildfly:deploy
Após a execução bem-sucedida deste comando, veremos aproximadamente o seguinte no log:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.224 s
Assim, a primeira implantação do aplicativo helloworld no JBoss EAP levou um pouco mais de 8 segundos.
Testando o helloworld
Seguindo rigorosamente o guia , abra no navegador :8080/helloworld e vemos o seguinte:

Fig. 2. Hello World original do JBoss EAP.
Fazendo alterações
Mudamos o parâmetro de entrada createHelloMessage(String name) de World para Marco:
writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");
Executamos novamente o seguinte comando:
$ mvn clean install wildfly:deploy
Em seguida, atualizamos a página no navegador e vemos que o texto foi alterado:

Fig. 3. Hello Marco no JBoss EAP.
Revertendo a implantação do helloworld e finalizando o JBoss EAP
Isso não é obrigatório, mas se você quiser reverter a implantação, pode fazê-lo com o seguinte comando:
$ mvn clean install wildfly:undeploy
Para encerrar a instância do JBoss EAP, basta pressionar Ctrl+C na janela do terminal.
Modernizando o helloworld
Agora vamos nos dedicar à modernização do aplicativo helloworld original.
Criando um novo branch
Criamos um novo branch de trabalho após a conclusão do projeto quickstart:
$ git checkout -b quarkus 7.2.0.GA
Alterando o arquivo pom.xml
Começaremos a alterar o aplicativo a partir do arquivo pom.xml. Para que o Quarkus possa inserir blocos XML, executaremos o seguinte comando na pasta helloworld:
$ mvn io.quarkus:quarkus-maven-plugin:0.23.2:create
Ao escrever este artigo, a versão 0.23.2 foi utilizada. O Quarkus frequentemente recebe novas versões, e você pode verificar qual é a versão mais recente no site .
O comando acima irá inserir os seguintes elementos no pom.xml:
- A propriedade , que define a versão do Quarkus a ser utilizada.
- O bloco para importar o Quarkus BOM (bill of materials), para que não seja necessário adicionar a versão para cada dependência do Quarkus.
- O plugin quarkus-maven-plugin, que é responsável pela empacotamento do aplicativo e oferece o modo de desenvolvimento.
- O perfil nativo para criar arquivos executáveis do aplicativo.
Além disso, no pom.xml, fazemos as seguintes alterações manualmente:
- Extraímos a tag do bloco e a colocamos acima da tag . Como na próxima etapa removeremos o bloco , precisamos manter .
- Removemos o bloco , pois este aplicativo não precisará mais do pom pai do JBoss ao trabalhar com o Quarkus.
- Adicionamos a tag e a colocamos sob a tag . Você pode especificar o número da versão que desejar.
- Removemos a tag , pois este aplicativo não é mais um WAR, mas um JAR comum.
- Modificamos as seguintes dependências:
- Alteramos a dependência javax.enterprise:cdi-api para io.quarkus:quarkus-arc, removendo provided, pois (de acordo com a documentação) esta extensão do Quarkus fornece injeção de dependência CDI.
- Alteramos a dependência org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec para io.quarkus:quarkus-undertow, removendo provided, pois (de acordo com a documentação) esta extensão do Quarkus fornece suporte a servlets.
- Removemos a dependência org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec, pois elas vêm acompanhadas das dependências que acabamos de modificar.
A versão do arquivo pom.xml com todas as alterações está disponível em .
Observe que o comando mvn io.quarkus:quarkus-maven-plugin:0.23.2:create acima não apenas altera o arquivo pom.xml, mas também adiciona ao projeto uma série de componentes, nomeadamente, os seguintes arquivos e pastas:
- O arquivo mvnw e mvnw.cmd e a pasta .mvn: o Maven Wrapper permite executar projetos Maven na versão especificada do Maven sem precisar instalar essa versão.
- Pasta docker (no diretório src/main/): aqui estão exemplos de arquivos Dockerfile para os modos native e jvm (junto com o arquivo .dockerignore).
- Pasta resources (no diretório src/main/): aqui está um arquivo application.properties vazio e um exemplo da página inicial do Quarkus index.html (veja mais em Execute o helloworld modernizado).
Executamos o helloworld
Para testar o aplicativo, usamos quarkus:dev, que executa o Quarkus no modo de desenvolvimento (veja mais nesta seção do guia sobre ).
Nota: esta etapa resultará em um erro, pois ainda não fizemos todas as alterações necessárias.
Agora executamos o comando para verificar como isso funcionará:
$ ./mvnw compile quarkus:dev
[INFO] Escaneando projetos...
[INFO]
[INFO] --------------------------------
[INFO] Construindo Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Usando codificação 'UTF-8' para copiar recursos filtrados.
[INFO] Copiando 2 recursos
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Nada para compilar - todas as classes estão atualizadas
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Ouvindo transporte dt_socket no endereço: 5005
INFO [io.qua.dep.QuarkusAugmentor] Iniciando a augmentação do quarkus
INFO [org.jbo.threads] Versão do JBoss Threads 3.0.0.Final
ERROR [io.qua.dev.DevModeMain] Falha ao iniciar o quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Falha na construção: Falha na construção devido a erros
[error]: A etapa de construção io.quarkus.arc.deployment.ArcProcessor#validate lançou uma exceção: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Dependência insatisfeita para o tipo org.jboss.as.quickstarts.helloworld.HelloService e qualificador [@Default]
- membro java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
- declarado na classe bean [tipos=[javax.servlet.ServletConfig, java.io.Serializable, org.jboss.as.quickstarts.helloworld.HelloWorldServlet, javax.servlet.GenericServlet, javax.servlet.Servlet, java.lang.Object, javax.servlet.http.HttpServlet], qualificadores=[@Default, @Any], alvo=org.jboss.as.quickstarts.helloworld.HelloWorldServlet]
at io.quarkus.arc.processor.BeanDeployment.processErrors(BeanDeployment.java:841)
at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:214)
at io.quarkus.arc.processor.BeanProcessor.initialize(BeanProcessor.java:106)
at io.quarkus.arc.deployment.ArcProcessor.validate(ArcProcessor.java:249)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at io.quarkus.deployment.ExtensionLoader$1.execute(ExtensionLoader.java:780)
at io.quarkus.builder.BuildContext.run(BuildContext.java:415)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1426)
at java.lang.Thread.run(Thread.java:748)
at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Causado por: javax.enterprise.inject.UnsatisfiedResolutionException: Dependência insatisfeita para o tipo org.jboss.as.quickstarts.helloworld.HelloService e qualificador [@Default]
- membro java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
- declarado na classe bean [tipos=[javax.servlet.ServletConfig, java.io.Serializable, org.jboss.as.quickstarts.helloworld.HelloWorldServlet, javax.servlet.GenericServlet, javax.servlet.Servlet, java.lang.Object, javax.servlet.http.HttpServlet], qualificadores=[@Default, @Any], alvo=org.jboss.as.quickstarts.helloworld.HelloWorldServlet]
at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:428)
at io.quarkus.arc.processor.BeanInfo.init(BeanInfo.java:371)
at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:206)
... 14 mais
Então, não funciona... Mas por quê?
A exceção UnsatisfiedResolutionException indica a classe HelloService, que é membro da classe HelloWorldServlet (membro java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService). O problema é que o HelloWorldServlet precisa de uma instância injetada de HelloService, mas não conseguimos encontrá-la (embora ambas as classes estejam no mesmo pacote).
É hora de voltar para e ler como o Quarkus funciona com , e consequentemente, com (CDI). Portanto, abrimos o guia Contexts and Dependency Injection e na seção lemos: "A classe bean, que não possui uma anotação bean definidora, não é buscada."
Vamos olhar a classe HelloService - realmente não há tal anotação. Portanto, precisamos adicionar uma, para que o Quarkus possa buscar e encontrar o bean. E como este é um objeto stateless, podemos claramente adicionar a anotação @ApplicationScoped da seguinte maneira:
@ApplicationScoped
public class HelloService {
Nota: Aqui, o ambiente de desenvolvimento pode pedir que você adicione o pacote necessário (veja a linha abaixo), e você terá que fazer isso manualmente, assim:
import javax.enterprise.context.ApplicationScoped;
Se você estiver em dúvida sobre qual escopo usar quando ele não estiver definido para o bean original, estude a documentação .
Agora testamos novamente executar o aplicativo com o comando .\/mvnw compile quarkus:dev:
$ .\/mvnw compile quarkus:dev
[INFO] Escaneando projetos...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Construindo Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Usando a codificação 'UTF-8' para copiar recursos filtrados.
[INFO] Copiando 2 recursos
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Mudanças detectadas - recompilando o módulo!
[INFO] Compilando 2 arquivos fonte para \/home\/mrizzi\/git\/forked\/jboss-eap-quickstarts\/helloworld\/target\/classes
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Ouvindo o transporte dt_socket no endereço: 5005
INFO [io.qua.dep.QuarkusAugmentor] (main) Iniciando a ampliação do quarkus
INFO [io.qua.dep.QuarkusAugmentor] (main) Ampliação do quarkus concluída em 576ms
INFO [io.quarkus] (main) Quarkus 0.23.2 iniciado em 1.083s. Ouvindo em: http:\/\/0.0.0.0:8080
INFO [io.quarkus] (main) Perfil dev ativado. Live Coding ativado.
INFO [io.quarkus] (main) Recursos instalados: [cdi]
Agora tudo está passando sem erros.
Iniciamos o helloworld modernizado
Como escrito no log, abrimos no navegador :8080 (página inicial do Quarkus por padrão) e vemos o seguinte:

Fig. 4. Página inicial do Quarkus dev.
Na anotação WebServlet, este aplicativo possui a seguinte definição de contexto:
@WebServlet("\/HelloWorld")
public class HelloWorldServlet extends HttpServlet {
Portanto, vamos ao navegador em :8080/HelloWorld e vemos o seguinte:

Figura 5: Página do desenvolvedor Quarkus para o aplicativo Hello World.
Bom, tudo está funcionando.
Agora, vamos fazer algumas alterações no código. Observe que o comando ./mvnw compile quarkus:dev ainda está funcionando, e não temos a intenção de interrompê-lo. Agora, vamos tentar aplicar as mesmas – triviais – alterações ao próprio código e ver como o Quarkus facilita a vida do desenvolvedor:
writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");
Salvamos o arquivo e então atualizamos a página da web para ver a mensagem Hello Marco, conforme mostrado na captura de tela abaixo:

Figura 6. Página Hello Marco no Quarkus dev.
Agora, vamos verificar a saída no terminal:
INFO [io.qua.dev] (vert.x-worker-thread-3) Arquivos de origem alterados detectados, recompilando [/home/mrizzi/git/forked/jboss-eap-quickstarts/helloworld/src/main/java/org/jboss/as/quickstarts/helloworld/HelloWorldServlet.java]
INFO [io.quarkus] (vert.x-worker-thread-3) Quarkus parou em 0.003s
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Iniciando a amplificação do quarkus
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Amplificação do quarkus concluída em 232ms
INFO [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 iniciado em 0.257s. Escutando em: http://0.0.0.0:8080
INFO [io.quarkus] (vert.x-worker-thread-3) Perfil dev ativado. Live Coding ativado.
INFO [io.quarkus] (vert.x-worker-thread-3) Recursos instalados: [cdi]
INFO [io.qua.dev] (vert.x-worker-thread-3) Tempo total de substituição a quente: 0.371s
A atualização da página acionou a detecção de alterações no código-fonte, e o Quarkus executou automaticamente o procedimento de "parar e iniciar". E tudo isso foi feito em apenas 0.371 segundos (a famosa "Java subatômica super rápida").
Compilando helloworld em um pacote JAR
Agora que o código está funcionando como deveria, vamos empacotá-lo com o seguinte comando:
$ ./mvnw clean package
Este comando cria dois arquivos JAR na pasta /target: o arquivo helloworld-.jar, que é o artefato padrão criado pelo comando Maven junto com as classes e recursos do projeto. E o arquivo helloworld—runner.jar, que é um JAR executável.
Observe que este não é um uber-jar, pois todas as dependências são simplesmente copiadas para a pasta /target/lib (e não empacotadas no arquivo JAR). Portanto, para executar este JAR de outra pasta ou em outro host, você deve copiar tanto o arquivo JAR quanto a pasta /lib, considerando que o elemento Class-Path no arquivo MANIFEST.MF do pacote JAR contém uma enumeração explícita dos JARs da pasta lib.
Para aprender a criar aplicativos uber-jar, consulte o guia .
Executando helloworld, empacotado em JAR
Agora podemos executar nosso JAR usando o comando padrão java:
$ java -jar ./target/helloworld--runner.jar
INFO [io.quarkus] (main) Quarkus 0.23.2 iniciado em 0.673s. Ouvindo em: http://0.0.0.0:8080
INFO [io.quarkus] (main) Perfil prod ativado.
INFO [io.quarkus] (main) Recursos instalados: [cdi]
Após a conclusão de todas essas etapas, acesse no navegador :8080 e verifique se tudo está funcionando corretamente.
Compilando helloworld em um arquivo executável nativo
Assim, nosso helloworld funciona como um aplicativo Java standalone, usando as dependências do Quarkus. Mas podemos ir além e transformá-lo em um arquivo executável nativo.
Instalando GraalVM
Primeiramente, para isso, precisamos instalar as ferramentas necessárias:
1. Baixe GraalVM 19.2.0.1 de .
2. Extraia o arquivo baixado:
$ tar xvzf graalvm-ce-linux-amd64-19.2.0.1.tar.gz
3. Vá para a pasta extraída.
4. Execute o comando abaixo para baixar e adicionar a imagem nativa:
$ ./bin/gu install native-image
5. Defina a pasta criada no passo 2 na variável de ambiente GRAALVM_HOME:
$ export GRAALVM_HOME={untar-folder}/graalvm-ce-19.2.0.1)
Mais informações e instruções de instalação em outros sistemas operacionais podem ser encontradas no guia .
Executando a construção do helloworld em um arquivo executável nativo
Leia o guia : "E agora vamos criar um arquivo executável nativo para nosso aplicativo, a fim de reduzir o tempo de inicialização e o tamanho em disco. O executável terá tudo o que é necessário para rodar o aplicativo, incluindo a máquina JVM (na verdade, uma versão reduzida que contém apenas o que é necessário para executar o aplicativo) e nosso aplicativo."
Para criar um arquivo executável nativo, é necessário ativar o perfil nativo do Maven:
$ ./mvnw package -Pnative
Nossa compilação levou um minuto e 10 segundos, e o arquivo final helloworld-runner foi criado na pasta /target.
Executando o arquivo executável nativo helloworld
No passo anterior, obtivemos o arquivo executável /target/helloworld-runner. Agora vamos executá-lo:
$ ./target/helloworld--runner
INFO [io.quarkus] (main) Quarkus 0.23.2 iniciado em 0.006s. Ouvindo em: http://0.0.0.0:8080
INFO [io.quarkus] (main) Perfil prod ativado.
INFO [io.quarkus] (main) Recursos instalados: [cdi]
Novamente, abrimos no navegador :8080 e verificamos se tudo está funcionando corretamente.
Continua...
Acreditamos que o método de modernização de aplicativos Java, abordado neste post (mesmo em um exemplo simples), usando as capacidades do Quarkus, deve ser ativamente aplicado na vida real. No entanto, você provavelmente enfrentará uma série de problemas, cujas soluções abordaremos parcialmente no próximo post, onde falaremos sobre como medir o uso de memória para avaliar a melhoria de desempenho, uma parte importante de todo o processo de modernização de aplicativos.
Fonte: habr.com
