Développons en Java 2.30 | |
Copyright (C) 1999-2022 Jean-Michel DOUDOUX | (date de publication : 15/06/2022) |
|
Niveau : | Fondamental |
La fonctionnalité majeure de Java 9 est le système de modules de la plate-forme Java (Java Platform Module System ou JPMS). JPMS modularise les bibliothèques de classes fournies par le JDK. JPMS peut aussi être utilisé par les développeurs pour modulariser les applications ou les bibliothèques. Cela permet aux développeurs de décomposer leurs applications en modules. Ces modules peuvent ensuite spécifier les autres modules dont ils ont besoin et les packages qu'ils exposent pour être utilisés par d'autres modules.
Le système de modules de la plateforme Java (JPMS) est issu des travaux du projet Jigsaw.
Les principaux objectifs de l'introduction de la modularisation dans la plate-forme Java sont :
L'écosystème Java dispose déjà de plusieurs systèmes de modules notamment OSGi et JBoss Modules. L'approche de ces systèmes existants est différente : ils ont été créés et utilisés pour des cas d'utilisation concrets et réels. Le but de JPMS est d'être globalement une extension de la JVM pour permettre un support des modules.
JPMS impacte de nombreux éléments de la plateforme Java : les bibliothèques, le langage, la JVM et les outils.
Le système de modules de la plateforme Java intègre :
JPMS ajoute le concept de modules à Java qui est un nouvel élément structurant :
L'utilisation du système de modules impliquent plusieurs choses :
L'utilisation de modules permet :
Un module peut être packagé sous la forme d'un jar modulaire qui est un fichier jar qui contient un fichier module-info.class à sa racine.
Le fichier module-info.class est issu de la compilation d'un fichier particulier nommé module-info.java. Ce fichier est le descripteur du module : il définit entre autres le nom du module, les packages dont les classes publiques peuvent être accédées par d'autres modules, les module requis (les dépendances), les services fournis ou consommés, ...
Pour utiliser un jar modulaire comme module, celui-ci doit être ajouté dans le modulepath et non plus dans le classpath. L'avantage que cela soit toujours un jar est qu'est toujours possible d'utiliser un jar dans le classpath. Dans ce cas, le fichier sera utilisé comme un fichier jar classique et non pas comme un module même s'il contient un fichier module-info.class qui sera simplement ignoré dans ce cas. Dans cette situation, tous les packages sont accessibles puisque le fichier est chargé via le classpath.
La modularité est une fonctionnalité essentielle de la plateforme Java pour lui permettre de futures évolutions.
Ce chapitre contient plusieurs sections :
La modularité est un principe de conception qui permet :
Dans le domaine des logiciels, la modularité consiste à l'écriture et à la mise en oeuvre d'un programme ou d'un système informatique sous la forme d'un certain nombre de modules, plutôt que sous la forme d'une conception monolithique. Cela revient à implémenter une application ou un système sous la forme d'un ensemble de modules.
La mise en oeuvre de modules encourage à utiliser des bonnes pratiques de conception telles que l'encapsulation et la séparation des préoccupations (separation of concerns).
Des interfaces sont utilisées pour permettre aux modules de communiquer. L'utilisation de modules permet de réduire le couplage et de faciliter le développement d'applications en réduisant la complexité du système.
D'une manière générale dans un langage de programmation, un module est un artéfact qui contient du code et des méta données (description du module, relation avec les autres modules, ...)
Un module devrait avoir certaines caractéristiques :
Un module devrait mettre en oeuvre trois principes :
Les plateformes Java antérieures à la version 9 possèdent plusieurs inconvénients lors du développement et du déploiement d'applications :
Le système de modules de Java tente d'apporter certaines solutions à ces problématiques.
Au fur et à mesure des versions de Java, la taille de son runtime n'a fait que croître. Par exemple, l'API Core de Java 8 contient 217 packages.
Cette taille sans cesse croissante de la plate-forme Java SE rend son utilisation de plus en plus difficile sur les petits appareils malgré le fait que de nombreux appareils de ce type soient capables d'exécuter une machine virtuelle Java. Cela augmente aussi la taille des livrables, ce qui peut poser des problèmes lors de déploiements sur de nombreux noeuds d'un cluster dans le cloud.
Avant Java 8, il n'y aucun moyen de définir et utiliser un sous-ensemble du runtime. Ainsi chaque runtime inclut toutes les bibliothèques telles que AWT, Swing, XML ou même Corba même si l'application n'en a pas besoin.
Java 8 a introduit la notion de compact profiles (JSR 337) qui définit trois sous-ensembles des API du runtime. Le fait de n'avoir que trois ensembles rend la solution rigide et ne permet donc pas personnaliser les API requises dans le runtime.
L'API Java Core a donc besoin d'être modularisée.
Le modèle de packaging reposant sur les fichiers de type JAR présente plusieurs inconvénients :
Ces inconvénients conduisent à différentes problématiques :
Il existe trois classpath :
Chacun est utilisé par un ClassLoader dédié. Toutes les classes requises sont chargées par un ClassLoader. Les ClassLoader proposés en standard dans la JVM appliquent le principe de délégation pour demander à son ClassLoader parent de tenter de charger la classe demandée.
Ce mécanisme de délégation améliore la sécurité (par exemple pour permettre de charger la classe java.lang.String du fichier rt.jar et pas d'un jar de l'application). Par contre, ce mécanisme de délégation ralentit le temps de chargement d'une classe. C'est d'autant plus important qu'une application peut charger des centaines voire des milliers de classes.
Un de ces ClassLoader peut charger des classes indiquées dans le classpath. Par défaut, toutes classes de l'application sont chargées par le même ClassLoader à partir des composants précisés dans le classpath. Il est possible d'utiliser d'autres ClassLoader ce qui rend le mécanisme de chargement de classes encore plus complexe.
Le classpath ne permet pas d'exprimer les relations entre les composants. Par conséquent, si un composant nécessaire est manquant, il ne sera pas détecté jusqu'à ce qu'il soit tenté de l'utiliser. Le classpath permet également de charger des classes d'un même package à partir de différents composants, ce qui entraîne un comportement imprévisible et des erreurs difficiles à diagnostiquer.
Comme il n'y aucune information sur les dépendances requises et que les classes sont chargées uniquement lorsque la JVM en a besoin, l'application peut démarrer et des soucis peuvent survenir en cours d'exécution.
La plateforme Java ne propose aucune fonctionnalité pour déterminer si les bons fichiers JAR sont disponibles. Le compilateur et la JVM repose sur le mécanisme du classpath. Ce comportement n'est pas fiable et conduit parfois à des comportements inattendus.
Le mécanisme de classpath, introduit depuis Java 1.0, implique plusieurs soucis :
Ces problématiques liées à l'utilisation du classpath sont connues sous le nom de Classpath Hell ou JAR Hell.
Le terme JAR Hell ou Classpath Hell désigne des problèmes induits par le mécanisme de chargement des classes issues du classpath. Ce mécanisme de chargement des classes de la JVM via le classpath est à l'origine de plusieurs problèmes.
Le mécanisme de classpath est utilisé lors de étapes de compilation et d'exécution. Il y a donc deux classpath :
Il est possible que ces deux classpath soit différents puisqu'ils doivent explicitement être définis pour le compilateur à la compilation et pour la JVM à l'exécution. Il est ainsi possible de la compilation réussisse car tous les éléments requis sont dans le classpath mais qu'un élément soit absent du classpath de la JVM à l'exécution et lève une exception de type NoClassDefFoundError ou ClassNotFoundException.
Bien sûr à la compilation, si une dépendance est absente, le compilateur émet une erreur et le code n'est pas compilé.
Le code compilé ne signifie pas que tout va bien se passer à l'exécution. Comme la JVM possède son propre classpath, il est possible d'avoir des erreurs à l'exécution si la classe est absente ou si la version de la classe est différente de celle utilisée à la compilation.
Il est possible que le classpath contienne plusieurs versions différentes d'un même jar : le classLoader charge alors la première version de la classe trouvée, ce qui peut provoquer des comportements erratiques.
Il est possible d'avoir plusieurs fois la même classe (possédant le même nom pleinement qualifié) dans différents éléments du classpath. C'est fréquent notamment lorsque le classpath contient plusieurs versions différentes d'une même bibliothèque à cause des dépendances transitives par exemple.
Le mécanisme de chargement de classes à partir du classpath charge la première classe trouvée, rendant impossible le chargement d'une autre version de la classe contenue dans le classpath (le terme anglais pour désigner ce comportement est Shadowing). Il n'y a aucune spécification sur l'ordre de chargement de classes donc aucune garantie sur la version de la classe qui sera chargée même si généralement les JVM scannent les JAR dans l'ordre dans lequel ils sont fournis dans le classpath. Cela peut ne pas être le cas sur toutes les JVM ni dans le cas où le caractère joker * ou des répertoires sont utilisés dans le classpath.
Cette problématique courante avec le classpath survient lorsque deux jars ont une dépendance vers deux versions différentes d'un autre jar. Il est ainsi possible qu'une même classe soient dans plusieurs jars inclus dans le classpath puisque plusieurs versions d'un jar sont dans le classpath.
Si les deux versions du jar sont ajoutées dans le classpath alors le comportement risque de ne pas être celui attendu. Une même classe ne peut être chargée qu'une seule fois par un même ClassLoader : c'est la première trouvée par le ClassLoader qui sera utilisée. La seconde version de la classe présente dans le classpath ne sera pas chargée. Dans le meilleur des cas, la version la plus récente est compatible avec la version précédente et il suffit simplement d'ajouter uniquement la version la plus récente dans le classpath. Ceci n'est vrai que s'il y a une compatibilité ascendante, ce qui n'est pas toujours le cas.
La version de la classe chargée n'est donc pas prédictible de manière fiable. Si les différences entre les versions d'une même classe ne concernent que le comportement, cela peut engendrer des bugs aléatoires difficilement identifiables induisant des comportements inattendus. La détection de ces problèmes n'est pas toujours facile.
Certains mécanismes liés au classpath ralentissent les performances notamment pour :
Depuis la première version de Java, une application exécutée dans une JVM ne connait pas ses dépendances. Un JAR ne permet pas de définir quels sont les autres JAR dont il dépend ce qui permettrait à la JVM de s'assurer sur tous les composants nécessaires sont présents dans le classpath.
Le problème se complexifie encore avec les dépendances transitives : ce sont les dépendances requises par une dépendance. Ces dépendances de dépendances peuvent elles-mêmes requérirent d'autres dépendances et ainsi de suite. Les dépendances transitives rendent la gestion des dépendances encore plus complexe et augmente donc les possibilités d'erreurs. C'est notamment le cas lors de la discordance de versions entre des jar dépendants.
Il est donc nécessaire de s'assurer manuellement que tous les jars utiles et nécessaire sont dans le classpath. C'est aussi le cas pour les dépendances optionnelles : ce sont des jars dont les classes ne sont nécessaires que selon l'utilisation de certaines fonctionnalités.
Des outils de build externes au JDK peuvent aider à gérer ces dépendances comme par exemple Maven de la fondation Apache. Cela ne résout toujours pas tous les problèmes : il est possible qu'il manque des composants dans le classpath.
En l'absence de configuration, il est courant d'avoir des NoClassDefFoundError au runtime, et ce bien après le démarrage de l'application. Ceci est dû au fait que les classes ne sont chargées que lorsque la JVM en a besoin et qu'il est possible que des dépendances soient manquantes.
La JVM ne peut détecter l'absence d'une classe dans le classpath qu'au moment où elle est requise et doit être chargée. Si la classe ne peut être chargée par la JVM car elle n'est pas trouvée dans le classpath, elle lève une exception de type NoClassDefFoundError ou ClassNotFoundException en cas de tentative de chargement par introspection (méthode forName() de la classe Class ou loadClass() et findSystemClass() de la classe ClassLoader).
Il n'est pas possible d'avoir des éléments visibles, par exemple uniquement par ceux du fichier JAR qui les contiennent, ou en dehors de celui-ci. Le mécanisme de contrôle d'accès du langage de programmation Java et de la machine virtuelle Java ne permet à aucun composant d'empêcher d'autres composants d'accéder à ses packages.
Historiquement, l'encapsulation est uniquement proposée au travers des modificateurs de visibilité. Les modificateurs de visibilité de Java peuvent être utilisés pour implémenter l'encapsulation entre les classes d'un même package. Mais entre plusieurs packages, il n'y a que la visibilité publique qui soit utilisable.
Toutes les classes publiques sont accessibles par toutes les autres du classpath. Cela limite les possibilités d'encapsulation notamment au niveau du JAR.
En conséquence de nombreuses classes de l'API Core de Java sont public mais ne devrait pas être utilisée. Mais comme elles sont publiques, il est possible de les utiliser directement ou indirectement si c'est une dépendance qui les utilise. C'est notamment le cas des classes des packages sun.misc, jdk.internal, ... La plus connues d'entre-elles est la classe sun.misc.Unsafe. Malgré son nom explicite, elle est largement utilisée notamment par des frameworks couramment utilisés.
Les API internes du JDK, principalement dans les packages sun.* sont encapsulées dans des modules qui ne les exportent pas : ces API ne sont donc plus utilisables.
La vocation de ces API n'était pas d'être utilisable en dehors du JDK mais comme elles étaient public et proposaient des fonctionnalités puissantes, elles ont été largement utilisées par des bibliothèques.
Certaines de ces API ont été remplacées et certaines sont encore accessibles pour le moment mais elles devront être retirées à terme.
Pour permettre une utilisation d'une classe par une classe d'un autre package, celle-ci doit être public. De fait, n'importe qu'elle autre classe du classpath peut l'utiliser : cela peut induire des problèmes de sécurité lié au fait que du code malicieux puisse invoquer des fonctionnalités critiques.
Pour compenser cela, Java 1.1 a introduit le SecurityManager dont le rôle est de vérifier si l'utilisation d'une fonctionnalité critique est autorisée ou non. Bien sûre pour que cela fonctionne, il est nécessaire que le SecurityManager soit invoqué dans le code pour vérifier l'autorisation de l'invocation d'une fonctionnalité.
De plus, en utilisant l'API Introspection, il est facile d'accéder à des membres privés même pour les modifier dans le cas de propriétés.
Un système de modules Java doit faciliter la séparation du code en modules distinct, respectant une encapsulation stricte et faiblement couplés. Les dépendances doivent être clairement spécifiées et strictement appliquées.
Ainsi, JPMS permet de structurer le code de manière modulaire en proposant une encapsulation forte et une configuration plus fiable, tout en permettant un couplage faible entre modules.
Le système de modules est utilisé dans le JDK lui-même et peut être utilisé dans les applications.
JPMS impacte de manière profond la plateforme, les outils, les applications et l'ensemble de l'écosystème Java. JPMS est probablement la première fonctionnalité introduite en Java qui a un impact sur toute la stack : JVM, Java Core, outils (compilateur, packaging, build, ...), bibliothèques, applications, ...
En plus des améliorations pour la plate-forme elle-même, le système de modules change complètement la façon dont les applications Java vont être structurées. L'un des objectifs est de renforcer l'encapsulation : avec les modules, il est possible de définir précisément les éléments qui seront utilisables pour les autres modules. Et pour la première fois, du code Java aura la possibilité de connaître ses dépendances et d'avoir une configuration plus fiable. Ensemble, ces fonctionnalités tentent de trouver partiellement une solution au Classpath Hell.
JPMS améliore la sécurité et facilite la maintenance des applications et des bibliothèques grâce à l'encapsulation forte et une meilleure fiabilité de la configuration.
JPMS change de manière importante comment les applications, notamment les grosses, sont développées et surtout déployées.
Les principaux objectifs de la modularisation proposée par JPMS sont :
Tous ces objectifs n'ont pas une importance équivalente pour le JDK et pour les applications.
Chaque module est identifié par un nom défini dans un descripteur de module. Ce descripteur de module doit aussi contenir la liste des modules requis en tant que dépendance. Cette configuration pour être utilisée à différentes étapes :
Ces différentes étapes pourront alors échouer si une dépendance est manquante ou si une anomalie est détectée, et ce dès leurs premières étapes.
Un des intérêts des modules est de permettre de renforcer l'encapsulation. Par défaut, aucune classe d'un module n'est accessible en dehors du module même si la classe est public. Pour permettre un accès à d'autres modules, il est nécessaire d'exporter le ou les packages concernés. Tous les autres packages non exportés ne sont accessibles uniquement que par le module lui-même.
Une fois un package exporté, les règles de visibilité d'une classe s'applique comme depuis toujours en Java.
Un module ajoute donc en plus de la visibilité un niveau d'accessibilité.
Le sens du modificateur public change dans un module. Par défaut, une classe publique dans un module n'est accessible que par les autres classes du module. Pour que cette classe publique puisse être utilisé dans d'autre module, il fait obligatoire exporter le package qui contient cette classe. Toutes les classes publiques d'un package exporté sont alors potentiellement accessibles par d'autres modules qui déclareront le module en dépendance.
Ainsi pour permettre l'utilisation d'une classe d'un module par un autre module, trois contraintes doivent être respectées :
La mise en oeuvre de ces trois règles permet un accès à une classe par un autre module.
Le fait de ne pas permettre par défaut un accès aux classes publiques d'un package permet de renforcer l'encapsulation. Il est par exemple possible de ne pas exposer des packages qui contiennent une implémentation et d'exposer les packages des interfaces.
L'encapsulation forte des modules va renforcer la sécurité et la maintenabilité :
La modularisation a été appliquée à Java Core : l'historique fichier rt.jar a été découpé en un certain nombre de modules. La maintenance d'éléments plus petits est plus simple que la maintenance d'un élément monolithique.
Les modules facilitent aussi l'ajout ou la suppression de fonctionnalités dans Java Core.
Avec la modularisation, il est possible de créer son propre JRE personnalisé composé uniquement des modules dont une application a besoin. Cela contribue à renforcer la plate-forme Java comme une solution pour des applications sur petits périphériques ou dans des conteneurs.
Le système de modules a été développé dans le projet Jigsaw http://jdk9.java.net/jigsaw
La JSR 376 (Java Platform Module system) spécifie les évolutions dans le langage Java, la JVM et l'API Java pour la mise en oeuvre du système de modules dans la plateforme Java.
La mise en oeuvre du système de modules repose sur plusieurs JEP.
Le rôle du projet Jigsaw est de permettre la mise en oeuvre d'un système de modules pour la plateforme Java nommé Java Platform Module System (JPMS).
Le but initial était de rendre la plateforme Java SE modulaire puis a été étendu pour permettre d'utiliser ce système pour rendre modulaire le JRE mais aussi les applications Java.
Les principaux buts du projet Jigsaw sont de définir un système standard de modules pour la plateforme Java et le langage Java afin de permettre :
Pour atteindre ces objectifs, le projet a conçu et implémenté un système de modules qui a été appliqué à la plateforme et au JDK au travers de différentes actions :
L'intégration du projet Jigsaw a été initialement prévue pour Java 7, repoussée à Java 8, pour finalement être décalée à nouveau pour finalement être intégrée à Java 9. La première mouture du système de module spécifié par la JSR 376 et implémenté par la JEP 261 a été intégré dans le build 111 du JDK 9 en mars 2016.
Le système de modules a été développé au travers de plusieurs JEP :
JEP |
Rôle |
Implémenter le Java Platform Module System (JPMS) tel que spécifié dans la JSR 376 |
|
Modulariser la plateforme Java tel que précisé dans la JSR 376 et implémenté dans la JEP 261 : définit la structure modulaire du JDK |
|
Décomposer le JDK et le JRE en images pour chaque module Définir un nouveau format des URI pour nommer les modules, les classes et les ressources de manière indépendante du format de l'image Suppression des fichiers rt.jar et tools.jar du JRE Suppression des mécanismes endorsed et extension |
|
Encapsuler la plupart des API à usage interne du JDK sauf celles qui sont largement utilisés et ne possèdent pas encore de solution de remplacement |
|
JEP 201: Modular Source Code |
Réorganiser le code source du JDK pour le rendre modulaire |
JEP 282: jlink the Java Linker |
Fournir un outil capable de créer un JRE personnalisé pour une application Créer un outil qui permet d'assembler et optimiser un ensemble de modules et leurs dépendances afin d'obtenir une image (jlink) |
A partir de Java 9, l'API Core de Java est obligatoirement modulaire. Le JDK lui-même a été modularisé pour être composé d'un peu moins d'une centaine de modules désignés comme étant des platform modules.
Il est possible d'obtenir le liste complète des platform modules en utilisant l'option --list-modules de la JVM
Résultat : |
$ java --list-modules | wc -l
98
|
Le système de modules fait la distinction entre les modules standard et les modules non standard. Les modules standard ont leurs spécifications gérées par la plateforme Java et leurs noms de modules commencent par "java.".
Les modules non standard ne devraient donc pas avoir leur nom commençant par "java.".
À la racine de l'arborescence des modules se trouve le module java.base, qui contient les classes essentielles notamment celles des packages java.lang, java.io, java.math, java.net, java.nio, java.security, java.text, java.time, java.util, ...
Les classes dans le classpath ont accès à l'intégralité des classes contenues dans les modules du JDK pour des raisons évidentes de compatibilité.
Les modules dans le module path doivent explicitement déclarer leur dépendance vers les modules requis y compris ceux du JDK.
La structure du JDK a été impactée :
La structure de sous-répertoires du JDK contient un répertoire nommé jmods. Ce répertoire contient des fichiers avec l'extension .jmod, un pour chaque module. Un fichier jmod contient des classes regroupées en packages, des ressources et des bibliothèques natives.
Le fichier src.zip contient toujours les sources de l'API Core de Java : il est dans le sous-répertoire lib du JDK. Il contient un sous-répertoire pour chaque module du JDK.
Plusieurs modules de Java 9 sont dépréciés avec l'attribut forRemoval=true :
Ces modules sont fournis dans Java 9 mais ne sont pas accessibles par défaut. Il faut explicitement les ajoutées en tant que module racine du graphe de modules. Tous ces modules sont retirés dans Java 11.
Les modules de la plateforme sont repartis en plusieurs groupes. Le groupe sert de préfixe dans le nom de chaque module :
Tous les modules de la plateforme ou applicatifs dépendent implicitement du module java.base. Le module java.base contient les classes et API de base (utils, collections, IO, concurrency, ...)
Le module java.base est le module de base : c'est un module particulier qui ne dépend d'aucun autre module. Tous les autres modules dépendent implicitement du module java.base. Le module java.base expose de nombreux packages notamment :
Des modules dédiés existent pour les autres fonctionnalités logging, desktop, xml, sql, naming, corba, ...
Il est possible d'utiliser l'option --list-modules de la JVM pour obtenir la liste des modules.
Résultat : |
$ java --list-modules
java.activation@9.0.1
java.base@9.0.1
java.compiler@9.0.1
java.corba@9.0.1
java.datatransfer@9.0.1
java.desktop@9.0.1
...
|
Le nom de chaque module se termine par un @ suivi du numéro de version du module.
Les modules du JDK varient en fonction de la version de Java utilisée soit parce que des fonctionnalités sont ajoutées ou retirées :
Module |
Versions de Java |
|||||||||
9 | <²p>10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
|
java.activation |
* |
* |
|
|
|
|
|
|
|
|
java.base |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.compiler |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.corba |
* |
* |
|
|
|
|
|
|
|
|
java.datatransfer |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.desktop |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.instrument |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.jnlp |
* |
* |
|
|
|
|
|
|
|
|
java.logging |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.management |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.management.rmi |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.naming |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.net.http |
|
|
* |
* |
* |
* |
* |
* |
* |
* |
java.prefs |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.rmi |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.scripting |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.se |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.se.ee |
* |
* |
|
|
|
|
|
|
|
|
java.security.jgss |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.security.sasl |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.smartcardio |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.sql |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.sql.rowset |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.transaction |
* |
* |
|
|
|
|
|
|
|
|
java.transaction.xa |
|
|
* |
* |
* |
* |
* |
* |
* |
* |
java.xml |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.xml.bind |
* |
* |
|
|
|
|
|
|
|
|
java.xml.crypto |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
java.xml.ws |
* |
* |
|
|
|
|
|
|
|
|
java.xml.ws.annotation |
* |
* |
|
|
|
|
|
|
|
|
javafx.base |
* |
* |
|
|
|
|
|
|
|
|
javafx.controls |
* |
* |
|
|
|
|
|
|
|
|
javafx.deploy |
* |
* |
|
|
|
|
|
|
|
|
javafx.fxml |
* |
* |
|
|
|
|
|
|
|
|
javafx.graphics |
* |
* |
|
|
|
|
|
|
|
|
javafx.media |
* |
* |
|
|
|
|
|
|
|
|
javafx.swing |
* |
* |
|
|
|
|
|
|
|
|
javafx.web |
* |
* |
|
|
|
|
|
|
|
|
jdk.accessibility |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.aot |
|
* |
* |
* |
* |
* |
* |
* |
|
|
jdk.attach |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.charsets |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.compiler |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.crypto.cryptoki |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.crypto.ec |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.crypto.mscapi |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.deploy |
* |
* |
|
|
|
|
|
|
|
|
jdk.deploy.controlpanel |
* |
* |
|
|
|
|
|
|
|
|
jdk.dynalink |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.editpad |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.hotspot.agent |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.httpserver |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.incubator.foreign |
|
|
|
|
|
|
* |
* |
* |
* |
jdk.incubator.jpackage |
|
|
|
|
|
|
* |
|
|
|
jdk.incubator.vector |
|
|
|
|
|
|
|
* |
* |
* |
jdk.incubator.httpclient |
* |
* |
|
|
|
|
|
|
|
|
jdk.internal.ed |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.internal.jvmstat |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.internal.le |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.internal.opt |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.internal.vm.ci |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.internal.vm.compiler |
|
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.internal.vm.compiler.management |
|
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jartool |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.javadoc |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.javaws |
* |
* |
|
|
|
|
|
|
|
|
jdk.jcmd |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jconsole |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jdeps |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jdi |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jdwp.agent |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jfr |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jlink |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jpackage |
|
|
|
|
|
|
|
* |
* |
* |
jdk.jshell |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jsobject |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.jstatd |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.localedata |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.management |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.management.agent |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.management.cmm |
* |
* |
|
|
|
|
|
|
|
|
jdk.management.jfr |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.management.resource |
* |
* |
|
|
|
|
|
|
|
|
jdk.naming.dns |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.naming.rmi |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.net |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.nio.mapmode |
|
|
|
|
|
* |
* |
* |
* |
* |
jdk.pack |
* |
* |
* |
* |
* |
|
|
|
|
|
jdk.packager |
* |
* |
|
|
|
|
|
|
|
|
jdk.packager.services |
* |
* |
|
|
|
|
|
|
|
|
jdk.plugin |
* |
* |
|
|
|
|
|
|
|
|
jdk.plugin.dom |
* |
|
|
|
|
|
|
|
|
|
jdk.plugin.server |
* |
* |
|
|
|
|
|
|
|
|
jdk.policytool |
* |
|
|
|
|
|
|
|
|
|
jdk.rmic |
* |
* |
* |
* |
* |
* |
|
|
|
|
jdk.random |
|
|
|
|
|
|
|
|
* |
* |
jdk.scripting.nashorn |
* |
* |
* |
* |
* |
* |
|
|
|
|
jdk.scripting.nashorn.shell |
* |
* |
* |
* |
* |
* |
|
|
|
|
jdk.sctp |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.security.auth |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.security.jgss |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.snmp |
* |
* |
|
|
|
|
|
|
|
|
jdk.unsupported |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.unsupported.desktop |
|
|
* |
* |
* |
* |
* |
* |
* |
* |
jdk.xml.bind |
* |
* |
|
|
|
|
|
|
|
|
jdk.xml.dom |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
jdk.xml.ws |
* |
* |
|
|
|
|
|
|
|
|
jdk.zipfs |
* |
* |
* |
* |
* |
* |
* |
* |
* |
* |
oracle.desktop |
* |
* |
|
|
|
|
|
|
|
|
oracle.net |
* |
* |
|
|
|
|
|
|
|
|
|