Socle V004 – GraalVM JavaScript

Socle V004 - GraalVM JavaScript

GraalVM et JavaScript (GraalJS)

Version : 4.0.0 Date : 2025-12-13

1. Vue d’ensemble

Le Socle V004 supporte l’exécution de scripts JavaScript pour l’enrichissement et la transformation de données via GraalJS, le moteur JavaScript de GraalVM.

Pourquoi GraalVM ?

  • Performance : Compilation JIT pour JavaScript, bien plus rapide que Nashorn
  • Compatibilité : Support ECMAScript moderne (ES2023+)
  • Interopérabilité : Accès bidirectionnel entre Java et JavaScript
  • Sécurité : Sandbox configurable pour isoler l’exécution

2. Prérequis

2.1 GraalVM CE 21.0.2

Important : Pour les applications utilisant GraalJS, GraalVM CE 21.0.2 est requis au lieu d’OpenJDK standard.

OpenJDK ne contient pas le runtime Truffle nécessaire à GraalJS. Utiliser OpenJDK avec GraalJS provoquera l’erreur :

org.graalvm.polyglot.PolyglotException:
The Truffle API cannot be used without GraalVM runtime.

2.2 Installation de GraalVM

# Télécharger GraalVM CE 21.0.2
wget https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz

# Extraire dans /opt
sudo tar -xzf graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz -C /opt/

# Créer un lien symbolique
sudo ln -s /opt/graalvm-community-openjdk-21.0.2+13.1 /opt/graalvm

# Vérifier l'installation
/opt/graalvm/bin/java -version

Sortie attendue :

openjdk version "21.0.2" 2024-01-16
OpenJDK Runtime Environment GraalVM CE 21.0.2+13.1 (build 21.0.2+13-jvmci-23.1-b30)
OpenJDK 64-Bit Server VM GraalVM CE 21.0.2+13.1 (build 21.0.2+13-jvmci-23.1-b30, mixed mode, sharing)

3. Configuration Maven

3.1 Version GraalJS

La version de GraalJS doit correspondre à la version du runtime GraalVM :

GraalVM CE GraalJS Version
21.0.2 (23.1) 23.1.0
22.0.0 (24.0) 24.0.0

3.2 Dépendances pom.xml

<properties>
    <graaljs.version>23.1.0</graaljs.version>
</properties>

<dependencies>
    <!-- GraalVM Polyglot API -->
    <dependency>
        <groupId>org.graalvm.polyglot</groupId>
        <artifactId>polyglot</artifactId>
        <version>${graaljs.version}</version>
    </dependency>

    <!-- GraalJS Community (runtime) -->
    <dependency>
        <groupId>org.graalvm.polyglot</groupId>
        <artifactId>js-community</artifactId>
        <version>${graaljs.version}</version>
        <type>pom</type>
        <scope>runtime</scope>
    </dependency>

    <!-- Script Engine (optionnel, pour JSR-223) -->
    <dependency>
        <groupId>org.graalvm.js</groupId>
        <artifactId>js-scriptengine</artifactId>
        <version>${graaljs.version}</version>
    </dependency>
</dependencies>

4. Options JVM

4.1 Option obligatoire pour uber-jars

Pour les fat JARs (Spring Boot repackaged), cette option est obligatoire :

-Dpolyglotimpl.DisableClassPathIsolation=true

Sans cette option, vous obtiendrez :

java.lang.NullPointerException: Cannot invoke "java.io.File.toPath()"
because the return value of "com.oracle.truffle.polyglot.FileSystems$InternalFileSystemContext.collectClassPathJars()"

4.2 Supprimer les avertissements

Pour supprimer les avertissements « interpreter only » :

-Dpolyglot.engine.WarnInterpreterOnly=false

4.3 Configuration systemd complète

[Service]
Environment="JAVA_OPTS=-Xms512m -Xmx1024m -XX:+UseG1GC -Dpolyglotimpl.DisableClassPathIsolation=true"
ExecStart=/opt/graalvm/bin/java $JAVA_OPTS -jar /opt/myapp/myapp.jar

5. Utilisation de GraalJS

5.1 Exemple basique

import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;

public class JavaScriptExecutor {

    public String execute(String script, Map<String, Object> bindings) {
        try (Context context = Context.newBuilder("js")
                .allowAllAccess(false)
                .allowHostAccess(HostAccess.ALL)
                .allowHostClassLookup(className -> false)
                .option("engine.WarnInterpreterOnly", "false")
                .build()) {

            Value jsBindings = context.getBindings("js");
            bindings.forEach(jsBindings::putMember);

            Value result = context.eval("js", script);
            return result.asString();
        }
    }
}

5.2 Options de sécurité

Option Description
allowAllAccess(false) Désactive tous les accès par défaut
allowHostAccess(HostAccess.ALL) Permet l’accès aux objets Java passés
allowHostClassLookup(className -> false) Empêche Java.type()
allowIO(false) Désactive les accès fichiers
allowNativeAccess(false) Désactive les appels natifs

6. Performance et initialisation

6.1 Temps d’initialisation

La première exécution de GraalJS prend environ 10-15 secondes car :

  • Chargement du runtime Truffle
  • Compilation JIT du moteur JavaScript
  • Initialisation des structures internes

Les exécutions suivantes sont très rapides (<100ms).

6.2 Pré-initialisation au démarrage

Pour éviter la latence sur le premier événement, vous pouvez pré-initialiser GraalVM :

@Component
public class GraalVMWarmup {

    private Engine sharedEngine;

    @PostConstruct
    public void init() {
        log.info("Pre-initializing GraalVM JavaScript engine...");
        long start = System.currentTimeMillis();

        this.sharedEngine = Engine.newBuilder()
            .option("engine.WarnInterpreterOnly", "false")
            .build();

        // Warm-up avec un script simple
        try (Context warmup = Context.newBuilder("js")
                .engine(sharedEngine)
                .allowAllAccess(false)
                .allowHostAccess(HostAccess.ALL)
                .build()) {
            warmup.eval("js", "var x = 1 + 1;");
        }

        log.info("GraalVM initialized in {} ms", System.currentTimeMillis() - start);
    }

    @PreDestroy
    public void cleanup() {
        if (sharedEngine != null) {
            sharedEngine.close();
        }
    }

    public Engine getSharedEngine() {
        return sharedEngine;
    }
}

6.3 Impact sur le démarrage

Configuration Démarrage app Premier événement
Sans pré-init ~40s ~12s
Avec pré-init ~52s (+12s) <100ms

7. Troubleshooting

7.1 « Truffle API cannot be used without GraalVM runtime »

Cause : Utilisation d’OpenJDK au lieu de GraalVM Solution : Installer et utiliser GraalVM CE 21.0.2

7.2 « NullPointerException in collectClassPathJars »

Cause : Classpath isolation dans un uber-jar Solution : Ajouter -Dpolyglotimpl.DisableClassPathIsolation=true

7.3 « Version mismatch: truffle-api-24.0.0 vs GraalVM 23.1 »

Cause : Version GraalJS incompatible avec le runtime GraalVM Solution : Aligner graaljs.version avec la version GraalVM installée

7.4 Script timeout

Cause : Initialisation GraalVM dépasse le timeout Solution : Augmenter le timeout à 30 secondes pour le premier appel, ou pré-initialiser

8. Références

Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *