Java 9, tu modules ?

Rémi ?

Vous connaissez Rémi ? Il est maître de conférences à Marne la Vallée, développeur d’ASM (j’ignorais ça), d’OpenJDK, expert pour le JCP (et clairement spécialiste des évolutions du langage, pas la partie JavaEE) où il a ajouté invokedynamic, et a travaillé sur les lambda, Jigsaw.

Java 9

Java 9 est la dernière grosse release, parce que c’est pénible. Ca va être remplacé par une release tous les six mois …​ c’est un peu plus rapide. Et donc, dans six mois, on verra var apparaître dans le langage Java.

Les modules

Historiquement, ça devait arriver en Java 6 ou 7 …​ Ca a pris un peu de retard.

Qui veut modulariser le JDK ?

Il y a d’une part des développeurs du JDK qui veulent le faire évoluer, mais ne peuvent pas à cause de toutes les APIs qui utilisent des classes « cachées » du JDK. Et d’autre part …​ des experts de sécurité qui considèrent les failles de la plateforme.

D’autres problèmes

L’enfer du CLASSPATH

Sans outil pour gérer automatiquement le CLASSPATH (genre maven, gradle et autres), c’est l’enfer. Par ailleurs, quand le ClassLoader charge une classe, il scanne linéairement tous les jars et charge la première classe qu’il trouve. Du coup, c’est long. Mais aussi super merdique quand on a deux versions du même artefact. Du coup, on aimerait bien que ce soit géré par la JVM. De la même manière, éviter que les NoClassDefFoundError ne sortent qu’à l’exécution serait une bonne idée pour les développeurs.

Alléger Java

Ce serait sympa de pouvoir faire tourner le code Java sur des plateformes plus légères (typiquement pour l’IoT). Personellement, ça meparaît vaguement possible avec des outils comme ProGuard de limiter la taille des dépendances, mais ce serait mieux d’avoir ça directement dans le JDK. Par exemple, rt.jar contient encore des parseurs XML (qui n’ont pas à être des classes privilégiées) ou CORBA (qui s’en sert encore ?).

Comment modulariser ?

standards

Aucun rapport avec Java, bien sûr

Il y a déja des standards de modules en Java

  • maven
  • gradle
  • JBoss
  • OSGi
  • JavaEE

Du coup, Jigsaw doit fournir un système de modules compatible avec tous ces systèmes prééexistants. Ce qui implique tristement qu’il ne peuvent pas contenir de numéros de version. Et ça, c’est moche.

Modularisons donc !

C’est quoi un module ?

Dans un module, il y a

  • des dépendances
  • et des classes qu’on veut garder invisibles aux autres packages

Par exemple, java se est constitué d’un paquet de modules …​ dont java.compiler. Ce qui signifie qu’il n’y a plus de distinctions JRE/JDK.

Tout ça est décrit dans un module-info.java qui va être compilé comme le reste du code. Ca empêche les modifications ultérieures, ce qui est bien.
A la compilation, il ne peut donc plus y avoir de « split-packages » (des packages déclarés dans deux JARS), ou de dépendances cycliques.

Qu’est-ce qu’on perd ?
  • les « split-package »
  • setAccessible(true) sur une classe d’un module fermé

Au passage, ça permettra enfin à Rémi d’optimiser les champs final (un truc dont j’étais sûr qu’il existait déja pourtant)

Enfin, sauf si on active le flag --illegal-access=permit (qui est parfaitement clair). Notez qu’il y a d’autres valeurs possibles (warn, debug). Bon, un jour, il disparaîtra.

En bonus, les classes JavaEE cachés dans JavaSE (java.xml.bind, java.xml.ws et autres) ne seront plus dans le JDK. Notez que ces classes sont toujours accessibles via des dépendances Maven.
Les classes sun. et com.sun. qui ne sont pas utilisées sont également supprimées.

A noter : L’annotation @ForRemovall (je ne suis pas sûr de l’orthographe) indique les classes qui seront supprimées dans la prochaine version du JDK. C’est une version étendue du classique @Deprecated.

Comment trouver les dépendances

Avec jdeps, par exemple, on peut facilement trouver toutes les dépendances d’un module. Notez que cet outil existe déja dans Java8.

Oui, mais comment je transforme mon application pour Java9 ?

Pour ça, Rémi a développé son application de test : ModuleTools qui lui permet de lire et d’écrire un fichier module-info.java ou module-info.class.Et dans son application, il déclare quelques modules, qui dépendent tous de java.base (comme les classes Java dépendent de java.lang.Object).

Contrairement à maven, un require de module n’est pas transitif (contrairement à maven). Du coup, il y a un require transitif, mais qui est limité à un niveau.

En terme d’organisation de fichiers, il est possible d’avoir plusieurs organisations, dont par exemple plusieurs modules dans le même dossier (ce qui n’est supporté ni par maven, ni par gradle, ni par les IDE).

Il est possible de stocker la version du JAR dans le module-info, et de la réutiliser depuis le code, mais le système de modules n’utilise pas la version.

Comment utiliser un JAR non modulaire ?

Typiquement, une dépendance de Maven Central.On ne peut évidement pas ajouter simplement le JAR dans le CLASSPATH. Il suffit en fait de mettre le jar dans le module-path, et Java va générer automatiquement un module-info, qui permet en plus à ce module automatique de dépendre de JARs du CLASSPATH.Et comment s’appelle ce module ? Soit le nom fourni dans le MANIFEST.MF comme Automatic-Module-Name, soit le nom du JAR sans le numéro de version. Du coup, en ce moment, sur GitHub, c’est la fête à l’Automatic-Module-Name. Au passage, il est possible d’utiliser les JARs modulaires comme des JARs classiques, en les mettant dans le CLASSPATH !

Comment limiter la visibilité ?

Si on veut faire un module interne, il est possible de limiter sa visibilité à certains modules spécifiques, et pas au monde entier. C’est très chouette pour mieux gérer la sécurité. C’est ce qu’ont fait les développeurs du JDK pour la tristement célèbre sun.misc.Unsafe dont une partie a été déplacée dans jdk.unsupported, et une autre partie dans un jdk.internal.misc. Du coup, la réflexion n’est plus possible sur les champs privés (dommage pour gaedo). C’aurait été pénible pour JPA …​ mais les fournisseurs d’implémentation JPA utilisent des agents qui changent le code avant son exécution. Cela dit, la réflexion reste possible grâce à open sur un package ou sur le module.Par ailleurs, le très moche ServiceLoader a été étendu pour fonctionner avec les modules …​ La différence, c’est que la description est un peu propre et intégrée au module-info. Et la description à la fois des fournisseurs d’implémentation et de l’utilisation de ces implémentations doit être fournie. C’est super cool, parce que ça permet de tester les injections de dépendances.

Packager ?

Avec jlink, on peut créer une image à la volée de l’application et de ses dépendances. Pratique pour l’embarqué, évidement, mais aussi pour Docker. Hélas, il ne peut pas y avoir de modules automatiques, parce qu’avec ces modules automatiques, il faut embarquer tout le JDK, et ça limite en plus énormément les possibilités d’optimisation de jlink. C’est un peu la carotte de Jigsaw. Ca permet aussi d’utiliser une JVM minimale de 3 Mo !. Bon, sans JIT, mais sur un Raspberry, c’est pas vraiment utile. Ca permet aussi de garantir la durée d’exécution …​ avec jaotc, qui génère du code natif (qui pourra même se passer de JIT). Le but à terme de cette expérience est (accrochez-vous) de réécrire la JVM en Java (pour se débarasser du code C++).

Evidement jlink (et l’ahead-of-time-compiling) est chouette pour les performances. Et pour l’espace disque.

Conclusion

Modulariser, ça n’est pas si facile, à la fois pour les implémenteurs et les utilisateurs. A ce sujet, la conclusion de Rémi est parfaitement claire : si vous avez une application non-modulaire, n’essayez pas de la modulariser. C’est à la fois long, complexe, et sans valeur ajoutée. En revanche, si vous vous lancez dans une nouvelle application, la modulariser dès le départ peut être utile.

Et merci à nos amis du chtijug pour une soirée sans faille, dans une super salle, et avec une vue vraiment chouette !

Publicités

Hop, hop, hop, un chtijug !

Un nouveau site (les terrains de Décathlon), pour deux présentations …​ au chaud !

Dix méthodes pour garder les développeurs

J’ai vu les slides de cette présentation, j’en ai donc une idée assez précise (malheureusement, je n’arrive pas à les retrouver dans la timeline de Cyril …​).

Les 3/4 des développeurs sont plus on moins à la recherche d’un nouvel emploi.

Bien recruter

D’abord, il faut s’arranger pour que le recrutement se passe bien, avec un peu de transparence : vision, cadre, salaire, code, soft skills (beuark, quelle frangliche dilbertien), tout ça doit être présenté.

En bonus, il y a certaines pratiques à éviter :

  • L’offre d’emploi impossible
  • Le code sur tableau blanc, remplacé par exemple par Codingame (mais ça n’est pas encore le vrai environnement …​)

Et d’autres à favoriser

  • Code review
  • Pair programming (eh, mais c’est ce que j’ai fait avec Youssef !)

Proposer une carrière

Oui, alors ça, bon, je connais. Et franchement, les développeurs non motivés, au bout de 3 ans, ils sont devenus chefs de projet.

Quant au graphique produit par N. Martignole sur la carrière d’un déveeloppeur, il est adapté à la vision moderne de l’informatique. Expert au bout de 5 ans, c’est vraiment de la blague.

Travailler sur l’environnement

Avant tout, arrêter l’open-space. Et c’est vrai que mes expériences récentes, dans des bureaux de moins de 15 personnes, m’ont réconcilié avec les gens : quelqu’un qui vient me voir vient réellement me voir, et ça change tout, à la fois en qualité de communication et en amour-propre.

En parallèle, avoir des machines de qualité, et avec de bons écrans, c’est évidement bénéfique. Et de la même manière, avoir de bons logiciels, ça ne coûte pas bien cher par rapport à la valeur ajoutée.

Hey, mais ils citent le test de Joël ! Cool. Bon, en fait, je suis sûr que ce test reprend tout le contenu de cette présentation, mais je ne vais pas râler.

Organiser son travail

Mettre en place des features teams, ça peut être bien. Ou les pizzas teams. En tout cas éviter les équipes façon galère de 50 personnes.

Software craftmanship

Bon, je vous encourage à relire le manifeste …​

Du coup, pour ça, il faut avant tout vouloir s’améliorer. En passant, pour vouloir s’améliorer, il faut avoir un sens du temps long …​ Autrement dit, envisager sa vie professionnelle sur plus de deux ans …​ voire plus de dix.

Tech radar

Bon, là, j’ avoue, j’ai soupiré. Mettre en place un tech radar, ça a un côté tellement poussiéreux, tellement pénible. J’ai vraiment du mal à voir en quoi ça peut présenter un intérêt, parce que ça met en place des comités théodule internes. Je comprend le besoin d’urbanisme, mais franchement, j’ai l’impression que c’est la très mauvaise méthode pour suivre l’évolution technologique.

Ne pas se mentir

Dire qu’on est cool, avec des consoles, du café gratuit, des nerfs (eh, mais on a tout ça, et en plus une Wii U), ça ne vaut pas le travail intéressant (mais on a aussi ça, ça va).

Autrement dit, il ne faut pas remplacer le sens de l’entreprise par tout un tas de bonu qui, eux, ne font pas sens. Cela dit, c’est raccord avec le premier point : lorsqu’on recrute un développeur, on l’intègre au sens de l’entreprise, à ses valeurs.

S’organiser en communautés

Alors théoriquement, c’est censé permettre de la transversalité, encore faut-il que les communautés soient vivantes. Et pour ça, il leur faut des moyens matériels : du temps, de l’espace, des outils de collaboration.

Malheureusement, dans le seul contexte où j’ai vu des gens essayer, ça n’a rien apporté.

Contribuer à l’open-source

Bon, alors là, ça tombe bien, parce que ce matin, un collègue a lancé un recensement des participations de l’entreprise à l’open-source. Et ce soir, il y a déja 60 contributions recensées ! Evidement, parfois, c’est difficile, parce que la sécurfité, tout ça. Dans ces cas-là, monter un Gitlab est une très chouette idée.

Participer aux événements

brown bag lunch, conférences, jugs (ou meetups, ça dépend comment vous voulez appeler ça), tout ça, c’est assez plaisant effectivement. Et puis ça permet à des speakers débutants de prendre l’habitude de parler en public.

Conclusion

Je crois que le message est bon. Mais je crois aussi que le public n’est pas le bon : faire cette conf à l’USI, ou dans un truc de décideurs, bien sûr. Mais je ne suis pas sûr de l’intérêt pour les développeurs.

Vert.x et Kubernetes

Eh, mais c’est Clément Escoffier ! Sans vouloir dire de mal de Cyril et Romain, j’étais surtout venu voir Vert.x et des conteneurs, et je n’ai pas été déçu. Donc Clément va développer des microservices Vert.x et les déployer dans un cloud …​ local (parce que le wifi ne semble pas très bien marcher). Le tout sans aucun slide.

Et il va packager son application avec le vert.x-maven-plugin pour générer des fat-jars. Ca n’est pas indispensable, mais c’est plus facile. Et ça permet à Clément de montrer un peu de maven-fu (parce qu’honnêtement, il en a sacrément, du maven-fu).

Donc en Java, un vert.x, c’est une sous-classe de AbstractVerticle …​ mais ça marche aussi dans la plupart des langages supportés par la JVM (Scala, Groovy, Ruby, Clojure sans doute, …​). Et dans une application Vert.x, pour chaque requête, on appelle la méthode de réponse …​ dans le même thread. Et c’est cool, parce qu’il n’y a pas de context switch.

Mais d’un autre côté, il ne faut pas bloquer ce thread. Sinon c’est la merde.

Pour continuer à développer, avec vertx-web, on peut créer un routeur, et assigner à chaque route une méthode correspondante. Ensuite, Clément nous implémente le serveur tranquillement, avec un mode d’écriture qui m’a beaucoup amusé, et qui est à mon avis la marque des développeurs expérimentés.Par contre, IntelliJ IDEA qui s’amuse à mettre le nom du paramètre en gris devant la valeur, ça me trouble un peu, même si je dois bien reconnaitre que c’est une sacrément bonne idée.

Et maintenant, comment déployer ça sur le cloud ? Et en particulier Kubernetes ? Et en particulier OpenShift ? Donc Kubernetes, ça permet de gérer facilement des gros paquets de conteneurs (logs, sécurité, volumes, …​). Et avec OpenShift, on a une interface, et c’est cool. Et en bonus, on a du build automatisé, ce qui est encore plus pratique. Et avec tout ça, en tant que dév, Clément va pousser son code, qui sera buildé et déployé par OpenShift, et les requêtes seront exécutées dessus.

Et quand ça marche, ça fournit des trucs assez spectaculaires, comme par exemple du déploiement avec rolling updates ! Ou par exemple le déploiement de nouveaux pods avec un simple click. Du coup, comment partager les données ? Avec Redis, par exemple. Et évidement, il faut du service discovery …​ qui est vraiment très facile à implémenter avec vertx.

Et maintenant, depuis notre service, on va en appeler un autre …​ en parallèle …​ à travers RxJava ! Et d’un coup, l’une des propriétés intéressantes de http prend tout son sens : comme c’est un protocole chunked, il n’est pas éncessaire de calculer toute la réponse avant de l’envoyer. Du coup, la mémoire est moins utilisée, l’application coûte moins cher. Et en bonus, le garbage collector n’a pas besoin d’arrêter l’application pour passer. Et ça, c’est vraiment beaucoup plus efficace.

Et c’est le petit moment gênant où la démo s’enraye…​ Mais elle revient sur les rails assez rapidement.

Et maintenant, Clément ajoute des circuit breakers, au cas où le pricer soit un peu trop lent. Et là aussi, comme on fait du vertx, il faut un circuit breaker qui n’utilise pas de threads (donc pas hystrix, par exemple).

Et pour finir la démo, Clément pousse le code sur GitHub en live !

Conclusion

Le talk est à la fois parfaitement spectaculaire dans la forme et vraiment intéressant dans le fond : Vertx permet de développer très facilement des services HTTP (oui, on peut faire autre chose, mais à quoi bon ?), et l’intégration avec OpenShift était aussi poussée que propre.

Compiler ou tester ?

En fin de cette semaine, une controverse intéressante a eu lieu sur twitter, habilement lancée par cet article de « Uncle » Bob Martin : The Dark Path.

Regardez par exemple cette sélection de tweets sur le sujet :

On peut dire que les avis sont contrastés.

Et comme la meilleure façon de ne laisser aucun doute sur le sujet de ma propre stupidité, laissez-moi vous donner mon avis sur le sujet.

A quoi sert le compilateur ?

Il y a des années, à l’époque du C, le compilateur servait à transformer le code source écrit dans un langage quasiment humainement compréhensible (le C) en un code exécutable par la machine.

Aujourd’hui, quelque soit la plateforme d’exécution (la JVM, la CLR de Microsoft, ou le résultat d’une transformation LLVM), le compilateur fait beaucoup plus de choses : il simplifie, optimise, vérifie le code d’une façon très poussée.

Et à mon avis, c’est l’argument de Romain Guy : si le système de type est suffisamment poussé, il est impossible d’écrire un code qui ne corresponde pas précisément aux souhaits du développeur. Mais à quel prix ? C’est ce que cherche à évaluer Uncle Bob.

Laisser le compilateur vérifier ? ou Tester

Parce que l’avantage des tests sur le compilateur, c’est quand même de laisser le développeur vérifier les choses à sa façon : il écrit l’histoire dans son test, et vérifie les choses comme il le souhaite. Qui plus est, le compilateur est certes capable de vérifier certaines erreurs bas niveau, mais il y a tout un tas de trucs que le compilateur ne sera absolument pas capable de vérifier, et qui nécessiteront des tests.

Alors quoi ?

Heureusement, aujourd’hui, la question est en fait tranchée : les développeurs utilisent les deux, en fonction à la fois du coût d’implémentation de chacun, et de l’expressivité de chacun. Cela dit, je continue à trouver cette question intéressante. Et, à titre personnel, je comprend parfaitement la décision de Kotlin et Swift d’empêcher les références null, même si je préfère une solution plus simple : je n’utilise autant que possible jamais de nullen Java. Et du coup, si NullPointerException il y a, c’est forcément un bug qui nécessitera un test. Est-ce que c’est plus coûteux que d’avoir des <Object>? à déréférencer ? Je n’ai pas l’impression. Mais bien sûr, je peux me tromper.

Le quidditch, c’est pas si facile

Pour la troisième fois, je me suis lancé dans un des contests de Codingame.

Cette fois-ci, Harry Potter était à l’honneur.

fantastic_bits_ld-compressor-1

Oui, enfin, sans les combats de sorciers

En particulier, le quidditch. Enfin, un quidditch un peu simplifié : seulement deux sorciers, et, surtout, pas de vif d’or. Et heureusement, parce que j’en ai bien bavé pour en arriver à résultat un peu meilleur que la dernière fois, et surtout, cette fois-ci, raisonnablement satisfaisant, vu le temps investi :

score

707 sur 1950 qui passent atteignent le bronze, c’est bien. Mais s’arrêter à la porte de la ligue gold, ça craint.

EDIT : heureusement, une heure plus tard

score_1

Alors, comme la dernière fois, il y a des leçons à retenir.

D’abord, mon code magique qui produit des tests directement à partir du jeu est très pratique, mais nécessite que le code n’utilise jamais de random() sous quelque forme que ce soit. Cela dit, c’est tellement pratique ! regardez mes classes InGameTest, comme par exemple ici

test_generator

Et n’oubliez pas que ça vient directement de l’état du jeu à instant donné !

Ensuite, utiliser le bug tracker de GitHub comme bloc-note, c’est évidement pratique pour me garantir que j’ai traité le sujet efficacement. Et je m’en resservirai.

Enfin, j’avais une première implémentation que je savais boiteuse dès jeudi. Mais, si vous regardez mon historique de commit, vous verrez … rien ! En effet, le code que j’ai mis en place samedi et dimanche n’a pas été placé dans GitHub avant ce soir … Le fameux rush de fin de projet :-). Cela dit, il n’y avait peut-être pas de commits, mais il y avait des tests ! Et j’utilise toujours avec autant de profit mon plugin Maven qui génère le fichier source de codingame. Mais pour en revenir à cette histoire d’implémentation, clairement, il va falloir que je passe plus rapidement à la deuxième implémentation. Je dis deuxième, mais pour Hypersonic, j’ai enchaîné 4 implémentations différentes, donc n’en avoir que deux, c’est déja un net progrès. D’ailleurs, je pense ne pas vraiment pouvoir en faire moins. En effet, mis à part pour les super-tueurs genre Magus, il paraît difficile d’obtenir une première version qui soit à la fois correcte conceptuellement et efficace.

Mais bon, tout ça ne doit pas ôter quelque chose que j’ai écrit sur hypersonic : coder une de ces satanées IA est un sacré challenge, mais aussi un sacré fun. Et pour le coup, le quidditch était très bien grâce à sa complexité.

PS : Bravo à nmahoude :

Je ne suis pas hypersonic sur CodinGame

Depuis deux ou trois semaines, avec quelques collègues, nous avons créé des comptes CodinGame et nous nous sommes lancé dans les jeux pour programmeurs qui y sont disponibles.

Au début, c’était simple …

Et puis, il y a deux semaines, ils ont lancé Hypersonic. Bon, hypersonic, c’est pas bien compliqué, c’est faire jouer un bot que vous programmez. Donc j’en ai codé un.

Et j’ai fini quasi-dernier en bronze.

Vous pouvez lire le post-mortem que j’ai écrit pour y voir les leçons techniques.

J’en tire quelques autres leçons, plus humaines.

La première est assez logique, pour un jeu : c’est la chose la plus amusante, la plus prenante, et la plus crispante que j’ai codé au moins cette année, et facilement ces cinq dernières années. Imaginer un algorithme, le développer, et le voir se comporter, même mal, a ce côté démiurgique qui est la raison pour laquelle je suis développeur. Le corollaire évident, c’est que je ne comprend toujours pas pourquoi mon métier est aussi chiant actuellement : j’ai utilisé des méthodes professionnelles pour développer ce bot : tests, profiling, … Et c’était vraiment amusant. J’en tire la conclusion que notre métier est chiant parce que les demandes des clients sont, la plupart du temps, d’un ennui mortel.

La seconde est assez logique, mais moins drôle : j’ai fini 1675ème ! C’est médiocre, évidement. Et c’est vrai que mon bot était médiocre : des timeouts partout, et des décisions suicidaires encore plus souvent. J’ai pris une vraie leçon d’humilité, ce qui est toujours bien.

La dernière est plus intéressante : on est certes loin du fameux « meilleur programmeur de France », mais il y a beaucoup de monde, et qui semble prêt à y passer beaucoup de temps. Autrement dit, les développeurs sont des gens passionnés, prêts à se confronter les uns aux autres, et à communiquer autour de leurs succès ou de leurs échecs.

Les furets sont au chtijug !

Tout ce qui est présenté est disponible sur GitHub, avec d’autres choses …

Donc, les furets font de l’assurance. Et ils ont fait d’abord différents sites au look … discutable.

En terme de code, actuellement, il y a 450K lignes de code, et un déploiement en prod par jour. Ca change pas mal des clients avec un déploiement par semestre …

DomainModel.stream()

A quoi ça sert

La classe Person, chez eux, est … grosse, et riche d’héritage. Normal pour un projet qui a 7 ans. Et évidement, ce modèle circule dans l’entreprise, du client Javascript à la base backoffice qui reprend les données sous des formats différents.

Evidement, comme c’est compliqué, ils ont cherché à simplifier tout ça, en utilisant globalement des domaines orientés colonne :

  • clé/valeur dans le client
  • modèle colonnes dans Cassandra
  • Vecteurs de données dans la base analytics

Du coup, utiliser un dictionnaire de clé partout devrait simplifier les choses.

Pour la suite, on va travailler sur un modèle simplifié.

Premier souci : comment ça se passe quand on a des relations 1-n ? A priori, « ca marche » … personnellement, je n’y crois pas trop, mais admettons.

Il y a aussi le souci des champs accessibles par plusieurs chemin, qui cette fois-ci sont dupliqués dans les données disponibles.

Et passons tout de suite au livecoding.

Manipulation simple du modèle

Donc, on a d’un côté notre modèle objet traditionnel, et de l’autre, grâce à un wrapper généré par l’outil des furets. Dans ce wrapper orienté clé/valeur, écrire des données se fait avec un simple

SampleModelWrapper.set(FieldId, value)

lire des données se fait simplement avec un

SampleModelWrapper.get(FieldId)

Et en terme d’implémentation, ça se fait facilement en générant des lambdas (évidement, avant Java8, ça aurait marché tout aussi bien avec des classes internes, mais bon, les lambdas sont à la mode). Et, en plus, l’inférence de type semble assez faible, puisque le type contenu dans le FieldId ne suffit pas à contraindre suffisament le type de sortie.

Manipulation avec des streams

Donc évidement, faire SampleModelWrapper#stream() retourne toutes les valeurs du modèle. Et on peut transformer les valeurs en map en traversant le même stream. Pratique, mais pas exceptionnel. Même si le speaker n’est pas de cet avis. Et en un sens, je le comprend, puisque le mapping est traditionnellement un problème compliqué : regardez Jackson/JAXB et autres qui galèrent pour bien faire les mêmes choses.

Petit bonus : on peut filtrer les éléments du wrapper selon différents tags, qui sont définis librement dans le wrapper. De cette manière, par exemple, on peut séparer les données du User et de l’Account du modèle d’exemple. Evidement, ça peut aussi servir à affiner l’accès à certaines interfaces selon les droits des utilisateurs.

A noter que le site des furets a environ 1000 clés dans les différents enums défnissant les modèles utilisés.

Introspection

Typiquement, quand on veut mettre les données dans une base Cassandra ou autre, il faut d’abord créer les colonnes typées associées, pour lesquelles on va naviguer le modèle, et pour chaque élément du modèle, on va créer une colonne dont le type sera obtenu grâce au FieldInfo correspondant à chaque champ.

Evidement, pour moi, il est assez curieux de voir des gens réinventer des notions analogues aux BeanInfo / PropertyDescriptor disponible dans le vieux monde des JavaBeans. Mais dans l’ensemble, ça fait le job assez (peut-être trop, en fait) simplement. Trop simplement, parce que par exemple, les annotations portées par le modèle initial ne sont pas transportées à travers le wrapper.

Conclusion partielle

Je comprend tout à fait l’intérêt de mapper un modèle de beans vers des séries de colonnes. En revanche, l’implémentation, certes joliment Java8, manque quand même de certains aspects clés : la gestion des collections fait vraiment peur, l’introspection est fichtrement limitée.

Cela dit, ça doit clairement bien limiter la complexité de la sérialisation, de la création d’interface graphique, ou même de persistence (non relationnelle, évidement, parce que sinon, c’est évidement merdique).

Il y a en fait là-dedans une idée très intéressante qui a sans doute un rapport certain avec le NoSQL. je m’explique. Lorsqu’on codait il y a dix/quinze ans avec une base de données relationnelle, avoir du code proprement décomposé était une bonne idée. Maintenant que la plupart des outils de stockage sont dénormalisés, à typage faible, bref, merdiques en terme de support des liens sémantiques, avoir un modèle riche est presque une gêne. Et c’est dans ce cadre que ce système de wrapper de modèle présente un intérêt.

Continuous delivery

Bon, si vous ne savez pas ce qu’est le continous delivery, c’est assez simple : c’est l’idée de pouvoir envoyer du code en prod au plus tôt. Et actuellement, les furets font plusieurs mises en prod par jour.

Historique

D’une façon amusante, en 2012, ils faisaient une MEP par mois, parce que leurs sprints SCRUM duraient un mois. Les tests étaient joués en fin de sprint et pouvaient entraîner des retards de MEP du fait de tests à corriger. Pas très pratique …

En 2013, ils ont tenté d’accélérer les choses avec des sprints « bonus » d’une semaine. Le premier impact est que le temps de build est passé de 15 mn à 3 mn.

En 2014/15, passage à Kanban avec une release chaque jour : les fonctionnalités sont marquées finies dans Kanban quand elles sont en prod. L’impact, évidement, c’est que la release est passée d’une journée entière à 2/3 heures. L’autre impact, c’est que les tests ont tous été automatisés. Histoire de se rassurer, les fonctionnalités passent quand même dans un environnement de staging pendant une journée pour vérifier que tout va bien.

Pour le futur, l’objectif est de livrer encore plus vite : les fonctionnalités devraient apparaître presque une par une en prod.

Curieusement, les MEP apportent toujours autant de code, voire même plus, parce qu’il y a beaucoup plus de fonctionnalités dedans.

Quelques aspects techniques

En-dehors des classiques environnements, il y a quelques points notables.

Les tests sont joués en parallèle sur un grid selenium, qui tourne sur une machine avec un RAMFS (128 Go de RAM chez OVH pour 300 €/mois).

Ils développé Zeno qui va sur une URL pour faire une capture d’écran. Et qui fait la différence entre la prod et la préprod. De cette façon, on peut vérifier que les différences visibles sont bien liées aux fonctionnalités développées (et pas à des bugs, par exemple). Cet outil pourrait par exemple être utilisé pour faire de la veille concurrentielle.

Evidement, le développement se fait en mode blue/green. Avec un double cluster.

Continuous delivery en détail

Il y a en gros 3 modèles de développement

  • trunk based : tout ce qui est dans le trunk part en prod. Ca implique le feature flipping qui est du code en trop.
  • feature branching : tout les dévs sont fait dans des branches, qui sont réintégrées dans le trunk au moment de la MEP. Ca pose évidement des problèmes d’intégration continue.
  • Et pour finir, le modèle github où le code est développé dans des branches, et réintégré dans le trunk via des pull requests, qui passeront dans l’intégration continue.

La grande astuce des furets pour simplifier tout ça est de tester les merge rapidement. Grâce à ce qu’ils appellent le continuous merge : lorsqu’un développeur a fini un développement, il est poussé sur un repository qui contient toutes les nouvelles features branches, et qui tente de les merger et de déployer le code sur l’environnement de staging.

git octopus

Chez eux, les branches en cours de dev s’appellent studies/ et celles qui sont finies s’appellent features/. Pour merger tout ça, ils ont un script appelé git octopus disponible sur github. Le truc magique, c’est qu’il peut détecter les conflits entre les branches à merger avant le merge final.

Pour résoudre les problèmes qui arrivent … ben là, essentiellement, c’est du git-fu (et vous savez ce que j’en pense). En fait, c’est pas vrai : tout ce qui est dit est également valable avec Subversion … et j’ai bien l’impression que c’est ce que gigomerge (mince, j’aurais bien mis le lien, mais je ne le retrouve plus) permet d’une certaine façon.

Pour faciliter la gestion des conflits, ils ont également créé un outil de gestion des conflits … qui en fait se contente de créer un workflow d’escalade des exceptions.

Conclusion

Deux présentations très intéressantes, faites par des gens qui se soucient manifestement beaucoup de ce qu’ils livrent, et de la façon dont ils le livrent. Il y a là-dedans des choses vraiment très intéressantes (git octopus, conceptuellement, est très bien, et cette histoire de repository définissant le niveau de maturité d’une feature est également très chouette) D’autres sont en revanche plus discutable (la résolution de conflit me paraît plus de l’ordre de l’astuce que de la solution vraiment propre).

Mais surtout, ce qui apparaît, c’est que, contrairement à bien des boîtes, ils ont l’ambition de se voir comme une boîte de développement logiciel, ce qui implique de produire de la qualité et de la visibilité. C’est sans doute ce qui leur permet de recruter des pointures, puisque nos deux intervenants paraissaient particulièrement compétents.

#devoxxfr – CDI 2

Injectons donc un peu de nouveauté maintenant … avec CDI 2. Et à mon avis, avec José et Antoine, ça devrait être très intéressant (sans doute aussi parce que CDI me fascine comme le serpent fascine la souris).

Comme d’habitude, comme c’est une discussion sur la spec, le premier point important, c’est : vous pouvez contribuer à CDI 2. Alors si vous avez envie d’aider l’expert group, n’hésitez pas.

CDI, pour l’instant, a 3 version

  1. CDI 1.0 (2009)
  2. CDI 1.1 (2013)
  3. CDI 1.2 (2014)

Et CDI 2 devrait sortir avant Janvier 2017.

Je vous passe les détails sur CDI ? Oui, je passe, parce que vous les connaissez évidement.

Bon, le suivi de CDI 2 est assuré par un meeting IRC par semaine, et ça avance bien. Il y a un cdi-spec.org pour participer plus facilement.

Je vous laisserai regarder la liste des nouvelles features, mais c’est vraiment vraiment chouette. La sécurité va être externalisée de la spec CDI pour être placée dans sa propre spec.

Donc on pourra démarrer de façon standard CDI en JavaSE (on peut le faire actuellement, mais c’est dépendant de l’implémentation). Ce qui est mieux que de dépendre d’un conteneur JavaEE. A noter que la spec CDI a des liens vers le TCK, d onc on peut facilement regarder le code de test correspondant à une feature. Très pratique pour vérifier qu’on a bien compris un point.

Il y a des discussions sur les scopes qui seront supportés en JavaSE. Typiquement, on pourrait ajouter un MehtodScope … Si il est utilisable pour, par exemple, l’utiliser lors d’un MouseEvent, ça vaudrait le coup.

Et passons aux événements !

La grosse feature est l’utilisation d’événements asynchrones. Parce qu’en CDI 1, le mode de transport n’est pas spécifié, mais tout le monde fait du transfert synchrone. Du coup, certains utilisateurs ont abusé cette fonctionnalité pour en faire un visiteur (et vu la tête des speakers, Antoine l’aurait plus volontiers fait que José). Du coup, comme ça a été fait, et comme on fait du Java, et comme ça respecte la spec, il faut le garder, sinon tout le code historique va mourir. Evidement, on ne veut pas vraiment de ça.
En bonus, les contextes CDI sont confondus avec les threads. Du coup, passer les événements en assynchrone va être compliqué.
Donc, ce sera l’appelant de la méthode Event.fire() qui pourra décider si l’événement est synchrone ou pas. Et l’observer doit pouvoir être sûr d’être dans le bon contexte. Et là, mais je sais qu’ils me diront que c’est pas terrible, ils vont ajouter Event.fireAsync() et @ObservesAsync … J’ai bien l’impression que j’aurais préféré Event.async().fire() et Observes(async=true) … Mais je ne suis pas dans la spec.

En revanche, là où il faut être attentif, c’est qu’un envoi incompatible ne donnera lieu à aucun déclenchement de méthode. C’est dommage qu’il n’y ait pas moyen de savoir qu’on fait une erreur dans ce cas.

Bon, par contre, pour les événements mutables, ben là, ne le faites pas. RELLEMENT, NE LE FAITES PAS.

Pour en revenir aux bonnes nouvelles, quand on fait fireAsync, on peut passer un ExecutorService pour définir le thread d’exécution de l’observer.

Par contre, pour récupérer l’état après un appel asynchrone, le fireAsync va retourner un CompletionStage … ce qui explique pourquoi il y a une méthode fireAsync() plutôt qu’un async().fire() …

Passons aux améliorations du support d’AOP.

Bon, on va pouvoir injecter des contextes dans des @Produces. mais ce qui est intéressant là-dedans, c’est la classe UnManaged, qui permet de faire de l’injection sur des objets non créés par CDI. Parce que pour le reste, CDI 2 va « juste » simplifier énormément la transformation d’une annotation.

Ca donne pas envie de faire du CDI 2 ? Eh ben si, vraiment.

#devoxxfr – flamegraphs

J’ai déja vu ces fameux flamegraphs, mais je ne me souviens plus vraiment dans quel article …

En gros, le flamegraph, c’est une vision de la pile d’exécution où le temps CPU est horizontal, et où les appels de méthode s’empilent les uns sur les autres.

Et donc, flamegraph vient de chez netflix, et fonctionne bien pour du java (ouais) sur du Linux (oooh).

Et le processus de génération est … un peu fastidieux. En effet, il faut enregistrer les performances côté OS avec perf, et transformer les résultats avec un script perl. cela dit, la capture au niveau OS est bien pratique, puisque le JIT transforme parfois le code Java en code natif. Et ensuite, il faut lancer l’enregistrement des performances côté java avec perf-agent pour également alimenter les données. Bon, il y a certes un outil qui contient tout ça, mais l’installation reste néanmoins fastidieuse. Parce que bon, sérieusement, me demander à moi de faire un yum install, deux git clone et un cmake/make/make install, c’est trop. Je veux dire, pourquoi je ferai ça ?

Bon, cela dit, l’analyse est très puissante : on peut surveiller le processus quand il est actif, mais aussi le temps passé dans le GC, et même le temps passé dans les autres états du process Linux (genre les attentes d’IO, les wait, …).

#devoxxfr – mon appli est secure … je crois

Allez c’est parti pour un peu de sécurité, enfin … je crois. Donc petit retour d’expérience sur la sécurité. Ou plus exactement sur les projets où on gère la sécurité à la fin.

Les MOA, les chefs de projet, ils ne parlent pas de la sécurité, parce que pour eux, ça n’est pas la sécurité.

Donc dans votre appli, vous mettez des trucs qui servent à rien : Spring security, OAuth, et même un WAF (web application firewall … genre f5, quoi).

Et comme ça arrive en fin du projet, même les simples bugs sont critiques et toute l’équipe a l’air nulle.

La sécurité, plus on s’approche des données, plus c’est fonctionnel, plus on s’éloigne, plus c’est technique.

On pourrait lire tous les bouquins de l’OWASP et comprendre ce qu’ils font, ou on pourrait … contrôler et valider que ça marche bien (autrement dit faire des tests).

Et donc, ils ont développé une application (highway to URLHell) qui montre tous les points d’accès de l’application. Et rien que ça, c’est déja très chouette.

Et là, on a une belle liste de boulettes :
deleteAll accessible par ‘importe quel user
dump de la base accessible
Opérations crud non protégées entre users (donc appel aux delete d’autres entrées)

ca attaque quand même assez fort en termes de liens entre authentification et autorisation.

Clairement, leur outil H2H est une super idée pour identifier le périmètre d’attaque d’une application. Et je vais sans doute le tester assez prochainement.

#devoxxfr – concaténation de strings

Je connais Rémi Forax via une mailing-list ou une autre depuis …au moins 2007 (vérifié dans mes archives mail).

Alors quand il parle, je l’écoute. Et donc, là, il nous parle de la concaténation de chaines en Java8 et Java9.

Donc on prend une concaténation à coups de « + » magiques, et qu’est-ce qu’il se passe ?

D’abord dans le bytecode.

Et Rémi a déja dit basiquement.

Donc en bytecode, ce sont des appels vers des StringBuilder.append (enfin, en Java8).

basiquement.

Mais bon, dixit « le bytecode, c’est relativement marrant à lire, mais en vrai ça sert à rien ». Donc un coup de javap, et nous pouvons voir le bytecode qui sera utilisé sous forme de code Java.

Et comme ça ne sert à rien, passons à l’assembleur. Oui, le vrai assembleur x64 (pas le x86 qui est moins bien).

Et franchement, c’est compliqué.

Maintenant, petit jeu de conversion int=>string. «  »+4 va plus vite que Integer.toString(4) parce que le JIT ne remplit pas le tableau de la chaîne de caractère de 0 avant d’y mettre l’entier !

Comme les optimisations de performances de la JVM sont conçues pour les applications les plus utilisées, il vaut mieux coder crétin comme un Websphere ou un Weblogic 6 plutôt que de faire le mariole.

Et en plus, l’optimisation de toString ne marche vraiment pas toujours. Autrement dit, elle ne marche sûrement pas dans votre code.

Bon, et alors là, basiquement, pour résoudre tout un tas de problèmes d’optimisation de toString, on peut sortir le marteau magique d’invokedynamic pour améliorer le toString.

Et du coup, en Java9, on peut mettre en place toute une série de stratégies de toString selon le contenu.

En plus, on s’est rendu compte en Java8 qu’il y avait plein de strings dans les applications Java. Et du coup, dans G1 en version Java9, il fait de la déduplication de chaînes à la volée. Un peu façon String.intern(), quoi.

Pour continuer avec les détails, les chaînes de caractère Java9 sont maintenant des tableaux de byte. Et je serai tenté de dire que ça va être un beau bazar. Et en fait, c’est le calcul de la valeur du coder (encodage du tableau de bytes) qui va augmenter la taille de l’assembleur.

Ca donne en fait une présentation assez chouette, qui montre encore une fois toute la palette des usages d’invokedynamic.