Thématiques principales

jeudi 16 novembre 2017

Evolution en Java 9

Nous voici arrivé sur notre dernier article sur les évolutions de Java. Nous avons vu les évolutions depuis la version 5 avec les articles suivant:
Ces évolutions ont été variées, certaines syntaxiques (les boucles, les lambda expressions par exemple) et d'autres conceptuelles (le generiques, les streams, etc...). Certaines de ces évolutions ont ete majeures, d'autres moins. Grâce a cela, en plus de ses atout initiaux, Java s'est imposé avec toutes ces années comme un langage de programmation incontournable au même titre qu'un C++.

Aujourd'hui nous en sommes a la version 9 de java. Sortie en septembre 2017, celle nouvelle version propose une évolution importante : les modules.

Avec cette évolution, sur laquelle nous reviendrons par la suite, Java marque le besoin de revenir a une dimension résonné du chargement en mémoire at runtime. Ceci parlera forcement au adepte de la technologie OSGI qui existe elle depuis déjà une bonne dizaines d'années (on peut même se demander pourquoi une convergence des deux techno n'a pas être proposé....)

Voyons donc les évolutions de cette version 9.

Des méthodes privées dans les interfaces

Une nouvelle fonctionnalité de Java 9 est de permettre la définition de méthode privée dans les interfaces permettant de simplifier l’écriture et la maintenabilité des méthodes par défaut des interfaces

L'API Collections et Streams

Les collections et les streams vont benefier de nouvelles methodes et entre autre la possibilité de construire des listes immutables sans devoir les initialisés laborieusement.


1
2
3
4
5
List<String> maList = new ArrayList<>();
maList.add("Toto");
maList.add("Tata");
maList.add("Tutu");
maList = Collections.unmodifiableList(maList);


On ecrira:


1
maList = List.of("Toto", "Tata", "Tutu");

Multi-Release JAR

Il est desormais possible de packager dans un meme jar, differentes version d'une meme classe afin de garantir la compatibilité descendante.

JShell

Java 9 propose maintenant une shell permettant comme pour python ou groovy d’interpréter du code java en suivant le modèle REPL (Read, Evaluate, Print, Loop) afin de faciliter l'apprentissage (accessoirement) et le prototypage.

Reactive Streams

L'API reactive stream est issu de la collaboration d'un certain nombre d'acteur tels que Kaazing, Netflix, Pivotal, Red Hat, Twitter, Typesafe. Cette API se base sur les principes de la programmation réactive decliné selon 4 axes principaux.

  • Responsive : Le système doit répondre aussi vite que possible
  • Resilient : Le système doit être capable d’être réactif même face a des erreurs. On y répond par la réplication, l'isolation, la délégation et la mise en conteneur (typiquement les technologies microservice, docker, kubernetes)
  • Elastic : Le système doit être capable de monter en charger rapidement, de s'adapter aux nouvelles demandes. Cela passe par la vitesse l'allocation des ressources, sa capacité à se répliquer pour absorber de la charge
  • Message-driven : Fonctionne sur un système de communication interne de messages asynchrones pour le support des flux de données et de contrôle (par exemple integration de Spring et apache Camel)

La programmation réactive est guidée par l'utilisation des messages. Cela nécessite de traiter les erreurs comme des messages. De même, les traitements doivent être gérer comme des flux, par bloc et non dans une globalité de façon a ne pas avoir de situation bloquante. ( On peut prendre facilement comme exemple un système réactif comme nodeJS)

A coté de l'API ReactiveStream existe bien sur d'autres API pour l'utilisation des concepts décrits ci dessus. On notera par exemple RxJava, Vert.x, RatPack, Akka Streams ou encore Reactor. Certains sont polyglottes et pourront faire l'affaire d'articles dédiés

Dans le concret, l'API Reactive Stream (java.util.concurrent.Flow) est basé sur le pattern observateur. A ceci prêt que le Sujet est appelé Publisher, l'observateur, Subscriber. Le pattern observateur n'interdit pas qu'un observateur soit lui même observé, ici on ne préconise pas de le faire, au contraire et on utilisera plutôt le Processor pour réaliser des relais entre Publisher et Subscriber.

Exemple:

ConcretPublisher:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class ConcretPublisher implements Publisher<String>,Consumer<String>{

    public Subscriber<String> arg0;

    @Override
    public void accept(String arg0) {
        this.arg0.onNext(arg0);
        
    }

    @Override
    public void subscribe(Subscriber<? super String> arg0) {
        this.arg0=(Subscriber<String>) arg0;
        
    }
}


ConcretProcessor


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class ConcretProcessor implements Processor<String,Integer>{

    private Subscriber<? super Integer> arg0;
    
    @Override
    public void onNext(String arg0) {
        System.out.println("On passe la main apres avoir string to int");
        this.arg0.onNext(Integer.parseInt(arg0));
    }

    @Override
    public void subscribe(Subscriber<? super Integer> arg0) {
        this.arg0=arg0;
        
    }

}


ConcretSubscriber


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class ConcretSubscriber implements Subscriber<Integer>{

    @Override
    public void onComplete() {
        System.out.println("ConcretSubscriber Complete");
        
    }

    @Override
    public void onError(Throwable arg0) {
        arg0.printStackTrace();
        
    }

    @Override
    public void onNext(Integer arg0) {
        System.out.println("Vla ma valeur "+arg0);
        
    }

}


Main


1
2
3
4
5
6
7
8
9
ConcretPublisher concretPublisher= new ConcretPublisher();
ConcretSubscriber concretSubscriber=new ConcretSubscriber();
ConcretProcessor concretProcessor=new ConcretProcessor();

concretPublisher.subscribe(concretProcessor);
concretProcessor.subscribe(concretSubscriber);

List<String> items = List.of("1", "2", "3", "4", "5", "6");
items.forEach(concretPublisher);


Nous donne:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
On passe la main apres avoir string to int
Vla ma valeur 1
On passe la main apres avoir string to int
Vla ma valeur 2
On passe la main apres avoir string to int
Vla ma valeur 3
On passe la main apres avoir string to int
Vla ma valeur 4
On passe la main apres avoir string to int
Vla ma valeur 5
On passe la main apres avoir string to int
Vla ma valeur 6


Un concept supplémentaire est le Subscription qui permet au Subscriber de gérer son abonnement auprès du Publisher. L'article ci et ci, illustre parfaitement les détails relevant de ce point de l'API.

Modularité

La modularité dans Java 9 a été introduite sous le nom de projet Jigsaw. L'idée du projet est d'ajouter la notion de module entre la notion de jar et de package. Ceux qui sont familiarisé avec OSGI, devrait vite comprendre l’intérêt de la chose: permettre d'exposer ou de limiter l’accès a certains packages d'un Jar et de définir quels sont les dépendances nécessaires au bon fonctionnement de ce même jar (dépendance a résoudre au démarrage de la JVM).

Les objectifs de ce projet sont d’améliorer:
  • les performance applicatives en permettant de limiter le chargement des classes et packages a sont plus stricts minimum.
  • la sécurité (je vous renvoie a l'article en référence pour les détails) 
  • la maintenabilité en fournissant un outil de gestion de la visibilité du contenu des modules et donc la liste des packages accessibles.
In action, ça dit quoi?

Reprenons l'exemple précédemment utilisé pour l'API ReactiveStreams. Avec la modularité, nous allons maintenant trouver un nouveau type de fichier nommé: module-info.java ainsi qu'un nouveau path: le module path (en remplacement/complément du classpath, nous verrons pourquoi)

Plaçons donc les classes métiers dans un projet séparé de la classe Main, l'utilisant. Les deux projets définiront donc deux modules différents.

Nous aurons donc un module main déclarant l'utilisation d'un autre modules (la librairie):


1
2
3
4
module proj9.main
{
    requires proj9.react;
}


et le module lib déclarant les packages exposés par le module (par défaut ils sont privés) :


1
2
3
4
module proj9.react
{
    exports projet9.react;
}


Dans la théorie ce n'est pas très compliqué. Sauf pour le cas de l'utilisation de la réflexivité ou il faudra employé le mot clef open (Je vous laisse consulter l'article pour plus d’informations).

A noter que désormais, avec le fichier module, nous construirons donc des fichiers jar "module" ou comme il serait possible d'en trouver dans le répertoire jmods du jdk, des fichiers jmod.

Je ne rentre pas dans les détails de transitivité des dépendances que vous pourrez trouver dans cet article. Mais sachez qu'il est possible d’accéder a des packages par la simple déclaration dans un module intermédiaire qui aurait utiliser le mot clef public après le require.

Attention, d'autres subtilités existent. Je ne vais pas entrer dans leur détails mais sachez qu'il faudra composer avec des notions de module non-nommé (contenant des classes et packages accessible de façon classique par le classpath, en fait des modules sans module-info.java) et enfin les modules automatiques qui sont la conversion d'un jar classique en module avec la capacité de lire dans tout type de module (automatique et non-nommé) et d'exporter tous leurs packages.

Enfin dernière particularité, afin de toujours pouvoir utiliser les ServiceLoader (bien que par défaut, Java 9 va quand même réussir a les trouver), vous pouvez les déclarer dans le module a l'aide du mot clef uses suivi du nom complet de la classe.

Je vous invite a lire cet article très complet sur la modularité dans Java 9.

Autres

  • Java 9 intègre également des amélioration sur l'interface avec les processus de l'OS permettant par exemple d'en récupérer le PID. 
  • UN moteur de rendu 2D a été mis a jour vers un moteur plus performant Marlin. L'annotation @Deprecated a été améliorer en permettant de préciser la version a partir de laquelle celle ci n'est plus conseillé et également si l’élément est prévu d’être supprimé.
  • Un vrai client http 2
  • Une javadoc compatible HTML5
  • Changement de fonctionnement du Garbage Collector (G1 a la place de Parrallel GC)
  • De nouveaux utilitaires
  • La simplification du debugage en production 
  • Une réorganisation profondes des répertoires du JDK/JRE
  • Des améliorations en terme de performance sur la manipulation des chaines de caractères ne nécessitant plus l'utilisation de StringBuilder pour les concaténations de chaines.


Maintenant il va falloir se poser la question avec ces modifications car la modularité fait peur comme le laisse penser les commentaires de cet article.

Alors comment allons nous migrer vers Java 9 ? Comment va se mettre a jour tout l'eco-systeme gravitant autour de Java (outil, frameworks, etc...)?
Différents exemples existent comme pour :
 Maintenant l'avenir nous dira si c'est aussi simple que cela.

Aucun commentaire:

Enregistrer un commentaire