Nota:

Introducción a GraalVM Native Image

Introducción

En este laboratorio se explica paso a paso cómo crear una aplicación Java nativa en la nube con GraalVM Native Image. Está dirigido a desarrolladores con conocimientos de Java.

La tecnología de GraalVM Native Image compila código Java de forma anticipada en un archivo ejecutable independiente. Solo el código que necesita la aplicación en tiempo de ejecución se agrega al archivo ejecutable.

Un archivo ejecutable producido por Native Image tiene varias ventajas importantes, ya que:

Muchos de los marcos de microservicios líderes admiten la compilación anticipada con GraalVM Native Image, incluidos Micronaut, Spring, Helidon y Quarkus.

Además, hay plugins de Maven y Gradle para Native Image para que pueda crear, probar y ejecutar fácilmente aplicaciones Java como archivos ejecutables.

Nota: Oracle Cloud Infrastructure (OCI) proporciona GraalVM Enterprise sin costo adicional.

Tiempo de laboratorio estimado: 45 minutos

Objetivos del laboratorio

En esta práctica, realizará las siguientes tareas:

NOTA: si ve el icono de portátil en el laboratorio, debe hacer algo, como introducir un comando. Manténgase atento a ello.

# This is where we you will need to do something

PASO 1: conexión a un host remoto y comprobación del entorno de desarrollo

Un host remoto proporciona su entorno de desarrollo: una instancia informática de OCI con Oracle Linux 8, 1 CPU y 32 GB de memoria.
El entorno de escritorio Luna Labs se mostrará antes de que el host remoto esté listo, lo que puede tardar hasta dos minutos.

La conexión al host remoto se realiza mediante la ejecución de un script de configuración en el entorno de Luna Desktop. Este script está disponible en el separador Recursos

  1. En el escritorio, haga doble clic en el icono Luna-Lab.html. Se abre la página para mostrar las credenciales y la información de Oracle Cloud Infrastructure específicas de su laboratorio.

  2. Se mostrará el separador Recursos. Tenga en cuenta que el engranaje que se muestra junto al título Recursos girará mientras la instancia informática se aprovisiona en la nube de teh.

  3. Cuando se aprovisiona la instancia, esto puede tardar hasta 2 minutos, verá lo siguiente en el separador Recursos.

    Separador Recursos Luna

  4. Copie la secuencia de comandos de configuración, que configura el entorno de código VS, en la ficha Recursos. Haga clic en el enlace Ver detalles para mostrar la configuración. Copie esto como se muestra en la captura de pantalla siguiente.

    Copiar script de configuración

  5. Abra un terminal, como se muestra en la captura de pantalla siguiente:

    Abrir terminal

  6. Pegue el código de configuración en el terminal, que le abrirá VS Code.

    Pegar terminal 1

    Pegar terminal 2

¡Y ha terminado! Enhorabuena, ahora se ha conectado correctamente a un host remoto en Oracle Cloud.

Nota sobre el entorno de desarrollo

Utilizará GraalVM Enterprise 21 como plataforma Java para este laboratorio. GraalVM es una distribución de JDK de alto rendimiento de Oracle basada en la solución segura y de confianza de Oracle Java SE.

Su entorno de desarrollo viene preconfigurado con GraalVM y las herramientas de Native Image necesarias para este laboratorio.

Puede comprobar fácilmente que ejecutando estos comandos en el terminal: puede crear un terminal desde el código VS, Terminal > New Terminal:

java -version

native-image --version

PASO 2: creación y ejecución de una aplicación de demostración

Utilizaremos una aplicación de demostración para mostrar GraalVM Native Image: una aplicación Java de línea de comandos que cuenta el número de archivos del directorio actual y sus subdirectorios. Como adicional agradable, la aplicación también calcula el tamaño total de los archivos.

El código fuente de la aplicación está disponible en el host remoto.

Nota en la aplicación de demostración

La aplicación consta de dos archivos Java que se pueden encontrar en el directorio src:

La aplicación se puede crear a mano o mediante perfiles de Maven. La configuración de compilación de Maven la proporciona el archivo pom.xml. Los perfiles de Maven son una gran manera de tener diferentes configuraciones de compilación en un único archivo pom.xml. Puede obtener más información sobre los perfiles de Maven aquí.

Puede examinar los archivos dentro del código VS abierto.

En esta práctica se utilizarán varios perfiles, cada uno de los cuales tiene una finalidad concreta:

  1. native: este perfil crea un archivo ejecutable con GraalVM Native Image.
  2. java_agent: este perfil crea la aplicación Java con un agente de rastreo que realiza un seguimiento de todos los usos del código dinámico en la aplicación y captura esta información en archivos de configuración. Más información al respecto más adelante.

Puede utilizar un perfil de Maven concreto transfiriéndolo como parámetro al comando mvn. El nombre del perfil se agrega al indicador -P. Puede ejecutar los siguientes comandos desde el terminal dentro del código de VS.

En el siguiente ejemplo se muestra cómo llamar a un perfil native al crear con Maven:

mvn clean package -Pnative

Ahora que tiene una comprensión básica de lo que la aplicación ejecuta para ver cómo funciona.

  1. Construya el proyecto y ejecútelo desde el Terminal que abrimos en VS Code:

    mvn clean package exec:exec
    

    El comando anterior hace lo siguiente:

    1. Limpia el proyecto para eliminar los artefactos generados o compilados.
    2. Crea un archivo JAR ejecutable que contiene la aplicación. Este archivo JAR será utilizado posteriormente por Native Image.
    3. Ejecuta la aplicación ejecutando el plugin exec.

    Debe ver lo siguiente incluido en la salida generada (el número de archivos informados por la aplicación puede variar):

    Counting directory: .
    Total: 15 files, total size = 511.9 KiB
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    

PASO 3: conversión de una aplicación Java en un archivo ejecutable

A continuación, va a crear una versión ejecutable de la aplicación con GraalVM Native Image. Como recordatorio rápido, GraalVM Native Image es una tecnología de compilación anticipada que convierte la aplicación Java en un archivo ejecutable independiente que no requiere que se ejecute JDK, es rápido de iniciar y eficaz.

GraalVM Native Image está preinstalado en el host remoto.

  1. Para empezar, compruebe que tiene un archivo JAR compilado en el directorio target:

    ls ./target
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 archive-tmp
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 classes
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 generated-sources
    -rw-rw-r-- 1 krf krf  496273 Mar  4 11:38 graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar
    -rw-rw-r-- 1 krf krf    7894 Mar  4 11:38 graalvmnidemos-1.0-SNAPSHOT.jar
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 maven-archiver
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 maven-status
    

    El archivo que necesitará es graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar.

  2. Genere un archivo ejecutable desde la línea de comandos. No necesita utilizar el plugin de Maven para utilizar GraalVM Native Image, pero puede ayudarle. Ejecute lo siguiente desde el directorio raíz del proyecto, demo:

    native-image -jar ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar --no-fallback -H:Class=oracle.App -H:Name=file-count
    

    Esto generará un archivo ejecutable denominado file-count en el directorio actual.

  3. Ejecute este archivo ejecutable de la siguiente manera:

    ./file-count
    
  4. Ahora la hora de la aplicación. En primer lugar, ejecútelo como archivo ejecutable y, a continuación, mediante el comando java normal:

    time ./file-count
    

    time java -cp ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar oracle.App
    

    El archivo ejecutable, generado por el comando native-image, se ejecuta significativamente más rápido que la aplicación Java correspondiente.

Vamos a profundizar un poco en cómo creó el archivo ejecutable.

¿Qué parámetros ha transferido al comando native-image en el paso 2 especificado?

Puede encontrar la documentación completa aquí.

También puede ejecutar la herramienta native-image mediante el plugin Maven de GraalVM Native Image. El archivo del proyecto pom.xml (configuración de Maven) contiene el siguiente fragmento que demuestra cómo crear un archivo ejecutable con el plugin:

<!-- Native Image -->
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <version>${native.maven.plugin.version}</version>
    <extensions>true</extensions>
    <executions>
    <execution>
        <id>build-native</id>
        <goals>
        <goal>build</goal>
        </goals>
        <phase>package</phase>
    </execution>
    </executions>
    <configuration>
        <skip>false</skip>
        <imageName>${exe.file.name}</imageName>
        <mainClass>${app.main.class}</mainClass>
        <buildArgs>
            <buildArg>--no-fallback</buildArg>
            <buildArg>--report-unsupported-elements-at-runtime</buildArg>
        </buildArgs>
    </configuration>
</plugin>

El plugin de Native Image Maven realiza el trabajo pesado de creación del archivo ejecutable. Puede desactivarla mediante la etiqueta <skip>true</skip>. Tenga en cuenta también que puede transferir parámetros a native-image a través de las etiquetas <buildArgs/>.

Puede encontrar la documentación completa sobre el plugin de GraalVM Native Image aquí.

Para crear el archivo ejecutable con el perfil de Maven, ejecute:

mvn clean package -Pnative

La creación de Maven coloca el archivo ejecutable, file-count, en el directorio target.

Puede ejecutar el archivo ejecutable de la siguiente manera:

./target/file-count

PASO 4: uso de reflejo: agregación de una dependencia a Log4J

En este paso, creará un archivo ejecutable que funcione con las funciones dinámicas de Java.

Supongamos que desea agregar una biblioteca o algún código a la aplicación que se basa en la reflexión. Un buen candidato para probar la reflexión es el marco de registro Log4J. Ya se ha agregado como dependencia en el archivo pom.xml del proyecto:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

Para cambiar la aplicación para que utilice log4j, edite el archivo ListDir.java y elimine los comentarios de algunas líneas.

  1. Abra el archivo ListDir.java mediante el código VS

  2. Elimine los comentarios de la línea que declara la importación de log4j y, a continuación, elimine los comentarios de las siguientes líneas:

    //import org.apache.log4j.Logger;
    

    //final static Logger logger = Logger.getLogger(ListDir.class);
    

    /*
    // Add some logging
    if(logger.isDebugEnabled()){
        logger.debug("Processing : " + dirName);
    }
    */
    

    /*
    // Add some logging
    if(logger.isDebugEnabled()){
        logger.debug("Processing : " + f.getAbsolutePath());
    }
    */
    
  3. Guarde el archivo

    Ahora que ha agregado el registro a la aplicación, puede ver el resultado de los cambios reconstruyendo y ejecutando.

    mvn clean package exec:exec
    

    Debe ver el mismo tipo de salida que vio anteriormente, pero con la adición de más registros.

  4. A continuación, cree un archivo ejecutable con el perfil de Maven:

    mvn clean package -Pnative
    
  5. Ejecute el archivo ejecutable que ha creado, que ahora contiene el registro:

    ./target/file-count
    

    Se genera un error:

    Exception in thread "main" java.lang.NoClassDefFoundError
            at org.apache.log4j.Category.class$(Category.java:118)
            at org.apache.log4j.Category.<clinit>(Category.java:118)
            at java.lang.Class.ensureInitialized(DynamicHub.java:552)
            at oracle.ListDir.<clinit>(ListDir.java:75)
            at oracle.App.main(App.java:63)
    Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Category
            at java.lang.Class.forName(DynamicHub.java:1433)
            at java.lang.Class.forName(DynamicHub.java:1408)
            ... 5 more
    

    ¿Qué ha pasado?

Trabajar con las Funciones Dinámicas de Java

Esta excepción se debe a la agregación de la biblioteca Log4J porque se basa en la reflexión.
La herramienta native-image realiza un análisis estático agresivo para ver qué clases se utilizan en la aplicación. Para las clases no utilizadas, la herramienta asumirá que no son necesarias. Esto se denomina suposición del "mundo cerrado": todo lo que se debe cargar se debe conocer al crear un archivo ejecutable. Si no se puede encontrar mediante análisis estático, no se incluirá en el archivo ejecutable.

La reflexión es una función principal de Java, por lo que ¿cómo se puede utilizar la reflexión y aprovechar las rápidas que ofrece GraalVM Native Image? Necesita una forma de hacer que la herramienta native-image conozca cualquier uso de reflexión.

Por suerte, la herramienta native-image puede leer en los archivos de configuración que especifican todas las clases a las que se hace referencia a través de la reflexión.

Puede hacerlo manualmente, o bien el agente de rastreo de Java que se suministra con el tiempo de ejecución de GraalVM Java puede hacerlo por usted. El agente genera archivos JSON que registran todas las instancias de reflexión, JNI, proxies y acceso a recursos que puede localizar mientras se ejecuta la aplicación.

Nota: Es importante ejercer todas las rutas de código de la aplicación al ejecutar el agente de rastreo para asegurarse de que se identifican todos los casos de reflexión.

Puede encontrar la documentación completa del agente de rastreo aquí.

PASO 5: uso del agente de rastreo

Ahora utilice el agente de rastreo para generar la configuración de reflexión mientras ejecuta la aplicación.

  1. Ejecute la aplicación con el agente de rastreo:

    java -agentlib:native-image-agent=config-output-dir=./src/main/resources/META-INF/native-image -cp ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar oracle.App
    

    Observe los archivos de configuración que ha creado el agente de rastreo:

    ls -l src/main/resources/META-INF/native-image/
    

    Aparecerá lo siguiente en la salida generada:

    total 56
    -rw-r--r--  1 kfoster  staff     4B Dec  2 19:13 jni-config.json
    -rw-r--r--  1 kfoster  staff    86B Nov  9 20:46 native-image.properties
    -rw-r--r--  1 kfoster  staff    65B Dec  2 19:13 predefined-classes-config.json
    -rw-r--r--  1 kfoster  staff     4B Dec  2 19:13 proxy-config.json
    -rw-r--r--  1 kfoster  staff   521B Dec  2 19:13 reflect-config.json
    -rw-r--r--  1 kfoster  staff   101B Dec  2 19:13 resource-config.json
    -rw-r--r--  1 kfoster  staff     4B Dec  2 19:13 serialization-config.json
    

    Nota: el proyecto contiene un perfil de Maven que puede hacer esto por usted. Ejecute el siguiente comando para utilizar el agente de rastreo:

    mvn clean package exec:exec -Pjava_agent
    
  2. Ahora vuelva a crear el archivo ejecutable. Esta vez se aplicarán los archivos de configuración producidos por el agente de rastreo:

    mvn package -Pnative
    
  3. Por último, ejecute el archivo generado:

    time ./target/file-count
    

    El archivo ejecutable funciona y produce mensajes de log en la salida, como se esperaba.

Esto funciona porque los archivos generados por el agente de rastreo han registrado las clases a las que hace referencia la reflexión. La herramienta native-image ahora sabe que se utilizan en la aplicación y, por lo tanto, no las excluye del archivo ejecutable generado.

Nota sobre la posición del parámetro -agentlib

Tenga en cuenta que los parámetros de agente deben presentarse antes que cualquier parámetro -jar o -classpath. También debe especificar un directorio en el que escribir los archivos. La ubicación recomendada está en src/main/resources/META-INF/native-image. La herramienta native-image selecciona automáticamente los archivos colocados en esta ubicación.

Nota sobre la configuración de la generación del archivo ejecutable

También puede transferir parámetros a la herramienta native-image mediante un archivo de propiedades Java (por defecto src/main/resources/META-INF/native-image/native-image.properties). Hay un archivo de ejemplo en el directorio demo para ofrecerle una idea de lo que puede hacer con él.

Conclusiones

En este laboratorio, ha probado varias funciones de GraalVM Native Image:

  1. Cómo generar un archivo ejecutable rápido desde una aplicación de línea de comandos Java
  2. Cómo utilizar Maven para crear un archivo ejecutable
  3. Cómo utilizar el agente de rastreo para automatizar el proceso de seguimiento y registro de la reflexión

Escriba aplicaciones Java en la nube eficientes, más seguras y ampliables al instante con GraalVM Native Image.

Más información

Más recursos de aprendizaje

Explore otras prácticas en docs.oracle.com/learn o acceda a contenido de aprendizaje más gratuito en el canal YouTube de Oracle Learning. Además, visite education.oracle.com/learning-explorer para convertirse en un explorador de formación de Oracle.

Para obtener documentación sobre los productos, visite Oracle Help Center.