<!--
  canonical-url: https://www.avonture.be/fr/blog/docker-java
  published:     2023-11-28
  This is a static plain-Markdown mirror generated at build time.
  Visit the canonical URL above for the fully rendered page, with images and interactive components.
-->

# Jouer avec Docker et Java

> Apprenez à compiler et exécuter des applications Java avec Docker. Mettez en place un environnement de développement Java en quelques secondes, sans rien installer localement — parfait pour les développeurs qui débutent avec Java et Docker.

---
![Jouer avec Docker et Java](/img/v2/experiments.webp)

**TL;DR**

Cet article montre comment compiler et exécuter du Java sans rien installer localement, grâce à l'image Docker officielle `openjdk` : `docker run ... eclipse-temurin:21 javac Main.java` pour compiler, puis `docker run ... eclipse-temurin:21 java Main` pour exécuter, avec un second exemple qui appelle une API REST depuis Java et affiche la réponse JSON.

Dans cet article, nous allons jouer avec Docker et Java, en utilisant les images Java prêtes à l'emploi publiées sur Docker Hub. La seule chose dont vous avez besoin sur votre machine, c'est [Docker itself](/blog/install-docker).

*La même approche « zéro installation, juste une image » est utilisée sur ce blog pour [PHP](/blog/docker-php-run-script-or-website), [Python](/blog/docker-python-devcontainer) et [Quarto](/blog/docker-quarto).*

> **Je ne connais rien à Java:**
>
> Sachez juste que je n'ai absolument aucune compétence en Java. Quel logiciel faut-il installer, comment lancer un script, etc. ? Je vais simplement m'appuyer sur quelques commandes Docker et, côté installation, oui, avec Docker c'est facile : rien à installer, rien à configurer.

## Deux commandes, et Java tourne {#two-commands-and-java-runs}

Une commande pour compiler, une pour exécuter :

```bash
`$ docker run -it --rm -v \${PWD}:/app -w /app -u 1000:1000 eclipse-temurin:21 javac Main.java

$ docker run --rm -v $PWD:/app -w /app eclipse-temurin:21 java Main
Hello, World`
```

Pas de JDK sur la machine, pas de `JAVA_HOME`, pas de `PATH` à corriger.

## Pourquoi ça fonctionne {#why-it-works}

* L'image `eclipse-temurin:21` embarque déjà le JDK complet, donc `javac` et `java` sont présents, dans la version exacte annoncée par le tag de l'image.
* `-v ${PWD}:/app` partage votre dossier courant avec le container : le container lit votre fichier `.java` et réécrit le fichier `.class` dans votre propre dossier.
* `-u 1000:1000` fait tourner le container sous votre identité, donc le `Main.class` généré appartient à votre utilisateur et non à `root`.

## Le code source {#the-source}

Ouvrez un shell Linux et lancez `mkdir -p /tmp/java && cd $_` pour créer un dossier `java` dans votre dossier temporaire Linux et y entrer.

Créez un nouveau fichier appelé `Main.java` avec ce contenu :

**Main.java**

```java
class Main {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}
```

Après la première commande, votre source `Main.java` a été compilée en un fichier `Main.class`. En lançant `ls -alh`, vous pouvez vérifier que, oui, le script java a bien été compilé :

```bash
$ ls -alh
total 24K
drwxr-xr-x  2 christophe christophe 4.0K Nov 22 15:02 .
drwxrwxrwt 29 root       root        12K Nov 22 14:58 ..
-rw-r--r--  1 christophe christophe  414 Nov 22 15:02 Main.class
-rw-r--r--  1 christophe christophe  117 Nov 22 14:58 Main.java
```

## Un exemple un peu plus difficile : appeler une API REST {#a-slightly-more-difficult-example-calling-a-rest-api}

Créez un nouveau fichier appelé `API.java` avec ce contenu :

**API.java**

```java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class API {
    public static void main(String[] args) {
        try {
            URL url = new URL("https://jsonplaceholder.typicode.com/todos/1");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "application/json");

            if (conn.getResponseCode() != 200) {
                throw new RuntimeException("Failed : HTTP Error code : "
                        + conn.getResponseCode());
            }

            InputStreamReader in = new InputStreamReader(conn.getInputStream());
            BufferedReader br = new BufferedReader(in);
            String output;

            while ((output = br.readLine()) != null) {
                System.out.println(output);
            }

            conn.disconnect();

        } catch (Exception e) {
            System.out.println("Exception in NetClientGet:- " + e);
        }
    }
}
```

Compilez-le en lançant `docker run --rm -v $PWD:/app -w /app -u 1000:1000 eclipse-temurin:21 javac API.java` ; vous obtenez le fichier `API.class`.

Enfin, lancez `docker run --rm -v $PWD:/app -w /app eclipse-temurin:21 java API` pour exécuter l'appel à l'API et afficher le résultat à l'écran :

```bash
$ docker run --rm -v $PWD:/app -w /app eclipse-temurin:21 java API
```

```json
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
```

*Cet exemple utilise l'URL d'exemple `https://jsonplaceholder.typicode.com/todos/1` pour générer un faux TODO. Le JSON sera affiché dans la console.*

## Sous le capot : les flags de docker run (à sauter si vous voulez juste l'utiliser) {#under-the-hood-the-docker-run-flags-skip-this-if-you-just-want-to-use-it}

Les commandes Docker run utilisées ci-dessus sont (presque toujours les mêmes) :

* `-it` pour démarrer Docker en mode interactif, ce qui permettra au script qui tourne dans le container de vous poser des questions par exemple,
* `--rm` pour demander à Docker de tuer et supprimer le container dès que le script a été exécuté (sinon vous aurez une tonne de containers Docker arrêtés mais non supprimés ; vous pouvez le vérifier en omettant le flag `--rm` puis en lançant `docker container list` dans la console),
* `-v ${PWD}:/app` pour partager votre dossier courant avec un dossier appelé `/app` dans le container Docker,
* `-w /app` pour dire à Docker que le répertoire courant, dans le container, sera le dossier `/app`,
* `-u 1000:1000` pour demander à Docker de réutiliser nos identifiants locaux, ainsi quand un fichier est modifié ou créé dans le container, il appartiendra à notre utilisateur,
* ensuite `eclipse-temurin:21`, qui est le nom et la version de l'image Docker à utiliser, et, enfin,
* `javac Main.java`, c'est-à-dire la ligne de commande à lancer dans le container.

## Conclusion {#conclusion}

Compiler et exécuter du Java, y compris un programme qui appelle une API REST, sans une seule ligne de configuration sur l'host : l'image apporte la toolchain, votre dossier apporte le code, et le container disparaît dès que la commande se termine. Vous êtes prêt à démarrer votre aventure Java. Amusez-vous bien.

Même idée, autre langage : j'ai aussi joué avec [Pascal](/blog/docker-pascal), [Assembly](/blog/docker-assembly) et [Python](/blog/docker-python) en suivant cette même approche Docker « zéro installation locale ».
