Quarkus: atualizações de aplicativos com o helloworld do JBoss EAP Quickstart

Olá a todos neste blog, e com você o quarto post da série Quarkus! (A propósito, registre-se e acesse o nosso webinar “ Este é o framework Java nativo do Quarkus - Kubernetes ”, que será realizado em 27 de maio. Mostraremos como começar do zero ou transferir soluções prontas) O



post anterior foi sobre como o Quarkus combina o MicroProfile e o Spring. Lembre-se de que o Quarkus está posicionado como "Java subatômico ultra-rápido", é "pilha Java orientada ao Kubernetes, aprimorada pelo GraalVM e OpenJDK HotSpot e compilada a partir das melhores bibliotecas e padrões". Hoje mostramos como atualizar aplicativos Java existentes usando os recursos Quarkus usando o aplicativo helloworld no repositório de início rápido da Red Hat JBoss Enterprise Application Platform (JBoss EAP)usando as tecnologias CDI e Servlet 3 suportadas pelo Quarkus.

É importante observar aqui que o Quarkus e o JBoss EAP se concentram no uso de ferramentas criadas com os padrões máximos. Não tem um aplicativo em execução no JBoss EAP? Não é um problema, ele pode ser facilmente migrado do servidor de aplicativos atual para o JBoss EAP usando o Red Hat Application Migration Toolkit . Depois disso, a versão final e funcional do código atualizado estará disponível no repositório github.com/mrizzi/jboss-eap-quickstarts/tree/quarkus , no módulo helloworld .

Esta postagem foi escrita usando os tutoriais do Quarkus , principalmente Criando seu primeiro aplicativo e Criando um executável nativo .

Temos um código


Primeiro, crie um clone local do repositório de iniciação rápida do JBoss EAP :

$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Cloning into 'jboss-eap-quickstarts'...
remote: Enumerating objects: 148133, done.
remote: Total 148133 (delta 0), reused 0 (delta 0), pack-reused 148133
Receiving objects: 100% (148133/148133), 59.90 MiB | 7.62 MiB/s, done.
Resolving deltas: 100% (66476/66476), done.
$ cd jboss-eap-quickstarts/helloworld/

Veja como o helloworld original funciona


Na verdade, a essência deste aplicativo é clara desde o nome, mas atualizaremos seu código estritamente científico. Portanto, para começar, observe esse aplicativo em sua forma original.

Expanda helloworld

1. Abra o terminal e vá para a raiz da pasta JBoss EAP (você pode baixá-lo aqui ), 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, o script EAP_HOME \ bin \ standalone.bat é usado para executar.

Após alguns segundos, o seguinte deve aparecer no log:

[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) started in 3315ms - Started 306 of 527 services (321 services are lazy, passive or on-demand)

3. Abra o navegador 127.0.0.1 : 8080 e veja o seguinte:



FIG. 1. Página inicial do JBoss EAP.

4. Siga as instruções no manual Build and Deploy the Quickstart : deploy helloworld e execute (a partir da pasta raiz do projeto) o seguinte comando:

$ mvn clean install wildfly:deploy

Após a execução bem-sucedida desse comando no log, veremos algo como o seguinte:

[INFO] ------------------------------------------------------------------------ 
[INFO] BUILD SUCCESS 
[INFO] ------------------------------------------------------------------------ 
[INFO] Total time: 8.224 s

Portanto, a primeira implantação do aplicativo helloworld no JBoss EAP levou pouco mais de 8 segundos.

Testamos o helloworld

Atuando estritamente de acordo com o guia Acesse o aplicativo , abra 127.0.0.1 : 8080 / helloworld em um navegador e veja o seguinte:



FIG. 2. Hello World original do JBoss EAP.

Fazer alterações

Altere o parâmetro de entrada createHelloMessage (String name) de World para Marco:

writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");

Novamente, execute o seguinte comando:

$ mvn clean install wildfly:deploy

Em seguida, atualizamos a página no navegador e vemos que o texto mudou:



FIG. 3. Olá Marco no JBoss EAP.

Revertemos a implantação do helloworld e saímos do JBoss EAP.

Isso é opcional, mas se você deseja cancelar a implantação, pode fazer isso com o seguinte comando:

$ mvn clean install wildfly:undeploy

Para desligar a instância do JBoss EAP, basta pressionar Ctrl + C na janela do terminal.

Atualizando o helloworld


Agora vamos atualizar o aplicativo helloworld original.

Criar uma nova ramificação

Crie uma nova ramificação de trabalho após a conclusão do projeto de início rápido:

$ 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 nele, execute 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 usada. O Quarkus geralmente possui novas versões; você pode descobrir qual versão é a mais recente em github.com/quarkusio/quarkus/releases/latest .

O comando acima irá inserir os seguintes elementos no pom.xml:

  • A propriedade <quarkus.version> que especifica a versão do Quarkus a ser usada.
  • O bloco <dependencyManagement> para importar o Quarkus BOM (lista de materiais) para não adicionar uma versão para cada dependência do Quarkus.
  • O plugin quarkus-maven-plugin, responsável por empacotar o aplicativo e fornecer o modo de desenvolvimento.
  • Um perfil nativo para criar executáveis ​​de aplicativos.

Além disso, fazemos manualmente as seguintes alterações no pom.xml:

  1. <groupId> <parent> <artifactId>. <parent>, <groupId>.
  2. <parent>, Quarkus pom JBoss.
  3. <version> <artifactId>. .
  4. <packaging>, WAR, JAR.
  5. :
    1. javax.enterprise:cdi-api io.quarkus:quarkus-arc, <scope>provided</scope>, ( ) Quarkus- injection CDI.
    2. Alteramos a dependência org.jboss.spec.javax.servlet: jboss-servlet-api_4.0_spec para io.quarkus: quarkus-undertow, removendo o <scope> fornecido </scope>, porque (de acordo com as docas) essa extensão do Quarkus fornece suporte ao servlet 's.
    3. Removemos a dependência org.jboss.spec.javax.annotation: jboss-annotations-api_1.3_spec, uma vez que elas são fornecidas com as dependências que acabamos de alterar.


A versão do arquivo pom.xml com todas as alterações está em github.com/mrizzi/jboss-eap-quickstarts/blob/quarkus/helloworld/pom.xml .

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 vários componentes ao projeto, a saber, os seguintes arquivos e pastas:

  • mvnw and mvnw.cmd .mvn: Maven Wrapper Maven Maven .
  • docker ( src/main/): Dockerfile native jvm ( .dockerignore).
  • resources ( src/main/): application.properties Quarkus index.html ( . Run the modernized helloworld ).

Iniciamos o helloworld
Para testar o aplicativo, usamos o quarkus: dev, que inicia o Quarkus no modo de desenvolvimento (para obter mais detalhes, consulte esta seção no manual do Modo de Desenvolvimento ).

Nota: espera-se que esta etapa resulte em um erro, porque ainda não fizemos todas as alterações necessárias.

Agora execute o comando para verificar como ele funciona:

$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Building Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Listening for transport dt_socket at address: 5005
INFO  [io.qua.dep.QuarkusAugmentor] Beginning quarkus augmentation
INFO  [org.jbo.threads] JBoss Threads version 3.0.0.Final
ERROR [io.qua.dev.DevModeMain] Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
	[error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.jboss.as.quickstarts.helloworld.HelloService and qualifiers [@Default]
	- java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declared on CLASS bean [types=[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], qualifiers=[@Default, @Any], target=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)
Caused by: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.jboss.as.quickstarts.helloworld.HelloService and qualifiers [@Default]
	- java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declared on CLASS bean [types=[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], qualifiers=[@Default, @Any], target=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 more

Então, não funciona ... E por quê?

A exceção UnsatisfiedResolutionException aponta para 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 do HelloService, mas ela não pode ser encontrada (embora ambas as classes estejam no mesmo pacote).

É hora de voltar à documentação e ler como o Quarkus funcionaInjetare, portanto, injeção de contextos e dependências (CDI). Portanto, abrimos o guia Injeção de Dependências e Contextos e lemos na seção Descoberta de Bean : “Uma classe de bean que não possui um bean de definição de anotação não é pesquisada.”

Observamos a classe HelloService - ela realmente não possui essa anotação. Portanto, ele deve ser adicionado para que o Quarkus possa pesquisar e encontrar um bean. E como esse é um objeto sem estado, podemos adicionar a anotação @ApplicationScoped da seguinte maneira:

@ApplicationScoped
public class HelloService {

Nota: aqui o ambiente de desenvolvimento pode solicitar que você adicione o pacote necessário (veja a linha abaixo), e isso terá que ser feito manualmente, assim:

import javax.enterprise.context.ApplicationScoped;

Se estiver em dúvida sobre qual escopo deve ser usado quando o bean de origem não estiver definido, consulte a documentação do JSR 365: Contextos e Injeção de Dependências para Java 2.0 - Escopo padrão .

Agora, novamente, tentamos iniciar o aplicativo com o comando ./mvnw compile quarkus: dev:

$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Building Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/mrizzi/git/forked/jboss-eap-quickstarts/helloworld/target/classes
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Listening for transport dt_socket at address: 5005
INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 576ms
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 1.083s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Agora tudo corre sem erros.

Iniciamos o helloworld atualizado
Como está escrito no log, abra no navegador 0.0.0.0 : 8080 (a página inicial padrão do Quarkus) e veja o seguinte:



FIG. 4. Página inicial do Quarkus dev.

A anotação do WebServlet para este aplicativo possui a seguinte definição de contexto:

@WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {

Portanto, vamos para 0.0.0.0 : 8080 / HelloWorld no navegador e vemos o seguinte:



FIG. 5: A página de desenvolvedor do Quarkus para o aplicativo Hello World.

Bem, tudo funciona.

E agora estamos fazendo alterações no código. Observe que o comando ./mvnw compile quarkus: dev ainda está funcionando e não vamos pará-lo. Agora vamos tentar aplicar as mesmas alterações - triviais - ao próprio código e ver como o Quarkus facilita a vida do desenvolvedor:

writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");

Salve o arquivo e atualize a página da web para ver Hello Marco, conforme mostrado na captura de tela abaixo:



FIG. 6. Olá página Marco no Quarkus dev.

Agora verifique a saída no terminal:

INFO  [io.qua.dev] (vert.x-worker-thread-3) Changed source files detected, recompiling [/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 stopped in 0.003s
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Quarkus augmentation completed in 232ms
INFO  [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 started in 0.257s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (vert.x-worker-thread-3) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (vert.x-worker-thread-3) Installed features: [cdi]
INFO  [io.qua.dev] (vert.x-worker-thread-3) Hot replace total time: 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 "parada inicial". E tudo isso foi concluído em apenas 0,371 segundo (aqui está, esse "Java subatômico ultra-rápido").

Construímos helloworld em um pacote JAR
Agora que o código funciona como deveria, empacotamos com o seguinte comando:

$ ./mvnw clean package

Este comando cria dois arquivos JAR na pasta / target: o arquivo helloworld-.jar, que é um artefato padrão montado pela equipe do Maven, juntamente com as classes e os 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 em um arquivo JAR). Portanto, para executar este JAR a partir de outra pasta ou em um host diferente, é necessário copiar o arquivo JAR e a pasta / lib, pois o elemento Caminho da Classe no arquivo MANIFEST.MF no pacote JAR contém uma lista explícita dos JARs de pastas lib.
Para aprender a criar aplicativos uber-jar, consulte o tutorial Uber-Jar Creation .

Inicie o helloworld, empacotado em um JAR

Agora você pode executar nosso JAR usando o comando java padrão:

$ java -jar ./target/helloworld-<version>-runner.jar
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 0.673s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile prod activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Depois de tudo isso, vá para o navegador em 0.0.0.0 : 8080 e verifique se tudo funciona como deveria.

Colocando o helloworld em um executável nativo

Portanto, nosso helloworld funciona como um aplicativo Java independente usando dependências do Quarkus. Mas você pode ir além e transformá-lo em um arquivo executável nativo.

Instalando o GraalVM
Antes de tudo, você precisa instalar as ferramentas necessárias para isso:

1. Faça o download do GraalVM 19.2.0.1 em github.com/oracle/graal/releases/tag/vm-19.2.0.1 .

2. Expanda o arquivo baixado:

$ tar xvzf graalvm-ce-linux-amd64-19.2.0.1.tar.gz

3. Vá para a pasta untar.

4. Execute o comando abaixo para baixar e adicionar uma imagem nativa:

$ ./bin/gu install native-image

5. Registramos a pasta criada na etapa 2 na variável de ambiente GRAALVM_HOME:

$ export GRAALVM_HOME={untar-folder}/graalvm-ce-19.2.0.1)

Para obter mais informações e instruções de instalação em outros sistemas operacionais, consulte Construindo um executável nativo - pré-requisitos .

Construímos o helloworld em um executável nativo.Lemos o
manual Construindo um executável nativo - produzindo um executável nativo : “Agora crie um executável nativo para nosso aplicativo para reduzir o tempo de inicialização e o tamanho do disco. O arquivo executável terá todo o necessário para executar o aplicativo, incluindo a máquina JVM (ou melhor, sua versão truncada, contendo apenas o necessário para executar o aplicativo) e o próprio aplicativo. ”

Para criar um executável nativo, você deve ativar o perfil nativo do Maven:

$ ./mvnw package -Pnative

Nossa montagem levou um minuto e 10 segundos e o arquivo final do helloworld - runner f foi criado na pasta / target.

Iniciamos o arquivo executável nativo helloworld

Na etapa anterior, obtivemos o arquivo executável / target / helloworld - runner. Agora execute-o:

$ ./target/helloworld-<version>-runner
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 0.006s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile prod activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Novamente, abra o navegador 0.0.0.0 : 8080 e verifique se tudo funciona como deveria.

Continua!

Acreditamos que o método de modernização de aplicativos Java usando os recursos do Quarkus considerados nesta publicação (embora com o exemplo mais simples) deva ser aplicado ativamente na vida real. Nesse caso, é provável que você encontre vários problemas, cuja solução consideraremos parcialmente na próxima postagem, que se concentrará em como medir o consumo de memória para avaliar melhorias de desempenho, uma parte importante de todo o processo de modernização de aplicativos.

All Articles