💾 Partagez simplement vos Java CLI Apps

Paris JUG Academy

9 Janvier 2024

Partagez simplement vos Java CLI Apps

Qui suis-je ?

Pierre-Yves Fourmond

Développeur Back

Consultant Senior (Tribu « Java / Kotlin »)

X / GitHub : @grumpyf0x48

#Java #Linux #Bash #API #CLI

Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Le besoin

Votre équipe a besoin :

  • D'une application en ligne de commande (CLI)
  • Facile à installer et utiliser
  • 🚀 Avec la volonté d'aller vite
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Quel type d'application ?

  • Des outils pour les techs
  • Des commandes pour la CI
  • Une étude / Un POC
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Comment adresser ce besoin ?

🤔 Et si on utilisait du scripting ?

Les langages de script sont généralement exécutés à partir de fichiers (dits, précisément, scripts) contenant le code source du programme qui sera interprété.

Historiquement, ils ont été créés pour raccourcir le processus traditionnel de développement édition, compilation, édition des liens, exécution propre aux langages compilés.

Source: Wikipedia

Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

On va faire du scripting en Java ?

Est-ce que Java est connu comme un langage de script ?

  • ❌

Peut-on faire du scripting en Java ?

  • ✅

Avec quel outil ?

  • Besoin de JBang sur la machine 😥
  • Restons sur le JDK
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Commençons par Java 8

class Hello {
    public static void main(String... args) {
        System.out.println("Hello " + String.join(",", args));
    }
}
java-8:$ javac Hello.java
java-8:$ java Hello Paris JUG
Hello Paris,JUG
  • On a deux étapes successives : compilation puis exécution
  • Ce n'est pas du scripting !
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Scripting avec Java 11

Java 11 implémente la JEP 330 (Launch Single-File Source-Code Programs) :

java-11:$ java Hello.java Paris JUG
Hello Paris,JUG
  • javac n'est plus appelé explicitement
  • C'est bien du scripting !
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Scripting avec Java 11 et Unix

Utilisons un Shebang pour lancer notre programme :

java-11:$ cat hello
#!/usr/bin/java --source 11

class Hello {
    public static void main(String... args) {
        System.out.println("Hello " + String.join(",", args));
    }
}
java-11:$ ./hello Paris JUG
Hello Paris,JUG

⚠️ --source est nécessaire ici pour préciser le mode de lancement

Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Scripting avec Java 11 et Unix 🤔

Pourquoi cette façon de faire pose un problème aux devs ?

  • Le nom du fichier source n'a plus l'extension .java
    • Les éditeurs ne comprendront pas que c'est du Java
  • La première ligne #!/usr/bin/java ... n'est pas valide en Java
    • On risque d'avoir une erreur de syntaxe
  • 💡 Et si on nommait le fichier Hello.java comme avant ?
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Scripting avec Java 11, Unix et une extension .java

😞 Perdu !

java-11:$ ./Hello.java
./Hello.java:1: error: illegal character: '#'
#!/usr/bin/java --source 11
^
./Hello.java:1: error: class, interface, or enum expected
#!/usr/bin/java --source 11
^
2 errors
error: compilation failed

❌ Le compilateur ne comprend pas la première ligne

Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Scripting avec Java 11 et Unix 🔎

  • Notre script se lance dans le terminal

  • On ne sait pas l'éditer 😥

  • On a besoin d'un Shebang compris par Bash et ignoré de Java

  • ///usr/bin/java --source 11 "$0" "$@"; exit $?
    
    class Hello {
        public static void main(String... args) {
            System.out.println("Hello " + String.join(",", args));
        }
    }
    
  • java-11:$ ./Hello.java Paris JUG
    Hello Paris,JUG
    
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Un script pour générer des données de test

///usr/bin/java --source 21 --enable-preview --class-path lib/picocli-4.7.5.jar:lib/commons-lang3-3.14.0.jar "$0" "$@"; exit $?

import ...

@Command(name = "GenerateData", mixinStandardHelpOptions = true, version = "0.1")
class GenerateData implements Callable<Integer> {

    @Option(names = {"-c", "--column"}, description = "Map a column with a fixed value, mapping function or value from a file")
    String[] columnMappings;

    @Option(names = {"-n", "--count"}, description = "Number of lines to generate")
    int lineCount;

    @Parameters(arity = "1", description = "The file containing the SQL create table request")
    File sqlRequestFile;

    void main(String... args) {
        System.exit(new CommandLine(new GenerateData()).execute(args));
    }

    @Override
    public Integer call() throws IOException {
        System.out.println(TableData.generate(sqlRequestFile, columnMappings, lineCount).toSQLInserts());
        return 0;
    }

    record TableData(TableDefinition tableDefinition, TableRows tableRows) implements Exportable { ... }
    record TableDefinition(String name, List<ColumnDefinition> columnDefinitions) implements Exportable { ... }
    record ColumnDefinition(String name, ColumnType type) { ... }
    record ColumnMappings(List<ColumnMapping> columnMappings) { ... }
    interface Exportable { String toSQLInserts(); }
}
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Démo : Le script en mode dev

Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Packager notre application

💡 Créer une archive au format zip avec des répertoires src, lib et bin :

~/generate-data:$ unzip -l build/GenerateData.zip
Archive:  build/GenerateData.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2023-12-23 15:35   generate-data/
        0  2023-12-23 15:35   generate-data/src/
    11431  2023-12-23 15:35   generate-data/src/GenerateData.java
        0  2023-12-23 15:35   generate-data/lib/
   657952  2023-12-23 15:35   generate-data/lib/commons-lang3-3.14.0.jar
   415128  2023-12-23 15:35   generate-data/lib/picocli-4.7.5.jar
        0  2023-12-23 15:35   generate-data/bin/
      333  2023-12-23 15:35   generate-data/bin/GenerateData.sh
---------                     -------
  1084844                     8 files
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Le Shell de lancement

Lancer l'application avec un environnement modifié :

APP_DIR=... $APP_DIR/src/GenerateData.java ...

Le classpath devient :

--class-path $APP_DIR/lib/picocli-4.7.5.jar:$APP_DIR/lib/commons-lang3-3.14.0.jar

😎 On est indépendant du répertoire de lancement

Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Comment construire notre archive ?

On a juste besoin d'un Makefile :

APP_NAME=GenerateData
APP_DIR=generate-data

BUILD=build
BUILD_APP=$(BUILD)/$(APP_DIR)

package: build
	cd $(BUILD) && zip --recurse-paths $(APP_NAME).zip $(APP_DIR)/

build: prepare
	cp $(APP_NAME).java $(BUILD_APP)/src
	cp $(APP_NAME).sh $(BUILD_APP)/bin
	cp --recursive lib/ $(BUILD_APP)

prepare: clean
	mkdir --parents $(BUILD_APP) $(BUILD_APP)/src $(BUILD_APP)/bin $(BUILD_APP)/lib

clean:
	rm --force --recursive $(BUILD)
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Démo : L'application installée

Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Avantages et inconvénients de cette approche

  • 👍 Avantages

    • Code source accessible et modifiable
    • Packaging simplifié
    • On n'a besoin que du JDK
  • 👎 Inconvénients

    • On est limité à un seul fichier source
    • Besoin de gérer nous-mêmes les dépendances
    • Le code est compilé à chaque fois
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Comment on peut améliorer ça ?

  • Limitation à un fichier source

  • Gérer nous-mêmes les dépendances

    • Packager l'archive zip avec Gradle (plugin application)
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

Autres façons pour livrer notre application

  • Un livrable classique
    • Un zip ou un jar de l'application compilée
    • Les plugins de Gradle
  • Un livrable natif avec GraalVM
  • Un paquet système (rpm, deb, pkg, ...) avec jpackage
Paris JUG Academy - 2024
Partagez simplement vos Java CLI Apps

🙋 Des questions ?

Et vous, vous faites comment pour partager vos applications dans vos équipes ?

Slides & Code de démo: https://github.com/java-cli-apps/java-cli-apps.github.io

Contact: https://twitter.com/grumpyf0x48

Paris JUG Academy - 2024

--- ![bg fit](images/qrcode.png)