A la dĂ©couverte de Picocli đ„ïž
Il y a quelques temps, jâai Ă©crit un article qui explique comment rapidement faire une CLI en Go.
Mais voilĂ , chassez le naturel et il revient au galop : je prĂ©fĂšre le Java âïž.
Je vous propose donc, durant cet article, de dĂ©couvrir comment Ă©crire rapidement et efficacement une CLI đ§âđ» en Java.
đ§ Câest quoi une CLI ?
CLI, pour Command Line Interface, ou comment interagir avec une utilisatrice ou un utilisateur au sein dâun terminal et non dâune interface graphique.
Jâai lâavantage (ou pas đ€Ł) dâavoir connu le monde du dĂ©veloppement et de lâinformatique au siĂšcle dernier.
A cette époque les interactions dans le terminal étaient légions et les interfaces graphiques pas si répandues.
Je vous parle dâun temps oĂč les terminaux passifs Ă©taient la norme (ainsi que le systĂšme dâexploitation Solaris đ
).
On peut donc, sans trop se tromper, se dire que la CLI est lâancĂȘtre des interactions Homme-Machine depuis que lâon est en capacitĂ© de saisir de la donnĂ©e (aux alentours de 1960 avec lâapparition des teletypes).
En rĂ©sumĂ©, une CLI doit permettre dâenchaĂźner une ou plusieurs actions Ă la suite dâune ligne de commande composĂ©e dâoptions et ou de paramĂštres ou dâun simple prompt.
Lâinformatique Ă©tant cyclique, les CLI sont redevenues Ă la mode notamment avec Kubernetes ou Docker qui proposent des CLI trĂšs avancĂ©es pour simplifier lâutilisation de leurs produits.
Lâautre avantage est quâelle permet de scripter simplement des actions sans passer par des appels API parfois un peu rĂ©barbatifs.
En parlant API, souvent, les CLI ne sont que de simples encapsulations des appels à ces API ou du SDK (Software Development Kit ) utilisé par les API.
Il existe plein de moyens et de technos pour crĂ©er une CLI : de la plus minimaliste (des scripts bash ou Windows par ex) jusquâĂ lâutilisation de langages dits haut niveau comme Go ou Java par exemple. Comme indiquĂ© en introduction, nous allons utiliser Java, mais pas que. En effet, nous sommes loin des annĂ©es 90 et des terminaux passifs, il existe maintenant bon nombre dâoutils et de Frameworks facilitant la vie des dĂ©veloppeuses et des dĂ©veloppeurs.
𧩠Frameworks et librairies utilisées
En 2023, je pense, que lâon ne code plus tout de A Ă Z, et avant de demander Ă ChatGPT on va passer par la case intermĂ©diaire : utiliser des Frameworks et ou librairies.
Dans mon cas je vais donc utiliser :
- un JDK Java, la derniĂšre version LTS, Ă ce jour la 17.
- un Framework quâil est cool pour Ă©crire des applications : Quarkus.
- une librairie quâelle est cool pour faire des CLI en Java : Picocli.
- GraalVM histoire de gĂ©nĂ©rer un âvraiâ exĂ©cutable
Mais faisons connaissance avec les trois briques principales de notre CLI.
đ§© Picocli
Picocli est un Framework permettant de, simplement, créer des CLI.
LâidĂ©e principale de ce Framework Ă©tant de minimiser le code technique pour Ă©crire la CLI en se concentrant sur le code fonctionnel.
On le verra plus tard dans le code, mais tout cela est permis par lâutilisation massive dâannotations Java.
Il sera alors simple de faire une jolie aide en ligne, avoir de la coloration dans celle-ci, faire de la validation de paramĂštres, âŠ
Les premiĂšres releases sous GitHub datent de 2017 et la derniĂšre (au moment de lâĂ©criture de cet article) date de juin 2023.
đ§© Quarkus
Quarkus, comme SpringBoot, fait partie de la famille des Frameworks qui sont arrivĂ©s aprĂšs lâĂšre du JEE (J2E pour les plus anciens comme moi đ„Č) qui nĂ©cessitait un serveur dâapplication. Lâobjectif est simple : vous faciliter la vie dans la conception dâune application et de la distribution de celle-ci. A noter que Quarkus a Ă©tĂ© pensĂ© (par RedHat) pour ĂȘtre pleinement optimisĂ© pour les applications Cloud Natives.
âčïž Note :
Bien entendu, Picocli peut trĂšs bien ĂȘtre utilisĂ© sans Quarkus, câest juste que jâadore ce Framework đ€©.
đ§© GraalVM
Lorsque je donne des confĂ©rences et que je parle de GraalVM, jâaime souvent indiquer quâenfin en tant que dĂ©veloppeuses et dĂ©veloppeurs Java on ne doit plus baisser la tĂȘte lorsque lâon parle de performances dâexĂ©cution ! Projet créé par Oracle, il a bouleversĂ© la vision que lâon pouvait avoir dâune application Cloud Native, rĂ©siliente, et Ă©crite en Java. Pour notre besoin, câest essentiellement la partie gĂ©nĂ©ration dâun exĂ©cutable de GraalVM qui va nous intĂ©resser : gĂ©nĂ©rer un exĂ©cutable rapide, pas (trop) gros et facilement distribuable.
đ Initialisation du projet
La premiĂšre chose est dâinitialiser le projet, et lĂ tout de suite Quarkus va aider đ€©.
Devinez quoi ?
Et oui : Quarkus propose sa propre CLI et avec celle-ci, il est possible dâinitialiser un projet pour crĂ©er une CLI qui est basĂ©e sur lâextension Picocli disponible dans lâĂ©cosystĂšme Quarkus.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ quarkus create cli fr.wilda.article:discover-picocli-article:0.0.1-SNAPSHOT
Looking for the newly published extensions in registry.quarkus.io
-----------
selected extensions:
- io.quarkus:quarkus-picocli
applying codestarts...
đ java
đš maven
đŠ quarkus
đ config-properties
đ§ dockerfiles
đ§ maven-wrapper
đ picocli-codestart
-----------
[SUCCESS] â
quarkus project has been successfully generated in:
--> /Users/stef/discover-picocli-article
-----------
Navigate into this directory and get started: quarkus dev
Cela gĂ©nĂšre une arborescence prĂȘte Ă lâemploi :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ exa -T
.
âââ LICENSE
âââ mvnw
âââ mvnw.cmd
âââ pom.xml
âââ README.md
âââ src
âââ main
âââ docker
â âââ Dockerfile.jvm
â âââ Dockerfile.legacy-jar
â âââ Dockerfile.native
â âââ Dockerfile.native-micro
âââ java
â âââ fr
â âââ wilda
â âââ article
â âââ GreetingCommand.java
âââ resources
âââ application.properties
Regardons avec un peu plus dâattention ce que lâon a comme fichiers :
- tout ce qui est nĂ©cessaire pour compiler et builder le projet avec Maven (mĂȘme si on verra plus tard que lâon utilisera directement la CLI de Quarkus pour faire ces actions) dans le
pom.xml
, - tout ce qui est nécessaire pour construire une image Docker dans
src/main/docker
, - un exemple de code dans
src/main/java
, - un fichier de configuration dans
src/main/resources
⚠Création de sa premiÚre CLI
Intéressons-nous à la classe GreetingCommand.java
qui a été générée:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package fr.wilda.article;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;
@Command(name = "greeting", mixinStandardHelpOptions = true)
public class GreetingCommand implements Runnable {
@Parameters(paramLabel = "<name>", defaultValue = "picocli",
description = "Your name.")
String name;
@Override
public void run() {
System.out.printf("Hello %s, go go commando!\n", name);
}
}
Un petit zoom sur les points clefs :
@Command
: permet de dĂ©clarer le point dâentrĂ©e de la CLI lorsque lâon a quâune seule commande,@Parameter
: permet de spĂ©cifier les paramĂštres que lâon passe Ă la CLI (ce qui est diffĂ©rent des options mais nous y reviendrons plus tard). Le paramĂštre est automatiquement stockĂ© dans la variable correspondante et il est possible de dĂ©clarer une valeur par dĂ©faut (optiondefaultValue
). A noter aussi, quâil est possible et mĂȘme conseillĂ© de positionner lâindex du paramĂštre avec lâoptionindex
lorsquâil y en a plusieurs. En lâabsence dâindex, lâordre est calculĂ© par rĂ©flexion, ce qui peut poser des problĂšmes ou donner des rĂ©sultats diffĂ©rents selon les compilateurs. Enfin, dans lâexemple fourni, lâoptionparamLabel
permet de spĂ©cifier quel label sera utilisĂ© dans le message dâaide pour identifier la valeur Ă positionner pour le paramĂštre.- ensuite, une mĂ©thode
run
sans argument et sans paramĂštre : câest cette mĂ©thode qui est le point dâentrĂ©e principal de notre CLI.
Vous pouvez donc en déduire que le travail de notre CLI est de :
- récupérer le premier paramÚtre pour le stocker dans le champ
name
- si il nây a pas dâattribut : de stocker la valeur
picocli
- puis dâafficher
Hello <valeur du paramĂštre>, go go commando!
ouHello picocli, go go commando!
On devrait donc avoir une utilisation de la forme :
1
2
$ discover-picocli-article Stef
Hello Stef, go go commando!
Allons vérifier cela tout de suite.
âĄïž ExĂ©cution de la CLI
Ce que jâaime avec Quarkus câest le developer mode qui permet de tester au fur et Ă mesure ce que lâon code sans avoir Ă relancer explicitement la compilation et le run.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ quarkus dev
[INFO] Scanning for projects...
[INFO]
[INFO] -------------< fr.wilda.article:discover-picocli-article >--------------
[INFO] Building discover-picocli-article 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- quarkus-maven-plugin:3.2.3.Final:dev (default-cli) @ discover-picocli-article ---
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-08-03 16:50:55,297 INFO [io.quarkus] (Quarkus Main Thread) discover-picocli-article 0.0.1-SNAPSHOT on JVM (powered by Quarkus 3.2.3.Final) started in 0.523s.
2023-08-03 16:50:55,307 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2023-08-03 16:50:55,307 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, picocli]
Hello picocli, go go commando!
2023-08-03 16:50:55,346 INFO [io.quarkus] (Quarkus Main Thread) discover-picocli-article stopped in 0.002s
--
Tests paused
Press [space] to restart, [e] to edit command line args (currently ''), [r] to resume testing, [o] Toggle test output, [:] for the terminal, [h] for more options>
On le voit sur la sortie de Quarkus ci-dessus, la CLI est appelĂ©e sans paramĂštre au lancement, dâoĂč le message Hello picocli, go go commando!
.
Mais cela ne sâarrĂȘte pas lĂ , en effet le mode developer de Quarkus va nous permettre dâinteragir avec les paramĂštres (et options) que lâon passerait Ă notre CLI sans quitter le mode developer.
Notamment avec la touche e
comme mentionné en bas de la sortie de Quarkus : [e] to edit command line args (currently '')
Essayons de passer un paramÚtre, et voyons le résultat :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2023-08-03 18:10:34,954 INFO [io.quarkus] (Quarkus Main Thread) discover-picocli-article stopped in 0.002s
--
Tests paused
Stef
2023-08-03 18:11:11,211 INFO [io.qua.dep.dev.RuntimeUpdatesProcessor] (Aesh InputStream Reader) Live reload total time: 0.144s
Hello Stef, go go commando!
2023-08-03 18:11:11,213 INFO [io.quarkus] (Quarkus Main Thread) discover-picocli-article stopped in 0.000s
--
Tests paused
Press [space] to restart, [e] to edit command line args (currently ''), [r] to resume testing, [o] Toggle test output, [:] for the terminal, [h] for more options>
Pas mal mais au final une CLI qui doit ĂȘtre lancĂ©e par une CLI ce nâest pas vraiment ce que lâon veut je pense đ€.
On va voir ensemble comment on pourrait transformer notre projet en une vraie CLI đ€.
đ Une CLI dans mon terminal
Commençons certainement par le plus Ă©vident, lâutilisation de la commande java
.
Avec cette commande on peut lancer une application packagée en fichier JAR
avec la commande : java -jar <nom de mon jar> <options> <paramĂštres>
.
Et encore une fois, Quarkus va nous aider Ă packager lâapplication pour ne pas avoir Ă crĂ©er les diffĂ©rents manifests nĂ©cessaires.
Pour cela, il faudra lancer la commande quarkus build
.
Cela a pour effet de générer le livrable quarkus-run.jar
dans ./target/quarkus-app
.
Câest ce livrable que nous allons pouvoir utiliser, câest aussi simple que cela đ.
Il ne reste plus quâĂ tester :
1
2
3
4
5
6
7
8
9
10
11
12
$ java -jar ./target/quarkus-app/quarkus-run.jar Stef
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-08-03 18:24:24,404 INFO [io.quarkus] (main) discover-picocli-article 0.0.1-SNAPSHOT on JVM (powered by Quarkus 3.2.3.Final) started in 0.399s.
2023-08-03 18:24:24,420 INFO [io.quarkus] (main) Profile prod activated.
2023-08-03 18:24:24,420 INFO [io.quarkus] (main) Installed features: [cdi, picocli]
Hello Stef, go go commando!
2023-08-03 18:24:24,465 INFO [io.quarkus] (main) discover-picocli-article stopped in 0.005s
Et voilà : on a une belle CLI opérationnelle !
Vous ne mâavez pas lâair convaincuâąeâąs đ ?
Oui il y a encore deux ou trois trucs Ă amĂ©liorer, dĂ©jĂ le cĂŽtĂ© utilisation : pas trĂšs simple et intuitif ! Ensuite, il y a beaucoup dâinformations qui, au final, ne mâintĂ©ressent pas pour le fonctionnel de ma CLI.
đĄ Suppression des logs
Attaquons-nous Ă ce problĂšme en premier, les logs.
Avant de tout dĂ©sactiver, jâaimerai quand mĂȘme les avoir en mode developer mais plus en mode production.
Vous commencez Ă vous en douter, Quarkus va, encore une fois, nous aider.
Pour cela on va positionner le paramétrage des logs à OFF
mais uniquement pour le mode production dans le fichier application.properties
dans lâarborescence ./src/main/resources
:
1
2
%prod.quarkus.log.level=OFF
%prod.quarkus.banner.enabled=false
Testons de nouveau notre CLI :
1
2
3
$ java -jar ./target/quarkus-app/quarkus-run.jar Stef
Hello Stef, go go commando!
đ„ïž Une vrai CLI dans le terminal
On lâa vu, la commande java
est un peu verbeuse et on se perd un peu sur ce qui est propre Ă la CLI que lâon dĂ©veloppe et ce qui est propre Ă la commande java
.
Pour cela, la premiĂšre amĂ©lioration que lâon peut faire câest encapsuler la commande, par exemple dans un script bash my-cli
dans le répertoire ./src/main/bin
:
1
2
3
#!/bin/bash
java -jar $PATH_TO_CLI/quarkus-run.jar $1
âčïž Note : lâutilisation de la variable dâenvironnement ici me permet de ne pas mettre de chemin en dur, il faudrait bien sĂ»r le spĂ©cifier dans la belle documentation dâinstallation de notre CLI đ.
On le rend exécutable : chmod +x my-cli
.
Puis on le rajoute dans le PATH
de la machine (ou on le copie dans un répertoire qui est déjà le PATH de la machine).
Testons notre CLI finale :
1
2
3
$ my-cli Stef
Hello Stef, go go commando!
Bon, cette fois ça y est, jâai ma CLI et je lâutilise directement en ligne de commande dans un terminal đ.
Alors pari gagnĂ© ? đ€
Non, effectivement et pour différentes raisons :
- lâutilisation dâun script bash nâest quâun artifice pour masquer lâutilisation de la commande
java
- cela nĂ©cessite toujours dâavoir une JVM dâinstallĂ©e
- cela ajoute donc cette empreinte supplĂ©mentaire en termes dâexĂ©cution et de consommation mĂ©moire et processeur
- ce nâest pas un vrai exĂ©cutable comme on peut lâimaginer pour une CLI
Voyons comment remĂ©dier Ă cela đ„.
đ GraalVM, le dernier chaĂźnon manquant
Pour moi une CLI câest un exĂ©cutable et câest tout ! On a vu prĂ©cĂ©demment, que pour avoir un seul exĂ©cutable, on doit tricher et que lâinstallation nâest pas fluide (installer un JRE, âŠ). Et je ne reviens pas sur les coĂ»ts de performances. Heureusement, depuis quelques annĂ©es, il est maintenant possible de gĂ©nĂ©rer des exĂ©cutables Ă partir dâun dĂ©veloppement Java.
Comment ?
GrĂące Ă GraalVM !
âïž Installation
Pour cela, rien de plus simple, il faut suivre le tutoriel de la documentation.
Ensuite, pour que la compilation avec Quarkus se dĂ©roule bien, il faut ajouter la variable dâenvironnement GRAALVM_HOME
avec le chemin dâinstallation de GraalVM, par exemple export GRAALVM_HOME=/Library/Java/JavaVirtualMachines/graalvm-jdk-17.0.8+9.1/Contents/Home
dans mon cas.
Et voilĂ GraalVM est installĂ© et prĂȘt Ă ĂȘtre utilisĂ© avec Quarkus.
âïž GĂ©nĂ©ration de lâexĂ©cutable
Je pense que, maintenant, vous en avez lâhabitude, mais encore un fois Quarkus va grandement nous faciliter la vie pour gĂ©nĂ©rer notre exĂ©cutable.
Il suffit dâajouter lâoption --native
Ă la commande quarkus build
, et le tour est joué !
quarkus build --native
Quarkus (avec lâaide de GraalVM) gĂ©nĂšre un exĂ©cutable dans le rĂ©pertoire ./target
de la forme artifactId-version-runner
.
Dans notre cas cela donne discover-picocli-0.0.1-SNAPSHOT-runner
.
Un petit renommage en my-cli
et le tour est joué mv discover-picocli-article-0.0.1-SNAPSHOT-runner my-cli
.
Il ne reste plus quâĂ tester tout ça :
1
2
3
$ my-cli Stef
Hello Stef, go go commando!
â ïž Quelques points dâattention â ïž
- Pour juste ce hello, world!, le binaire a une taille de 35 Mo sur un MacBook M1. Câest certes assez gros mais il ne faut pas oublier que cela embarque aussi les libs nĂ©cessaires Ă lâexĂ©cution, Ă voir comment cela Ă©volue avec le code mĂ©tier de la CLI đ.
- Sauf si je suis passĂ© Ă cĂŽtĂ©, mais la gĂ©nĂ©ration du binaire dĂ©pend de la machine sur laquelle vous gĂ©nĂ©rez ce binaire, câest pour moi un gros manque par rapport Ă ce quâil est possible de faire en Go avec COBRA.
đšâđ» Pour aller plus loin
Cet article nâest que le dĂ©but de la crĂ©ation dâune CLI avec Picocli, Quarkus et GraalVM.
Nous nâavons pas du tout explorĂ© toute la puissance de Picocli pour crĂ©er notre CLI.
Il nous reste Ă jouer avec les options, les paramĂštres, lâaide, âŠ
Ce sera pour la partie 2 de cet article que je trouve dĂ©jĂ bien assez long đ
.
đ En conclusion
Si vous ĂȘtes arrivĂ©s jusque lĂ merci de mâavoir lu et si il y a des coquilles nâhĂ©sitez pas Ă me faire une issue ou PR đ.
Merci Ă ma relectrice, Fanny, qui vous permet de lire cet article sans avoir trop les yeux qui saignent đ.
Vous trouverez lâensemble du code source dans le repository discover-picocli-article.
Comments