Rétrocompatibilité JDK9

2

La prochaine version du JDK est annoncée pour le 22/09/2016, nous y trouverons entre autres plusieurs nouvelles fonctionnalités et outils comme jShell, le REPL Java 9, JMH une solution de microbenchmark, le nouveau client HTTP/2.

Nous nous intéressons ici aux problèmes de compatibilités avec les versions antérieures.

Alan Bateman a fait une présentation “Prepare for JDK9” (disponible ici) sur les nouveautés du prochain JDK et le projet Jigsaw à la conférence JavaOne, San Francisco. Alan a travaillé sur le JDK depuis 12 ans comme développeur puis spec lead.

Oracle a dans cette version introduit une notion de module. Il est maintenant possible de définir un module et de rendre accessible uniquement les packages voulus. Une classe public ne sera donc accessible depuis un autre module que si son package a été exporté dans son module. Le but étant de différencier les packages internes des packages publiques.

Le JDK a donc été modularisé afin d’encapsuler les API internes. Le graphe de dépendances suivant est l’aboutissement d’un lourd travail de réorganisation interne. (Un historique est disponible ici).

Copyright © 2015, Oracle and/or its affiliates. All rights reserved.

Le travail réalisé dans la JEP 260 vise à rendre autant que possible inaccessibles les API internes du JDK. Cela comprend l’ensemble du package sun.* mais également les packages spécifiques com.sun.* et jdk.*. Ces deux derniers sont partiellement internes et l’annotation @jdk.Exported permet d’identifier, depuis le JDK 8,  les classes dont l’utilisation externe est supportée.

Le JDK8 a également introduit un outil peu connu, jdeps, permettant de lister les dépendances par package d’un JAR Java afin d’identifier les utilisations des API JDK internes. (voici un exemple sur un jar mockito).

jdeps-mockito

La question se posait donc de l’encapsulation du controversé sun.misc.Unsafe
Cette classe avec plus de 140 méthodes permet beaucoup d’opérations bas niveau.
Elle est utilisée notamment par les frameworks suivants:

  • Netty
  • Hazelcast
  • Cassandra
  • Scala Specs
  • Apache Spark
  • Apache Kafka
  • Apache Wink
  • Apache Storm
  • Apache Flink
  • Byte Buddy
  • Hibernate
  • Liquibase
  • Spring Framework 
  • Ehcache (sizeof)
  • Apache Hadoop
  • Apache HBase
  • Mockito
  • EasyMock
  •  JMock
  •  PowerMock
  • XRebel
  • XStream
  • Akka
  • Disruptor
  • JRuby
  • Scala
  • Grails
  • Gson
  • Neo4j
  • GWT

Restreindre l’accès à Unsafe a un impact considérable sur l’écosystème Java et rendrait cette neuvième version l’une des plus lourdes à gérer en terme de compatibilité. Un document a été créé pour sensibiliser le groupe de travail sur la JEP 260 à ce problème (disponible ici).

Deux catégories d’API internes sont alors identifiées à partir de données récoltées sur les dépôts Maven:

  • Les non-critiques : les classes non utilisées en dehors du JDK ou utilisées par pure commodité comme   sun.misc.BASE64Decoder  (qui est top 1 des plus utilisées du JDK)
  • Les critiques : impossible ou presque à implémenter hors du JDK, comme le fameux Unsafe.

La solution actuellement gardée est donc d’encapsuler les API non critiques, d’encapsuler les API critiques avec un équivalent en Java SE 8 standard ou spécifique au JDK mais annotée @jdk.Exported.
Et finalement ne pas encapsuler les autres API critiques, les rendre deprecated dans le JDK 9 et potentiellement les encapsuler / supprimer dans le JDK 10 en apportant un moyen de contournement.

Autres changements pouvant affecter la compatibilité:

  • La remise à plat de la structure de dossiers du JDK et suppression des jar rt.jar et tools.jar

arbo-jdk7image04Copyright © 2015, Oracle and/or its affiliates. All rights reserved.

  • Les ClassLoader ne sont plus des instances de java.net.URLClassLoader, il faut veiller aux casts.
  • Suppression des flags -Xbootclasspath et -Xbootclasspath/p.
  • Suppression de la propriété système  “sun.boot.class.path
  • Suppression des méthodes deprecated suivantes bloquant la modularisation :
    • java.util.logging.LogManager::addPropertyChangeListener
    • java.util.logging.LogManager::removePropertyChangeListener
    • java.util.jar.Pack200.Packer::addPropertyChangeListener
    • java.util.jar.Pack200.Packer::removePropertyChangeListener
    • java.util.jar.Pack200.Unpacker::addPropertyChangeListener
    • java.util.jar.Pack200.Unpacker::removePropertyChangeListener
  • Le versioning du JDK JEP 223,  va être modifié (cela n’a pas encore été intégré à la release snapshot courante) suivant le pattern $MAJOR.$MINOR.$SECURITY
    Par exemple  1.7.0_55-b13   devient   7.5.14+13.

Il faudra donc vérifier que l’utilisation des propriétés système suivantes reste cohérente.

java.version
java.runtime.version
java.vm.version
java.specification.version
java.vm.specification.version

L’utilisation de l’underscore comme unique caractère de nommage est proscrite.

underscore-unique-identifierEn résumé :

  • Vérifier l’utilisation des API internes au JDK à l‘aide de Jdeps.
  • Vérifier le code sensible au changement de version.
  • Vérifier l’utilisation des propriétés système.
  • Vérifier l’utilisation de l’underscore pour le nommage.
Partagez cet article.

A propos de l'auteur

Consultant Zenika, passionné de craftmanship et par les technologies JAVA.

2 commentaires

Ajouter un commentaire