vendredi 7 février 2014

XMLUnit: une petite lib qui dépanne bien





Aujourd'hui, vous l'aurez compris, on cause XML. Nombreux sont les systèmes d'informations qui exploitent ce format pour échanger des données ou les structurer. XML étant assez souple dans sa représentation, il peut être difficile de valider dans les tests un résultat obtenu par rapport à un résultat attendu.


Bien sûr, la validation au regard du schéma permet de faire une validation structurelle, mais à ce moment là c'est votre marshaller et son intégration que vous testez et non votre application à proprement parler. Si c'est le contenu et non la structure qui doit être vérifié, quelles sont les options?


Vous avez à disposition les moyens "d'unmarshaller" le XML et vous pouvez procéder à la vérification par rapport aux objets du modèle. Pas mal, mais si c'est une grappe complète d'objet que vous devez confronter dans un mode "result vs expected", il va y avoir de la ligne de code et pas qu'un peu!


Vous pouvez tenter de traiter le flux de façon non structurée, sans tenir compte du XML (et encore moins des outils associés) et en se référant aux caractères. Ne riez pas, je l'ai vu. Et bien entendu, le moindre changement d'encodage ou de préfix d'espace de nom rend votre test caduque.


Vous pouvez créer un extracteur basé sur des expressions XPath et les comparer une à une. En fonction de la complexité et du volume du graphe à vérifier, même remarque que pour la première option, vous allez pisser de la ligne.








XMLUnit est une bibliothèque qui permet de procéder à des comparaisons de documents XML. Ce n'est pas un projet Github (Sourceforge), le site est moche et n'utilise pas Twitter Bootstrap, l'API n'est pas spécialement "fluent" et elle n'est pas nouvelle. Le score hype est au plus bas (jeu vous le vends bien hein?), mais il fait le job avec un minimum d'effort. Je vais éviter de paraphraser la documentation, je vous invite évidemment à vous y référer, et tenter de simplement zoomer sur les points qui ont retenu mon attention.





La base (noter l'espace sur le deuxième arbre):






  assertXMLEqual("<root><value>test</value></root>",
"<root ><value>test</value></root>");





Revenons sur la première option: le projet comprend une couche qui permet d'intégrer le document dans le modèle métier. Mieux, les méthodes equals sont implémentées de façon à prendre en compte l'intégralité du graphe contenu. Du coup la comparaison en est simplifiée et en plus c'est le code de prod qui permet de faire la vérification et le test est typé métier, évidemment s'il n'y a pas match vous ne saurez pas d'où ça vient. 




Imaginons maintenant qu'une partie des valeurs du graphe soit purement technique et difficilement reproductible, telle qu'un timestamp indiquant la date de génération, exit l'utilisation d'equals et il est temps de se résigner à pisser du code.







C'est là qu'XMLUnit apporte un plus: il est possible d'avoir le contrôle sur les différences relevées par le moteur grâce à des listeners:









        final DifferenceListener dl = new DifferenceListener() {
@Override
public int differenceFound(Difference difference) {
if(difference.getControlNodeDetail() == null ||
difference.getControlNodeDetail().getNode() == null ||
difference.getControlNodeDetail().getNode().getParentNode() == null)
return RETURN_ACCEPT_DIFFERENCE;
else if (difference.getControlNodeDetail().getNode().getParentNode().getNodeName().equals("timestamp"))
return RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR;
return RETURN_ACCEPT_DIFFERENCE;
}

@Override
public void skippedComparison(Node control, Node test) { }
};

final Diff diff = new Diff(control, shouldBeSimilar);
diff.overrideDifferenceListener(dl);
assertXMLEqual(diff, true);






Je vous ai préparé un Gist pour illustrer l'utilisation: 
https://gist.github.com/bleporini/631581










mercredi 21 août 2013

JVM: Le prix de l'immutabilité




Je l'avoue, durant mes vacances au bord de l'eau, entre les apéritifs et les siestes syndicales, une question est venue me chatouiller: les évangélistes de la programmation fonctionnelle nous bassinent avec les avantages de l'immutabilité mais au bout du compte ça doit pas être gratuit cette affaire là? Combien ça coûte? Du coup je me suis fixé comme objectif pour ma rentrée de réunir des éléments permettant d'avoir un peu plus de recul sur cette question. Je vous propose de suivre ma démarche dans ce billet.


Tout d'abord, rappelons que la principale vertu de l'immutabilité est qu'un objet non modifiable peut être accédé par plusieurs threads sans nécessiter de verrouillage, permettant d'exclure toute situation de compétition (race condition) sur une ressource. Il semble que cela soit une des clefs permettant de passer de la gestion de la concurrence au parallélisme. Soit, toutefois les valeurs d'un objet doivent parfois être modifiées, impliquant la création d'une nouvelle entité immuable avec la ou les nouvelle(s) valeur(s), l'allocation d'une nouvelle instance et potentiellement la collecte de l'ancienne. C'est précisément ces effets de bord qui retiennent mon attention.


Le POC prend la forme de tests unitaires dans lesquels tous les objets d'une liste sont modifiés: une case class comprenant un membre unique de type Long et l'opération appliquée est l'ajout du nombre de nanosecondes contenue dans une journée. La situation est observée sous deux angles différents: l'objet est modifiable et sa variable d'instance est mise à jour ou l'objet est immuable et une nouvelle entité est créée. Le langage est Scala, le code est trivial et vous pouvez le trouver les détails sur Github (https://github.com/bleporini/immutabilityCost).


Les classes:


  case class Immutable(date:Long)
case class Mutable(var date:Long)
Les fonctions de modification:
    def addOneDay(from:Immutable):Immutable= Immutable(from.date+oneDayNs)

def addOneDay(from:Mutable):Mutable={
from.date = from.date + oneDayNs
from
}
L'application des fonctions:
mo /*la liste*/ map transformer /* la foncntion modifiante */

Le problème du micro benchmark


Le programme s'exécute sur la JVM, un environnement d'exécution "managé", c'est à dire qu'en plus de faire le ménage dans la mémoire (Garbage Collection), la machine virtuelle, et plus particulièrement le JIT (Just In Time compiler), réalise plein de choses à l'insu de notre plein gré à des fins d'optimisations, comme compiler du bytecode en code natif et l'intégrer dans les piles d'appel (OSR), "inliner" des méthodes, etc. Ce comportement peut contribuer de façon importante dans la difficulté à obtenir des temps d'exécution répétables.


Il ne me semble pas opportun de procéder à un paramétrage hasardeux visant à stabiliser les résultats. Mon approche s'atèle plutôt à observer le comportement de la JVM lors de l'exécution en boucle du test pour déterminer quand les manipulations sur le code sont stabilisées et les temps mesurés sont à peu près constants. En gros le chrono est déclenché quand la JVM est "chaude".


En ce qui concerne le GC, je m'assure simplement en analysant les logs produits par le paramètre -XX:+PrintGCDetails qu'il n'y a pas de collecte complète qui pourrait fausser les résultats.


Du côté de la manipulation de code, une exécution verbeuse (état d'avancement sur la sortie standard) du test en boucle conjuguée à l'affichage des informations de compilation (-XX:+PrintCompilation) me permet de visualiser à partir de quand le code n'est plus manipulé. Dans le cas étudié il faut moins d'une centaine d'itération pour que JIT calme ses ardeurs (les 700 premières manipulations vous sont graciées…):




[…]
704 75 scala.collection.Iterator$class::foreach (26 bytes)
704 65 scala.collection.TraversableLike$$anonfun$map$1::apply (6 bytes) made not entrant
704 66 scala.collection.TraversableLike$$anonfun$map$1::apply (20 bytes) made not entrant
711 76 blep.ImmutabilityTest$$anonfun$1$$anonfun$bencher$1$1::apply (9 bytes)
711 77 blep.ImmutabilityTest$$anonfun$1$$anonfun$bencher$1$1::apply (9 bytes)
713 78 blep.ImmutabilityTest$$anonfun$1::blep$ImmutabilityTest$$anonfun$$addOneDay$1 (23 bytes)
713 79 blep.ImmutabilityTest$Immutable::date (5 bytes)
713 80 blep.package$::oneDayNs (5 bytes)
714 81 scala.collection.TraversableLike$$anonfun$map$1::apply (6 bytes)
714 82 scala.collection.TraversableLike$$anonfun$map$1::apply (20 bytes)
720 83 java.lang.StringBuilder::append (8 bytes)
0 nth time: 15316 us
723 84 scala.collection.immutable.VectorBuilder::display1 (5 bytes)
724 85 scala.collection.immutable.VectorBuilder::depth (5 bytes)
724 86 scala.collection.immutable.VectorBuilder::display0_$eq (6 bytes)
724 87 scala.collection.immutable.VectorPointer$class::gotoNextBlockStartWritable (757 bytes)
728 88 scala.collection.immutable.VectorIterator::display0_$eq (6 bytes)
729 89 scala.collection.immutable.VectorPointer$class::gotoNextBlockStart (336 bytes)
752 90 scala.collection.immutable.VectorIterator::display1 (5 bytes)
100 nth time: 1423 us
200 nth time: 1394 us
300 nth time: 2639 us
400 nth time: 1362 us
500 nth time: 1369 us
600 nth time: 1419 us
Le calibrage issu de ces informations conduisent à effectuer l'expérimentation dans les conditions suivantes: la liste comprend 100000 éléments, l'application de la modification est effectuée 10000 fois sur la liste, chaque itération est chronométrée et une moyenne globale plus une sur les 100 dernières sont calculées. C'est un peu bourrin, mais cela permet de générer des résultats assez stables.


Dernier détail, il est évidemment important que le lanceur (SBT) "forke" la JVM pour les tests, il serait ballot que le comportement soit parasité par l'outil de build...


Je n'ai pas tenu compte des potentielles problématiques liées au cache L1/L2/L3 du socle matériel, si quelqu'un sait comment récolter ce genre d'informations sur Mac, merci de contribuer!


La partie la plus "velue" aura été d'ajuster les paramètres de lancement dans SBT (S pour simple, vous êtes sûrs?).


Les résultats


Venons en aux chiffres:

















































Méthode Itérations Max Min Moy Moy(100) GC Pauses Pauses Tot FGC Pauses
Mutable 10000 34620 951 2505 1111 66 0,52 0
Immutable 10000 45160 1137 4327 1417 126 1,76 0
30 % 20 % 73 % 28 % 91 % 238 %

Ce n'est donc pas une surprise, comparativement, le GC ramasse : il se déclenche deux fois plus pour le même travail et génère 3,4 fois plus de temps de pauses quand le traitement engendre de nouvelles entités à chaque modification.


Mais au fond, le résultat qui m'intéresse le plus est la différence au niveau du temps d'exécution, le nerf de la guerre, l'immutabilité dure à priori pas loin de 30% plus longtemps. Voilà, tu t'es fait attiré sur cet article au titre tendance, tu as suivi un geek qui fait mumuse avec des additions pour arriver à la conclusion que générer plus de travail prend plus de temps… tu peux retourner voir si les specs de l'iPhone 6 n'ont pas fuité!


Plus sérieusement, le temps d'exécution étant la finalité principale à mon sens, que peut-on tirer de ce chiffre de 30%?


30% d'augmentation du taux de chômage c'est considérable et c'est une catastrophe, 30% d'augmentation de salaire, ça change la vie. En revanche un ordinateur 30% plus puissant qu'un autre ça fait pas une grosse différence à l'utilisation et mon avis est que ce constat se transpose aux programmes informatiques. Je m'explique: quand je clique sur un bouton, que l'appli web réponde en 100ms ou 130ms, c'est rapide pourtant il y a 30% d'écart; si au contraire quand je clique et que la réponse arrive au bout de 10 min ou 13 min, ça rame la mort dans les deux cas! Donc 30% ce n'est pas négligeable mais cela ne nous fait pas changer de classification: on ne passe pas de "ça se traîne" à "ça envoie du bois".


Peut-on dès lors troller qu'adopter l'immutabilité entraîne 30% de dégradations sur les temps d'exécution? Je vois déjà le tweet ravageur:



D'une part, l'expérimentation zoome sur un point particulier et la différence entre les deux zones mesurées se résume à "allocation d'un Long et l'addition de deux Long" contre "allocation d'une instance de case class, d'un Long et addition de deux Long". Le trait est forcément grossi.


D'autre part, dans la plupart des applications, beaucoup plus de temps est potentiellement consommé dans les I/O (réseau ou disque), dans l'interrogation des systèmes de persistance, ou encore lors de la génération de vue ou de réponses, etc que dans l'allocation et la collecte. Dans la vraie vie, le gain de 30% n'est évidemment pas global.


De plus, si les entités mutables de votre application sont partagées entre plusieurs threads, vous êtes voués à recourir à l'utilisation de moniteurs pour synchroniser les accès (autant en lecture qu'en écriture), du coup le temps gagné sur l'immutabilité peut aisément être perdu, surtout que la programmation concurrente est souvent mal maîtrisée.


Sans compter que si des copies défensives (ça m'arrive jamais…) doivent sécuriser l'accès aux membres de vos classes, l'inconvénient de la gestion de la concurrence sera cumulé avec celui des allocations et collectes induites par les duplications d'objets...


Compte tenu de ces données, mon opinion est que l'immutabilité coûte certes plus, mais dans la plupart des situations la charge supplémentaire induite ne sera guère perceptible, sauf si vous avez la chance de travailler sur une application extrêmement exigeante dans laquelle la moindre latence est chassée, telle q'un automate temps réel…




vendredi 19 avril 2013

Why Scala: La stack (2/2)







Passons maintenant au morceau épineux. Play est un framework web dont la particularité est d'être entièrement stateless, il est mu par Netty, le serveur HTTP asynchrone de RedHat. Il offre des API pour Java comme pour Scala depuis la version 2. C'est évidemment cette dernière facette qui retient mon attention.






Play ne repose pas sur la spécification Servlet (Netty non plus d'ailleurs), le seul standard auquel il semble adhérer est HTTP. Il se décompose principalement en trois couches:


Le routeur: il défini dans un fichier les URI pris en charge par l'application ainsi que le contrôleur associé et la fonction qui sera invoquée.





GET     /                           controllers.Application.index


Au début, je me suis dit que c'était plutôt nase par rapport aux contrôleurs annotés du monde Java mais en fait cela présente un avantage non négligeable: tous les accès HTTP de l'application sont centralisés dans une seule ressource au lieu d'être disséminés dans le code. Ce point a attiré mon attention car j'ai été récemment sensibilisé sur la sécurité applicative et la maîtrise des points d'entrée de l'application est un point clef.





Les contrôleurs: ils prennent la forme d'objects Scala et les fonctions sont chargées d'intégrer le modèle (à la charge du développeur) et doivent retourner des implémentations d'Action





def index = Action {    Ok(views.html.index("Your new application is ready."))  }


Les Action permettent de spécifier la nature du retour: délégation à une vue, JSon, le code HTTP de la réponse, les cookies, etc.







La vue: il est équipé d'un moteur de templating spécifique basé sur Scala: il supporte notamment le Currying et permet de manipuler les collections 






Nous sommes dans la dimension fonctionnelle, donc le typage est primordial, les routes comme les vues sont donc typées, ce ne sont pas simplement des fichiers plats évalués au runtime mais à la compilation.






Somme toute, à quelques différences identitaires près, c'est proche de ce dont nous avons l'habitude au niveau du paradigme.






En plus d'application exemples plutôt riches, Play propose de créer un squelette d'application utilisable instantanément:






$ play new the-babel-tower
_ _
_ __ | | __ _ _ _| |
| '_ \| |/ _' | || |_|
| __/|_|\____|\__ (_)my
|_| |__/

play! 2.1-RC1 (using Java 1.7.0_07 and Scala 2.10.0-RC1), http://www.playframework.org

The new application will be created in /private/tmp/the-babel-tower

What is the application name?
> the-babel-tower

Which template do you want to use for this new application?

1 - Create a simple Scala application
2 - Create a simple Java application

> 1
OK, application the-babel-tower is created.

Have fun!

$ cd the-babel-tower/

$ play run
[info] Loading project definition from /private/tmp/the-babel-tower/project
[info] Set current project to the-babel-tower (in build file:/private/tmp/the-babel-tower/)

[info] Updating {file:/private/tmp/the-babel-tower/}the-babel-tower...
[info] Done updating.
--- (Running the application from SBT, auto-reloading is enabled) ---

[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000

(Server started, use Ctrl+D to stop and go back to the console...)


La page de garde comprend une directive qui génère une vue descriptive de l'application:







@(message: String)

@main("Welcome to Play 2.0") {

@play20.welcome(message)

}



Je ne sais pas pour vous, mais moi ça m'a fait penser au fameux phpinfo(). Et bien tout ça, ça roxxe car dans le développement d'applications web en java, maven permet de faire appel à une myriade de templates d'applications, mais à chaque fois que j'en ai essayé un, j'ai trouvé des dépendances et des configurations dont je n'avais pas nécessairement besoin me contraignant à un peu de ménage. Du coup, quand je démarre un projet j'ai pris l'habitude d'un simple archetype:create que je complète manuellement. Play pose l'essentiel, c'est fonctionnel et n'attend que votre code.








Back to basics. Play est très proche des couches basses. Il est aisé de positionner des headers, des cookies ou le code de retour. Il affiche se destiner au développement d'applications massivement sollicitées et hautement scalables, du coup la philosophie n'est pas d'empiler les couches d'abstractions au dessus de HTTP et de HTML à l'opposé d'autres frameworks MVC, comme si pour tirer le meilleur parti d'une infrastructure le développeur devait maîtriser toutes les couches. En tant que geek, même si c'est une vérité de La Palice, j'abonde en ce sens et j'aime avoir du contrôle sur les couches basses.


Le revers de la médaille est qu'il ne faut pas s'attendre à une galaxie de composants à la PrimeFaces, offrant des fonctionnalités riches pour un minimum d'efforts, comme s'il y avait une relation inversée entre rapidité de développement et poids sur l'environnement d'exécution… Le salut passe donc par Twitter Bootstrap (ou autre pour les HTML Heroes).


C'est d'ailleurs ce qui m'a encouragé à évincer le moteur de templating de Play: j'ai misé sur du HTML statique complété par du MVC client (Angular) et ai pris le parti d'exploiter Play qu'au travers de requêtes REST. Comme déjà argumenté dans un autre billet, si l'architecture est sans état, autant déléguer la charge de génération des vue au client, sa CPU est gratuite pour l'application!






C'est dit et redit, Play est stateless, ce qui implique qu'aucune donnée liée à la session applicative ne figure dans la mémoire du serveur, pas même l'authentification. D'ailleurs pour faire simple il n'y a aucune API intégrée de contrôle d'accès et de gestion des rôles! Les données de session sont stockées dans un cookie de type session signé: il n'est pas forgeable et permet à chacune des instances de l'application de valider le cookie présenté grâce à une clef de cryptage partagée. Cela implique que l'utilisation de la session applicative est totalement différente d'une application web stateful, exit les objets vomis dans la session sous peine de voir le poids des transferts s'envoler et les temps de réponse s'effondrer. Décidément, le profil du développeur éligible se distingue de plus en plus de celui rencontré communément pour le développement des applications d'entreprise…






Revenons sur un point: pas d'API de sécurité intégrée, enfin j'exagère il y a un object (play.api.mvc.Security) offrant deux fonctions. Security ne suffit pas et d'ailleurs le sample Zentasks guide tout de même les newbies tels que moi en proposant une trait à ajouter aux contrôleurs des ressources soumises à contrôle d'accès: elle vérifie l'authentification comme le périmètre de l'utilisateur sur l'application. Dans ses moments là, malgré les efforts investis sur le langage, il devient clair que pouvoir lire aisément du code Scala écrit par autrui va prendre encore un peu de temps, même si après coup, les concepts s'avèrent faciles à intégrer. Je vous invite à consulter le code de Zentasks (
https://github.com/playframework/Play20/blob/master/samples/scala/zentasks/app/controllers/Application.scala#L70). Le guide de référence de la version précédente de cite également un plugin tiers (Deadbolt), j'ai préféré ne pas miser sur cette extension, il m'a paru évident qu'une montée de version du framework pourrait ne pas forcément être suivie par ses extensions, d'autant que l'histoire a démontré que la compatibilité ascendante n'était pas une priorité de Play. Coïncidence: la mention vers ce plugin n'existe plus dans la documentation 2.1, d'ailleurs le chapitre sur la sécurité a disparu… JSR-250 and @RolesAllowed, I miss you!






J'ai décidé de bâtir sur une conception REST, il va donc me falloir du JSON. En Java c'est cool: JAXB, Jackson et POJO annotés, le sujet est traité. Avec Play, ce n'est pas beaucoup plus compliqué mais c'est moche: il intègre également Jackson mais l'utilisation est programmatique (basé sur des maps) au lieu d'être déclarative:






Json.obj(
"users" -> Json.arr(
Json.obj(
"name" -> "bob",
"age" -> 31,
"email" -> "bob@gmail.com"
),
Json.obj(
"name" -> "kiki",
"age" -> 25,
"email" -> JsNull
)
)
)



L'avantage est que la transformation n'est pas soudée à un modèle.







J'ai parlé de Slick précédemment et l'intégration se fait sans surprise, manuellement! Avec le cake pattern. Il y a néanmoins un projet d'intégration que je n'ai pas testé (
https://github.com/freekh/play-slick)






C'est la tendance, pour rationaliser les threads, les requêtes http sont servies de façon asynchrone. Toutefois Play brouille un peu les pistes car il permet que le contenu de l'action du contrôleur soit lui même asynchrone (liée à des I/O, charge transférée à un backend, ou délégué à un autre pool de threads):






def index = Action {
val futureInt = scala.concurrent.Future { intensiveComputation() }
Async {
futureInt.map(i => Ok("Got result: " + i))
}
}







Du coup de prime abord, on est en droit de se poser en toute bonne fois une question parfaitement fausse: pourquoi les actions ne sont elles pas asynchrones par défaut comme peut le proposer Grizzly (embarqué dans Glassfish)? Mais pas de doute, Play traite bel et bien les requêtes de façon asynchrone par défaut, vous en trouverez l'explication détaillée 
ici .






Play revendique être destiné au développement des applications web modernes, à ce titre il documente les types de restitutions un peu particuliers comme les réponses par morceaux (chunked responses), les comets sockets et les web sockets. Tout ça c'est très geek et permet de pousser des données du serveur vers le navigateur. C'est évidemment très apprécié. Ce qu'il l'est moins c'est l'API associée: les Enumerator, Enumeratee and co. Toutefois, ce n'est pas parce que je trouve la conception indigeste qu'elle est mauvaise et je pars du postulat que les gens qui s'aventurent à écrire des frameworks sont forcément brillants, mais quand je suis tombé sur un article titré
"Understanding Play2 Iteratees for Normal Humans", je me suis dit que je n'avais pas du être le seul… J'ai un peu transpiré mais j'ai fini par réussir à utiliser cette API.






Pour continuer sur l'aspect 'application moderne', Play 2 gère les sources CoffeeScript et LESS CSS. C'est cool d'autant que si le code contient une erreur de 'transpilation', elle est identifiée et mise en forme dans le navigateur (il fait mieux qu'IntelliJ sur ce point là!). 






Les modifications du code prises en compte à chaud en mode développement est un autre point positif de Play. Cela change par rapport à Java, car sans JRebel, il faut s'en remettre aux limites du hot code replace du compilateur, ou encore paramétrer le conteneur pour scanner les différences et redéployer l'application le cas échéant. Le coût de la compilation des classes Scala est largement compensé par le fonctionnement automatique, de plus le serveur ne conservant pas l'état des clients, c'est transparent côté navigateur. Un bon point.






Alors finalement, après ce florilège de points tantôt à l'avantage de Play et tantôt pas, que puis-je proposer comme conclusion? D'abord, a-t-il sa place dans la galaxie des frameworks web? Oui assurément, car j'ai décrété précédemment que Scala avait la sienne dans l'écosystème et il faut une technologie web qui repose sur ce langage. Mais du coup Play 2 Scala, c'est pour qui? Même réponse que pour Scala: pour des développeurs murs ou de jeunes stars.
Simple ou simpliste? Il y a un dogme sur lequel j'aimerais m'attarder, Play est souvent qualifié de 'stack légère' et du coup on a l'impression que tout est facile et rapide. Alors on l'a démontré, c'est rapidement prêt à coder, c'est léger à héberger mais la productivité ne me semble pas au rendez-vous, en tous cas au niveau des IHM, le budget développement le plus important d'un projet web: pas ou peu de composants ou autre taglib, il faut taper du HTML et du Javascript, c'est évidemment tendance mais sûrement pas productif. Donc légère implique également légère en fonctionnalités! Ces éléments nous apporte la réponse à la question 'dans quel contexte utiliser Play?': s'il faut des stars et que c'est long à développer, eh bien pas pour le développement web d'entreprise déjà! D'autant que j'ai vu des applications JSF 1.2 codées moyennement se comporter correctement jusqu'à 900 utilisateurs concurrents sur un noeud modeste avec une JVM 32bits, donc avant d'avoir un besoin des atouts de Play, il faut être sûr d'avoir une activité soutenue, ou avoir un budget d'hébergement faible et des capacités en développement (du temps et pas d'argent… PME ou artisanat, non?). Ce qui me pousse à penser qu'il est résolument taillé pour le web, le vrai, pas l'intranet, et même pour le Cloud (alerte #buzzword) vu son appétit d'oiseau en ressources matérielles, d'ailleurs l'éditeur ne s'y trompe pas et propose de déployer chez différents fournisseurs en quelques commandes l'application fraîchement générée.  














Voilà, c'est la fin de mon périple en Fonctionnalie. J'étais parti plein d'espoirs fondés sur ces technologies alternatives, qu'en reste-t-il aujourd'hui? J'ai pris du plaisir à coder d'une autre manière, Scala m'a montré de nouveaux horizons et obligé à revoir des algorithmes de base et ça rafraîchit lorsque le quotidien est meublé par de la "tuyauterie". La pile proposée m'a demandé de me rapprocher des couches basses et j'ai kiffé. Ca c'est pour le côté geek. Maintenant si on devait m'annoncer que mon karma était de ne jamais pouvoir programmer avec ces outils que me manquerait-il le plus? Les lambdas et la fluidité de l'API des collections, les case classes, le pattern matching et l'Option. Voilà pour le langage, mais du côté des frameworks, Slick est fun mais actuellement décevant et JPA est maîtrisé et complet (l'une des specs les plus importantes de JavaEE); Play est hype, agréable mais ne révolutionne pas et j'ai l'impression que le défunt Struts 1 était plus riche! De plus Play est stateless, mais il n'est que stateless; or si les technos web du JCP s'appuient sur des conceptions stateful, il reste néanmoins possible de développer des applications sans état avec les frameworks habituels.  


En anticipant à peine, Java8, ses lambdas et son API Stream conjugués à Lombok (pour donner aux beans la concision des case classes mais pas toutes leurs fonctionnalités)  et à L'Optional de Guava  apparaissent être des palliatifs acceptables... en tous cas en attendant de voir si la stack autour de Scala arrive à se démocratiser.



dimanche 31 mars 2013

Retour sur DevoxxFr 2013


















Les cast codeurs viennent de nous dire au revoir, c'est le signal de clôture de cette édition 2013 de Devoxx France… Je vais vous tenter de vous livrer mes impressions encore tièdes et vous propose de partager les faits qui m'ont marqué au cours de ces trois jours intenses!






On n'attire pas les mouches avec du vinaigre. Nous, les geeks de 2013, avons la chance d'évoluer à une époque au cours de laquelle il semble que la demande soit inférieure aux offres d'emploi tandis que les autres corporations traversent la crise. Rappelons que cela n'a pas toujours été le cas, car il y a maintenant une douzaine d'années, lorsque les caps du passage à l'euro et Y2K ont été passés, la bulle internet a éclaté et le marché est devenu assez terne durant quelques années. Séquence "nostalgie qui ne me rajeunit pas" et "j'apprécie ce que j'ai". Donc un nombre important de stands étaient tenus par des entreprises qui exposaient autant pour proposer leurs services que pour faire leurs emplettes sur le marché des consultants. Et comment attirer l'attention des membres d'une population majoritairement masculine? Avec de charmantes managers, commerciales ou encore chargée de recrutement. Oui Mr GONCALVES, chevalier blanc tout de


rose
noir vêtu, après le
procès d'intention dressé contre Sony au sujet d'une affiche misogyne d'un gout douteux, j'attends un nouveau plaidoyer pour dénoncer l'instrumentalisation de la femme à Devoxx!

















Goodies. Ne stigmatisons pas il n'y avait pas que ça pour attirer le geek sur les stands. Et tradition oblige, pour récolter des contacts les exposants nous ont distribué des t-shirts, des mugs, des clefs USB, des autocollants, des lingettes, des boîtes à meuh, de la bière à la pression, des friandises et des décapsuleurs! D'ailleurs je décerne la palme du Goody à RedHat pour son décapsuleur OpenShift qui est bien supérieur à celui d'Oracle puisque celui-ci a en plus une capacité de 4Go. J'aurais payé pour l'avoir et il ne m'a coûté que mon adresse mail. Bref une belle ambiance.













Un parfum d'USI? La première keynote de cette année a été une surprise avec une rétrospective sur l'histoire des écritures. Clarisse Herrenschmidt a balayé devant nous 6000 ans d'évolutions des signes pour nous amener habilement à réfléchir sur la transition dont nous sommes les acteurs. Ca m'a fait penser à la  conférence de Michel SERRES à l'USI. En tout cas une très bonne idée. 


Ensuite, Stephan JANSEN nous a présenté la finalisation de la transition Flash vers HTML5 sur Parleys. Ca déchire le prépuce… S'il subsistaient dans la salle des HTML5 sceptiques, il y a fort à parier qu'il y en a un peu moins aujourd'hui.


Devoxx a également accueilli Martin ODERSKY, invité de marque puisqu'il n'est autre que le "papa" de Scala. Là les choses ont été différentes… J'en ai déjà parlé, Scala a tendance à diviser et ce talk ne me paraît pas avoir arrangé les choses. Rapidement des slides sont apparues mettant dos à dos du code Scala et Java pour mettre en valeur la concision du premier face à la verbosité de second. Quand il finit par avancer que les adeptes de la programmation fonctionnelle et ceux de l'orienté objet pourraient être réunis s'ils développaient en Scala, je me suis levé et je suis parti. Mr ODERSKY, j'aime le vent de fraîcheur qu'apporte votre langage dans le paysage mais j'ai détesté votre keynote. 






Mon DevoxxFr 2013 s'est composé essentiellement de conférences et elles furent pour la plupart riches et les thèmes présentés confirment les tendances hype: technos front avec HTML5 et JS (un zeste de Dart et TypeScript), DevOps, Cloud, programmation fonctionnelle, Big Data et évidemment tout ce qui touche au langage Java et à JavaEE puisqu'ils font évidemment partie des fondamentaux. A noter qu'un virus s'est répandu dans les différents sujets, le Raspberry Pi: Java Fx sur Pi, WAS sur Pi, piloter un Pi avec un minitel et j'en passe… j'ai eu l'impression de voir le remix de la collection des "Martine" dans lequel l'héroïne aurait cédé sa place à l'ordinateur à 20$.  


 Et au passage, j'aimerais témoigner de la qualité de la sélection des talks "indépendants" car tous ces sujets ne m'ont pas forcément intéressé mais ils présentaient en moyenne une qualité élevée et il y en avait pour tous les niveaux. Je précise "indépendants" (ne veux pas dire menés par des free-lances) car il y avait également les "partner slots": des confs qui sont réservés pour les entreprises qui sponsorisent l'évènement. Et là il y a eu des #fails à mon goût. DevoxxFr est une conf de techos: ils veulent voir du code et même du live coding, du protocole, de l'exécution, mais pas une plaquette produit exportée au format PowerPoint. Pire certains l'ont visiblement fait en connaissance de cause pariant sauver les meubles avec un titre racoleur genre "les secrets d'optimisation de performance d'un superhero", et paf des features tout le long. Sanction: je me lève et je pars. Messieurs les sponsors, je ne milite évidemment pas pour une éviction des partner slots, vos solutions sont loin d'être dénuées d'intérêt mais j'attends de la démo, du log ou de la capture de trame, bref je veux que vous me mettiez le nez sous le capot  avant de montrer la valeur de votre produit. DevoxxFr est, pour nous les informaticiens, l'occasion de collecter du savoir, nous ne pouvons pas nous contenter d'un spot pub de 50 minutes. J'ai échangé avec d'autres participants et mon n'avis n'est pas isolé, j'ai même entendu "quand je vois que c'est un partner slot, je n'y vais même pas". Il paraît nécessaire que la copie soit revue et que vous partagiez avec nous. Si un boycott se généralisait, tout le monde serait perdant. Notons que je n'ai pas participé à tous les talks sponsorisés et que j'en ai vu certains qui répondaient à mes exigences, tous ne s'inscrivent donc pas dans cette démarche.






"Compile once, run everywhere" Depuis 1995 on nous raconte une belle histoire en vantant les mérites de cette technologie qui pose une couche d'abstraction entre le matériel et le code source. Aujourd'hui l'abstraction proposée par la programmation fonctionnelle nous éloigne encore un peu plus du hardware. Parmi tous les speakers, j'ai trouvé un extra terrestre: Martin THOMPSON. Pourquoi un extra terrestre? Eh bien ses propos sont anachroniques mais remettent clairement en question ce détachement: comment éviter des invalidation de cache de CPU (avec du padding), les gains apportés par un modèle mémoire prévoyant d'exploiter des segments contigus, les lenteurs identifiées tardivement à cause de différences entre la plateforme de développement et l'environnement d'exploitation et j'en passe. Tout est donc bousculé. Si le tuning que nous devons apporter à notre code tient compte de ces considérations, il devient par là même spécifique à une architecture matérielle et me voilà dans une conférence en 2013 qui présente des concepts qui sont orthogonaux avec la genèse même de Java. Qu'en penser? Tout d'abord les unités de temps manipulées par THOMPSON sont le nombre de cycles d'horloge et les nano secondes, et il se réfère au nombre d'instructions assembleur exécutées par cycle pour apprécier la performance d'un programme. Je ne sais pas pour vous mais de mon côté, on me demande de m'intéresser à des problèmes de lenteur quand l'effet sur l'expérience utilisateur engendre des attentes de plusieurs secondes (voire minutes!). Du coup les invocations de méthode que je chasse affichent une durée de l'ordre de la centaine de milli secondes. Ces écarts énormes dans nos préoccupations respectives sont directement liés à la différence de nos univers: mon quotidien est l'informatique de gestion: un client léger, un serveur d'app, une base de données, des services web et les caches qui m'intéressent stockent des objets métiers et pas des octets (enfin pas directement). En gros des I/O, de la sérialisation, du parsing, des algos assez pauvres, bref énormément de tuyauterie plaçant mes préoccupations dans des sphères nettement moins exigeantes que les siennes. Mais merci à lui de nous rappeler cette réalité et de nous présenter des pistes d'optimisation à explorer quand toutes les évidences de haut niveau habituelles seront épuisées.


Au passage il nous fait profiter de son expérience en production avec le G1GC qui ne semble pas aussi prometteur que nous pouvions l'escompter, le poussant à revenir sur le CMS. On en apprend également sur les effets de l'immutabilité de la programmation fonctionnelle sur le GC et il n'hésite pas à nous balancer quelques Scuds en demandant pourquoi des fonctionnalités du langage telles que les lambdas représentent tant dans nos attentes quand il reste tellement à optimiser dans l'existant… Dès qu'elles seront disponibles dans Parleys, précipitez vous sur ses prestations.






Je termine ce billet en remerciant l'équipe organisatrice de la manifestation: l'ambiance est au rendez-vous, y en a pour tous et la soirée "meet and greet" (en observant la tête de quelques uns vendredi matin, ça aurait pu s'appeler "meet and drink" :o) ) est une super opportunité de rencontres et d'échange en mangeant du fromage. Vivement DevoxxFr 2014!



vendredi 22 février 2013

Why Scala: La stack (1/2)








Dans mon
billet précédent, j'expose mon retour d'expérience concernant Scala. Maintenant que je possède le langage (enfin quelques éléments), que me manque-t-il pour coder une application de la vraie vie? Un outil de build, une couche pour faciliter les accès JDBC et un framework web me paraissent être le minimum. Je vais me pencher sur les cas de
SBT,
Slick et
Play Framework 2.1 (what else?).


SBT




C'est l'outil de build qui me semble le plus répandu pour le développement Scala. Il est incontournable pour Play Framework (la console se base dessus). Si on regarde un peu en arrière,  pour nous délivrer de javac, Apache à créé Ant. C'était bien mais trop freestyle et des configurations complexes ont été engendrées en masse. Qu'à cela ne tienne, puisque la gestion technique du projet nécessite un outil plus coercitif, Apache nous apporte une fois de plus la lumière avec Maven (on va dire que l'ampoule a été vissée à partir de Maven 2). Aujourd'hui les critiques fusent autour du manque de flexibilité. Comme tous, j'ai du faire l'investissement autour de Maven il y a quelques années, il n'est pas parfait mais je le maîtrise et il fait le taf, au prix de quelques acrobaties parfois. 




Je trouve que l'outil de build est un non sujet. Que l'application soit construite avec Ant, Maven, Graddle ou des scripts shell, quelle différence pour l'utilisateur? Pire: pour le code? Devoir encore passer du temps sur SBT me paraît être une perte sèche et m'irrite un peu je l'avoue. Je me limite donc à des copier-coller de config et aux commandes compile, test et idea. Pas d'appréciation.



Slick





Anciennement ScalaQuery, Slick est un framework d'accès aux données en Scala. Il propose trois voies d'accès: 






  • plain SQL queries: à priori peu d'intérêt, je ne me suis peu attardé dessus

  • lifted embedding: j'ai présenté les possibilités de la boucle for en Scala dans mon précédent billet et notamment le sentiment que l'on peut interroger les collections comme des tables relationnelles. Et c'est ce que ce mode propose et c'est ce qui m'a d'ailleurs attiré vers Slick:



//Domain definition
case class Supplier(id:Int,name:String)

//DAO
object Suppliers extends Table[Supplier]("SUPPLIER"){
//Column definition
def id = column[Int]("id", O.PrimaryKey)
def name = column[String]("name")

//Projection definition
def * = id ~ name <>(Supplier, Supplier.unapply _)
}

//Interrogation
val q = for {
s < - Suppliers
if s.name === "my supp"
} yield s.id

q.list/* have fun here with Scala lists!*/.foreach(println)


Slick propose un troisième mode, direct embedding, l'exemple va vous rappeler quelqu'un:

@table(name="COFFEES")
case class Coffee(
@column(name="NAME")
name : String,
@column(name="PRICE")
price : Double
)


Et là comme j'ai pu le faire, vous êtes en train de vous dire que c'est du JPA! Enfin ça le sera peut être un jour car la doc stipule deux bloqueurs:


The direct embedding currently only supports database columns, which can be mapped to either
String, Int, Double.
Ouch même pas les dates!



The direct embedding currently does not feature insertion of data. WTF? On peut interroger des données qu'on a pas pu insérer? Pas exactement, il faut utiliser un des deux autres modes pour persister.





Mes attentes principales autour d'un outil d'accès aux données sont de générer le SQL pour me faire rêver que le changement de SGBD sera sans impact sur le code et des fonctions de mapping relationnel-objet pour limiter le code boilerplate. Du coup des trois modes il n'en reste qu'un: lifted embedding.




Le langage de requêtage basé sur la boucle for est fort séduisant:




  • il est très concis et s'intègre agréablement dans le code, après je ne l'ai pas challengé avec un schéma de 200 tables non plus...

  • il est statiquement typé: pour avoir la même chose avec JPA 2.0, il faut générer un méta modèle et utiliser l'API criteria (mon royaume pour une corde et un arbre que j'aille me pendre…)






Je mène ces explorations par pure convenance personnelle et ce sont les technologies traditionnelles qui me permettent de ramener le pain quotidien, il est donc facile de deviner que ma référence en la matière est JPA. D'ailleurs dans les différences fondamentales, on notera deux points marquants:




Les entités mappées ne sont pas managées: le framework n'a aucune idée de la situation d'un objet du domaine au regard de son état en base; de plus il n'est même pas obligatoire d'associer un DAO avec une classe, il peut tout aussi bien exploiter des tuples.




Les relations ne sont donc pas portées par le modèle métier mais par les DAO, ce qui implique que la navigation dans le graphe passe nécessairement par une interrogation explicite. Imaginons l'extension de l'exemple précédent:



      object Coffees extends Table[Coffee]("COFFEE"){
def id = column[Int]("id", O.PrimaryKey)
def name = column[String]("name")
def supplierId = column[Int]("sup_id", O.NotNull)

def * = id ~supplierId~name <>(Coffee,Coffee.unapply _)
def supplier = foreignKey("fk_supId", supplierId,Suppliers)(_.id)

}

(Suppliers.ddl ++ Coffees.ddl).create

Suppliers.insert(Supplier(1, "my supp"))

Coffees.insert(Coffee(1,1,"Ristretto"))

val q = for {
c < - Coffees
s < - c.supplier
} yield (s.name, c.name)
q.list.foreach(println)
}



De prime abord, ces points semblent être des lacunes comparés à JPA. Mais en prenant un peu de recul, lors de la plupart (hmm… peut être même la totalité?) de mes interventions sur les projets, j'ai pu observer que l'ORM était souvent hors de contrôle. Oui, "sans maîtrise la puissance n'est rien" (j'adore), et toute la magie apportée par l'instrumentation des classes, les invocations d'assesseurs interrogeant automatiquement la BDD, la fusion d'entités et j'en passe, éclipsent totalement la mécanique sous-jacente. Le résultat: des torrents de SQL noient littéralement nos bases, devant également faire face à des requêtes monstrueuses aux jointures improbables, impliquant des plans d'exécution moins accessibles que le Saint Graal lui même, et souvent pour afficher moins de colonnes que les doigts d'une main. Ne nous trompons pas, je ne dresse pas le procès de JPA, une très belle technologie, mais celui de ses utilisateurs.




Alors finalement, moins de fonctionnalités réduit la fracture entre les développeurs et la modélisation relationnelle. Slick semble parvenir à ce compromis: il concilie un niveau d'abstraction acceptable en gardant à sa charge les basses besognes, telles que la génération du SQL, avec un code qui laisse l'informaticien conscient des concepts mis en oeuvre. A méditer.









Chers internautes, je me dois maintenant d'interrompre ce séjour sur l'île aux enfants pour nous ramener dans la vraie vie, alors dites au revoir à Casimir et préparez vous à la descente.









D'abord les snippets présentés sont évidemment résumés dans un souci de lisibilité. Slick exploite le côté obscur de Scala (selon moi évidemment), les wrappers implicites. Pour que notre boucle for appliquées aux tables compile il faut importer un driver permettant d'intégrer le langage de Slick avec la source de données JDBC (à l'image du dialecte Hibernate). Ensuite, il faut ouvrir ou récupérer la session courante:



import Database.threadLocalSession
import scala.slick.driver.H2Driver.simple._

Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") withSession {
val q = for(....
}




Admettons devoir intégrer ce code dans une application, l'URL et le driver JDBC, on peut les caser dans un fichier de conf, mais pour le driver Slick on fait quoi? On ne va pas laisser des H2Driver traîner dans toutes les classes, et puis c'est bon pour les tests ou les POC mais quand le temps viendra d'utiliser une vraie base on fera quoi? La solution se base sur le cake pattern qui modélise l'injection de dépendance statiquement typée en Scala (plus de détails sur
http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di/). Je vous laisse consulter l'exemple fourni sur
https://github.com/slick/slick-examples/blob/master/src/main/scala/com/typesafe/slick/examples/lifted/MultiDBCakeExample.scala#L61. Il présente un gâteau avec trois ingrédients: l'encapsulation du driver Slick, le compsant d'accès aux données et les DAO. Je le trouve nettement plus indigeste que:


@PersistenceContext
private EntityManager em;



Affaire de goût? 




Les exemples fournis se basent uniquement sur la session, quid de la gestion transactionnelle? En parcourant la documentation de référence, je n'ai rien trouvé. Cependant j'ai réussi à dénicher qu'il fallait d'abord ouvrir une session puis regrouper toutes les commandes dans un bloc transactionnel:



Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") withSession {
threadLocalSession.withTransaction{
[...]
}
}







Personne ne leur à montré @Transactionnal? Nous voilà quelques années en arrière…






Slick propose les query templates qui permettent d'exécuter une requête plusieurs fois avec des paramètres différents. Nous savons tous combien les optimiseurs SQL apprécient cette attention et nous la rendent avec des temps d'exécution diminués. Seulement les query templates ne couvrent que l'interrogation (select), ce qui implique que les manipulations (update/delete) paramétrées ne sont disponibles qu'au travers du mode plain SQL queries. Une lacune difficilement excusable, gageons qu'il s'agit d'un problème de jeunesse.









La documentation d'API n'a que le mérite d'exister, les développeurs doivent respecter une des lois XP qui précise que si des commentaires sont nécessaires c'est que le code n'est pas clair… Bref, une Scala Doc qui est tout bonnement anémique et qui daigne nous lâcher royalement une phrase de description dans quelques une des classes.









Je trouve Slick prometteur, car une fois que tous les petits défauts de jeunesse seront comblés, Scala bénéficiera d'une technologie d'accès aux données qui sera parfaitement intégrée au langage. En revanche passer de la version 0.11 à la version 1.0 (releasée il y a peu) est un peu rapide. 1.0 me paraît être un jalon dans lequel les fonctionnalités de base sont comblées, avec éventuellement quelques bogues ou une intégrabilité balbutiante après tout 1.0 est un gage de jeunesse. Seulement voilà, l'embedded mode est tout simplement inexploitable et le lifted mode incomplet. N'aurait-il pas été plus utile au projet de finaliser le deuxième avant de s'aventurer sur le premier? Mais ne jetons pas le bébé avec l'eau du bain, l'évolution de ce framework reste définitivement à surveiller, même si son numéro de version actuel devrait être 0.8 au lieu de 1.0. 








M'épancher sur le cas de Play ferait un billet assez long, donc je m'arrête là et vous proposerais la suite rapidement c'est promis!



samedi 2 février 2013

Why Scala?




Le geek est une espèce qui présente un comportement addictif au renouveau perpétuel.



Les geeks se découpent en plusieurs castes dont une d'entre elles regroupe ceux qui codent en Java. Depuis quelques temps les membres de celle-ci souffrent car ce langage ne bouge guère. Pour répondre à ce mal, fleurissent des langages alternatifs qui s'exécutent tout de même sur la JVM, dont un en particulier: Scala. Il entraîne d'ailleurs la naissance de guerres de clocher au sein même de la caste. 


Tiraillé par le manque d'évolution (quoi je suis le seul qui n'a même pas eu une demi molle en découvrant le diamond operator de Java7??) et curieux de comprendre ce qui défraie autant de passion, j'ai décidé de monter en compétence sur cette technologie (pas si nouvelle que ça d'ailleurs - 2003). J'ai mangé le pavé de Martin Odersky 'Programming in Scala', transpiré sur les katas S-99, suivi la session proposé par Coursera et quelques mois plus tard, j'aimerais dresser mon bilan de l'expérience.






Ce qui m'a plu





Scala est un langage permettant de résoudre les problèmes par l'approche fonctionnelle (mais pas exclusivement car il reste compatible avec l'orienté objet) et il m'a donc été nécessaire d'appréhender les concepts liés à ce style de programmation. Voici un florilège de ce que j'en ai apprécié:


Evidemment les lambdas. Un buzzword qui fait beaucoup couler d'octets sur la toile, un des fondements de la programmation fonctionnelle, le saint graal qui doit permettre de tirer aisément parti des architectures multicores. J'avoue être un poil perdu: les lambdas sont-elles un concept fondamental ou simplement du sucre syntaxique. Une chose est claire: le code en est plus concis et je kiffe.

 Un des principaux intérêts des lambdas est sont intégration dans le SDK, donc ce n'est pas un scoop, les listes en Scala, ça poutre.



La boucle for est sacrément revisitée: fini les boucles imbriquées grâce à cette nouvelle syntaxe qui permet d'intégrer des produits cartésiens ainsi que filtres de façon élégante… on a l'impression de requêter les collections comme on le ferait avec des tables relationnelles. Quelques exemples sympas:

Produit cartésien:

scala> for ( i <- 1 to 2; j <- List("un","deux")) yield(i,j)
res4: scala.collection.immutable.IndexedSeq[(Int, java.lang.String)] = Vector((1,un), (1,deux), (2,un), (2,deux))



Jointure

scala> for ( i <- 1 to 2; j <- List((1,"un"),(2,"deux")) ; if i==j._1) yield(i,j)
res5: scala.collection.immutable.IndexedSeq[(Int, (Int, java.lang.String))] = Vector((1,(1,un)), (2,(2,deux)))





L'inférence de type. Il trouve tout seul:

scala> val i=0
i: Int = 0


Le compilateur n'est plus malentendant et il n'est plus nécessaire de lui répéter plusieurs fois le type. Scala mise définitivement sur la concision du langage.





Les fonctions partielles ou Currying. Imaginez une fonction pour laquelle seule une partie des paramètres est définie, mais ça donne quoi? Une fonction qui attends le reste de paramètres. Cool non? Exemple:


scala> def stupidAdditionExample(i:Int)(j:Int)=i+j
stupidAdditionExample: (i: Int)(j: Int)Int

scala> val partial = stupidAdditionExample(2)_
partial: Int => Int =




scala> partial(3)

res0: Int = 5








L'immutabilité. Bloch en vante largement les vertus pour éviter d'avoir à poser des verrous pour synchroniser les accès concurrents dans Effective Java , mais franchement dans un quotidien de développement d'application web de gestion reposant sur des conteneurs et JPA, on est content de le savoir mais on a l'impression de n'être que partiellement concerné. Dans Scala on ne peut pas passer à côté et les variables ne deviennent qu'une possibilité optionnelle. En plus des considérations techniques avancées par Bloch, la programmation fonctionnelle ajoute le fait que la mutabilité des variables n'est issu d'aucun concept mathématique ou algorithmique mais est simplement le reflet de la possibilité de modifier le contenu des registres du socle matériel. La mutabilité n'est pas la réalité et c'est mal pour la concurrence. J'ai cru au début que ça allait piquer car il faut lutter contre de vieilles habitudes et en fait non… je me suis vu contraint de faire appel à une variable une seule fois (une sale histoire d'InputStream, je ne peux rien dire de plus la cicatrice est encore fraîche).  









"Je te donne peut-être une valeur… et peut-être pas", ça ce sont les options. L'intérêt n'est pas flagrant de prime abord mais du coup tous les "if null else" disparaissent (et les NPE de surcroît) . De plus l'API offre la possibilité de modifier le contenu et de définir des valeurs par défaut contextuelles:


scala> val peutetre=Some(1)
peutetre: Some[Int] = Some(1)

scala> peutetre.get
res1: Int = 1

scala> val rien=None
rien: None.type = None

scala> rien.get
java.util.NoSuchElementException: None.get

scala> rien.getOrElse(1)
res3: Int = 1

scala> peutetre.map(i=> "valeur = " + i).getOrElse("Rien")
res6: java.lang.String = valeur = 1

scala> rien.map(i=> "valeur = " + i).getOrElse("Rien")
res7: java.lang.String = Rien





Les applications nécessitent souvent un singleton et les patterns compliqués et souvent buggés ont longtemps fleuri, la réponse de Spring a été de fournir des singletons de fait et enfin Java 5 a permis de mettre tout le monde d'accord grâce à une utilisation dérivée des Enum. Mais voilà si cette dernière option est techniquement justifiée, elle reste sémantiquement très discutable. Qu'à cela ne tienne, puisqu'il s'agit d'un besoin récurrent Scala l'intègre dans le langage avec l'object. Efficace et pertinent.







L'un des leitmotiv de Scala est que la réduction du nombre de lignes de code d'une application à périmètre fonctionnel constant réduit les probabilités de bugs. Donc pour permettre la diminution du code "boilerplate", le langage apporte une quantité importante de sucre syntaxique. Le geek est friand par nature du sucre syntaxique. J'ai kiffé. Quelques exemples:








Qui n'a jamais rêvé de la possibilité de définir la structure d'un bean anémique avec un oneliner? Les case class le permettent car seuls les membres sont définis, le compilateur s'occupe des assesseurs, du constructeur et des méthodes equals/hashcode (et plus encore).


scala> MyBean(2).equals(3)
res8: Boolean = false

scala> MyBean(2).equals(MyBean(2))
res9: Boolean = true






Les placeholders paraissent de prime abord un peu rugueux mais ils deviennent vite habituels. En gros, ils permettent de ne pas déclarer ni nommer les paramètres qu'une closure utilise en fonction de leur position. Ainsi la réduction suivante


scala> List(1,2,3).foldLeft(0)((acc,elem)=>acc+elem)
res19: Int = 6
Peut s'écrire également:


scala> List(1,2,3).foldLeft(0)(_+_)
res18: Int = 6



Il y a plein d'autres utilisations possibles des placholder (ils ont été utilisés pour présenter les fonctions partielles notamment).







Un des atouts des case class est leur utilisation avec le pattern matching. Il s'agit d'une autre forme d'instanceOf basé  sur les extracteurs (tiens encore une fonctionnalité intégrée aux case classes). Je vous livre en l'état un exemple très incorrect mais qui permet une illustration succincte du concept:


scala> MyBean(3) match { case MyBean(i) => i+2 }
res20: Int = 5



Cela fonctionne que parce que la définition d'une case class implique la création d'un objet compagnon qui fourni un extracteur:


scala> MyBean.unapply(MyBean(3))
res4: Option[Int] = Some(3)






La liste pourrait s'allonger de façon assez ennuyeuse: pas besoin de ';', pas besoin de parenthèses pour les fonctions sans paramètres, contrôle sur l'évaluation des paramètres (by name / by value), etc. Mais je vais m'en tenir là et passer aux notes un peu plus douloureuses.








Ce qui m'a déplu







La conversion implicite, au moyen de wrappers, est une fonctionnalité qui permet de décorer automatiquement les objets et par ce biais de leur ajouter des méthodes. Cela apporte la liberté syntaxique d'un langage dynamique dans un langage basé sur le typage. Séduisant non? Exemple:

scala> class StringWrapper(s:String){
| def taille=s.length
| }
defined class StringWrapper

scala> implicit def toWrapString(s:String)=new StringWrapper(s)
toWrapString: (s: String)StringWrapper

scala> "yes".taille
res0: Int = 3


Mais pour les activer il faut souvent les importer, de plus naviguer dans les API d'une bibliothèque tierce devient un enfer. J'aime pas.



La programmation fonctionnelle mise sur des conceptions basées sur des appels récursifs. Seulement voilà, la JVM est la plateforme d'exécution et elle n'apprécie qu'avec modération cette pratique (gare à la StackOverflowError). Pour contourner cette limitation, Scala suggère d'utiliser la 'tail recursion' qui se caractérise par un appel récursif en dernière instruction de la fonction. Cette pratique lui permet de modifier le code à la compilation en boucle for. Le bémol est que l'abstraction du language vis à vis des considérations techniques est brisée car la conception est stigmatisée par les limites de la JVM. C'est pas sa faute mais j'aime pas.







Variance, covariance, contravariance, nonvariance… les possibilités de contrôle des paramètres de type sont complètes… et donc également complexes… Difficile de s'y retrouver sans avoir la doc ouverte au bon chapitre. Un exemple:


trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] extends collection.MapLike[A, B, This] withParallelizable[(A, B), ParMap[A, B]]



Le code est issu du SDK (
ici) Je me m'aventurerais pas à en nier la pertinence, en revanche je suppute que l'être vivant qui déchiffre ces paramètres d'une traite est sûrement capable de donner le nombre exact d'allumettes qui viennent de tomber par terre...






Lorsque l'on tente de dériver une case class dans une autre case class, voici le message produit par le compilateur (avec l'option deprecation):


scala> case class MyBean(id:Int)
defined class MyBean

scala> case class MySubBean(name:String) extends MyBean(2)


:9: warning: case class `class MySubBean' has case ancestor `class MyBean'.

Case-to-case inheritance has potentially dangerous bugs which are unlikely to be fixed.

You are strongly encouraged to instead use extractors to pattern match on non-leaf nodes.






Y a sûrement une excellente raison, mais le message est déroutant.





Les paramètres implicites font à mes yeux partie de ces concepts qui apportent de la magie à un langage... enfin je dirais plutôt de la sorcellerie:

scala> def imprime(implicit x:Int)=println(x)
imprime: (implicit x: Int)Unit

scala> implicit val test=3
test: Int = 3

scala> imprime
3



Simple dans cet exemple, mais vous vous doutez bien que les règles liées ne le sont pas autant et si une autre valeur implicite traîne dans les parages, les choses se corsent. Bref je trouve cette fonctionnalité dangereuse.







Un détail ennuyeux: le code Scala compilé l'est pour une version précise de scala ce qui a notamment entraîné l'apparition d'une nouvelle dimension dans les coordonnées des dépendances...







"With great power comes great responsibility..." Scala m'apparaît conçu pour apporter une solution à tous les problèmes qui ont pu être levés dans  les langages auparavant et tente d'intégrer toute fonctionnalité séduisante. Cela le rend il universel? S'il l'est, il l'est pour des développeurs universels! Je le trouve assez élitiste, ce qui me semble être son principal défaut et un frein à son adoption. 





Ma conclusion











S'il est difficile d'attendre d'un langage d'être l'élément qui garanti la réussite d'un projet, Scala prend à sa charge des sujets sensibles tels que la concurrence, les threads et j'en passe. C'est un plus car le développeur peut augmenter son attention sur le code métier. 


C'est un langage qui mérite sa place dans l'ecosystème, pas comme un leader du secteur, mais peut-être comme un Concept Car ou une Formule 1, la plebe dont nous faisons partie espérant voir quelques unes de ses avancées alléchantes déclinées dans  nos outils quotidiens... Et puis le langage est une chose mais qu'est il sans une stack de développement? C'est un autre sujet (to be continued)...









dimanche 6 janvier 2013

AngularJS + Play! Framework: Authentification





En tant que newbie sur Angular, je me suis posé la question de l'authentification dans ce type d'applications.

Pour ceux qui veulent aller directement à la présentation du code, c'est
.



Depuis longtemps je suis habitué au développement de web apps dont les pages sont générées dynamiquement sur le serveur (JSP, JSF, PHP, etc.): cette problématique est donc comblée dans la plupart des cas par une redirection vers un formulaire d'authentification. Or avec un framework du type AngularJS, les pages sont générées dynamiquement sur le client à partir de templates nourries de données obtenues du serveur collectées grâce à du code client (Javasript).

D'un point de vue technique cette approche a évidemment énormément de sens, car:





  • Toutes les ressources clientes sont donc "cachables": templates, Javascript

  • A l'heure du Cloud (un buzzword, un), le coût d'hébergement est proportionnel à la sollicitation, il est tout naturel de transférer sur la machine du client (gratuite) toute charge éligilble

  • L'état du client est géré... sur le client (un truc de malade), permettant de réduire la pression sur la mémoire de la partie serveur (au point précédent on a économisé de la CPU, là on s'occupe de la RAM!). Sans compter que si la partie serveur ne maintient plus d'état, n'importe quelle instance pourra servir n'importe quelle requête... Nous parlons donc de conception Stateless (buzzword #2), permettant de déployer les noeuds en ayant aucun autre souci que la répartition de charge (pas de synchronisation entre les noeuds), m'amenant tout doucement vers le 3ème buzzword, la scalabilité horizontale.



En réfléchissant, si la traditionnelle redirection vers le formulaire de login ne semble pas être une option adaptée, que reste-t-il? Facile: le protocole HTTP prévoit au moins deux codes de retour liés aux problèmes de contrôles d'accès: 401, authentification requise, et 403, authentification refusée. Parfait, le premier m'explique que je dois présenter mes papiers et le second me signale que l'authentification fournie ne comprend pas l'accréditation nécessaire pour accéder à la ressource demandée.



Donc me voilà parti sur Google avec les mots clefs "Angular authentication 401" et je tombe sur un article fort intéressant:
http://www.espeo.pl/2012/02/26/authentication-in-angularjs-application . La démonstration est complète: toutes les requêtes tenues en échec suite à une erreur 401 sont bufferisées, en attente de la connexion de l'utilisateur et retentées le cas échéant. Et si on veut quelque chose de plus basique? Un peu plus newbie? Genre une redirection vers un formulaire de login à la première erreur 401 et une redirection a la racine suite à authentification? C'est sûrement pas aussi complet, mais l'aspect naïf permet de monter tranquillement en compétence sur AngularJS.



Avant toute chose, il nous faut un backend, mon choix c'est porté sur Play 2.1-RC1 car:



  • Il est stateless par essence

  • C'est hype, j'en avais envie et c'est moi le chef du blog (#noTroll)

Point de départ standard avec une application Play vide, ensuite on récupère une partie de l'authentification du sample
zentask en modifiant quelques aspects:



  • Tout d'abord, en cas de nécessité d'authentification, pas de redirection => 401:


  private def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Application.login)
devient:


  private def onUnauthorized(request: RequestHeader) = Results.Unauthorized





  • La démonstration nécessite une ressource protégée:




  def protectedResource = IsAuthenticated{
    username => _ =>
    Ok(Json.obj("test"->"1234"))
  }






  • Comme on veut faire une application cliente qui se connecte à un backend, pas d'utilisation des templates Play et modification de la route pour que la racine pointe sur une ressource statique:

GET     /                           controllers.Assets.at(path="/public", file="index.html")



Le code est disponible
ici.



Vérifications:





  • Pas autorisé:




$ curl http://localhost:9000/protectedResource -v
* About to connect() to localhost port 9000 (#0)
*   Trying ::1...
* connected
* Connected to localhost (::1) port 9000 (#0)
> GET /protectedResource HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5
> Host: localhost:9000
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Content-Length: 0
<
* Connection #0 to host localhost left intact
* Closing connection #0


L'application réfute bien l'accès avec un code 401.





  • Authentification:




$ curl http://localhost:9000/login -d 'mail=tony@stark.com&password=ironman' -v
* About to connect() to localhost port 9000 (#0)
*   Trying ::1...
* connected
* Connected to localhost (::1) port 9000 (#0)
> POST /login HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5
> Host: localhost:9000
> Accept: */*
> Content-Length: 36
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 36 out of 36 bytes
< HTTP/1.1 200 OK
< Set-Cookie: PLAY_SESSION=1701c43b7f845bdde0e38c0f43705d54b6815977-mail%3Atony%40stark.com; Path=/; HTTPOnly
< Content-Length: 0
<
* Connection #0 to host localhost left intact
* Closing connection #0

Code 200, un cookie signé de session en retour… tout va bien, donc on retente l'accès à la ressource protégée en présentant le sésame:




$ curl http://localhost:9000/protectedResource -b "PLAY_SESSION=1701c43b7f845bdde0e38c0f43705d54b6815977-mail%3Atony%40stark.com" -v
* About to connect() to localhost port 9000 (#0)
*   Trying ::1...
* connected
* Connected to localhost (::1) port 9000 (#0)
> GET /protectedResource HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5
> Host: localhost:9000
> Accept: */*
> Cookie: PLAY_SESSION=1701c43b7f845bdde0e38c0f43705d54b6815977-mail%3Atony%40stark.com
>
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Content-Length: 15
<
* Connection #0 to host localhost left intact
{"test":"1234"}* Closing connection #0


On obtient donnée attendue, authentification réussie!



Maintenant je m'attaque au client:




<html ng-app="angularAuth" authenticator>
<head>
  <title>Angular authent</title>
    <script type="text/javascript" src="/public/javascripts/angular/angular.min.js"></script>
    <script type="text/javascript" src="/public/javascripts/angular/angular-resource.min.js"></script>
    <script type="text/javascript" src="/public/javascripts/app.js"></script>
    <script type="text/javascript" src="/public/javascripts/controllers.js"></script>
</head>
<body>
<h1>Simple authentication example</h1>
<div ng-view></div>
</body>
</html>


Mon module AngularJS est sommaire:




angular.module("angularAuth",['authServiceProvider']).
    config(['$routeProvider',function($routeProvider){
        $routeProvider.
            when("/", {templateUrl:"public/partials/protectedContent.html", controller:ProtectedCtrl}).
            when("/login",{templateUrl:"public/partials/login.html", controller:LoginCtrl}).
            otherwise({redirectTo:"/"})
    }]).
    directive('authenticator',function($location){
        return function(scope, elem, attrs){
            scope.$on('event:auth-loginRequired',function(){
                $location.path("/login")
            })
        }
    })  ;



En gros, quand "/" est demandé, affichage du template tirant la ressource protégée et lorsque "/login" est demandé, affichage du formulaire de login et redirection dans tous les autres cas.

Ensuite un listener va être positionné afin de réagir à l'évènement de demande d'authentification. L'utilisation de la directive permet de le placer au niveau du module, soit une fois pour toutes. Ne pas oublier de mentionner la directive dans le fichier html (moi, j'ai cherché un moment au début :-D).



Le concept présenté par Witold Szczerba est de tirer parti de la possibilité de poser des intercepteurs sur le service $http, c'est ce que je vais faire mais de façon plus naïve dans le module "authServiceProvider" injecté dans "angularAuth":




angular.module('authServiceProvider', []).
config(['$httpProvider', function($httpProvider) {

$httpProvider.responseInterceptors.push(function($q,$rootScope,$log){
function success(response) {
// $log.info(response)
return response
}

function error(response) {
if (response.status === 401) {
$log.error("401!!!!")
$rootScope.$broadcast('event:auth-loginRequired')
}
return $q.reject(response)
}

return function(promise) {
return promise.then(success, error)
}

})

}])



La fonction: si une requête retourne une erreur 401, alors l'évènement de demande d'authentification est propagé et stimule ainsi la directive et le formulaire de login apparaît. Je vous épargne un couplet sur l'API Promise d'AngularJS et cous encourage à aller consulter le Reference Guide.



Les contrôleurs du
formulaire d'authentification, de récupération de la ressource protégée et les
templates associés sont spartiates et consultables sur GitHub.



En introduction, j'ai présenté les arguments liés à la rationalisation de l'infrastructure d'hébergement, en revanche à l'heure actuelle il est clair que la charge de développement est supérieure comparée à une application dans laquelle les vues sont générées par le serveur: on y déclare simplement l'emplacement du formulaire de login.



La démo est testable sur
Heroku.