Quarkus: actualizaciones de la aplicación utilizando el ejemplo helloworld de JBoss EAP Quickstart

¡Hola a todos en este blog, y con ustedes la cuarta publicación de la serie Quarkus! (Por cierto, regístrese y vaya a nuestro seminario webEsto es Quarkus - Framework Java nativo de Kubernetes ”, que se llevará a cabo el 27 de mayo. Le mostraremos cómo comenzar desde cero o transferir soluciones ya preparadas). La



publicación anterior fue sobre cómo Quarkus combina MicroProfile y Spring. Recuerde que Quarkus está posicionado como "Java subatómico ultrarrápido", es "una pila Java orientada a Kubernetes, afilada por GraalVM y OpenJDK HotSpot y compilada a partir de las mejores bibliotecas y estándares". Hoy mostramos cómo actualizar las aplicaciones Java existentes usando las características de Quarkus usando la aplicación helloworld del repositorio de inicio rápido de la plataforma de aplicaciones empresariales Red Hat JBoss (JBoss EAP)utilizando las tecnologías CDI y Servlet 3 compatibles con Quarkus.

Es importante tener en cuenta aquí que tanto Quarkus como JBoss EAP se centran en usar herramientas que están construidas con los estándares máximos. ¿No tienes una aplicación ejecutándose en JBoss EAP? No es un problema, se puede migrar fácilmente desde el servidor de aplicaciones actual a JBoss EAP utilizando el Kit de herramientas de migración de aplicaciones de Red Hat . Después de eso, la versión final y funcional del código actualizado estará disponible en el repositorio github.com/mrizzi/jboss-eap-quickstarts/tree/quarkus , en el módulo helloworld .

Esta publicación fue escrita usando tutoriales de Quarkus , principalmente Creando su primera aplicación y Construyendo un ejecutable nativo .

Obtenemos un código


Primero, cree un clon local del repositorio de inicio rápido 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/

Vea cómo funciona el helloworld original


En realidad, la esencia de esta aplicación es clara por el nombre, pero actualizaremos su código estrictamente científico. Por lo tanto, para comenzar, mire esta aplicación en su forma original.

Expand helloworld

1. Abra el terminal y vaya a la raíz de la carpeta JBoss EAP (puede descargarlo aquí ), es decir, a la carpeta EAP_HOME.

2. Inicie el servidor JBoss EAP con el perfil predeterminado:

$ EAP_HOME/bin/standalone.sh

Nota: en Windows, el script EAP_HOME \ bin \ standalone.bat se usa para ejecutar.

Después de un par de segundos, lo siguiente debería aparecer en el registro:

[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 el navegador 127.0.0.1 : 8080 y vea esto:



Higo. 1. Página de inicio de JBoss EAP.

4. Siga las instrucciones en el Manual de inicio rápido de Build and Deploy : implemente helloworld y ejecute (desde la carpeta raíz del proyecto) el siguiente comando:

$ mvn clean install wildfly:deploy

Después de la ejecución exitosa de este comando en el registro, veremos algo como lo siguiente:

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

Entonces, el primer despliegue de la aplicación helloworld en JBoss EAP tomó poco más de 8 segundos.

Probamos helloworld

Actuando estrictamente de acuerdo con la Guía de acceso a la aplicación , abra 127.0.0.1 : 8080 / helloworld en un navegador y vea esto:



Higo. 2. Original Hello World de JBoss EAP.

Realizar cambios

Cambiar el parámetro de entrada createHelloMessage (String name) de World a Marco:

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

Nuevamente, ejecute el siguiente comando:

$ mvn clean install wildfly:deploy

Luego, actualizamos la página en el navegador y vemos que el texto ha cambiado:



Higo. 3. Hola Marco en JBoss EAP.

Revertimos la implementación de helloworld y salimos de JBoss EAP.

Esto es opcional, pero si desea cancelar la implementación, puede hacerlo con el siguiente comando:

$ mvn clean install wildfly:undeploy

Para cerrar la instancia de JBoss EAP, simplemente presione Ctrl + C en la ventana de terminal.

Actualización de helloworld


Ahora vamos a actualizar la aplicación original de helloworld.

Crear una nueva rama

Cree una nueva rama de trabajo después de que finalice el proyecto de inicio rápido:

$ git checkout -b quarkus 7.2.0.GA

Cambio del archivo pom.xml

Comenzaremos a cambiar la aplicación desde el archivo pom.xml. Para que Quarkus pueda insertar bloques XML en él, ejecute el siguiente comando en la carpeta helloworld:

$ mvn io.quarkus:quarkus-maven-plugin:0.23.2:create

Al escribir este artículo, se utilizó la versión 0.23.2. Quarkus a menudo tiene nuevas versiones; puede encontrar la versión más reciente en github.com/quarkusio/quarkus/releases/latest .

El comando anterior insertará los siguientes elementos en pom.xml:

  • La propiedad <quarkus.version> que especifica la versión de Quarkus a utilizar.
  • El bloque <dependencyManagement> para importar Quarkus BOM (lista de materiales) para no agregar una versión para cada dependencia de Quarkus.
  • El complemento quarkus-maven-plugin, que es responsable de empaquetar la aplicación y proporcionar el modo de desarrollo.
  • Un perfil nativo para crear ejecutables de aplicaciones.

Además, realizamos manualmente los siguientes cambios en 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. Cambiamos la dependencia org.jboss.spec.javax.servlet: jboss-servlet-api_4.0_spec a io.quarkus: quarkus-undertow, eliminando el <scope> proporcionado </scope>, porque (según los muelles) esta extensión de Quarkus proporciona soporte para servlet 's.
    3. Eliminamos la dependencia org.jboss.spec.javax.annotation: jboss-annotations-api_1.3_spec, ya que vienen con las dependencias que acabamos de cambiar.


La versión del archivo pom.xml con todos los cambios se encuentra en github.com/mrizzi/jboss-eap-quickstarts/blob/quarkus/helloworld/pom.xml .

Tenga en cuenta que el mvn io.quarkus: quarkus-maven-plugin anterior: 0.23.2: el comando create no solo cambia el archivo pom.xml, sino que también agrega una serie de componentes al proyecto, a saber, los siguientes archivos y carpetas:

  • 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 helloworld
Para probar la aplicación, utilizamos quarkus: dev, que inicia Quarkus en modo de desarrollo (para más detalles, consulte esta sección en el manual del Modo de desarrollo ).

Nota: se espera que este paso genere un error, porque todavía no hemos realizado todos los cambios necesarios.

Ahora ejecute el comando para verificar cómo 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

Entonces, no funciona ... ¿Y por qué?

La excepción UnsatisfiedResolutionException apunta a la clase HelloService, que es miembro de la clase HelloWorldServlet (miembro java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet # helloService). El problema es que HelloWorldServlet necesita una instancia inyectada de HelloService, pero no se puede encontrar (aunque ambas clases están en el mismo paquete).

Es hora de volver a la documentación y leer cómo funciona QuarkusInyectary, por lo tanto, Contextos e Inyección de Dependencias (CDI). Por lo tanto, abrimos la guía de Inyección de dependencias y contextos y leemos en la sección Descubrimiento de beans : "No se busca una clase de bean que no tenga una anotación que defina el bean".

Observamos la clase HelloService: realmente no tiene esa anotación. Por lo tanto, debe agregarse para que Quarkus pueda buscar y encontrar un bean. Y dado que este es un objeto sin estado, bien podemos agregar la anotación @ApplicationScoped de la siguiente manera:

@ApplicationScoped
public class HelloService {

Nota: aquí el entorno de desarrollo puede pedirle que agregue el paquete requerido (consulte la línea a continuación), y esto tendrá que hacerse manualmente, de esta manera:

import javax.enterprise.context.ApplicationScoped;

Si tiene dudas sobre qué ámbito utilizar cuando el bean de origen no está configurado, consulte la documentación de JSR 365: Contextos e inyección de dependencias para Java 2.0: ámbito predeterminado .

Ahora nuevamente, intentamos iniciar la aplicación con el 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]

Ahora todo va sin errores.

Lanzamos el helloworld actualizado.
Como está escrito en el registro, abra en el navegador 0.0.0.0 : 8080 (la página de inicio de Quarkus predeterminada) y vea esto:



Higo. 4. Página de inicio de Quarkus dev.

La anotación de WebServlet para esta aplicación tiene la siguiente definición de contexto:

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

Por lo tanto, vamos a 0.0.0.0 : 8080 / HelloWorld en el navegador y vemos lo siguiente:



Higo. 5: La página de desarrollo de Quarkus para la aplicación Hello World.

Pues todo funciona.

Y ahora estamos haciendo cambios en el código. Tenga en cuenta que el comando ./mvnw compile quarkus: dev todavía funciona y no lo vamos a detener. Ahora intentemos aplicar los mismos cambios triviales al código mismo y ver cómo Quarkus hace la vida más fácil para el desarrollador:

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

Guarde el archivo y luego actualice la página web para ver Hello Marco, como se muestra en la captura de pantalla a continuación:



Higo. 6. Hola página de Marco en Quarkus dev.

Ahora verifique la salida en la 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

La actualización de la página activó la detección de cambios en el código fuente, y Quarkus realizó automáticamente el procedimiento de "detener inicio". Y todo esto se completó en solo 0.371 segundos (aquí está, ese "Java subatómico ultrarrápido").

Construimos helloworld en un paquete JAR
Ahora que el código funciona como debería, lo empaquetamos con el siguiente comando:

$ ./mvnw clean package

Este comando crea dos archivos JAR en la carpeta / target: el archivo helloworld-.jar, que es un artefacto estándar ensamblado por el equipo de Maven junto con las clases y recursos del proyecto. Y el archivo helloworld es runner.jar, que es un JAR ejecutable.

Tenga en cuenta que este no es un uber-jar, ya que todas las dependencias simplemente se copian en la carpeta / target / lib (y no se empaquetan en un archivo JAR). Por lo tanto, para ejecutar este JAR desde otra carpeta o en un host diferente, debe copiar tanto el archivo JAR como la carpeta / lib allí, dado que el elemento Class-Path en el archivo MANIFEST.MF en el paquete JAR contiene una lista explícita de los JAR de carpetas lib.
Para aprender a crear aplicaciones uber-jar, consulte el tutorial de creación de Uber-Jar .

Inicie helloworld, empaquetado en un JAR

Ahora puede ejecutar nuestro JAR usando el comando estándar de Java:

$ 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]

Una vez hecho todo esto, vaya al navegador en 0.0.0.0 : 8080 y verifique que todo funcione como debería.

Poniendo helloworld en un ejecutable nativo

Entonces, nuestro helloworld funciona como una aplicación Java independiente que usa dependencias de Quarkus. Pero puede ir más allá y convertirlo en un archivo ejecutable nativo.

Instale GraalVM
Antes que nada, para esto necesita instalar las herramientas necesarias:

1. Descargue GraalVM 19.2.0.1 desde github.com/oracle/graal/releases/tag/vm-19.2.0.1 .

2. Expanda el archivo descargado:

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

3. Vaya a la carpeta untar.

4. Ejecute el siguiente comando para descargar y agregar una imagen nativa:

$ ./bin/gu install native-image

5. Registramos la carpeta creada en el paso 2 en la variable de entorno GRAALVM_HOME:

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

Para obtener más información e instrucciones de instalación en otros sistemas operativos, consulte Creación de un ejecutable nativo: requisitos previos . Desarrollamos

helloworld en un archivo ejecutable nativo.
Leímos Cómo crear un archivo ejecutable nativo - Producir un manual ejecutable nativo : “Ahora cree un archivo ejecutable nativo para nuestra aplicación para reducir su tiempo de lanzamiento y tamaño de disco. El archivo ejecutable tendrá todo lo necesario para ejecutar la aplicación, incluida la máquina JVM (o más bien, su versión truncada, que contiene solo lo que se necesita para ejecutar la aplicación) y nuestra propia aplicación ".

Para crear un ejecutable nativo, debe habilitar el perfil nativo de Maven:

$ ./mvnw package -Pnative

Nuestro ensamblaje tomó un minuto y 10 segundos, y el archivo final helloworld - runner f fue creado en la carpeta / target.

Iniciamos el archivo ejecutable nativo helloworld

En el paso anterior obtuvimos el archivo ejecutable / target / helloworld - runner. Ahora ejecútelo:

$ ./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]

Nuevamente, abra el navegador 0.0.0.0 : 8080 y verifique que todo funcione como debería.

¡Continuará!

Creemos que el método de modernización de las aplicaciones Java utilizando las capacidades de Quarkus consideradas en esta publicación (aunque con el ejemplo más simple) debe aplicarse activamente en la vida real. En este caso, es probable que encuentre una serie de problemas, cuya solución consideraremos parcialmente en la próxima publicación, que se centrará en cómo medir el consumo de memoria para evaluar la mejora del rendimiento, una parte importante de todo el proceso de actualización de la aplicación.

All Articles