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

Laisser un commentaire