Modules Java: JRE modulaire

construire un JRE personnalisé. Ce tutoriel fait partie d’une série de trois articles qui permettront de cerner ce nouveau concept afin de l’utiliser pour plus de robustesse et maintenabilité des applications développées en Java:

  1. Modules Java: Présentation et concepts
  2. Cet article: Modules Java: JRE modulaire
  3. Modules Java: Une application exemple – Conception
  4. Modules Java: Une application exemple – Intérêts

1. Une structure plus simple et plus efficace du système.

La meilleur façon d’appréhender cette évolution de la structure est de faire une comparaison de l’existant antérieurement (version 1.8-) avec la nouvelle structure. Pour effectuer cette comparaison, on doit disposer de deux versions de Java sur le système. Une 1.8- et une 9+. Exécuter les deux commandes, observer les résultats et comparer (adapter, si besoin, les numéros de version dans les chemins cibles aux versions installées):
tree -d -I legal\|man\|docs /usr/lib/jvm/java-1.8.0-openjdk-amd64/
tree -d -I legal\|man\|docs /usr/lib/jvm/java-9-openjdk-amd64/
La commande tree n’est pas installée systématiquement sur le système utilisé. Si c’est le cas, installez la avec la commande: sudo apt install tree. Dans ces deux commandes on n’affiche que les dossiers et on exclu les dossiers: legal/, man/ et docs/ qui ne contiennent que de la documentation et sont hors d’intérêt pour cet exposé. Modules Java A gauche l’ancienne arborescence Java 1.8-, à droite la nouvelle de Java 9+. On peut constater dès le départ une nette simplification et une meilleur organisation de la structure des dossiers. Dans le détail:
  • L’apparition de deux nouveaus dossiers: conf/ destiné à contenir les fichiers de configuration et jmods/ destiné à contenir les fichiers des modules java du système, JRE et JDK.
  • Le déplacement de la partie configuration du JRE dans le dossier conf/
  • La disparition du dossier de la JRE.
  • Un seul dossier bin/ et un seul dossier lib/ destinés à acueillir respectivement les fichiers exécutables et les librairies essentiellement natives du JRE et du JDK.

2. Une utilisation plus simple de Java 9+

Si on considère que Java est installé dans le dossier JAVA_INSTALLATION/ (donc /usr/lib/jvm/java-9-openjdk-amd64/ pour un système Ubuntu/Debian):
  • Toutes les commandes exécutables du JRE et du JDK sont dans : JAVA_INSTALLATION/bin. Il suffit donc d’ajouter ce chemin à la configuration du système (chemin PATH).
  • Toutes les librairies natives du système sont dans JAVA_INSTALLATION/lib. Si besoin, il suffit d’indiquer ce chemin au système pour ce type de librairie.
  • Tous les modules (librairies Java du système) sont dans JAVA_INSTALLATION/jmods, il suffit donc d’utiliser ce chemin pour le paramètre –module-path de Java 9+.
Maintenant, vérifions les contenus de ces trois dossiers importants. Adaptez, dans cette commande,  le numéro de version de Java dans le chemin cible. Cette version pourrait être  10 ou 11 (Novembre 2018):
ls -l /usr/lib/jvm/java-9-openjdk-amd64/bin
Le résultat partiel de cette commande est:
-rwxr-xr-x 1 root root  10424 oct.   7 17:06 jaotc
-rwxr-xr-x 1 root root  10368 oct.   7 17:06 jar
-rwxr-xr-x 1 root root  10376 oct.   7 17:06 jarsigner
-rwxr-xr-x 1 root root  10344 oct.   7 17:06 java
-rwxr-xr-x 1 root root  10400 oct.   7 17:06 javac
-rwxr-xr-x 1 root root  10408 oct.   7 17:06 javadoc
-rwxr-xr-x 1 root root  10400 oct.   7 17:06 jlink
-rwxr-xr-x 1 root root  10368 oct.   7 17:06 jmod
On remarque la présence des exécutables java (JRE), javac (JDK), jlink (JDK)..
ls -l /usr/lib/jvm/java-9-openjdk-amd64/lib
Le résultat partiel de cette commande est:
-rw-r--r-- 1 root root     22976 oct.   7 17:06 libjimage.so
-rw-r--r-- 1 root root     96960 oct.   7 17:06 libnet.so
-rw-r--r-- 1 root root     72672 oct.   7 17:06 libnio.so
-rw-r--r-- 1 root root     10288 oct.   7 17:06 libprefs.so
-rw-r--r-- 1 root root      6128 oct.   7 17:06 librmi.so
On remarque la présence des librairies dynamiques partagées natives libnet.so, libnio.so, librmi.so ..
ls -l /usr/lib/jvm/java-9-openjdk-amd64/jmods
Le résultat partiel de cette commande est:
-rw-r--r-- 1 root root 19040174 oct.   7 17:06 java.base.jmod
-rw-r--r-- 1 root root   128362 oct.   7 17:06 java.logging.jmod
-rw-r--r-- 1 root root   381849 oct.   7 17:06 java.rmi.jmod
-rw-r--r-- 1 root root  6503544 oct.   7 17:06 jdk.compiler.jmod
-rw-r--r-- 1 root root   245838 oct.   7 17:06 jdk.jartool.jmod
-rw-r--r-- 1 root root   630021 oct.   7 17:06 jdk.jshell.jmod
On remarque la présence des modules java.base (JRE), java.logging (JRE), jdk.jartool (JDK)..

3. Le nouveau format de paquet JMOD

A l’exploration du contenu du dossier jmods/, on remarque que les fichiers des modules JRE et JDK ont l’extension .jmod. Il s’agit d’un nouveau format introduit par la version 9 et qui comporte des capacités plus évoluées que celles du format JAR. Le format JMOD permet d’embarquer dans un même fichier les bytecode java compilés (.class) avec des fichiers statiques, qui peuvent servir à la configuration par exemple, ainsi que du code natif. Toutefois, les fichiers ainsi créés sont destinés seulement pour des environnements de développement. L’utilitaire jmod, fichier exécutable dans le dossier bin/, permet de gérer ce type de fichier. Notament, le paramètre create permet la création de ces paquets. Pour plus de détails à propos de son fonctionnement exécuter:
jmod --help
Enfin, la connaissance de la liste des modules disponibles pour le système installé ne passe pas forcément par l’exploration du dossier jmods/. On peut afficher cette liste avec la commande:
java --list-modules

4. Création d’une image exécutable personnalisée

Le principe de fonctionnement de Java repose sur le code intermédiaire (bytecode) et l’environnement d’exécution (JRE). Ce principe permet d’exécuter le même programme, écrit une seule fois, sur des plateformes différentes. Le mécanisme prévoit que chaque plateforme, qui supporte Java, dispose de son JRE spécifique. En même temps tous les JRE doivent pouvoir exécuter le même bytecode qui résulte de la compilation des sources Java. Au fil du temps, des difficultés sont apparus. D’abord le JRE et son élément central, le fichier rt.jar, sont devenus imposants. Dans sa dernière version 1.8, rt.jar pèse 63Mo. En même temps les terminaux légers avec de faibles ressources (tablettes, smartphones, smartwatch..), commencent à prendre une place de plus en plus importante chez les utilisateurs finaux. La modularisation du JRE dans Java 9 permet, désormais, de créer une image compacte et optimisée capable d’exécuter un programme écrit en Java. Ceci consiste à créer un sous-ensemble du JRE qui ne comprend que le strict nécessaire au cas spécifique de l’application cible. La suite de cet exposé utilise l’application d’exemple créée dans le premier article qui traite des modules Java. L’application modulaire, exécutable étant créée, on procède à la génération de limage auto exécutable (JRE compris). Il faut commencer par se positionner dans le dossier où a été créée cette application. Ensuite, céez un sous dossier modules/:
mkdir modules
On crée, ensuite la version modulaire prête à l’emploi de notre application:
jmod create --class-path lib/premiermodule.jar modules/net.meddeb.premiermodule.jmod
Maintenant, on peut créer l’image auto-exécutable de l’application à l’aide de l’utilitaire jlink:
jlink --module-path /usr/lib/jvm/java-9-openjdk-amd64/jmods/:modules/ --add-modules net.meddeb.premiermodule --launcher executeHello=net.meddeb.premiermodule/net.meddeb.hellomodule.Hello --output distribution
Le paramètre –output de cette commande permet de définir le dossier où sera généré le résultat.Dans notre cas on a choisi un dossier qui s’appelle distribution/. Pour explorer le résultat de cette commande et voir ce qui a été généré:
tree -L 2 distribution/
Dans le résultat, remarquez le fichier exécutable executeHello qui est le nom donnée dans la commande de jlink : modules java L’exécution se fait par l’intermédiaire de la commande qui suit, comme s’il s’agissait d’un exécutable natif:
distribution/bin/executeHello
]]>