Une équipe de développement informatique qui travaille avec Java prépare des montées de version Java pour certains de ces projets. Sa politique consiste à passer systématiquement d'une version à support de long-terme (LTS) à une version LTS plus récente. Les versions considérées se trouvent donc à ce jour parmi : 8, 11, 17 et 21. L'équipe se préoccupe notamment des évolutions portant sur les API standard. Tirer profit de nouveaux éléments du langage est traité séparément, et donc seules sont considérées ici les modifications et suppressions concernant les éléments de l'API standard entre versions LTS. Le site [The Java Version Almanach](https://javaalmanac.io/) est utilisé comme référence pour cette tâche. Le site rend accessibles des données issues des comparaisons des API `{java}public` et `{java}protected`. Par exemple, la comparaison des API entre Java 8 et Java 21 est accessible via cette URL : > [https://javaalmanac.io/jdk/21/apidiff/8/](https://javaalmanac.io/jdk/21/apidiff/8/) et le fichier des données source utilisées pour construire cette vue est accessible via : > [https://data.javaalmanac.io/v1/jdk/versions/21/apidiffs/8](https://data.javaalmanac.io/v1/jdk/versions/21/apidiffs/8) Les données sont fournies dans le format [JSON (JavaScript Object Notation)](https://www.json.org/json-en.html), un format léger de représentation et d'échange de données spécifié dans la norme [ECMA-404](https://ecma-international.org/publications-and-standards/standards/ecma-404/). La notation est fondée sur : - des **types composés** : - des ensembles de **paires clé/valeur** (_objets Javascript_) : délimités par `{...}`, les clés apparaissent entre `"`, les valeurs peuvent être de tous types - des **listes** : délimitées par `[...]`, avec leurs éléments séparés par `,` - des types scalaires : - booléens (`true` et `false`) - nombres (sans distinctions entiers/flottants) - chaînes de caractères Unicode (entre `"`) - `null` (absence de valeur) Le contenu JSON ci-dessous est un extrait des différences entre Java 8 et Java 21. On trouve dans l'objet _racine_ une clé `{json}"deltas"`, qui a pour valeur associée la liste de toutes les modifications calculées. ```json { "base":{ "feature":"8", "vendor":"tem", "version":"8.0.432" }, "target":{ "feature":"21", "vendor":"tem", "version":"21.0.5+11-LTS" }, "deltas":[ { "type":"package", "name":"java.applet", "status":"notmodified", "javadoc":"https://docs.oracle.com/...", "deltas":[ { "type":"class", "name":"Applet", "status":"modified", "javadoc":"...", "addedTags":[ "deprecated", "forRemoval" ] }, { "type":"interface", "name":"AppletContext", "status":"modified", "javadoc":"...", "addedTags":[ "deprecated", "forRemoval" ] } ] } ] } ``` Les deux modifications particulières de l'exemple portent sur la classe `{java}java.applet.Applet` et l'interface`{java}java.applet.AppletContext`, qui ont donc toutes les deux le statut (`{json}"status"`) `{json}"modified"` et les mentions ajoutées (`{json}"addedTags"`) `{json}"deprecated"` et `{json}"forRemoval"` (celles-ci sont donc non seulement déconseillées, mais on annonce explicitement leur disparition à venir : cf. [JEP draft: Remove the Terminally Deprecated Applet API](https://openjdk.org/jeps/8345525)). Chaque nœud d'une branche de modifications `{json}"deltas"` (élément de la liste) renseigne sur un niveau d'élément du langage Java, que cet élément ait été modifié ou non. Ici, le package `{java}java.applet` a le statut `{json}"notmodified"`, mais apparaît donc puisque sa clé `{json}"deltas"` contient certains de ses membres qui l'ont été. > [!Note] A noter : certains outils comme le navigateur Firefox permettent une visualisation arborescente des documents JSON dans laquelle il est facile de se déplacer et de faire des recherches. Dans le cadre du travail de l'équipe, les éléments du langage qui sont retenus sont : `{java}package`, `{java}class`, `{java}interface`, `{java}record`, `{java}enum`, `{java}method` et `{java}field`. Les statuts retenus sont : `{json}"modified"` et `{json}"removed"`. Pour `{json}"modified"`, toutes les étiquettes (_tags_) ajoutées (`{json}"addedTags"`) ou retirées (`{json}"removedTags"`) sont considérées utiles. On trouve notamment pour valeurs d'étiquettes : `{json}"final"`, `{json}"abstract"`, `{json}"deprecated"`, `{json}"forRemoval"`, `{json}"extends SomeType"`, `{json}"implements SomeType"`, `{json}"sealed"`, `{json}"throws ExceptionType"`. # Outil de récupération des données brutes Vous allez développer un outil permettant d'extraire des fichiers de données des informations décrivant les modifications entre deux versions de l'API qui intéressent l'équipe. On demande à ce que les fichiers source au format JSON soient accessibles localement. On préfère éviter un téléchargement à la demande pour pallier de possibles indisponibilités des fichiers en ligne. Compte tenu de la faible combinatoire, on souhaite développer une méthode qui permettra de télécharger localement les fichiers bruts pour toutes les paires pertinentes de versions LTS (on pensera à ne pas télécharger à nouveau les données immuables d'un fichier en cas de téléchargements incrémentaux à l'occasion de la publication d'une nouvelle version LTS). Proposez un outil en Java permettant d'effectuer cela. On vous demande pour cela d'avoir recours aux possibilités de transfert efficace (notamment sans passage par une mémoire tampon de l'application) entre _Channels_ de l'[API NIO](https://blogs.oracle.com/javamagazine/post/java-nio-nio2-buffers-channels-async-future-callback) (_Non-blocking IO_). Les méthodes suivantes seront utilisées : - [`{java}Channels.newChannel(InputStream)`](https://docs.oracle.com/en/java/javase/21/docs//api/java.base/java/nio/channels/Channels.html#newChannel(java.io.InputStream)) - [`{java}getChannel()`](https://docs.oracle.com/en/java/javase/21/docs//api/java.base/java/io/FileOutputStream.html#getChannel()) de `{java}FileOutputStream` - [`{java}transferFrom(ReadableByteChannel, long, long)`](https://docs.oracle.com/en/java/javase/21/docs//api/java.base/java/nio/channels/FileChannel.html#transferFrom(java.nio.channels.ReadableByteChannel,long,long)) de `{java}FileChannel` ```java title:"votre code pour l'outil de téléchargement" // votre code ici ``` ^TP3OutilTelechargementNEPASEDITER En réalisant ce travail, vous aurez peut-être remarqué que [`{java}getChannel()`](https://docs.oracle.com/en/java/javase/21/docs//api/java.base/java/io/FileOutputStream.html#getChannel()) retourne une classe abstraite ([`{java}FileChannel`](https://docs.oracle.com/en/java/javase/21/docs//api/java.base/java/nio/channels/FileChannel.html)), et qu'utiliser une variable de son type vous a permis d'invoquer la méthode [`{java}transferFrom(ReadableByteChannel, long, long)`](https://docs.oracle.com/en/java/javase/21/docs//api/java.base/java/nio/channels/FileChannel.html#transferFrom(java.nio.channels.ReadableByteChannel,long,long)), abstraite pour ce type. >[!Question] Qu'est-ce que cela signifie pour le type _effectif _ de retour de `{java}getChannel()` ? Quel est ce type pour votre configuration ? >votre réponse ici ^TP3TypeEffectifgetChannelNEPASEDITER # Outil de préparation des données ## Description de la structure de données cible Une fois les fichiers accessibles localement, il s'agit d'en extraire une structure de données adaptée aux besoins de l'équipe. > [!Note] Il existe bien des manières d'interroger directement des fichiers JSON, mais on suppose des besoins ultérieurs qui auront à être traités par l'application d'aide à la migration. On vise à obtenir une liste d'objets d'un type tel que `{java}AlteredLanguageElement`, qui contiendra l'ensemble des éléments ayant été modifiés ou supprimés. Ces éléments ne devront pas pouvoir être modifiés une fois créés. Chaque élément permettra de connaître son type, et contiendra les informations retenues pour décrire ses modifications. Un élément modifié fera référence à son élément _parent_ (ex. possiblement un **package** pour une **classe**, une **interface** pour une **méthode**, etc.) sous sa forme non modifiée (ex. type `{java}LanguageElement`). En effet, un élément modifié peut avoir un parent qui lui ne l'a pas été (ex. un champ est modifié mais pas directement la classe qui le contient). Les éléments non modifiés auront eux aussi un type et des informations pertinentes pour leur type, et une référence à leur élément parent si pertinent. Dans le résultat final, les éléments non modifiés (`{java}LanguageElement`) ne seront donc accessibles qu'au travers de la chaîne de rattachement (élément vers son parent). On rappelle que certains éléments du langage Java peuvent avoir plusieurs types de parents (ex. une **méthode** peut être dans une **classe**, une **interface**, un **record**, un **enum**; une classe peut être dans un **package**, une **classe**, etc.). Les élément du langage qui seront retenus seront (cela ne dit rien sur votre possible hiérarchie de types) : **package**, **classe**, **interface**, **record**, **enum**, **method** et **field**. ```java title:"votre code pour les types proposés" // votre code ici ``` ^TP3OutilPreparationTypesNEPASEDITER ## Construction de la structure de données cible On vous demander de faire vos traitements sur une représentation de l'arbre JSON en mémoire. Pour cela, vous utiliserez la librairie [Jackson databind](https://github.com/FasterXML/jackson-databind), l'une des librairies les plus utilisées pour la manipulation du format JSON en Java. Votre code devra récursivement parcourir les listes associées aux clés `{json}"deltas"` pour accumuler une liste d'éléments des types retenus ayant été modifiés ou supprimés (`{java}AlteredLanguageElement`). Liens utiles : - [documentation vers l'API de Jackson core](https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/latest/index.html) - [documentation vers l'API de Jackson databind](https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/latest/index.html) - la [classe ObjectMapper](https://javadoc.io/static/com.fasterxml.jackson.core/jackson-databind/2.18.2/com/fasterxml/jackson/databind/ObjectMapper.html) permet de convertir du JSON sous forme texte en objet de type [JsonNode](https://javadoc.io/static/com.fasterxml.jackson.core/jackson-databind/2.18.2/com/fasterxml/jackson/databind/JsonNode.html), sur lequel des parcours et des traitements sont possibles On vous demande tout d'abord de vous assurer que le fichier en cours de traitement correspond bien aux versions souhaitées, informations contenues dans les objets associés aux clés `{json}"base"` et `{json}"target"`. ```java title:"votre code pour la vérification des versions origine et destination" // votre code ici ``` ^TP3OutilPreparationCheckVersionsNEPASEDITER >[!Question] Pourquoi réaliser une telle vérification peut, ou non, avoir un intérêt ? >votre réponse ici ^TP3JsonInteretVerificationsNEPASEDITER >[!Question] Est-il facile d'exploiter un document de type JSON sans connaissance de sa grammaire particulière (nature des éléments, structure) ? De quels types d'informations aurait-on besoin pour faciliter ce travail ? Et comment doit se comporter un programme en cas de non conformité (par exemple, absence d'un élément attendu, présence d'éléments inconnus) ? Comment se comporterait votre programme en particulier ? >votre réponse ici ^TP3JsonGrammaireNEPASEDITER >[!Question] Que permettent de faire les [schémas JSON](https://json-schema.org/), et de quels types de problèmes pourraient-il protéger dans le cadre du traitement de ces données ? (pensez, pour votre réponse, que la source pourrait être moins fiable que celle utilisée ici, et/ou que le format de données pourrait connaître des évolutions, etc.) >votre réponse ici ^TP3JsonSchemasNEPASEDITER Une fois les versions validées, le fichier peut être parcouru (si on ne rencontre pas certains problèmes, cf. questions ci-dessus) pour la construction de la liste de résultats attendus. Pour cette partie, un recours aux **streams** n'est pas attendu. Pensez à vérifier certains résultats en les confrontant à [la source](https://javaalmanac.io/). ```java title:"votre code pour la construction de la liste de résultat et des tests" // votre code ici ``` ^TP3OutilPreparationConstructionListeNEPASEDITER >[!Question] A titre d'exemple, donnez le nombre complet de résultats extraits pour les différences retenues entre Java 8 et Java 21. >votre réponse ici ^TP3JsonSchemasNEPASEDITER # Outil de construction de tickets Vous disposez à présents d'objets Java permettant de décrire des éléments modifiés des API standard entre deux versions LTS du langage. Cela permet, par exemple, de confronter une _base de code_, développée avec la version d'origine du langage, pour connaître l'ensemble des éléments utilisés dans la base de code qui auraient été supprimés ou modifiés. La construction de la liste des éléments de langage utilisés dans une base de code a déjà été effectuée par ailleurs, ainsi que la sélection des éléments modifiés correspondant (éléments dans l'intersection). Pour simplifier, on va considérer ici que _tous_ les éléments extraits précédemment sont retenus, et qu'ils demanderont donc un travail spécifique par la suite. L'équipe souhaite créer des **tickets** qui seront ensuite affectés à des personnes pour traitement. Chaque **ticket** correspondra à un élément modifié, qui devra être associé à un niveau de criticité parmi : "critique", "modérée", "faible" et "indéterminée". Afin de contribuer à établir une notion de **criticité** par ticket, on vous demande de réaliser différents traitements sur la liste construite précédemment (celle qui contient vos `{java}AlteredLanguageElement`), en utilisant l'API **Streams**. - afficher tous les éléments avec une étiquette ajoutée `deprecated` ```java title:"votre code pour l'affichage des éléments avec une étiquette ajoutée 'deprecated'" // votre code ici ``` ^TP3StreamsAllDeprecatedTagNEPASEDITER - afficher toutes les classes avec une étiquette ajoutée `deprecated` ```java title:"votre code pour l'affichage des classes avec une étiquette ajoutée 'deprecated'" // votre code ici ``` ^TP3StreamsClassesDeprecatedTagNEPASEDITER >[!Question] En passant : vous semble-t-il possible que l'étiquette `deprecated` puisse de trouver parmi les étiquettes enlevées (`removedTags`) d'un élément, et pourquoi ?. >votre réponse ici ^TP3DeprecatedRemovedTagsNEPASEDITER - construire une liste de toutes les méthodes qui ont été supprimées ```java title:"votre code pour construire une liste de toutes les méthodes qui ont été supprimées" // votre code ici ``` ^TP3MethodesSupprimeesNEPASEDITER - trouver si une méthode d'un certain nom (quelle que soit sa liste de paramètres) a été modifiée et en retourner la liste ```java title:"votre code pour construire une liste de toutes les méthodes portant un certain nom donné en paramètre (quelles que soient leur liste de paramètres)" // votre code ici ``` ^TP3MethodeNommeesNEPASEDITER On vous demande de créer un type pour les **tickets**, qui contiendra notamment les champs : - une classe de **criticité** (d'un type interne à votre type : `{java}enum Criticity { CRITICAL, MODERATE, WEAK, INDERTERMINATE };`) - un objet de description du ticket (tel qu'un `{java}AlteredLanguageElement`, mais pas uniquement) - le nom (optionnel) d'une personne à qui le ticket aurait été affecté ainsi que : - une méthode statique qui retourne une classe de criticité pour un élément de langage modifié, étant donnée une fonction transmise en paramètre (ex. `{java}Function`) qui permettrait de calculer un score sur 100. Lors de l'appel, il serait donc possible de fournir directement une expression lambda pour ce paramètre, donc le rôle serait de décrire comment se calcule le score de criticité d'un élément particulier. ```java title:"votre code pour votre type Ticket" // votre code ici ``` ^TP3TicketNEPASEDITER On vous demande finalement d'utiliser les **streams** pour : créer un nouveau ticket pour chaque `{java}AlteredLanguageElement`, en calculant préalablement son niveau de criticité, puis en regroupant les tickets dans une _map_ par niveau de criticité (en obtenant donc un objet de type `{java}Map>`). Pour la fonction lambda affectant un score sur 100 à un `{java}AlteredLanguageElement`... limitez-vous ici d'un simple tirage aléatoire. ```java title:"votre code pour la construction de la map de tickets organisés par niveau de criticité" // votre code ici ``` ^TP3ConstructionMapTicketsParCriticiteNEPASEDITER On vous demande après réflexion de faire une variante du travail précédent, où les tickets sont toujours organisés par niveau de criticité, mais également par type d'élément de langage modifié (donc une _map_ `{java}Map>>`), donc en utilisant deux niveaux de regroupement. ```java title:"votre code pour la construction de la map de tickets organisés par niveau de criticité" // votre code ici ``` ^TP3ConstructionMapTicketsParCriticiteTypeNEPASEDITER