¡Hola a todos en este blog, y aquí está la cuarta publicación de la serie sobre Quarkus!

trataba sobre cómo Quarkus une MicroProfile y Spring. Recordemos que se posiciona como ‘Java subatómica ultrarrápida’, también conocido como ‘stack Java orientado a Kubernetes, optimizado para GraalVM y OpenJDK HotSpot y construido a partir de las mejores bibliotecas y estándares’. Hoy mostraremos cómo modernizar las aplicaciones Java existentes utilizando las capacidades de Quarkus, a través del ejemplo , en la que se utilizan tecnologías compatibles con Quarkus como CDI y Servlet 3.
Es importante señalar que tanto Quarkus como JBoss EAP se enfocan en utilizar herramientas que están construidas principalmente sobre estándares. ¿No tienes una aplicación que funcione en JBoss EAP? No hay problema, se puede migrar fácilmente desde el servidor de aplicaciones actual a JBoss EAP utilizando . Después de eso, la versión final y funcional del código modernizado estará disponible en el repositorio , en el módulo .
Al escribir esta publicación se utilizaron , principalmente y Building a .
Obteniendo el código
Lo primero que haremos será crear un clon local del repositorio :
$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Clonando en 'jboss-eap-quickstarts'...
remoto: Enumerando objetos: 148133, hecho.
remoto: Total 148133 (delta 0), reutilizado 0 (delta 0), pack-reused 148133
Recibiendo objetos: 100% (148133/148133), 59.90 MiB | 7.62 MiB/s, hecho.
Resolviendo deltas: 100% (66476/66476), hecho.
$ cd jboss-eap-quickstarts/helloworld/
Veamos cómo funciona el helloworld original
En esencia, la naturaleza de esta aplicación es clara por su nombre, pero modernizaremos su código de manera rigurosa. Así que, primero, echemos un vistazo a esta aplicación en su forma original.
Despliegue del helloworld
1. Abre una terminal y navega hasta la carpeta raíz de JBoss EAP (puedes descargarlo ), es decir, a la carpeta EAP_HOME.
2. Inicia el servidor JBoss EAP con el perfil predeterminado:
$ EAP_HOME/bin/standalone.sh
Nota: en Windows se utiliza el script EAP_HOMEbinstandalone.bat para iniciar.
Después de unos segundos, el registro debería mostrar algo como lo siguiente:
[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) iniciado en 3315ms - Iniciado 306 de 527 servicios (321 servicios son perezosos, pasivos o bajo demanda)
3. Abre en el navegador :8080 y verás esto:

Fig. 1. Página de inicio de JBoss EAP.
4. Sigue las instrucciones en la guía : desplegamos helloworld y ejecutamos (desde la carpeta raíz del proyecto) el siguiente comando:
$ mvn clean install wildfly:deploy
Después de ejecutar este comando con éxito, veremos aproximadamente lo siguiente en el registro:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.224 s
Así que el primer despliegue de la aplicación helloworld en JBoss EAP tomó un poco más de 8 segundos.
Probando helloworld
Siguiendo estrictamente la guía , abrimos en el navegador :8080/helloworld y vemos lo siguiente:

Fig. 2. Hola Mundo original de JBoss EAP.
Haciendo cambios
Cambiamos el parámetro de entrada createHelloMessage(String name) de World a Marco:
escritor.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");
Ejecutamos de nuevo el siguiente comando:
$ mvn clean install wildfly:deploy
Luego actualizamos la página en el navegador y vemos que el texto ha cambiado:

Fig. 3. Hola Marco en JBoss EAP.
Revirtiendo el despliegue de helloworld y deteniendo JBoss EAP
No es obligatorio, pero si deseas deshacer el despliegue, puedes hacerlo con el siguiente comando:
$ mvn clean install wildfly:undeploy
Para detener la instancia de JBoss EAP, simplemente presiona Ctrl+C en la ventana de la terminal.
Modernizando helloworld
Ahora nos dedicaremos a modernizar la aplicación de inicio helloworld.
Creando una nueva rama
Creamos una nueva rama de trabajo después de que se complete el proyecto de inicio:
$ git checkout -b quarkus 7.2.0.GA
Modificando el archivo pom.xml
Comenzaremos a cambiar la aplicación desde el archivo pom.xml. Para que Quarkus pueda insertar bloques XML en él, ejecutaremos 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 lanza frecuentemente nuevas versiones, y se puede verificar cuál es la más reciente en el sitio .
El comando anterior insertará los siguientes elementos en pom.xml:
- La propiedad , que especifica la versión de Quarkus que se utilizará.
- El bloque para importar Quarkus BOM (bill of materials), para no agregar la versión para cada dependencia de Quarkus.
- El plugin quarkus-maven-plugin, que se encarga de empaquetar la aplicación y proporciona el modo de desarrollo.
- El perfil nativo para crear ejecutables de la aplicación.
Además, en pom.xml manualmente realizamos los siguientes cambios:
- Extraemos la etiqueta del bloque y la colocamos por encima de la etiqueta . Dado que en el siguiente paso eliminaremos el bloque , es necesario conservar .
- Eliminamos el bloque , ya que al trabajar con Quarkus esta aplicación ya no necesitará el pom padre de JBoss.
- Agregamos la etiqueta y la colocamos debajo de la etiqueta . Puedes especificar cualquier número de versión que desees.
- Eliminamos la etiqueta , ya que esta aplicación ya no es un WAR, sino un JAR normal.
- Modificamos las siguientes dependencias:
- Cambiamos la dependencia javax.enterprise:cdi-api por io.quarkus:quarkus-arc, eliminando provided, ya que (según la documentación) esta extensión de Quarkus proporciona la inyección de dependencias CDI.
- Cambiamos la dependencia org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec por io.quarkus:quarkus-undertow, eliminando provided, ya que (según la documentación) esta extensión de Quarkus brinda soporte para servlets.
- Eliminamos la dependencia org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec, ya que viene incluida con las dependencias que acabamos de modificar.
La versión del archivo pom.xml con todos los cambios se encuentra en la siguiente dirección .
Ten en cuenta que el comando mvn io.quarkus:quarkus-maven-plugin:0.23.2:create no solo modifica el archivo pom.xml, sino que también añade varios componentes al proyecto, específicamente, los siguientes archivos y carpetas:
- El archivo mvnw y mvnw.cmd y la carpeta .mvn: Maven Wrapper permite ejecutar proyectos de Maven de una versión determinada sin instalar esa versión.
- La carpeta docker (en el directorio src/main/): aquí se encuentran ejemplos de archivos Dockerfile para los modos native y jvm (junto con el archivo .dockerignore).
- La carpeta resources (en el directorio src/main/): aquí se encuentra un archivo application.properties vacío y un ejemplo de la página de inicio de Quarkus index.html (más detalles en Ejecutar el helloworld modernizado).
Ejecutamos helloworld
Para probar la aplicación, utilizamos quarkus:dev, que ejecuta Quarkus en modo desarrollo (más detalles en esta sección de la guía de ).
Nota: Este paso, como era de esperar, provocará un error, ya que no hemos realizado todos los cambios necesarios.
Ahora ejecutamos el comando para verificar cómo funcionará:
$ . /mvnw compile quarkus:dev
[INFO] Escaneando proyectos...
[INFO]
[INFO] --------------------------------
[INFO] Construyendo Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Usando codificación 'UTF-8' para copiar recursos filtrados.
[INFO] Copiando 2 recursos
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Nada que compilar - todas las clases están actualizadas
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Escuchando para transporte dt_socket en la dirección: 5005
INFO [io.qua.dep.QuarkusAugmentor] Comenzando la augmentación de quarkus
INFO [org.jbo.threads] Versión de JBoss Threads 3.0.0.Final
ERROR [io.qua.dev.DevModeMain] Falló al iniciar quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Error de construcción: La construcción falló debido a errores
[error]: El paso de construcción io.quarkus.arc.deployment.ArcProcessor#validate lanzó una excepción: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Dependencia no satisfecha para el tipo org.jboss.as.quickstarts.helloworld.HelloService y calificadores [@Default]
- miembro java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
- declarado en CLASS 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], calificadores=[@Default, @Any], objetivo=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(Native Method)
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: Dependencia no satisfecha para el tipo org.jboss.as.quickstarts.helloworld.HelloService y calificadores [@Default]
- miembro java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
- declarado en CLASS 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], calificadores=[@Default, @Any], objetivo=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 más
Entonces, no funciona... ¿Y por qué?
La excepción UnsatisfiedResolutionException indica la clase HelloService, que es un miembro de la clase HelloWorldServlet (miembro de Java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService). El problema es que HelloWorldServlet necesita una instancia inyectada de HelloService, y no se puede encontrar (aunque ambas clases están en el mismo paquete).
Es hora de volver a y leer cómo funciona , y por lo tanto, también (CDI). Por lo tanto, abrimos la guía de Contexts and Dependency Injection y en la sección leemos: «Una clase Bean que no tiene una anotación bean definitoria no es buscada».
Miremos la clase HelloService: realmente no tiene tal anotación. Por lo tanto, necesitamos agregarla para que Quarkus pueda buscar y encontrar el bean. Y dado que se trata de un objeto stateless, 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 (ver la línea a continuación), y deberá hacerlo manualmente, así:
import javax.enterprise.context.ApplicationScoped;
Si duda sobre qué área scope utilizar en el caso en que no se haya especificado para el bean original, revise la documentación .
Ahora intentemos ejecutar la aplicación nuevamente con el comando .\/mvnw compile quarkus:dev:
$ .\/mvnw compile quarkus:dev
[INFO] Escaneando proyectos...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Construyendo Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Usando codificación 'UTF-8' para copiar recursos filtrados.
[INFO] Copiando 2 recursos
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Cambios detectados - recompilando el módulo!
[INFO] Compilando 2 archivos fuente a \/home\/mrizzi\/git\/forked\/jboss-eap-quickstarts\/helloworld\/target\/classes
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Esperando transporte dt_socket en la dirección: 5005
INFO [io.qua.dep.QuarkusAugmentor] (main) Comenzando la augmentación de quarkus
INFO [io.qua.dep.QuarkusAugmentor] (main) La augmentación de Quarkus se completó en 576ms
INFO [io.quarkus] (main) Quarkus 0.23.2 iniciado en 1.083s. Escuchando en: http:\/\/0.0.0.0:8080
INFO [io.quarkus] (main) Perfil dev activado. Codificación en vivo activada.
INFO [io.quarkus] (main) Características instaladas: [cdi]
Ahora todo se ejecuta sin errores.
Iniciamos el helloworld modernizado
Como se indica en el registro, abrimos en el navegador :8080 (la página de inicio predeterminada de Quarkus) y vemos lo siguiente:

Fig. 4. Página de inicio de Quarkus dev.
En la anotación WebServlet de esta aplicación se especifica la siguiente definición de contexto:
@WebServlet("\/HelloWorld")
public class HelloWorldServlet extends HttpServlet {
Por lo tanto, abrimos el navegador en :8080/HelloWorld y vemos lo siguiente:

Fig. 5: Página de desarrollo de Quarkus para la aplicación Hello World.
Bueno, ya funciona.
Ahora realizamos cambios en el código. Tenga en cuenta que el comando ./mvnw compile quarkus:dev aún está funcionando, y no vamos a detenerlo. Ahora intentemos aplicar los mismos cambios triviales al propio código y veamos cómo Quarkus facilita la vida del desarrollador:
escritor.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");
Guardamos el archivo y luego actualizamos la página web para ver el mensaje Hello Marco, como se muestra en la captura de pantalla a continuación:

Fig. 6. Página Hello Marco en Quarkus dev.
Ahora verifiquemos la salida en la terminal:
INFO [io.qua.dev] (vert.x-worker-thread-3) Se detectaron cambios en los archivos fuente, 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 detenido en 0.003s
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Comenzando la augmentación de quarkus
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) La augmentación de Quarkus se completó en 232ms
INFO [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 arrancado en 0.257s. Escuchando en: http://0.0.0.0:8080
INFO [io.quarkus] (vert.x-worker-thread-3) Perfil dev activado. Live Coding activado.
INFO [io.quarkus] (vert.x-worker-thread-3) Características instaladas: [cdi]
INFO [io.qua.dev] (vert.x-worker-thread-3) Tiempo total de reemplazo caliente: 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 "parar-iniciar". Y todo esto se realizó en solo 0.371 segundos (ahí está, esa es la "Java subatómica ultrarrápida").
Construimos helloworld en un paquete JAR
Ahora que el código funciona como debería, empaquetamos con el siguiente comando:
$ ./mvnw clean package
Este comando crea dos archivos JAR en la carpeta /target: el archivo helloworld-.jar, que es el artefacto estándar construido por el comando Maven junto con las clases y recursos del proyecto. Y el archivo helloworld—runner.jar, que es un JAR ejecutable.
Tenga en cuenta que este no es un uber-jar, ya que todas las dependencias se copian simplemente en la carpeta /target/lib (y no se empaquetan en el archivo JAR). Por lo tanto, para ejecutar este JAR desde otra carpeta o en otro host, es necesario copiar tanto el archivo JAR como la carpeta /lib, teniendo en cuenta que el elemento Class-Path en el archivo MANIFEST.MF en el paquete JAR contiene una enumeración explícita de los JAR de la carpeta lib.
Para saber cómo crear aplicaciones uber-jar, consulte la guía .
Ejecutamos helloworld, empaquetado en JAR
Ahora podemos ejecutar nuestro JAR con el comando estándar de java:
$ java -jar ./target/helloworld--runner.jar
INFO [io.quarkus] (main) Quarkus 0.23.2 iniciado en 0.673s. Escuchando en: http://0.0.0.0:8080
INFO [io.quarkus] (main) Perfil prod activado.
INFO [io.quarkus] (main) Funciones instaladas: [cdi]
Después de que todo esto se haya completado, ingrese en el navegador a :8080 y verifique que todo funcione como se espera.
Compilando helloworld en un archivo ejecutable nativo
Así que nuestro helloworld funciona como una aplicación Java independiente, utilizando las dependencias de Quarkus. Pero se puede ir más allá y convertirlo en un archivo ejecutable nativo.
Instalando GraalVM
Primero, necesitamos instalar las herramientas necesarias:
1. Descargamos GraalVM 19.2.0.1 de .
2. Descomprimimos el archivo descargado:
$ tar xvzf graalvm-ce-linux-amd64-19.2.0.1.tar.gz
3. Vamos a la carpeta descomprimida.
4. Ejecutamos el siguiente comando para descargar y agregar la imagen nativa:
$ ./bin/gu install native-image
5. Especificamos 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)
Más detalles e instrucciones de instalación en otros sistemas operativos se pueden encontrar en la guía .
Construimos helloworld en un archivo ejecutable nativo
Leemos la guía : "Ahora vamos a crear un archivo ejecutable nativo para nuestra aplicación, para reducir el tiempo de inicio y el tamaño en disco. El archivo ejecutable contendrá todo lo necesario para ejecutar la aplicación, incluyendo la máquina JVM (más bien, una versión recortada que solo contiene lo que se necesita para ejecutar la aplicación) y nuestra propia aplicación."
Para crear un archivo ejecutable nativo, debemos activar el perfil nativo de Maven:
$ ./mvnw package -Pnative
Nuestro proceso de compilación tomó un minuto y 10 segundos, y el archivo final helloworld-runner se creó en la carpeta /target.
Ejecutamos el archivo ejecutable nativo helloworld
En el paso anterior obtuvimos el archivo ejecutable /target/helloworld-runner. Ahora lo ejecutaremos:
$ ./target/helloworld--runner
INFO [io.quarkus] (main) Quarkus 0.23.2 iniciado en 0.006s. Escuchando en: http://0.0.0.0:8080
INFO [io.quarkus] (main) Perfil prod activado.
INFO [io.quarkus] (main) Funciones instaladas: [cdi]
Abrimos de nuevo en el navegador :8080 y verificamos que todo funcione como se espera.
¡Continúa!
Creemos que el método de modernización de aplicaciones Java utilizando las capacidades de Quarkus, discutido en esta publicación (aunque sea con un ejemplo sencillo), debería aplicarse activamente en la vida real. Sin embargo, es probable que se enfrente a varias problemáticas, cuya solución abordaremos parcialmente en la próxima publicación, donde trataremos cómo medir el consumo de memoria para evaluar la mejora del rendimiento, una parte importante de todo el proceso de modernización de aplicaciones.
Fuente: habr.com
