Modularité dans Java 9: ​​empiler avec Project Jigsaw, Penrose et OSGi

Cet article donne un aperçu des propositions, spécifications et plates-formes visant à rendre la technologie Java plus modulaire en Java 9. Je vais discuter des facteurs contribuant au besoin d'une architecture Java plus modulaire, décrire brièvement et comparer les solutions qui ont été proposées, et présenter les trois mises à jour de modularité prévues pour Java 9, y compris leur impact potentiel sur le développement Java.

Pourquoi avons-nous besoin de la modularité Java?

La modularité est un concept général. Dans les logiciels, il s'applique à l'écriture et à la mise en œuvre d'un programme ou d'un système informatique sous la forme d'un certain nombre de modules uniques, plutôt que sous la forme d'une conception monolithique unique. Une interface standardisée est alors utilisée pour permettre aux modules de communiquer. Le partitionnement d'un environnement de constructions logicielles en modules distincts nous aide à minimiser le couplage, à optimiser le développement d'applications et à réduire la complexité du système.

La modularité permet aux programmeurs de tester les fonctionnalités de manière isolée et de s'engager dans des efforts de développement parallèles au cours d'un sprint ou d'un projet donné. Cela augmente l'efficacité tout au long du cycle de vie du développement logiciel.

Certains attributs caractéristiques d'un module authentique sont:

  • Une unité de déploiement autonome (couplage lâche)
  • Une identité cohérente et unique (ID du module et version)
  • Exigences et dépendances facilement identifiées et découvertes (installations de compilation et de déploiement standard et méta-informations)
  • Une interface ouverte et compréhensible (contrat de communication)
  • Détails d'implémentation cachés (encapsulation)

Les systèmes conçus pour traiter efficacement les modules doivent effectuer les opérations suivantes:

  • Prise en charge de la modularité et de la découverte des dépendances au moment de la compilation
  • Exécutez des modules dans un environnement d'exécution qui prend en charge un déploiement et un redéploiement faciles sans temps d'arrêt du système
  • Mettre en œuvre un cycle de vie d'exécution clair et robuste
  • Fournir des installations pour un registre et une découverte faciles des modules

Les solutions orientées objet, orientées composants et orientées services ont toutes tenté de permettre une modularité pure. Chaque solution a son propre ensemble de bizarreries qui l'empêchent cependant d'atteindre la perfection modulaire. Examinons brièvement.

Classes et objets Java en tant que constructions modulaires

La nature orientée objet de Java ne répond-elle pas aux exigences de modularité? Après tout, la programmation orientée objet avec Java accentue et impose parfois l'unicité, l'encapsulation des données et le couplage lâche. Bien que ces points soient un bon début, notez les exigences de modularité qui ne sont pas satisfaites par le cadre orienté objet de Java: l'identité au niveau de l'objet n'est pas fiable; les interfaces ne sont pas versionnées: et les classes ne sont pas uniques au niveau du déploiement. Un couplage lâche est une bonne pratique, mais certainement pas appliquée.

La réutilisation des classes en Java est difficile lorsque les dépendances tierces sont si facilement utilisées à mauvais escient. Les outils de compilation tels que Maven cherchent à combler cette lacune. Les conventions de langage et les constructions après coup telles que l'injection de dépendances et l'inversion de contrôle aident les développeurs dans nos tentatives de contrôler l'environnement d'exécution, et parfois elles réussissent, surtout si elles sont utilisées avec une discipline stricte. Malheureusement, cette situation laisse la corvée de créer un environnement modulaire jusqu'aux conventions et configurations de cadre propriétaires.

Java ajoute également des espaces de noms de package et une visibilité de la portée au mix afin de créer des mécanismes modulaires de compilation et de déploiement. Mais ces fonctionnalités de langage sont facilement évitées, comme je vais l'expliquer.

Les packages comme solution modulaire

Les packages tentent d'ajouter un niveau d'abstraction au paysage de programmation Java. Ils fournissent des fonctionnalités pour des espaces de noms de codage uniques et des contextes de configuration. Malheureusement, cependant, les conventions de package sont facilement contournées, conduisant fréquemment à un environnement de couplages dangereux au moment de la compilation.

L'état de modularité actuel de Java (mis à part OSGi, dont je parlerai sous peu) est le plus souvent réalisé à l'aide d'espaces de noms de package, de conventions JavaBeans et de configurations de framework propriétaires comme celles trouvées dans Spring.

Les fichiers JAR ne sont-ils pas assez modulaires?

Les fichiers JAR et l'environnement de déploiement dans lequel ils fonctionnent améliorent considérablement les nombreuses conventions de déploiement héritées autrement disponibles. Mais les fichiers JAR n'ont pas d'unicité intrinsèque, à part un numéro de version rarement utilisé, qui est caché dans un manifeste .jar. Le fichier JAR et le manifeste facultatif ne sont pas utilisés comme conventions de modularité dans l'environnement d'exécution Java. Ainsi, les noms de package des classes dans le fichier et leur participation à un chemin de classe sont les seules parties de la structure JAR qui confèrent de la modularité à l'environnement d'exécution.

En bref, les JAR sont une bonne tentative de modularisation, mais ils ne remplissent pas toutes les exigences d'un environnement véritablement modulaire. Les cadres et plates-formes comme Spring et OSGi utilisent des modèles et des améliorations de la spécification JAR pour fournir des environnements permettant de créer des systèmes très performants et modulaires. Au fil du temps, cependant, même ces outils succomberont à un effet secondaire très malheureux de l'enfer JAR de spécification JAR!

Classpath / JAR enfer

Lorsque l'environnement d'exécution Java autorise des mécanismes de chargement JAR arbitrairement complexes, les développeurs savent qu'ils se trouvent dans l' enfer des chemins de classe ou dans l' enfer JAR . Un certain nombre de configurations peuvent conduire à cette condition.

Tout d'abord, considérons une situation dans laquelle un développeur d'application Java fournit une version mise à jour de l'application et l'a empaquetée dans un fichier JAR portant exactement le même nom que l'ancienne version. L'environnement d'exécution Java ne fournit aucune fonctionnalité de validation pour déterminer le fichier JAR correct. L'environnement d'exécution chargera simplement les classes à partir du fichier JAR qu'il trouve en premier ou qui satisfait l'une des nombreuses règles de chemin de classe. Cela conduit au mieux à un comportement inattendu.

Une autre instance de l'enfer JAR survient lorsque deux ou plusieurs applications ou processus dépendent de versions différentes d'une bibliothèque tierce. En utilisant les fonctions de chargement de classe standard, une seule version de la bibliothèque tierce sera disponible au moment de l'exécution, ce qui entraînera des erreurs dans au moins une application ou un processus.

Un système de modules Java complet et efficace devrait faciliter la séparation du code en modules distincts, faciles à comprendre et faiblement couplés. Les dépendances doivent être clairement spécifiées et strictement appliquées. Des installations devraient être disponibles pour permettre la mise à niveau des modules sans avoir un effet négatif sur les autres modules. Un environnement d'exécution modulaire doit permettre des configurations spécifiques à un domaine particulier ou à un marché vertical, réduisant ainsi le temps de démarrage et l'empreinte système de l'environnement.

Solutions de modularité pour Java

Outre les fonctionnalités de modularité mentionnées jusqu'à présent, les efforts récents en ajoutent quelques-uns. Les fonctionnalités suivantes sont destinées à optimiser les performances et à permettre l'extension de l'environnement d'exécution:

  • Code source segmenté : code source séparé en segments distincts et mis en cache, chacun contenant un type spécifique de code compilé. Les objectifs incluent l'ignorance du code non-méthode pendant les balayages de mémoire, les builds incrémentiels et une meilleure gestion de la mémoire.
  • Enforcements au moment de la construction: constructions de langage pour appliquer les espaces de noms, le contrôle de version, les dépendances et autres.
  • Installations de déploiement : prise en charge du déploiement d'environnements d'exécution évolutifs en fonction de besoins spécifiques, tels que ceux d'un environnement d'appareil mobile.

Un certain nombre de spécifications et de cadres de modularité ont cherché à faciliter ces fonctionnalités, et quelques-uns ont récemment atteint le sommet des propositions pour Java 9. Un aperçu des propositions de modularité Java est ci-dessous.

JSR (demande de spécification Java) 277

Actuellement inactif est Java Specification Request (JSR) 277, le Java Module System; introduite par Sun en juin 2005. Cette spécification couvrait la plupart des mêmes domaines que OSGi. Comme OSGi, JSR 277 définit également la découverte, le chargement et la cohérence des modules, avec un support limité pour les modifications d'exécution et / ou la vérification d'intégrité.

Les inconvénients du JSR 277 incluent:

  • Pas de chargement et déchargement dynamiques des modules / bundles
  • Aucun contrôle d'exécution pour l'unicité de l'espace de classe

OSGi (Initiative Open Service Gateway)

Introduite par l'Alliance OSGI en novembre 1998, la plate-forme OSGI est la réponse de modularité la plus largement utilisée à la question standard formelle pour Java. Actuellement à la version 6, la spécification OSGi est largement acceptée et utilisée, en particulier ces derniers temps.

Par essence, OSGi est un système modulaire et une plate-forme de service pour le langage de programmation Java qui implémente un modèle de composant complet et dynamique sous la forme de modules, de services, de bundles déployables, etc.

Les couches principales de l'architecture OSGI sont les suivantes:

  • Environnement d'exécution : environnement Java (par exemple, Java EE ou Java SE) sous lequel un bundle sera exécuté.
  • Module : Où le cadre OSGi traite les aspects modulaires d'un bundle. Les métadonnées du bundle sont traitées ici.
  • Cycle de vie : l'initialisation, le démarrage et l'arrêt des bundles se produisent ici.
  • Registre de services : où les offres groupées répertorient leurs services pour que d'autres offres les découvrent.

L'un des plus gros inconvénients d'OSGi est son absence de mécanisme formel pour l'installation native des packages.

JSR 291

JSR 291 est un framework de composants dynamiques pour Java SE basé sur OSGi, est actuellement en phase finale de développement. Cet effort se concentre sur l'intégration d'OSGi à Java grand public, comme cela a été fait pour l'environnement mobile Java par JSR 232.

JSR 294

Le JSR 294 définit un système de méta-modules et délègue la réalisation réelle des modules enfichables (versions, dépendances, restrictions, etc.) à des fournisseurs externes. Cette spécification introduit des extensions de langage, telles que des «superpackages» et des modules liés hiérarchiquement, pour faciliter la modularité. Une encapsulation stricte et des unités de compilation distinctes font également partie de l'objectif de la spécification. Le JSR 294 est actuellement inactif.

Projet Jigsaw

Project Jigsaw est le candidat le plus probable pour la modularité de Java 9. Jigsaw cherche à utiliser des constructions de langage et des configurations d'environnement pour définir un système de modules évolutif pour Java SE. Les principaux objectifs de Jigsaw comprennent:

  • Rendre très facile la mise à l'échelle du runtime Java SE et du JDK vers de petits appareils.
  • Améliorer la sécurité de Java SE et du JDK en interdisant l'accès aux API JDK internes et en appliquant et en améliorant la SecurityManager.checkPackageAccessméthode.
  • Amélioration des performances des applications grâce à l'optimisation du code existant et à la facilitation des techniques d'optimisation des programmes d'anticipation.
  • Simplification du développement d'applications dans Java SE en permettant la construction de bibliothèques et d'applications à partir de modules fournis par les développeurs et à partir d'un JDK modulaire
  • Exiger et appliquer un ensemble fini de contraintes de version

JEP (proposition d'amélioration Java) 200

La proposition d'amélioration Java 200 créée en juillet 2014, cherche à définir une structure modulaire pour le JDK. JEP 200 s'appuie sur le framework Jigsaw pour faciliter la segmentation du JDK, selon les profils compacts Java 8, en ensembles de modules qui peuvent être combinés au moment de la compilation, de la construction et du déploiement. Ces combinaisons de modules peuvent ensuite être déployées sous la forme d'environnements d'exécution à échelle réduite composés de modules compatibles Jigsaw.

JEP 201

JEP 201 cherche à s'appuyer sur Jigsaw pour réorganiser le code source JDK en modules. Ces modules peuvent ensuite être compilés en tant qu'unités distinctes par un système de construction amélioré qui applique les limites des modules. JEP 201 propose un schéma de restructuration du code source dans tout le JDK qui met l'accent sur les limites des modules au niveau supérieur des arborescences de code source.

Penrose

Penrose gérerait l'interopérabilité entre Jigsaw et OSGi. Plus précisément, cela faciliterait la possibilité de modifier les micro-noyaux OSGi afin que les bundles s'exécutant dans le noyau modifié utilisent des modules Jigsaw. Il repose sur l'utilisation de JSON pour décrire les modules.

Plans pour Java 9

Java 9 est une version majeure unique pour Java. Ce qui le rend unique, c'est son introduction de composants modulaires et de segments dans tout le JDK . Les principales fonctionnalités prenant en charge la modularisation sont:

  • Code source modulaire : dans Java 9, le JRE et le JDK seront réorganisés en modules interopérables. Cela permettra la création d'exécutions évolutives pouvant être exécutées sur de petits appareils.
  • Cache de code segmenté : Bien que n'étant pas strictement modulaire, le nouveau cache de code segmenté de Java 9 suivra l'esprit de la modularisation et bénéficiera de certains des mêmes avantages. Le nouveau cache de code prendra des décisions intelligentes pour compiler les segments de code fréquemment consultés en code natif et les stocker pour une recherche optimisée et une exécution future. Le tas sera également segmenté en 3 unités distinctes: code non-méthode qui sera stocké en permanence dans le cache; code qui a un cycle de vie potentiellement long (connu sous le nom de «code non profilé»); et du code transitoire (appelé «code profilé»).
  • Application au moment de la construction : le système de construction sera amélioré, via JEP 201, pour compiler et appliquer les limites des modules.
  • Facilités de déploiement : des outils seront fournis dans le projet Jigsaw qui prendront en charge les limites, les contraintes et les dépendances des modules au moment du déploiement.

Version d'accès anticipé de Java 9

Bien que la date de sortie exacte de Java 9 reste un mystère, vous pouvez télécharger une version à accès anticipé sur Java.net.

En conclusion

Cet article a présenté un aperçu de la modularité au sein de la plate-forme Java, y compris les perspectives de modularité dans Java 9. J'ai expliqué comment des problèmes de longue date tels que l'enfer des chemins de classe contribuent au besoin d'une architecture Java plus modulaire et discuté de certaines des nouvelles modularités les plus récentes fonctionnalités proposées pour Java. J'ai ensuite décrit et contextualisé chacune des propositions ou plates-formes de modularité Java, y compris OSGi et Project Jigsaw.

Le besoin d'une architecture Java plus modulaire est clair. Les tentatives actuelles ont échoué, bien qu'OSGi soit très proche. Pour la version Java 9, Project Jigsaw et OSGi seront les principaux acteurs de l'espace modulaire pour Java, Penrose fournissant éventuellement le lien entre eux.

Cette histoire, «Modularité dans Java 9: ​​empiler avec Project Jigsaw, Penrose et OSGi» a été initialement publiée par JavaWorld.