Go et Rust font partie de ses “nouveaux” langages très exitants : à la fois modernes mais aussi très puissants.
Je les ai présentés et comparés lors de ce meetup organisé par SQLI Nantes en juillet 2015.
Support de présentation :
La vidéo (à partir de 66’ pour ma conférence “Go & Rust”) :
J’ai eu à intervenir récemment sur un programme écrit en Play Framework v2.3, dont le rôle est assez simple : faire passe-plat entre un client et un serveur et effectuant notamment des transformations protocolaires (comme REST vers TCP par exemple). Ce qui m’a donné l’occasion d’utiliser les RedeemablePromise de Play Framework, pas du tout documentées à ce jour.
Client asynchrone
Les différents échanges de messages entre les systèmes peuvent être représentés à l’aide du diagramme de séquence suivant :
Le programme en Play Framework est nommé “PassePlat” dans ce diagramme.
Les messages montants sont implémentés sous forme d’une API RESTful ; tandis que le retour du “Serveur”, asynchrone, est implémentée avec des callbacks et une requête REST vers le client d’origine. Voici un exemple de pseudo-code Java sur le traitement du message de retour :
1234567891011121314151617181920212223
.../** * Receive responses from "server" */publicclassServerReceiver{publicvoidonServerResponse(Responseresponse){// Utilisation de la librairie play.libs.ws.WS// pour effectuer des appels RESTWS.url("http://client_url/api/response").setContentType("application/json").post(response.asJson());}publicvoidonServerError(Throwableerror){WS.url("http://client_url/api/response").setContentType("application/json")// Serialize exception as JSON.post(Helper.asJson(error);}}...
Le système complet fonctionne bien, mais n’est pas très satisfaisant :
le Client et le PassePlat sont très couplés. En effet, le Client doit connaître l’adresse du PassePlat pour lui envoyer les messages montants et le PassePlat doit connaitre l’adresse du Client pour lui renvoyer le message de retour. Bref, une dépendance cyclique ;
Le fonctionnement du Serveur est asynchrone (messages montants et de retours sont décorrélés) et cette implémentation a déporté l’asynchronisme jusqu’au Client, alors que le client (l’humain cette fois-ci :-) voulait plutôt un fonctionnement synchrone du Client, ce qui était plus facile à appréhender pour lui.
Client synchrone
Nous avons donc travaillés sur une implémentation du système plutôt comme ceci :
L’appel du Client vers le PassePlat est donc bloqué tant que l’acquittement et la réponse du Serveur ne sont pas parvenus au PassePlat.
Comment ?
On peut faire cela très simplement avec les RedeemablePromise de Play Framework.
Les RedeemablePromise sont une implémentation du design pattern promise, désormais répandu dans l’informatique pour résoudre le callback hell -l’enfer des callbacks-, problème très fréquent avec la programmation asynchrone. En effet, votre code est exécuté au sein de callbacks en réponse à des évènements : fin de traitement, lecture d’un fichier, arrivée d’un message par Web Socket, …
Voyons comment nous pouvons les utiliser pour répondre à notre nouveau besoin.
Adaptons la classe qui réceptionne les évènements venant du Serveur :
1234567891011121314151617181920212223
.../** * Receive responses from "server" */publicclassServerReceiver{// Initialisation de la promesse, à videprivateRedeemablePromise<Response>promise=RedeemablePromise.empty();// get/setpublicvoidonServerResponse(Responseresponse){// La promesse est résolue ou complétée avec succès icipromise.success(response);}publicvoidonServerError(Throwableerror){// La promesse est résolue ou complétée en erreur avec l'exceptionpromise.failure(error);}}...
L’élément clé ici est le fait de “résoudre” ou “compléter” la promesse en succès ou en erreur selon le cas. Attention à n’oublier aucun cas de fin de traitement du Serveur, sinon le Client restera bloqué dans ces cas non prévus.
Jetons maintenant un coup d’oeil au contrôleur REST sur le PassePlat qui reçoit les requêtes HTTP depuis le Client et qui “bloque” tant que la réponse du Serveur n’est pas parvenue :
1234567891011121314151617181920
...publicstaticPromise<Result>sendMessage(...){// Envoi du message entrant vers le serveur// server.send(message)ServerReceiverserverReceiver=newServerReceiver();// Ecoute des messages retourserver.listen(serverReceiver);return// Promesse de type Promise<Response>serverReceiver.getPromise()// Conversion 'success' en Promise<Result>.map(response->ok(JSON.toJson(response)););}...
Dans le cas nominal, la Response sera convertie en Result au sein de la méthode map (programmation fonctionnelle).
Et dans le cas où la promesse a été résolue en erreur ?
Par défaut, Play va générer une réponse HTTP avec un code retour 500 et une sérialisation de l’exception renvoyée. Si vous souhaitez définir vous-même votre propre retour, il faut que le traitement de l’erreur génère un Result standard.
Voici comment adapter la transformation de la promesse pour renvoyer une erreur 500 et une sérialisation de l’exception en cas de promesse résolue en erreur :
1234567891011121314
...return// Promesse de type Promise<Response>promise// Conversion 'success' en Promise<Result>.map(response->ok(Json.toJson(response));)// Conversion 'failure' en Promise<Result>.recover(error->// Serialize exception as JSON// and send HTTP 500 statusinternalServerError(Json.toJson(error)););
Au final, nous avons pu rendre l’appel du Client synchrone en à peine quelques lignes de code grâce à l’API riche de Play Framework.
Enfin, vous remarquerez que Java 8 améliore significativement la lisibilité du code. Cependant, on reste loin de Scala, de Groovy ou tout simplement de Javascript.
J’ai eu le plaisir de participer à cette superbe conférence que fut le Devfest Nantes 2014, à la fois en tant que visiteur mais aussi en tant qu’orateur.
J’y ai présenté la conférence “Mettez un Panda Roux dans votre Webview”, dont voici le résumé :
Au-delà du simple navigateur pour Android, Firefox est aussi une plateforme d’exécution d’applications HTML5 ouverte, les Open Web Apps. Après une présentation de Mozilla Geckoview et de ce qu’il peut apporter à vos applications HTML5 sous Android par rapport à une Webview standard (modernité, performances…), nous détaillerons l’initiative de Mozilla avec les Open Web Apps et verrons concrètement comment l’utiliser pour déployer des applications HTML5 sous Android.