<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Blog Excilys</title>
	
	<link>http://blog.excilys.com</link>
	<description>Langages, Architectures &amp; Méthodologies</description>
	<lastBuildDate>Mon, 22 Apr 2013 08:44:58 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/blogexcilyscom" /><feedburner:info uri="blogexcilyscom" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>DevopsDays Paris 2013</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/JAcKwjRImLw/</link>
		<comments>http://blog.excilys.com/2013/04/22/devopsdays-paris-2013/#comments</comments>
		<pubDate>Mon, 22 Apr 2013 08:44:58 +0000</pubDate>
		<dc:creator>Mathieu GRENONVILLE</dc:creator>
				<category><![CDATA[J'y étais !]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[devopsdays]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=6316</guid>
		<description><![CDATA[Les DevopsDays Paris ont eu lieu ces jeudi 18 et vendredi 19 avril 2013. Nous y étions, et voici ce que nous en retiendrons : [SPOILER] Du positif ! [/SPOILER] “Devops”, c’est quoi ? Tout le monde (ou presque) a du entendre ce buzz-word au moins une fois. Né de la concaténation de Developper et [...]]]></description>
				<content:encoded><![CDATA[<div style="text-align:justify">
<p>Les DevopsDays Paris ont eu lieu ces jeudi 18 et vendredi 19 avril 2013. Nous y étions, et voici ce que nous en retiendrons : [SPOILER] Du positif ! [/SPOILER]</p>
<p><a href="http://blog.excilys.com/wp-content/uploads/2013/04/devops2013_logo.jpg"><img src="http://blog.excilys.com/wp-content/uploads/2013/04/devops2013_logo-300x187.jpg" alt="devops2013_logo" width="300" height="187" class="aligncenter size-medium wp-image-6342" /></a></p>
<p><span id="more-6316"></span></p>
<p>“Devops”, c’est quoi ? Tout le monde (ou presque) a du entendre ce <em>buzz-word</em> au moins une fois. Né de la concaténation de Developper et Ops (abréviation de operations), le devops n’est ni une méthode, ni un poste (même si certaines sociétés embauchent des devops). Devops, c’est avant tout <strong>une culture, une mentalité, une way of life</strong>. C’est aussi beaucoup de bon sens.</p>
<p>Tout part d’un constat simple : le but d’un développeur est de livrer des nouvelles <strong>fonctionnalités</strong>. Le but d’un opérationnel est d’avoir une <strong>plateforme stable</strong>. Une livraison implique toujours un risque d’instabilité, mais sans livraison, pas de nouvelles fonctionnalités. Devops, c’est une culture qui va amener les gens à prendre conscience de ce que fait l’équipe d’à coté, et essayer de prendre en compte les contraintes de chacun dans le but <strong>d’améliorer la qualité</strong> du produit/projet.</p>
<p>L’amélioration continue avec la boucle <em>“Build, Measure, Learn”</em> est un excellent point de départ pour amener la culture devops au sein d’une entreprise. C’est d’ailleurs sous cet angle que nous avons choisi de vous présenter les différentes conférences qui nous ont le plus marqués.</p>
<h3>Conférences</h3>
<h4>Build</h4>
<h5>Comment l&#8217;ops a amélioré mon dev</h5>
<blockquote><p>“I don’t want to be called at night, so I call myself a developer”</p></blockquote>
<p><a href="https://twitter.com/Argorak" target="_blank">Florian Gilcher</a> nous explique pourquoi connaitre le point de vue d’un op permet d’améliorer la qualité d’un développement et de mieux gérer la complexité “accidentelle”. Prenons l’exemple d’un développeur qui ne s’occupe pas des problématiques ops. Il va écrire un programme qui va :</p>
<ul>
<li>lire des données (depuis différents formats)</li>
<li>traiter les données</li>
<li>écrire les résultats (dans différents formats)</li>
</ul>
<p>un seul livrable mais une classe par action, tout est bon. <strong>Simple pour le dev, mais compliqué pour les ops</strong> : il faut tout redéployer (donc interrompre) à chaque changement, il peut y avoir des problèmes de scalabilité, etc.</p>
<p>En prenant en compte le point de vue des ops, le dev écrira plutôt <strong>un exécutable par tâche</strong> : pas besoin d’arrêter la lecture pour corriger le traitement, si une tâche est beaucoup plus gourmande en ressources, le monitoring permettra de le détecter et l’op d’ajouter plus de processus.</p>
<p>On arrive donc à un exemple peut-être légèrement moins simple pour le dev mais beaucoup <strong>plus facile à gérer pour les ops</strong>. Desormais, Florian se concentre plus sur la qualité “externe” de son appli (sa configuration, sa CLI, etc) que sur la qualité du code “interne”.<br />
Florian nous explique également les problèmes d’une séparation stricte entre dev et ops. Cela devient difficile voire impossible de refactorer la plateforme : les choses vont devenir politiques très vite ! Et sans pratique, les compétences pour ce genre de refactoring risquent de disparaître.<br />
Certaines parties du code risquent également de ne pas pouvoir bénéficier de revue de code, faute de gens pouvant le faire.</p>
<p>La culture devops permet de diminuer les frictions entre les dev et les ops, de <strong>partager la connaissance</strong> et de réfléchir à l’application dans sa globalité.</p>
<h5>Success story @ GOV.UK</h5>
<p><a href="https://twitter.com/KushalP" target="_blank">Kushal Pisavadia</a> nous présente l’origine de gov.uk et comment les choses se sont déroulées jusqu’à aujourd’hui.</p>
<p>Le point de départ est un rapport de 2010 intitulé <a href="http://www.gov.uk/government/publications/directgov-2010-and-beyond-revolution-not-evolution-a-report-by-martha-lane-fox" target="_blank">Directgov 2010 and beyond: revolution not evolution</a>. Ce rapport constate que le site web du gouvernement britannique d’alors, DirectGov, n’est clairement pas adapté et préconise de <strong>ne pas le faire évoluer mais de le réinventer</strong>. Pour cela, le rapport propose :</p>
<ul>
<li>un centre d’excellence <em>dans</em> le gouvernement</li>
<li>corriger la publication (il y avait des centaines de sites web indépendants)</li>
<li>corriger les services proposés (trop complexes)</li>
<li>devenir fournisseur de services pour d’autres sites (en proposant des API publiques)</li>
</ul>
<p>Pour cela, gov.uk s’est donné comme objectif de <strong>se concentrer sur les besoins de l’utilisateur</strong>, de <strong>livrer rapidement</strong> et de <strong>tout mesurer</strong>. En été 2011 sort le site <a href="http://alpha.gov.uk/" target="_blank">http://alpha.gov.uk/</a> s’appuyant sur <a href="http://www.gov.uk/designprinciples" target="_blank">ces principes</a>. Le projet a joué la carte du libre en utilisant des logiciels libres mais aussi en libérant leur code source sur <a href="https://github.com/alphagov" target="_blank">Github</a>. Gov.uk utilise des outils comme capistrano, jenkins ou puppet pour accélérer les déploiements.</p>
<p>Cette vitesse est un vrai atout : <strong>4h</strong> pour qu’un développement arrive en production, jusqu’à <strong>20</strong> déploiements par jour, &#8230; Ou encore, le dernier patch critique de ruby a été déployé seulement <strong>1h30</strong> après sa sortie !</p>
<p>Ce projet est clairement dans l’<strong>esprit devops</strong>, allant jusqu’à avoir une <a href="http://www.gov.uk/service-manual/operations/devops.html" target="_blank">page à part entière dessus !</a> La page décrivant <a href="http://www.gov.uk/service-manual" target="_blank">les bonnes pratiques</a> pour développer un service en général est également très instructive.</p>
<h4>Measure</h4>
<h5>Le territoire et la carte, une histoire de visibilité</h5>
<p><a href="http://www.spootnik.org/entries/2013/04/22_map-territory-a-story-of-visibility.html" target="_blank">Au cours de sa présentation</a>, <a href="https://github.com/pyr" target="_blank">Pierre-Yves Ritschard</a> fait une analogie : la plateforme est le territoire, le monitoring est sa carte. Or notre métier étant très abstrait, nous n’avons pas accès au territoire, seulement à la carte. C’est pourquoi nous devons absolument avoir une carte <strong>la plus précise possible</strong> (même si cette carte n’est pas le territoire).</p>
<p>Pierre-Yves prend l’exemple des serveurs dans les années 2000 et ceux de 2013. D’un côté, un LAMP (Linux, Apache, MySQL, PHP) et de l’autre HA_PROXY, un cluster de Nginx, Reddis, mysql, cassandra, PHP. Le nombre de serveur a <strong>explosé</strong> en 13 ans. En revanche, la plupart des outils de monitoring sont <strong>identiques</strong> : CPU, mémoire.</p>
<p>L’explosion du nombre de serveurs est en partie due à la <strong>complexité toujours plus grande des sites</strong>. Désormais, la page d’accueil d’un journal agrège des tweets, d’autres journaux, des tendances, des commentaires. C’est pourquoi il faut inventer de nouvelle manière de monitorer sa plateforme : “Extracting meaningful state data from heterogeneous event sources over time”. Il devient nécessaire de mettre en face des métriques traditionnelles d’autres événements : CPU et chiffre d’affaire en fonction du temps.</p>
<h5>L&#8217;approche Event-Stream</h5>
<p>Une des méthodes proposée par Pierre-Yves est l’approche Event Stream. Une grande quantité de petits producteurs vont générer des données pour quelques gros consommateurs.</p>
<p>Les producteurs de données devront normaliser et streamer les évènements. Les consommateurs vont consommer ces évènements en les agrégeant (ratios, sommes, &#8230;), les corrélant (mettre en rapport un opération de maintenance et le temps de réponse.) et décider (surveiller, alerter, ignorer, scaler).</p>
<p>Comment améliorer la visibilité dans tout ça ? Il faut trouver les bonnes métriques en <strong>partant du business</strong>(ex. la home du journal). Ensuite, descendre vers les métriques traditionnelles. Là encore, il conviendra de choisir les <strong>bons outils</strong>, <strong>impliquer</strong> tout le monde et <strong>challenger</strong> les gens. Une des conséquences sera l&#8217;amélioration de la qualité et la baisse du taux d&#8217;erreurs.</p>
<h4>Learn</h4>
<h5>Produit vs Projet</h5>
<p>Pourquoi internet regorge-t&#8217;il de produits qui fonctionnent et les DSI de projets qui échouent ? <a href="https://twitter.com/El_Picador" target="_blank">Rémy-Christophe Schermesser</a> commence par nous rappeler ce qu’est un projet :</p>
<blockquote><p>On appelle projet un ensemble finalisé d’activités et d’actions entreprises dans le but de répondre à un besoin défini dans des délais fixés et dans la limite d&#8217;une enveloppe budgétaire allouée.<br />
 wikipedia.fr
</p></blockquote>
<p>En revanche le produit c’est tout ce qui peut satisfaire un besoin. C’est donc beaucoup plus facile de rater un projet qu’un produit ! Il faut donc redéfinir la notion de succès pour un projet. Est-ce vraiment du temps, un budget, et un besoin ? Pour un produit, on prend en compte uniquement l’<strong>amélioration du travail des utilisateurs</strong> !</p>
<p>Dans la démarche produit, toute idée est bonne à prendre. L’appliquer à un projet est certainement une excellente idée. Mais comment réussir ? <strong>Build, measure, learn</strong>, là encore !</p>
<p>Une fonctionnalité n’a de la valeur que si elle est utilisée. Le but est de créer le <strong>Minimum Viable Product</strong>, puis de l’améliorer en allant à la rencontre des utilisateurs.</p>
<p>Un autre problème régulièrement rencontré dans les projets est la conduite du changement. Pourquoi Gmail peut changer la vue de création de messages, et pourquoi ne peut-on pas changer de place un bouton dans notre back-office ? Il est important de comprendre que l’utilisateur ne peut <strong>pas être laissé seul face à ce changement</strong>. L’apparition la première fois d’une aide comme gmail l’a fait pour la vue “nouveau mail” est une excellente idée (les devs pourront aller voir du coté de <a href="http://heelhook.github.io/chardin.js/" target="_blank">Chardin.js</a>)</p>
<p>Ensuite, pour mesurer la satisfaction utilisateur, il faut savoir que les utilisateurs <strong>mentent</strong> aux questionnaires (voir <a href="http://www.ted.com/talks/malcolm_gladwell_on_spaghetti_sauce.html" target="_blank">l’exemple de la sauce tomate dans les années 50 aux USA</a>). Ce n’est donc pas la bonne solution. Remy-Christophe propose plutôt de s&#8217;intéresser à la seule métrique <strong>impartiale et utile</strong> : le temps passé pour une tâche.</p>
<p>Enfin, apprendre ! Et apprendre à échouer ! Et échouer pour apprendre.</p>
<p>Conclusion : Think Product, Do project. La collaboration entre équipes est le secret.</p>
<h5>L’apprentissage de la culture devops chez un dev</h5>
<p><a href="https://twitter.com/theodo" target="_blank">Fabrice Bernhard</a> nous propose une conférence intitulée <em>“Transforming Devs into Devops”</em>. Le constat est que le business a besoin de <strong>rapidité</strong>. Dans la nature, ce n’est pas la grosse bête qui mange la petite, c’est <strong>la rapide qui mange la lente</strong>. C’est pourquoi il est important de limiter un maximum le gaspillage de temps : les méthodes agiles ont amélioré la communication MOA-MOE, le devops améliore la communication Dev-Ops.</p>
<p>Fabrice présente ensuite sa méthode en tant que directeur technique Theodo pour transformer un dev en devops. Prenez un développeur junior sorti d’école. La caricature veut qu’il utilise Filezilla pour modifier son portail étudiant, et le <a href="http://roland.entierement.nu/blog/2008/01/22/cpold-la-poudre-verte-du-suivi-de-versions.html" target="_blank">CPOLD</a>+mail comme gestionnaire de sources. Saupoudrez ce dev junior avec du <strong>git, du SCRUM, du TDD</strong> et laissez mariner 2 mois à température sur une banquise <strong>GNU/Linux</strong>. Le dev junior va devenir un dev devops-friendly. Il faut ensuite l’amener à déployer sur une plateforme ayant suffisamment d’<strong>importance pour le responsabiliser</strong>. Fabric semble être le bon outil pour l’automatisation.</p>
<p>Attention cependant,<strong> un dev et de (bons) outils n’est pas devops</strong>. Il y a encore du chemin à parcourir. Fabrice parle de Pair Devopsing, de sensibilisation aux performances (en incluant dans la définition d’une tache terminée des temps de réponse, en affichant dans la salle des rapport de tests de performances), et surtout en <strong>partageant la responsabilité</strong> d’une plateforme stable. </p>
<h3>Openspaces</h3>
<p><a href="http://blog.excilys.com/wp-content/uploads/2013/04/devops2013_openspace.jpg"><img src="http://blog.excilys.com/wp-content/uploads/2013/04/devops2013_openspace-150x150.jpg" alt="devops2013_openspace" width="150" height="150" class="alignleft size-thumbnail wp-image-6336" /></a></p>
<p><a href="http://blog.excilys.com/wp-content/uploads/2013/04/devops2013_openspace2.jpg"><img src="http://blog.excilys.com/wp-content/uploads/2013/04/devops2013_openspace2-150x150.jpg" alt="devops2013_openspace2" width="150" height="150" class="alignright size-thumbnail wp-image-6337" /></a></p>
<p>Pour nous, c&#8217;était un mystère. C&#8217;est semble-t-il suffisamment intéressant pour y consacrer <strong>autant d&#8217;importance</strong> que les conférences. Et l&#8217;expérience nous montre que c&#8217;est effectivement très intéressant ! Le déroulement est simple : On commence par ouvrir les openspaces en prenant 1h30 pour construire un tableau avec des post-it contenant des thèmes de discussion proposés par l&#8217;assemblée. <strong>Chacun est libre de proposer un sujet </strong>(question, retour d&#8217;expérience, recette de piña colada), qui sera ensuite soumis à un vote, fusionné avec des sujets identiques.</p>
<p>Comme l&#8217;a dit un des organisateurs, ce n&#8217;est pas un concours de popularité, un sujet peut ne pas être choisi, voire l&#8217;auteur de la proposition peut se retrouver seul dans la salle. &#8220;Depuis quand n&#8217;avez vous pas pris 45 minutes pour vous introspecter ?&#8221;</p>
<p>Les discussions durent environ 45 minutes, les horaires de démarrage et de fin sont donnés à titre <strong>indicatif</strong> (dans la mesure du raisonnable, <em>&#8220;show must go on&#8221;</em>). Les organisateurs nous ont fortement incités à <strong>papillonner</strong> entre les différentes salles. Les règles qui sont en vigueur sont les suivantes :</p>
<ul>
<li>Whoever comes is the right people</li>
<li>Whatever happens is the only thing that could have</li>
<li>Whenever it starts is the right time</li>
<li>When it’s over, it’s over</li>
</ul>
<p>Et une règle d&#8217;or, celle des deux pieds : <strong>Si vous avez l&#8217;impression de ne rien apprendre, prenez vos deux pieds et allez ailleurs.</strong></p>
<p>Il est très difficile de faire un résumé des différentes sessions, l&#8217;ambiance ressemblant beaucoup à une discussion dans un openspace (tiens donc !) où le sujet final est souvent loin de celui annoncé. Pour se faire une idée des thèmes abordés, voici les discussions auxquelles nous avons assisté.</p>
<h4>Refactoring infrastructures</h4>
<p>Une discussion sur la problématique d’un participant : un écosystème d’applications (discutant en HTTP) à <strong>repenser entièrement</strong>. Les conseils ont été de versionner les API et de refactorer morceau par morceau. On a également parlé d’améliorer les remontées de métriques vers les devs et évoquer la possibilité d’un tableau de bord centralisant monitoring, données métier, etc</p>
<h4>Diffusion de la culture Devops dans l&#8217;entreprise</h4>
<p>La question initiale venait d’un des participants qui voudrait <strong>mettre en avant la culture devops</strong> dans son entreprise, celle-ci n’étant “que” agile. La discussion a assez vite dérivé vers la problématique de conserver cette culture devops lors de la “deuxième génération” d’embauche : Il est facile de déplacer la responsabilité sur les devs de certaines choses (ce qui s’est passé pour un des participants). Ainsi, la <strong>culture s’entretient</strong>, et nécessite une surveillance de tous les instants (measure), et prendre des actions correctives en cas de soucis. C’est finalement un <strong>combat de tous les jours</strong>.</p>
<h4>Comment être un meilleur dev java pour ses ops ?</h4>
<p>En utilisant la règle des deux pieds, on se retrouve parfois à prendre une discussion en plein milieu. Ce qui donne (à la traduction en français près) : “&#8230; sécurité entre les ops qui partent du principe que tout est fermé et les devs pour qui tout est ouvert.”.</p>
<p>Les ops présents ont également exprimé leur<strong> besoin d’avoir accès (en ligne de commande) à l’état de santé de l’application </strong>(un health-check : en état de marche ou HS) pour le load-balancer. Il faut aussi pouvoir interagir avec l’application: changer le niveau de log sans avoir à la redémarrer ; si elle possède un cache en écriture, flusher ce cache pour pouvoir faire un backup (consistent).</p>
<p>Enfin, communiquer sur les problématiques de mémoire ! Xmx, Xms, &#8230; Les besoins en mémoire d’une application (sans parler du tuning de jvm) <strong>ne s’inventent pas</strong>.</p>
<h4>retours d’expérience de devs et ops ayant vécu le passage devops</h4>
<p>La principale chose à retenir est : <strong>communiquez</strong> ! Impliquez les ops tôt dans le projet !</p>
<h4>Mini-jeu de Patrick Debois</h4>
<p>Il s’agissait plus d’une expérience que d’un openspace proprement dit. Le but du jeu était d’<strong>analyser une infrastructure</strong> dans son ensemble pour ensuite dégager <strong>des indicateurs et des métriques</strong> à monitorer. Le jeu se déroule en groupe de 4-5. Une des personnes du groupe parle de son architecture, et les autres participants la questionnent pour identifier les métriques.</p>
<p>Notre groupe a plutôt ressemblé à un cours sur le monitoring, puisque l’infrastructure choisie était celle de <a href="https://twitter.com/hgomez">@hgomez</a>, dont on ne peut que vous recommander <a href="http://www.devoxx.com/display/FR13/Monitoring+Open+Source+pour+Java+avec+JmxTrans,+Graphite+et+Nagios" target="_blank">la présentation sur le monitoring qui a eu lieu à la DevoxxFr 2013</a> (<a href="http://www.slideshare.net/cyrille.leclerc/open-source-monitoring-for-java-with-graphite" target="_blank">les slides</a>, en attendant la rediffusion sur parleys.)</p>
<h3>Conclusion</h3>
<p>Deux jours, c’est presque trop court ! Nous revenons avec des idées plein la tête, une vision transformée, et une féroce envie d’aller aux <a href="http://parisdevops.fr/meetups.html" target="_blank">Paris devops</a> !</p>
<h4>Annexes</h4>
<p>Les slides des ignites talks dont nous n&#8217;avons pas parlé, mais dont le format est parfait ! </p>
<ul>
<li><a href="http://zeroturnaround.com/labs/rebel-labs-release-it-ops-devops-productivity-report-2013/">http://zeroturnaround.com/labs/rebel-labs-release-it-ops-devops-productivity-report-2013/</a> </li>
<li><a href="http://www.slideshare.net/devopsdays/what-if-devops-was-invented-by-coca-cola">http://www.slideshare.net/devopsdays/what-if-devops-was-invented-by-coca-cola</a></li>
<li><a href="http://devopsdays.org/events/2013-paris/proposals/PerformancesPerpetuellesIgnite/">http://devopsdays.org/events/2013-paris/proposals/PerformancesPerpetuellesIgnite/</a> (<a href="http://gatling-tool.org/" target="_blank">Gatling</a> roxx !)</li>
<li><a href="https://github.com/philandstuff/devopsdaysparis">https://github.com/philandstuff/devopsdaysparis</a> : Unit Testing puppet code</li>
</ul>
<p>Pour le moment, peu de ressources sont disponibles, nous essayerons de maintenir la liste à jour !</p>
</div>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2013/04/22/devopsdays-paris-2013/" data-text="DevopsDays Paris 2013"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2013/04/22/devopsdays-paris-2013/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2013%2F04%2F22%2Fdevopsdays-paris-2013%2F&amp;title=DevopsDays%20Paris%202013" id="wpa2a_2"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/JAcKwjRImLw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2013/04/22/devopsdays-paris-2013/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2013/04/22/devopsdays-paris-2013/</feedburner:origLink></item>
		<item>
		<title>Faire une application Comet avec Play 2</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/7ow7dV9sIWI/</link>
		<comments>http://blog.excilys.com/2013/03/11/faire-une-application-comet-avec-play-2/#comments</comments>
		<pubDate>Mon, 11 Mar 2013 12:51:19 +0000</pubDate>
		<dc:creator>Denis VAUMORON</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[comet]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[play]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=5120</guid>
		<description><![CDATA[Cet article a pour but de présenter l&#8217;utilisation de l&#8217;approche Comet au sein du framework Play a travers la création d&#8217;une application de chat basique. Rappel sur les protagonistes Play est un framework MVC de développement rapide pour Java et Scala, dont la version 2 est sortie le 13 mars 2012. Comet est une approche [...]]]></description>
				<content:encoded><![CDATA[<p>Cet article a pour but de présenter l&#8217;utilisation de l&#8217;approche Comet au sein du framework Play a travers la création d&#8217;une application de chat basique.</p>
<h3>Rappel sur les protagonistes</h3>
<p><a href="http://www.playframework.org/">Play</a> est un framework MVC de développement rapide pour Java et Scala, dont la version 2 est sortie le 13 mars 2012.<br />
<a href="http://en.wikipedia.org/wiki/Comet_(programming)">Comet</a> est une approche permettant au serveur web d&#8217;envoyer des informations au navigateur web sans que celui-ci l&#8217;ait explicitement demandé.<br />
<span id="more-5120"></span></p>
<h3>Premier pas</h3>
<p>Pour utiliser Play vous devez avoir au moins java 6. L&#8217;installation de Play est simple, une fois le zip téléchargé, il suffit de le décompresser dans un dossier où vous avez les droits de lecture et d&#8217;écriture (lancer play écrit des fichiers dans les dossiers de l&#8217;archive), ajouter le dossier de play dans votre PATH et vous êtes prêt à continuer.<br />
Pour créer le projet, placer vous dans le répertoire où vous souhaitez créer le projet et tapez la commande suivante:</p>
<pre>play new chatApp</pre>
<p>&nbsp;<br />
Play vous demande un titre pour votre application, mettons par exemple &#8220;Chat application&#8221;, puis vous avez à choisir le modèle d&#8217;application, nous allons utiliser le modèle java. Play crée alors le dossier chatApp pour vous, si vous explorez son contenu vous y trouverez notamment les dossiers suivants:</p>
<ul>
<li>app/ qui contient le coeur de l&#8217;application séparé entre modèles (models/), vues (views/) et contrôleurs (controllers/)</li>
<li>conf/ qui contient les fichiers de configuration dont application.conf, routes et messages (pour l&#8217;i18n)</li>
<li>project/ qui contient les scripts de build <a href="http://www.scala-sbt.org/">sbt</a></li>
<li>public/ qui contient les fichiers statiques (javascript, css, image, etc&#8230;)</li>
<li>test/ qui contient les fichiers de test JUnit</li>
</ul>
<h3>L&#8217;utilisation d&#8217;eclipse</h3>
<p>Bien que l&#8217;on puisse utiliser un simple éditeur de texte (Play gère la compilation et le déploiement), personnellement j&#8217;ai du mal à me passer de l&#8217;auto complétion, or Play s&#8217;intègre très facilement avec Eclipse, pour cela placez-vous dans le dossier de votre projet puis lancez la console Play:</p>
<pre>play</pre>
<p>&nbsp;<br />
et utilisez la commande suivante :</p>
<pre>eclipsify</pre>
<p>&nbsp;<br />
Vous pouvez alors importer le projet dans Eclipse. Si vous utiliser un autre IDE référez vous à cette <a href="http://www.playframework.org/documentation/2.0.4/IDE">documentation</a>.</p>
<p>Attention ! Play utilise l&#8217;encodage UTF-8, assurez vous d&#8217;avoir correctement configuré votre éditeur.</p>
<h3>Lancer le serveur</h3>
<p>Pour lancer le serveur utilisez la commande suivante dans la console Play:</p>
<pre>run</pre>
<p>&nbsp;<br />
Vous pouvez alors ouvrir votre navigateur et aller à l&#8217;adresse <a href="http://localhost:9000">http://localhost:9000/</a> pour constater que vous avez une belle page de démonstration. La question existentielle que vous devez vous posez c&#8217;est : &#8220;mais pourquoi ?&#8221;, un début de réponse se trouve dans le fichier conf/routes, plus précisement la ligne :</p>
<pre>GET     /                           controllers.Application.index()</pre>
<p>&nbsp;<br />
Celle-ci indique au serveur de rediriger un HTTP Get sur l&#8217;url / vers la méthode index de la classe Application qui ressemble à ça :</p>
<pre>

<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Result index<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> ok<span style="color: #009900;">&#40;</span>index.<span style="color: #006633;">render</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Your new application is ready.&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>Comme vous pouvez le constater celle-ci renvoie un objet Result qui represente la réponse HTTP à renvoyer au navigateur, ici ok (qui correspond au code HTTP 200) avec un contenu. Ce contenu est généré par un template compilé en une simple classe java avec une méthode render. Ce template est défini dans app/views/index.scala.html :</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@(message: String)<br />
<br />
@main(&quot;Welcome to Play 2.0&quot;) {<br />
<br />
&nbsp; &nbsp; @play20.welcome(message)<br />
<br />
}</div></td></tr></tbody></table></div>

</pre>
<p>La première ligne indique la signature de la méthode, la suite peut être un mélange de code Scala préfixé par @, et du HTML, ici @main appelle le template défini dans main.scala.html et @play20.welcome génère le contenu de la page de démonstration. Le template main.scala.html définit la structure de la page :</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@(title: String)(content: Html)<br />
<br />
&lt;!DOCTYPE html&gt;<br />
<br />
&lt;html&gt;<br />
&nbsp; &nbsp; &lt;head&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;title&gt;@title&lt;/title&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;link rel=&quot;stylesheet&quot; media=&quot;screen&quot; href=&quot;@routes.Assets.at(&quot;stylesheets/main.css&quot;)&quot;&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;link rel=&quot;shortcut icon&quot; type=&quot;image/png&quot; href=&quot;@routes.Assets.at(&quot;images/favicon.png&quot;)&quot;&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;script src=&quot;@routes.Assets.at(&quot;javascripts/jquery-1.7.1.min.js&quot;)&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;<br />
&nbsp; &nbsp; &lt;/head&gt;<br />
&nbsp; &nbsp; &lt;body&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; @content<br />
&nbsp; &nbsp; &lt;/body&gt;<br />
&lt;/html&gt;</div></td></tr></tbody></table></div>

</pre>
<p>Les instructions @routes.Assets.at permettent de pointer vers le contenu du dossier public.</p>
<h3>Et si on commençait&#8230;</h3>
<p>Pour commencer changeons simplement le retour de la méthode index pour ok(&#8220;Hello world&#8221;). Pour voir le changement, pas besoin de redémarrer le serveur, vous avez juste à rafraichir la page. Et si vous vous trompez ? Essayez d&#8217;enlever la parenthèse fermante par exemple, le serveur Play affiche une belle page vous indiquant l&#8217;erreur de compilation&#8230;<br />
Pour notre application de chat, nous allons avoir besoin de trois actions : l&#8217;affichage de la page, l&#8217;envoi d&#8217;un message et la récupération des messages. On met à jour le fichier routes :</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">GET &nbsp; &nbsp; / &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; controllers.Application.index()<br />
GET &nbsp; &nbsp; /comet &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;controllers.Application.comet()<br />
POST &nbsp; &nbsp;/ajax &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; controllers.Application.ajax()</div></td></tr></tbody></table></div>

</pre>
<p>Toujours pareil, on recharge la page et on voit le résultat, et là PAF, ça ne compile pas, eh oui, le fichier routes lui aussi est compilé. Pour corriger cette erreur on ajoute à Application les méthodes suivantes :</p>
<pre>

<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Result comet<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> TODO<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Result ajax<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> TODO<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>On rafraichit la page, victoire ! ça compile, on essaye /ajax ou /comet, et là, on a une belle page TODO. En fait, le serveur renvoie le code HTTP 501 not implemented.<br />
Passons aux choses sérieuses, l&#8217;utilisation de Comet. Pour Play un résultat Comet est un résultat comme les autres :</p>
<pre>

<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Result comet<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Comet comet <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Comet<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;parent.receiveMessage&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @Override<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> onConnected<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// TODO</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> ok<span style="color: #009900;">&#40;</span>comet<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>Vous remarquerez l&#8217;argument passé au constructeur indiquant la méthode javascript qui sera appelée par le navigateur ainsi que la méthode onConnected qui sera appelée lorsque le flux sera prêt.<br />
Maintenant il nous faut nous occuper des messages, on va donc créer une classe Message (oui j&#8217;aime faire des surprises) :</p>
<pre>

<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">package</span> <span style="color: #006699;">models</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">play.data.validation.Constraints.Required</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> Message <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; @Required<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">String</span> message<span style="color: #339933;">;</span><br />
<br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>Notez l&#8217;utilisation de l&#8217;annotation @Required qui permet à play de gérer la validation du formulaire. Pour tester la validation et récupérer le contenu, on utilise la classe Form (immutable) :</p>
<pre>

<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Form<span style="color: #339933;">&lt;</span>Message<span style="color: #339933;">&gt;</span> messageForm <span style="color: #339933;">=</span> form<span style="color: #009900;">&#40;</span>Message.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Result ajax<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Form<span style="color: #339933;">&lt;</span>Message<span style="color: #339933;">&gt;</span> filledForm <span style="color: #339933;">=</span> messageForm.<span style="color: #006633;">bindFromRequest</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>filledForm.<span style="color: #006633;">hasErrors</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Message message <span style="color: #339933;">=</span> filledForm.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// TODO utilisation du message</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> ok<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>Il ne nous manque plus que le serveur de chat avec la classe ChatServer (encore une surprise) :</p>
<pre>

<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">package</span> <span style="color: #006699;">models</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">java.util.ArrayList</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">java.util.List</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">play.libs.Comet</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> ChatServer <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> ChatServer INSTANCE <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> ChatServer<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">List</span> listeners <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">ArrayList</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> addListener<span style="color: #009900;">&#40;</span>Comet listener<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; listeners.<span style="color: #006633;">add</span><span style="color: #009900;">&#40;</span>listener<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> sendMessage<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> message<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">for</span><span style="color: #009900;">&#40;</span>Comet listener <span style="color: #339933;">:</span> listeners<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; listener.<span style="color: #006633;">sendMessage</span><span style="color: #009900;">&#40;</span>message<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>Comme vous pouvez le constater notre serveur de chat se contente de renvoyer à tous les flux Comet le message reçu. Nous avons tous les composants, maintenant éditons la vue :</p>
<pre>

<div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br /></div></td><td><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@(messageForm: Form[Message])<br />
<br />
@import helper._<br />
<br />
@main(&quot;Chat application&quot;) {<br />
<br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">ul</span> <span style="color: #000066;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;messageList&quot;</span>&gt;&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">ul</span>&gt;</span><br />
<br />
&nbsp; &nbsp; @form(routes.Application.ajax()) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; @inputText(messageForm(&quot;message&quot;))<br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">input</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;submit&quot;</span> <span style="color: #000066;">value</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;send&quot;</span>&gt;</span><br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">script</span>&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; var form = $(&quot;form&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; form.submit(function(event) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; event.preventDefault();<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.ajax({<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: &quot;POST&quot;,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; url: form.attr(&quot;action&quot;),<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data: form.serialize()<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $(&quot;#message&quot;).val(&quot;&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; });<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; var receiveMessage = function(event) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $(&quot;#messageList&quot;).append(&quot;<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">li</span>&gt;</span>&quot; + event + &quot;<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">li</span>&gt;</span>&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">iframe</span> <span style="color: #000066;">class</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;invisible&quot;</span> <span style="color: #000066;">src</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;/comet&quot;</span>&gt;&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">iframe</span>&gt;</span><br />
<br />
}</div></td></tr></tbody></table></div>

</pre>
<p>&nbsp;<br />
L&#8217;import de helper._ permet d&#8217;utiliser les tags @form et @input pour créer le formulaire. On utilise JQuery pour transformer la soumission du formulaire en appel Ajax et effacer le contenu du champ. La classe css invisible sert juste à masquer l&#8217;iframe qui reçoit le flux Comet :</p>
<pre>

<div class="codecolorer-container css default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="css codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #6666ff;">.invisible</span> <span style="color: #00AA00;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">display</span><span style="color: #00AA00;">:</span> None<span style="color: #00AA00;">;</span><br />
<span style="color: #00AA00;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>Au final, votre Application.java devrait ressembler à ça :</p>
<pre>

<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">package</span> <span style="color: #006699;">controllers</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">models.ChatServer</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">models.Message</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">play.*</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">play.data.*</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">play.libs.*</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">play.mvc.*</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">views.html.*</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> Application <span style="color: #000000; font-weight: bold;">extends</span> Controller <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Form messageForm <span style="color: #339933;">=</span> form<span style="color: #009900;">&#40;</span>Message.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Result index<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> ok<span style="color: #009900;">&#40;</span>index.<span style="color: #006633;">render</span><span style="color: #009900;">&#40;</span>messageForm<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Result comet<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Comet comet <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Comet<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;parent.receiveMessage&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @Override<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> onConnected<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ChatServer.<span style="color: #006633;">INSTANCE</span>.<span style="color: #006633;">addListener</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> ok<span style="color: #009900;">&#40;</span>comet<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> Result ajax<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Form filledForm <span style="color: #339933;">=</span> messageForm.<span style="color: #006633;">bindFromRequest</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>filledForm.<span style="color: #006633;">hasErrors</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ChatServer.<span style="color: #006633;">INSTANCE</span>.<span style="color: #006633;">sendMessage</span><span style="color: #009900;">&#40;</span>filledForm.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">message</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> ok<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>Et voilà, c&#8217;est fini, vous pouvez ouvrir plusieurs navigateur et envoyer des messages entre eux&#8230;</p>
<h3>Conclusion</h3>
<p>Les sources sont disponible sur <a href="https://github.com/dvaumoron/chatApp">github</a>. Pour aller plus loin vous pouvez jeter un oeil dans le dossier samples de play, vous y trouverez des exemples en java et en scala, dont notamment une application de chat avancée utilisant les websockets avec chatRoom et robot. Et bien sur il y a la <a href="http://www.playframework.org/documentation">documentation</a> (en anglais).</p>
<p>A bientôt pour de nouvelles aventures sur le blog Excilys…</p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2013/03/11/faire-une-application-comet-avec-play-2/" data-text="Faire une application Comet avec Play 2"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2013/03/11/faire-une-application-comet-avec-play-2/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2013%2F03%2F11%2Ffaire-une-application-comet-avec-play-2%2F&amp;title=Faire%20une%20application%20Comet%20avec%20Play%202" id="wpa2a_4"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/7ow7dV9sIWI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2013/03/11/faire-une-application-comet-avec-play-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2013/03/11/faire-une-application-comet-avec-play-2/</feedburner:origLink></item>
		<item>
		<title>La pagination facile avec Spring 3.x et jQuery.</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/zhlE8uWAvXE/</link>
		<comments>http://blog.excilys.com/2013/01/28/la-pagination-facile-avec-spring-3-x-et-jquery/#comments</comments>
		<pubDate>Mon, 28 Jan 2013 12:53:58 +0000</pubDate>
		<dc:creator>Mohamed EL KASHEF</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[dataTable]]></category>
		<category><![CDATA[jackson]]></category>
		<category><![CDATA[JQuery]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[pagination]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[Spring 3.x]]></category>
		<category><![CDATA[tutoriel]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=5701</guid>
		<description><![CDATA[Introduction Qu&#8217;est ce que je veux faire ? Mon objectif principal est la présentation de résultats de recherche sous forme de tableau avec gestion de la pagination. Du grand classique! Quelles sont mes contraintes ? Pour des raisons de performance et de bon sens, je veux que la pagination soit gérée côté serveur pour éviter [...]]]></description>
				<content:encoded><![CDATA[<h3><a href="http://blog.excilys.com/2013/01/28/la-pagination-facile-avec-spring-3-x-et-jquery/sequence-affichage-resultats-de-recherche/" rel="attachment wp-att-5728"><br />
</a>Introduction</h3>
<h4>Qu&#8217;est ce que je veux faire ?</h4>
<p>Mon objectif principal est la présentation de résultats de recherche sous forme de tableau avec gestion de la pagination. Du grand classique!</p>
<h4>Quelles sont mes contraintes ?</h4>
<ul>
<li>Pour des raisons de performance et de bon sens, je veux que la pagination soit gérée côté serveur pour éviter de charger entièrement tous les résultats;</li>
<li>J&#8217;aimerais que la navigation entre les différentes pages de recherche soit fluide;</li>
<li>Enfin, j&#8217;aimerais en tant que développeur que ça marche et que ça soit relativement beau avec le minimum d&#8217;effort.</li>
</ul>
<h4>Comment je vais m&#8217;y prendre ?</h4>
<p>Comme j&#8217;ai envie que ça soit beau sans faire trop d&#8217;effort, je pense très rapidement à <a title="jquery" href="http://jquery.com/">jQuery</a><em>.</em>  Après une rapide recherche, j&#8217;opte pour un plugin très populaire de présentation de données tabulaires : <a title="datatable" href="http://datatables.net/"><strong>dataTable</strong></a>. En effet, celui-ci permet en très peu de configuration de gérer la &#8220;présentation des résultats paginés&#8221; tout en déléguant la gestion de la pagination  au serveur : bingo!</p>
<p>Par contre, si j&#8217;opte pour cette solution les résultats à afficher devront être récupérés grâce à des requêtes <a title="ajax wikipedia" href="http://fr.wikipedia.org/wiki/Ajax_(informatique)">AJAX</a>. D&#8217;un côté ça m&#8217;arrange car ça me permet de coller à ma deuxième contrainte, qui était de rendre la navigation plus fluide;  mais d&#8217;un autre côté, ça m&#8217;embête un peu parce que je n&#8217;ai pas envie de rentrer dans les problématiques techniques d&#8217;AJAX.</p>
<p>Je fais donc ma petite enquête et me rends compte que <a title="Spring 3 documentation" href="http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/"><strong>Spring 3.x</strong></a> apporte un support intéressant pour AJAX censé faciliter la vie du développeur. <em>Spring</em> et <em>jQuery</em>, je signe sans hésitation et me lance dans l&#8217;aventure.<br />
<span id="more-5701"></span></p>
<h3>Présentation de l&#8217;exemple</h3>
<p>Avant de commencer les choses sérieuses, voici un aperçu de la page des résultats paginés :</p>
<p><a href="http://blog.excilys.com/2013/01/28/la-pagination-facile-avec-spring-3-x-et-jquery/recherche-articles/" rel="attachment wp-att-5897"><img class="aligncenter size-full wp-image-5897" alt="Recherche Articles" src="http://blog.excilys.com/wp-content/uploads/2012/12/Recherche-Articles.png" width="761" height="366" /></a></p>
<ul>
<li>L&#8217;exemple porte sur un écran de recherche d&#8217;articles de presse.  Un article possède un titre, un auteur, une date de publication et appartient à une rubrique;</li>
<li>L&#8217;application &#8220;exemple&#8221; utilise Spring MVC (version 3.x) comme framework de présentation. J&#8217;utiliserai essentiellement les annotations comme élément de configuration (pour les contrôleurs notamment). Par souci de lisibilité, je n&#8217;aborderai pas  la configuration élémentaire de Spring MVC. Pour plus d&#8217;information concernant Spring MVC se référer à cette <a title="spring mvc" href="http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html">page</a>.</li>
<li>Dans le même esprit, je vais essentiellement me concentrer sur les éléments relatifs à la pagination en faisant l&#8217;impasse sur certaines &#8220;bonnes pratiques&#8221;. Ainsi les articles de presse seront stockés dans une liste <em>static </em>en mémoire et pas dans une base de données relationnelle avec gestion des transactions et tout le tralala. Amis puristes, vous êtes prévenus <img src='http://blog.excilys.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </li>
<li>Les fragments de code présentés par la suite servent uniquement de support et sont intentionnellement incomplets. Vous pouvez toujours consulter les <a title="pagination-spring-datatable.zip" href="http://blog.excilys.com/wp-content/uploads/2012/12/pagination-spring-datatable.zip">sources</a> jointes à cet article pour avoir plus de détails.</li>
</ul>
<h3>Fonctionnement général</h3>
<p>Voici un diagramme qui présente le fonctionnement général de l&#8217;affichage des résultats paginés :</p>
<p>&nbsp;</p>
<h3><a href="http://blog.excilys.com/2013/01/28/la-pagination-facile-avec-spring-3-x-et-jquery/sequence-affichage-resultats-de-recherche/" rel="attachment wp-att-5728"><img class="aligncenter" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/12/Séquence-affichage-resultats-de-recherche.png" width="819" height="533" /></a></h3>
<div></div>
<p>&nbsp;</p>
<p>[1] : dataTable envoie une requête AJAX au contrôleur Spring qui contient à la fois les données du formulaire de recherche ainsi que certaines informations nécessaires à la pagination comme l&#8217;index du premier prochain résultat (iDisplayStart) et le nombre maximum de résultats que l&#8217;on veut afficher par page.</p>
<p>[2] [3] Le contrôleur récupère le nombre total de résultats correspondant aux critères de recherche (sans prendre en compte la pagination) et récupère par la suite uniquement les résultats à afficher sur une page : il se sert pour cela des informations de pagination envoyées dans la requête AJAX.</p>
<p>[4] Spring retourne une réponse JSON contenant toutes les informations nécessaires à l&#8217;affichage des résultats.</p>
<p>[5] dataTable construit et affiche le tableau des résultats.</p>
<h3>Mise en place</h3>
<h4>Coté Spring&#8230;</h4>
<p>Dans l&#8217;introduction, j&#8217;ai mentionné le fait que Spring 3.x possédait un meilleur support pour AJAX. Ce n&#8217;est pas tout à fait juste car ce support n&#8217;est pas exclusivement destiné à AJAX même s&#8217;il en facilite grandement l&#8217;utilisation. Voici les principaux protagonistes :</p>
<ul>
<li><strong>HttpMessageConverter</strong>: Dans le protocole HTTP le client et le serveur communiquent en échangeant des données textuelles. Cependant dans les méthodes des contrôleurs Spring, on peut avoir un objet d&#8217;un type spécifique comme argument ou comme type de retour. La sérialisation/dé-sérialisation de l&#8217;objet en/à partir d&#8217;un format textuel est réalisé par les objets implémentant l&#8217;interface <a title="HttpMessageConverter" href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/http/converter/HttpMessageConverter.html">HttpMessageConverter</a>.</li>
</ul>
<div></div>
<ul>
<li> <strong>MappingJacksonHttpMessageConverter</strong>: En ce qui nous concerne, on aimerait bien avoir un objet qui convertisse  les requêtes contenant du JSON en un javabean et vice versa. C&#8217;est ce que permet de faire la classe <a title="MappingJacksonHttpMessageConverter" href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/http/converter/json/MappingJacksonHttpMessageConverter.html">MappingJacksonHttpMessageConverter</a>. Cette implémentation de HttpMessageConverter est basée sur un framework puissant de gestion du format JSON : <a title="Jackson" href="http://jackson.codehaus.org/">Jackson</a>.</li>
</ul>
<ul>
<li><strong>@RequestBody</strong> : Placée sur un paramètre d&#8217;une méthode, cette annotation permet de convertir le corps d&#8217;une requête (body) HTTP en un type Java donné en utilisant le HttpMessageConverter approprié (fonction du type mime).</li>
</ul>
<ul>
<li><strong>@ResponseBody</strong> : Placée sur une méthode, cette annotation indique qu&#8217;il faut écrire directement l&#8217;objet de retour dans la réponse et non dans le modèle de la vue.  En fonction du type mime attendu, l&#8217;objet retourné sera converti dans le format approprié par le HttpMessageConverter associé au type mime. Dans notre cas, le type mime est <em>application/json</em>  et MappingJacksonHttpMessageConverter convertit l&#8217;objet de retour en texte au format JSON.</li>
</ul>
<p>Pour pouvoir utiliser MappingJacksonHttpMessageConverter couplé avec @RequestBody et @ResponseBody il faut rajouter quelques éléments de configuration à Spring MVC :</p>
<ol>
<li>Utiliser l&#8217;élement <em><strong>&lt;mvc:annotation-driven/&gt;</strong></em> dans le fichier xml de configuration de Spring. Ceci permet de configurer (entre autre) les différentes implémentations de HttpMessageConverter dont celle qui nous intéresse à savoir : MappingJacksonHttpMessageConverter.</li>
<li>Rajouter la dépendance : <em><strong>jackson-databind.jar</strong> </em>(et toutes les dépendances transitives ). Avec Maven ça donne :</li>
</ol>
<pre>

<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;dependency<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>com.fasterxml.jackson.core<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>jackson-databind<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>2.1.1<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/dependency<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></div>

</pre>
<p>Une fois la configuration mise en place, i l ne reste plus qu&#8217;à implémenter le contrôleur dont voici les principaux éléments:</p>
<pre>

<div class="codecolorer-container java5 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br /></div></td><td><div class="java5 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@Controller<br />
@RequestMapping<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;articles/search.htm&quot;</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> ArticleController <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; ...<br />
<br />
&nbsp; &nbsp; @RequestMapping<span style="color: #009900;">&#40;</span>method = RequestMethod.<span style="color: #006633;">POST</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; @ResponseBody<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> ArticleReponseJson search<span style="color: #009900;">&#40;</span>@ModelAttribute<span style="color: #009900;">&#40;</span>COMMAND_NAME<span style="color: #009900;">&#41;</span>ArticleSearchBean searchBean,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;@RequestBody MultiValueMap<span style="color: #339933;">&lt;</span><span style="color: #003399; font-weight: bold;">String</span>, <span style="color: #003399; font-weight: bold;">String</span><span style="color: #339933;">&gt;</span>parametresAjax<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-weight: bold;">int</span> count = articleService.<span style="color: #006633;">count</span><span style="color: #009900;">&#40;</span>searchBean<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003399; font-weight: bold;">Integer</span> debut = getIntFirstValue<span style="color: #009900;">&#40;</span>parametresAjax, <span style="color: #0000ff;">&quot;iDisplayStart&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003399; font-weight: bold;">Integer</span> nbElements = getIntFirstValue<span style="color: #009900;">&#40;</span>parametresAjax, <span style="color: #0000ff;">&quot;iDisplayLength&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003399; font-weight: bold;">List</span><span style="color: #339933;">&lt;</span>Article<span style="color: #339933;">&gt;</span> listeArticles = articleService.<span style="color: #006633;">search</span><span style="color: #009900;">&#40;</span>searchBean, debut, nbElements<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> prepareJsonResponse<span style="color: #009900;">&#40;</span>listeArticles, count, parametresAjax<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> ArticleReponseJson prepareJsonResponse<span style="color: #009900;">&#40;</span><span style="color: #003399; font-weight: bold;">List</span><span style="color: #339933;">&lt;</span>Article<span style="color: #339933;">&gt;</span> articles, <span style="color: #006600; font-weight: bold;">int</span> count,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MultiValueMap<span style="color: #339933;">&lt;</span><span style="color: #003399; font-weight: bold;">String</span>, <span style="color: #003399; font-weight: bold;">String</span><span style="color: #339933;">&gt;</span> parametresAjax<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; ArticleReponseJson reponseJson = <span style="color: #000000; font-weight: bold;">new</span> ArticleReponseJson<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; reponseJson.<span style="color: #006633;">setsEcho</span><span style="color: #009900;">&#40;</span>getIntFirstValue<span style="color: #009900;">&#40;</span>parametresAjax, <span style="color: #0000ff;">&quot;sEcho&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; reponseJson.<span style="color: #006633;">setiTotalDisplayRecords</span><span style="color: #009900;">&#40;</span>count<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; reponseJson.<span style="color: #006633;">setiTotalRecords</span><span style="color: #009900;">&#40;</span>count<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; reponseJson.<span style="color: #006633;">setAaData</span><span style="color: #009900;">&#40;</span>articles<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> reponseJson<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399; font-weight: bold;">Integer</span> getIntFirstValue<span style="color: #009900;">&#40;</span>MultiValueMap<span style="color: #339933;">&lt;</span><span style="color: #003399; font-weight: bold;">String</span>, <span style="color: #003399; font-weight: bold;">String</span><span style="color: #339933;">&gt;</span> parametres, <span style="color: #003399; font-weight: bold;">String</span> key<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003399; font-weight: bold;">Integer</span> res = <span style="color: #006600; font-weight: bold;">null</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003399; font-weight: bold;">String</span> s = parametres.<span style="color: #006633;">getFirst</span><span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; &nbsp;font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>StringUtils.<span style="color: #006633;">isNotEmpty</span><span style="color: #009900;">&#40;</span>s<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; res = <span style="color: #003399; font-weight: bold;">Integer</span>.<span style="color: #006633;">parseInt</span><span style="color: #009900;">&#40;</span>s<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> res<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; ...<br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>Concernant le contrôleur, on peut noter les éléments suivants :</p>
<ul>
<li>La méthode est annotée par <em>@ResponseBody</em> afin que l&#8217;objet de retour soit directement sérialisé dans la réponse. Grâce à la configuration ci-dessus, l&#8217;objet sera sérialisé au format JSON avant d&#8217;être écrit dans la réponse.</li>
</ul>
<div></div>
<ul>
<li>Les critères de recherche sont récupérés dans l&#8217;objet de commande <em>ArticleSearchBean</em>.</li>
</ul>
<div></div>
<ul>
<li>Un des paramètres est annoté par <em>@RequestBody </em>ce qui permet de récupérer les paramètres au format JSON passés dans la requête (ceux qui seront envoyés par  <em>dataTable </em>pour gérer la pagination). On les récupère dans une MultiValueMap (extension d&#8217;une Map proposée par Spring permettant de stocker plusieurs valeurs pour une clé). Encore une fois, ceci est possible grâce à l&#8217;intervention &#8220;implicite&#8221; de <em>MappingJacksonHttpMessageConverter.</em></li>
</ul>
<div><em><br />
</em></div>
<ul>
<li>Une fois le nombre de résultats total et la liste des articles à afficher récupérés du service, on initialise un bean de type <code class="codecolorer text default"><span class="text">ArticleReponseJson</span></code>  qui sera sérialisé pour former la réponse JSON :</li>
</ul>
<pre>

<div class="codecolorer-container java5 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="java5 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> ArticleReponseJson <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">/* Parametre de controle */</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399; font-weight: bold;">Integer</span> sEcho<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">/* Nombre total de résultats correspondant aux critères de recherche */</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399; font-weight: bold;">Integer</span> iTotalDisplayRecords<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">/* Liste des resultats <br />
&nbsp; &nbsp; &nbsp; &nbsp;Par défaut dataTable cherche les résultats dans une propriété nommée aaData.<br />
&nbsp; &nbsp; &nbsp; &nbsp;On peut configurer le nom de la propriété.<br />
&nbsp; &nbsp; */</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399; font-weight: bold;">List</span><span style="color: #339933;">&lt;</span>Article<span style="color: #339933;">&gt;</span> aaData = <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399; font-weight: bold;">ArrayList</span><span style="color: #339933;">&lt;</span>Article<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; ...<br />
<br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<h4>&#8230; puis coté jQuery&#8230;</h4>
<p>Le plugin <em>dataTable</em> est très configurable au point de s&#8217;y perdre. Je vais présenter une configuration &#8220;très simple&#8221; qui met uniquement en évidence les propriétés relatives à la pagination.  Il faut toutefois garder à l&#8217;esprit qu<em>e dataTable</em> offre beaucoup de possibilités comme le filtrage des résultats par mot clé, le tri et bien d&#8217;autres encore.</p>
<p>Ceci étant dit, voici la configuration que j&#8217;ai retenue :</p>
<pre>

<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #003366; font-weight: bold;">function</span> search<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><br />
$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;#resultats&quot;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">dataTable</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;bFilter&quot;</span> <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;bInfo&quot;</span> <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;bSort&quot;</span> <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;sDom&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">'&lt;&quot;#top&quot;p&gt;rt&lt;&quot;#bottom&quot;p&gt;'</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;oLanguage&quot;</span> <span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;oPaginate&quot;</span> <span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;sFirst&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;&lt;&lt;&quot;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;sLast&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;&gt;&gt;&quot;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;sNext&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;&gt;&quot;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;sPrevious&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;&lt;&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;sPaginationType&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;full_numbers&quot;</span><span style="color: #339933;">,</span> <span style="color: #006600; font-style: italic;">//Style de la pagination</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;iDisplayLength&quot;</span> <span style="color: #339933;">:</span> <span style="color: #CC0000;">5</span><span style="color: #339933;">,</span> <span style="color: #006600; font-style: italic;">//Résultats affichés 5 par 5</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;bServerSide&quot;</span> <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">,</span> <span style="color: #006600; font-style: italic;">//Pagination gérée coté serveur</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;bProcessing&quot;</span> <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">,</span><span style="color: #006600; font-style: italic;">//Affichage d'une icone pendant le chargement</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;bDestroy&quot;</span> <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">,</span><span style="color: #006600; font-style: italic;">//La grille d'affichage est détruite avant chaque chargement</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;sAjaxSource&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;search.htm&quot;</span><span style="color: #339933;">,</span> <span style="color: #006600; font-style: italic;">//URL de recupération des résultats</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;fnServerData&quot;</span> <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>sSource<span style="color: #339933;">,</span> aoData<span style="color: #339933;">,</span> fnCallback<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #006600; font-style: italic;">// Définition de la requete AJAX</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ajax</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;dataType&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">'json'</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;type&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;POST&quot;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;url&quot;</span> <span style="color: #339933;">:</span> sSource<span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;data&quot;</span> <span style="color: #339933;">:</span> aoData<span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;success&quot;</span> <span style="color: #339933;">:</span> fnCallback<span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;timeout&quot;</span> <span style="color: #339933;">:</span> <span style="color: #CC0000;">30000</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;fnServerParams&quot;</span> <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>aoData<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #006600; font-style: italic;">// Paramètres supplémentaires à ajouter au corps du POST</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">each</span><span style="color: #009900;">&#40;</span>$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;#form-search&quot;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">serializeArray</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #006600; font-style: italic;">//Données du formulaire</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>index<span style="color: #339933;">,</span> elt<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; aoData.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>elt<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; <span style="color: #3366CC;">&quot;aoColumns&quot;</span> <span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span> <span style="color: #009900;">&#123;</span> <span style="color: #006600; font-style: italic;">//Définition des colonnes à afficher</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;mDataProp&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;titre&quot;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;mDataProp&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;auteur&quot;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;mDataProp&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;rubrique&quot;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">&quot;mDataProp&quot;</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;datePublication&quot;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#93;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>

</pre>
<p>J&#8217;ai pris le soin de commenter les lignes relatives à la pagination. Pour une description plus détaillée des différentes options, veuillez vous référer à la <a title="documentation datatable" href="http://datatables.net/ref">documentation officielle</a> du plugin dataTable.</p>
<h4>et enfin coté JSP.</h4>
<pre>

<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;html<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;head<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;script</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $(document).ready(function(){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; search();<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; });<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/head<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;body<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;div</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;contenu&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;form:form</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;form-search&quot;</span> <span style="color: #000066;">commandName</span>=<span style="color: #ff0000;">&quot;searchBean&quot;</span> <span style="color: #000066;">method</span>=<span style="color: #ff0000;">&quot;POST&quot;</span> <span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; ...<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/form:form<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;table</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;resultats&quot;</span> <span style="color: #000066;">width</span>=<span style="color: #ff0000;">&quot;100%&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;caption<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Liste des articles<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/caption<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;thead<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;tr<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;th<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Titre<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/th<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;th<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Auteur<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/th<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;th<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Catégorie<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/th<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;th<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Date de publication<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/th<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/tr<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/thead<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;tbody<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;">&lt;!-- Contenu géré par dataTable --&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/tbody<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/table<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/div<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/body<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></div>

</pre>
<p>On notera que seul le &#8220;squelette&#8221; du tableau a été déclaré dans la JSP : en effet, le contenu de la balise <code class="codecolorer text default"><span class="text">tbody</span></code> est vide car c&#8217;est à la charge de dataTable de construire le contenu du tableau avec les résultats de recherches.</p>
<h3>Pour aller plus loin</h3>
<p><span style="line-height: 13px">Comme mentionné plus haut, MappingJacksonHttpMessageConverter utilise en coulisse une librairie de gestion de JSON nommé Jackson. Celle-ci permet de configurer finement le mapping entre un champ JSON et une propriété Java. En ce qui nous concerne, on voudrait bien que la date de publication d&#8217;un article (de type java.util.Date) soit affichée dans un format lisible (ex : 2012-12-25). Ceci peut être réalisé en annotant la propriété <code class="codecolorer text default"><span class="text">datePublication</span></code> de la classe Article par </span><strong style="line-height: 13px">@JsonFormat(&#8220;yyyy-MM-dd&#8221;)</strong><span style="line-height: 13px"> : </span></p>
<pre>

<div class="codecolorer-container java5 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java5 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> Article <span style="color: #009900;">&#123;</span><br />
...<br />
<br />
&nbsp; &nbsp; @JsonFormat<span style="color: #009900;">&#40;</span>pattern = <span style="color: #0000ff;">&quot;yyyy-MM-dd&quot;</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399; font-weight: bold;">Date</span> getDatePublication<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> datePublication<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
...<br />
<span style="color: #009900;">&#125;</span></div></div>

</pre>
<h3></h3>
<h4>Liens :</h4>
<ul>
<li><a title="Spring documentation" href="http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/">Spring 3.x</a></li>
<li><a title="datatable" href="http://datatables.net/">Plugin jQuery datatable</a></li>
<li><a title="Jackson" href="http://jackson.codehaus.org/">Jackson: Librairies de  gestion du JSON</a></li>
<li><a title="archive pagination-spring-datable" href="http://blog.excilys.com/wp-content/uploads/2012/12/pagination-spring-datatable.zip">Archive contenant les sources de l&#8217;application exemple</a></li>
</ul>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2013/01/28/la-pagination-facile-avec-spring-3-x-et-jquery/" data-text="La pagination facile avec Spring 3.x et jQuery."></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2013/01/28/la-pagination-facile-avec-spring-3-x-et-jquery/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2013%2F01%2F28%2Fla-pagination-facile-avec-spring-3-x-et-jquery%2F&amp;title=La%20pagination%20facile%20avec%20Spring%203.x%20et%20jQuery." id="wpa2a_6"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/zhlE8uWAvXE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2013/01/28/la-pagination-facile-avec-spring-3-x-et-jquery/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2013/01/28/la-pagination-facile-avec-spring-3-x-et-jquery/</feedburner:origLink></item>
		<item>
		<title>Introduction à Ember.js</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/RDXZS69DYtk/</link>
		<comments>http://blog.excilys.com/2013/01/09/introduction-a-ember-js/#comments</comments>
		<pubDate>Wed, 09 Jan 2013 08:53:08 +0000</pubDate>
		<dc:creator>Arnaud GOURLAY</dc:creator>
				<category><![CDATA[Frameworks]]></category>
		<category><![CDATA[Ember]]></category>
		<category><![CDATA[framework web]]></category>
		<category><![CDATA[Javacript]]></category>
		<category><![CDATA[mvc]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=5859</guid>
		<description><![CDATA[Cette année 2012 à été riche pour l’écosystème javascript et particulièrement pour les frameworks MVC côté client. Je me propose dans cet article de vous présenter un challenger méconnu à côté de Backbone.js ou de Angular.js.   &#160; Ember.js est un framework MVC en javascript dont le but est de créer des applications web se [...]]]></description>
				<content:encoded><![CDATA[<p dir="ltr" style="text-align: justify">Cette année 2012 à été riche pour l’écosystème javascript et particulièrement pour les frameworks MVC côté client.</p>
<p dir="ltr" style="text-align: justify">Je me propose dans cet article de vous présenter un challenger méconnu à côté de Backbone.js ou de Angular.js.</p>
<p style="text-align: justify"><b><b> </b></b></p>
<div class="wp-caption aligncenter" style="width: 230px"><img alt="Ember.js" src="https://secure.gravatar.com/avatar/792333d2bad390e8a2e23f5e2f41f214?s=420&amp;d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-org-420.png" width="220" height="220" /><p class="wp-caption-text">Ember.js</p></div>
<p><span id="more-5859"></span></p>
<p>&nbsp;</p>
<p dir="ltr" style="text-align: justify">Ember.js est un framework MVC en javascript dont le but est de créer des applications web se rapprochant des applications desktop, dites “single page web application”.</p>
<p dir="ltr" style="text-align: justify">Pour voir quelques exemples d’applications en production réalisées avec Ember.js, rendez vous sur <a title="Groupon" href="http://www.groupon.com/">Groupon</a>, <a title="Capitaine Train" href="http://www.capitainetrain.com/">Capitaine Train</a> ou encore <a title="Travis CI" href="https://travis-ci.org/">Travis CI</a>.</p>
<p dir="ltr" style="text-align: justify">L’idée derrière les “Single Page Web App” est grossièrement de télécharger l’intégralité de l’application à la première requête de l’utilisateur, puis de communiquer uniquement avec le serveur par requête Ajax pour manipuler les données via une API REST si possible. L’application “Ember” est également  découpée selon le fameux pattern MVC afin d&#8217;apporter une séparation des responsabilités côté client permettant une plus grande maintenabilité &#8230; enfin vous connaissez la musique.</p>
<p dir="ltr" style="text-align: justify">Ember.js arrive lentement mais surement à maturité avec la version 1.0 qui se profile à l’horizon. Critiqué par le passé pour ses nombreux changements rendant certaines montées de version sportives, l’équipe de développement souhaite à présent stabiliser le code. Il est intéressant de savoir que Ember.js partage une certaine similarité de philosophie avec Ruby, ceci est dû à l’origine du projet ainsi qu’à ses membres avec notamment <a title="Yehuda Katz" href="http://yehudakatz.com/">Yehuka Katz</a>, développeur de Ruby on Rails et jQuery.</p>
<p dir="ltr" style="text-align: justify">Les tâches effectuées par des applications web sont répétitives. Tout se résume souvent à récupérer des données depuis un serveur, les afficher à l’écran puis mettre à jour les informations lorsqu’elles changent. Voyons à présent quels sont les outils proposés par Ember.js pour nous faire gagner du temps et cesser de nous préoccuper de la génération du DOM.</p>
<h3 style="text-align: justify">A better JavaScript</h3>
<p>Au delà du MVC que nous verrons par la suite, ember.js apporte un certain nombre d’améliorations au javascript qui apparaîtront pour certaines avec <a title="ECMA Script 6" href="http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts">ECMA Script 6</a>.</p>
<p dir="ltr" style="text-align: justify">●     un système de classe :</p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Person <span style="color: #339933;">=</span> Ember.<span style="color: #660066;">Object</span>.<span style="color: #660066;">extend</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; firstName <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span><br />
&nbsp; lastName <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">null</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p dir="ltr" style="text-align: justify">●     les Mixins :</p>
<p dir="ltr" style="text-align: justify">Les mixins  qui permettent de composer des aspects au sein d’objet.</p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Speaker <span style="color: #339933;">=</span> Ember.<span style="color: #660066;">Mixin</span>.<span style="color: #660066;">create</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; hello<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000066;">alert</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span>‘firstName’<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span>” “<span style="color: #339933;">+</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span>‘lastName’<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
Person <span style="color: #339933;">=</span> Ember.<span style="color: #660066;">Object</span>.<span style="color: #660066;">extend</span><span style="color: #009900;">&#40;</span>Speaker<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p dir="ltr" style="text-align: justify">On peut à présent appeler la méthode ‘hello’ sur les instances de Person.</p>
<p dir="ltr" style="text-align: justify">●     Computed properties</p>
<p dir="ltr" style="text-align: justify">Tout simplement une propriété dont la valeur est issue d’une fonction.</p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Person <span style="color: #339933;">=</span> Ember.<span style="color: #660066;">Object</span>.<span style="color: #660066;">extend</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; firstName<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span><br />
&nbsp; lastName<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span><br />
&nbsp; fullName<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> firstName <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'firstName'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> lastName <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'lastName’'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; <span style="color: #000066; font-weight: bold;">return</span> firstName <span style="color: #339933;">+</span> <span style="color: #3366CC;">' '</span> <span style="color: #339933;">+</span> lastName<span style="color: #339933;">;</span><br />
&nbsp; <span style="color: #009900;">&#125;</span>.<span style="color: #660066;">property</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'firstName'</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">'lastName'</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #003366; font-weight: bold;">var</span> tom <span style="color: #339933;">=</span> Person.<span style="color: #660066;">create</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; firstName<span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;Tom&quot;</span><span style="color: #339933;">,</span><br />
&nbsp; lastName<span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;Dale&quot;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
tom.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'fullName'</span><span style="color: #009900;">&#41;</span></div></td></tr></tbody></table></div>
<p dir="ltr" style="text-align: justify">Il est à préciser que le résultat <em>fullName</em> est mis en cache pour éviter de recalculer à chaque fois les propriété et que ce cache est automatiquement invalidé lorsqu’une des propriétés en dépendance change.</p>
<p dir="ltr" style="text-align: justify">A présent, passons en revu les différentes briques de l’application.</p>
<h3 style="text-align: justify">Application &amp; Router</h3>
<p dir="ltr" style="text-align: justify">Commençons par l’élément central de l’application, à savoir l’objet Application qui est responsable de relier toutes les briques du MVC ensemble.</p>
<p style="text-align: justify">window.App = Ember.Application.create();</p>
<p dir="ltr" style="text-align: justify">L’élément application permet de définir un namespace qui contiendra par la suite l’intégralité des objets composants Ember afin d’en limiter le scope. Lors de la création, le template root aussi appelé “application template” est rendu. Voici une spécificité de Ember, l’application ne possède qu’une seule page dont le contenu sera modifié en fonction de l’état courant.</p>
<div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br /></div></td><td><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #00bbdd;">&lt;!DOCTYPE html&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">html</span> <span style="color: #000066;">lang</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;en&quot;</span>&gt;</span><br />
&nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">head</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">title</span>&gt;</span>Ember.js first step<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">title</span>&gt;</span><br />
&nbsp; <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">head</span>&gt;</span><br />
&nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">body</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">script</span> <span style="color: #000066;">src</span> <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;js/jquery.js&quot;</span>&gt;&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">script</span> <span style="color: #000066;">src</span> <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;js/handlebars.js&quot;</span>&gt;&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">script</span> <span style="color: #000066;">src</span> <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;js/ember.js&quot;</span>&gt;&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">script</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text/x-handlebars&quot;</span> data-template-<span style="color: #000066;">name</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;application&quot;</span>&gt;</span><br />
&nbsp; &nbsp; &nbsp; <span style="color: #009900;">&lt;header&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">h1</span>&gt;</span>MVC app<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">h1</span>&gt;</span><br />
&nbsp; &nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span>header&gt;</span><br />
<br />
&nbsp; &nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">div</span>&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp;{{outlet}}<br />
&nbsp; &nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">div</span>&gt;</span><br />
<br />
&nbsp; &nbsp; &nbsp; <span style="color: #009900;">&lt;footer&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; contact me<br />
&nbsp; &nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span>footer&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span><br />
<br />
&nbsp; <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">body</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">html</span>&gt;</span></div></td></tr></tbody></table></div>
<p dir="ltr" style="text-align: justify">L’élément {{outlet}} sert de point d’ancrage pour d’autres templates qui seront injectés par le router en fonction de la navigation de l’utilisateur dans l’application. On peut bien évidemment créer plusieurs outlet en les nommant, par exemple pour un footer dynamique:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{{outlet footerOutlet}}</div></td></tr></tbody></table></div>
<p dir="ltr" style="text-align: justify">On peut ensuite mapper une URL à un outlet spécifique et à son contrôleur. Ci-dessous, on configure le comportement de footerOutlet sur l’URL ‘/’ pour être managé par ‘footer’. Cela correspond à une convention qui indique à Ember qu’il doit binder cette zone avec footerController et footerView.</p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">window.<span style="color: #660066;">App</span>.<span style="color: #660066;">Router</span> <span style="color: #339933;">:</span> Ember.<span style="color: #660066;">Router</span>.<span style="color: #660066;">extend</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; root<span style="color: #339933;">:</span> Ember.<span style="color: #660066;">Route</span>.<span style="color: #660066;">extend</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; index<span style="color: #339933;">:</span> Ember.<span style="color: #660066;">Route</span>.<span style="color: #660066;">extend</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; route<span style="color: #339933;">:</span> <span style="color: #3366CC;">'/'</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; connectOutlets<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>router<span style="color: #339933;">,</span> event<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; router.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'applicationController'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">connectOutlet</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'footerOutlet'</span><span style="color: #339933;">,</span>’footer’<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><br />
&nbsp; <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span></div></td></tr></tbody></table></div>
<p dir="ltr" style="text-align: justify">Je ne rentre pas plus dans les détails d’implémentation car l’API du composant Router est en train de subir quelques changements avec l’arrivée de la version 1.0.</p>
<p dir="ltr" style="text-align: justify">La documentation est en pleine mise à jour et peut être trouvée <a title="http://emberjs.com/guides/routing/" href="http://emberjs.com/guides/routing/">ici </a>.</p>
<h3 style="text-align: justify">View &amp; Templating</h3>
<p dir="ltr" style="text-align: justify">La partie vue est généralement considérée comme l’interface utilisateur de l’application et se base sur l’association d’un template et d’un controleur au niveau du routeur.</p>
<p style="text-align: justify"><b><b></p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">window.<span style="color: #660066;">App</span>.<span style="color: #660066;">viewTest</span> <span style="color: #339933;">=</span> Ember.<span style="color: #660066;">View</span>.<span style="color: #660066;">create</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; templateName<span style="color: #339933;">:</span> <span style="color: #3366CC;">'test-template'</span><span style="color: #339933;">,</span><br />
&nbsp; viewName<span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;viewTest&quot;</span><span style="color: #339933;">,</span><br />
&nbsp; click<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>evt<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000066;">alert</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;viewTest was clicked!&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p></b></b></p>
<div class="codecolorer-container html4strict default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">script</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text/x-handlebars&quot;</span> data-template-<span style="color: #000066;">name</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;test-template&quot;</span>&gt;</span><br />
The name of the view is <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">strong</span>&gt;</span>{{view.viewName}}<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">strong</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span></div></td></tr></tbody></table></div>
<p dir="ltr" style="text-align: justify">Fini la concaténation de string pour générer du html (c’est vraiment mal), Ember utilise le système de templating <a title="handlebars" href="http://handlebarsjs.com/">Handlebars </a>dont Yehuda Katz est également l’auteur. La création de ce moteur avait pour but d’améliorer certains aspect de <a title="Mustache.js" href="https://github.com/janl/mustache.js/">Mustache.js</a>.</p>
<p dir="ltr" style="text-align: justify">Pour citer les améliorations majeures, on trouve:</p>
<p dir="ltr" style="text-align: justify">●     La précompilations des templates pour de meilleures performances.</p>
<p dir="ltr" style="text-align: justify">●     Ajout des fonctions helpers qui permettent de décorer un attribut.</p>
<p dir="ltr" style="text-align: justify">Les valeurs affichées dans le template sont directement ‘bindées’ à celle du modèle. Ce qui se traduit par une mise à jour du template automatique au moindre changement dans le modèle.</p>
<h3 style="text-align: justify">Model</h3>
<p dir="ltr" style="text-align: justify">La partie modèle correspond aux structures de données de l’application et est responsable de la  notification de tout élément l’observant pour connaitre son état courant. Tous les éléments du modèle héritent de Object comme vu précédemment avec le système de classe.</p>
<p dir="ltr" style="text-align: justify">En plus des computed properties, le type Object supporte le data binding pour partager des propriétés et l’observation de propriété pour être notifié lorsque une valeur change.</p>
<p dir="ltr" style="text-align: justify">Voici un exemple :</p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">window.<span style="color: #660066;">App</span>.<span style="color: #660066;">Person</span> <span style="color: #339933;">=</span> Ember.<span style="color: #660066;">Object</span>.<span style="color: #660066;">extend</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; firstName<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span><br />
&nbsp; lastName<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span><br />
&nbsp; fullName<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> firstName <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'firstName'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> lastName <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'lastName'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> firstName <span style="color: #339933;">+</span> <span style="color: #3366CC;">' '</span> <span style="color: #339933;">+</span> lastName<span style="color: #339933;">;</span><br />
&nbsp; <span style="color: #009900;">&#125;</span>.<span style="color: #660066;">property</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'firstName'</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">'lastName'</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #003366; font-weight: bold;">var</span> person <span style="color: #339933;">=</span> Person.<span style="color: #660066;">create</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; firstName<span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;Mac&quot;</span><span style="color: #339933;">,</span><br />
&nbsp; lastName<span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;Gyver&quot;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
person.<span style="color: #660066;">addObserver</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'fullName'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; <span style="color: #000066;">alert</span><span style="color: #009900;">&#40;</span>‘Le nom et<span style="color: #339933;">/</span>ou le prénom à été modifié”<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
person.<span style="color: #660066;">set</span><span style="color: #009900;">&#40;</span>lastName<span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;Intosh&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #006600; font-style: italic;">// observer will fire</span></div></td></tr></tbody></table></div>
<p dir="ltr" style="text-align: justify">Il est ainsi facile d’enrichir son modèle côté client et d’ajouter des traitements lors de l’interception d’événements.</p>
<h3 style="text-align: justify">Controller</h3>
<p dir="ltr" style="text-align: justify">Le contrôleur contient la logique associée aux actions de l’utilisateur effectuées sur la partie vue.</p>
<p dir="ltr" style="text-align: justify">Cette couche n’est pas spécialement novatrice, il est cependant bon de savoir qu’il existe des contrôleurs spécialisés dans la gestion de collection. Cela représente un des principaux use case, voici comment afficher une liste facilement.</p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">window.<span style="color: #660066;">App</span>.<span style="color: #660066;">listController</span> <span style="color: #339933;">=</span> Ember.<span style="color: #660066;">ArrayController</span>.<span style="color: #660066;">create</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
$.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'people.json'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>data<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
MyApp.<span style="color: #660066;">listController</span>.<span style="color: #660066;">set</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'content'</span><span style="color: #339933;">,</span> data<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p style="text-align: justify">On peut par la suite facilement relié une vue à la collection contenu dans le contrôleur.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{{#each window.App.listController}}<br />
&nbsp;{{firstName}} {{lastName}}<br />
{{/each}}</div></td></tr></tbody></table></div>
</p>
<h3 style="text-align: justify">Ember-data</h3>
<p dir="ltr" style="text-align: justify">Jusqu’a présent, nous n’avons pas réellement abordé le sujet de la communication avec le serveur. Dans un premier temps, il est possible d’adopter le pattern ActiveRecord en ajoutant dans chaque objet du modèle des méthodes CRUD qui réalisent des requêtes Ajax via JQuery pour communiquer avec le serveur. Mais cette démarche se révèle vite fastidieuse et peu performante car il faut synchroniser soi-même les données à la main, gérer le cache pour ne pas harceler le serveur bref on se retrouve rapidement à développer un ORM en javascript&#8230;</p>
<p dir="ltr" style="text-align: justify">C’est là qu’intervient le projet <a title="Ember-Data" href="https://github.com/emberjs/data">Ember-Data</a> qui se propose de gérer tout la persistance de votre application. Pour l’instant c’est un projet à part entière, mais il devrait être intégré a la release 1.0 de Ember. Son but est donc de créer un ORM côté client pour décorréler le code applicatif client de la partie serveur, le but ultime étant de pouvoir remplacer le backend sans impacter le code client.</p>
<p dir="ltr" style="text-align: justify">On retrouve donc les fonctionnalités usuelles d’un ORM :</p>
<p dir="ltr" style="text-align: justify">●     gestion des méthodes CRUD</p>
<p dir="ltr" style="text-align: justify">●     un système de transaction</p>
<p dir="ltr" style="text-align: justify">●     un système de cache</p>
<p dir="ltr" style="text-align: justify">●     synchronisation automatique avec validation et merge</p>
<p dir="ltr" style="text-align: justify">L’architecture s’appuie sur une couche d’adapteurs / serialiseurs responsables de la communication avec les API serveur. Le projet est assez jeune et n’est pas encore prêt pour la production au dire de ses développeurs, je ne l’ai pas personnellement testé au delà du use case nominal mais il est assez prometteur.</p>
<h3 style="text-align: justify">Conclusion</h3>
<p dir="ltr" style="text-align: justify">En ce moment la concurrence est rude du côté de la jungle des frameworks MVC, il est donc essentiel de voir en quoi Ember.js se différencie de ses opposants. N’ayant pas de réelle expérience avec d’autres frameworks MVC javascript mon jugement peut manquer d’objectivité, cependant je pense que Ember est un des frameworks les plus riches qui existe actuellement pour réaliser des “single page application” complexes.</p>
<p dir="ltr" style="text-align: justify">Selon moi voici ses points forts :</p>
<p dir="ltr" style="text-align: justify">●     Grâce à son composant Router, il peut gérer différents états.</p>
<p dir="ltr" style="text-align: justify">●     Il possède un système de binding puissant.</p>
<p dir="ltr" style="text-align: justify">●     Ember n’est pas qu’une bibliothèque, mais un framework riche amenant son architecture.</p>
<p dir="ltr" style="text-align: justify">●     Et enfin Ember-data qui sera une avancée importante pour la persistance.</p>
<p dir="ltr" style="text-align: justify">Espérons que le passage en 1.0 permettra de stabiliser le projet qui subit régulièrement de gros changement. Si cet article vous a intéressé et que vous souhaitez aller plus loin avec les frameworks MVC je vous conseille ces ressources:</p>
<p dir="ltr" style="text-align: justify"><a href="http://blog.remarkablelabs.com/2012/11/11-emberjs-resources-to-get-you-started">http://blog.remarkablelabs.com/2012/11/11-emberjs-resources-to-get-you-started</a></p>
<p dir="ltr" style="text-align: justify"><a href="http://coding.smashingmagazine.com/2012/07/27/journey-through-the-javascript-mvc-jungle">http://coding.smashingmagazine.com/2012/07/27/journey-through-the-javascript-mvc-jungle</a></p>
<p><a style="text-align: justify" href="http://blog.stevensanderson.com/2012/08/01/rich-javascript-applications-the-seven-frameworks-throne-of-js-2012/">http://blog.stevensanderson.com/2012/08/01/rich-javascript-applications-the-seven-frameworks-throne-of-js-2012/</a></p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2013/01/09/introduction-a-ember-js/" data-text="Introduction à Ember.js"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2013/01/09/introduction-a-ember-js/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2013%2F01%2F09%2Fintroduction-a-ember-js%2F&amp;title=Introduction%20%C3%A0%20Ember.js" id="wpa2a_8"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/RDXZS69DYtk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2013/01/09/introduction-a-ember-js/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2013/01/09/introduction-a-ember-js/</feedburner:origLink></item>
		<item>
		<title>Introduction à Spring-Batch</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/AHYnC9GNZiw/</link>
		<comments>http://blog.excilys.com/2013/01/03/introduction-a-spring-batch/#comments</comments>
		<pubDate>Thu, 03 Jan 2013 15:18:02 +0000</pubDate>
		<dc:creator>François MARLIAC</dc:creator>
				<category><![CDATA[Frameworks]]></category>
		<category><![CDATA[batch]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[tutoriel]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=4780</guid>
		<description><![CDATA[Contexte Dans le cadre de la refonte d&#8217;une application extranet chez un client, j&#8217;ai été plus particulièrement chargé du développement des batchs. Les batchs à développer devaient tous réaliser des tâches similaires : transferts FTP de fichiers (upload et download) traitements sur les fichiers ainsi obtenus (redimensionnement d&#8217;images JPEG, parsing de fichiers XML) enregistrement des [...]]]></description>
				<content:encoded><![CDATA[<h3 align="LEFT">Contexte</h3>
<p align="LEFT">Dans le cadre de la refonte d&#8217;une application extranet chez un client, j&#8217;ai été plus particulièrement chargé du développement des batchs. Les batchs à développer devaient tous réaliser des tâches similaires :</p>
<ul>
<li>transferts FTP de fichiers (upload et download)</li>
<li>traitements sur les fichiers ainsi obtenus (redimensionnement d&#8217;images JPEG, parsing de fichiers XML)</li>
<li>enregistrement des méta-données sur les fichiers ainsi manipulés en base de données.</li>
</ul>
<p align="LEFT">Dans l&#8217;ancienne version de l&#8217;application, les batchs étaient développés grâce à un framework interne (vieillissant). J&#8217;ai donc recherché des frameworks de batch permettant de remplacer l&#8217;ancienne architecture tout en s&#8217;intégrant avec les composants utilisés dans la partie web de l&#8217;application (développée en Spring et Struts 1).</p>
<h3 align="LEFT">Choix du framework</h3>
<p align="LEFT">Dans un souci de facilitation de la maintenance, le choix de Spring-batch s&#8217;est rapidement imposé. En effet, la plupart des composants (DAOs, services) nécessaires pour réaliser les tâches ci-dessus avaient déjà été développés sous la forme beans Spring utilisés par l&#8217;application web ; la principale tâche restant à accomplir étant le câblage de ces différents composants. Il était donc plus efficace et plus maintenable de réutiliser les composants existants et de les injecter dans les batchs via IOC.</p>
<p align="LEFT">Le développement a donc consisté à créer les classes composant la partie « front » des batchs et à gérer leur enchaînement.</p>
<p align="LEFT">Le but de cet article n’est pas de décrire de manière détaillée le fonctionnement de Spring-batch. Il s’agit plutôt de réaliser par étapes un petit tutoriel, la documentation officielle de Spring étant très complète mais peu adaptée à une prise en main rapide : <a href="http://static.springsource.org/spring-batch/reference/html-single/index.html">http://static.springsource.org/spring-batch/reference/html-single/index.html</a></p>
<p align="LEFT">Ce tutoriel traitera surtout du développement de tâches pour lesquels un composant de Spring-batch n&#8217;existe pas déjà. En effet, ce framework propose des classes permettant de mettre en place rapidement (et généralement en ne manipulant que les fichiers de configuration Spring) des tâches récurrentes dans le domaine des batchs :</p>
<ul>
<li>parsing de fichiers XML pour insertion dans une base de données ;</li>
<li>export de données d&#8217;une base vers un fichier plat ;</li>
<li>&#8230;</li>
</ul>
<p align="LEFT">Des exemples fonctionnels pour ces tâches sont disponibles sur le web, notamment dans la documentation officielle ; en revanche, il n&#8217;existe que très peu d&#8217;exemples de la mise en place de tâches &#8220;spécifiques&#8221; : dans le cas qui nous intéresse, ces tâches étaient par exemple l’upload/download de fichiers via FTP et de manipulation sur des fichiers images (en tant que fichiers, et donc sans lire leur contenu comme c’est le cas avec des fichiers CSV ou XML plus fréquemment manipulés par les batchs).</p>
<p><span id="more-4780"></span></p>
<h3 align="LEFT">Vocabulaire utilisé</h3>
<p align="LEFT">Avant d&#8217;identifier les composants à développer pour notre batch, un rappel du vocabulaire utilisé dans le domaine des batchs (et plus particulièrement de Spring-batch) est nécessaire :</p>
<p align="LEFT"><strong>Job :</strong> Un job désigne un traitement batch.</p>
<p align="LEFT"><strong>Step :</strong> Phase atomique d’un batch. Un job est composé de steps ; il s&#8217;agit d&#8217;une étape du batch. Un step encapsule les informations permettant de définir et de contrôler le déroulement du batch.</p>
<p align="LEFT"><strong>Tasklet :</strong> Tâche exécutée par un step, par exemple “Parser le fichier test.xml”, “Insérer un enregistrement en base de données”&#8230; Dans le cadre de Spring-batch, cela consiste en une implémentation de l&#8217;interface Tasklet. Un step n&#8217;exécute qu&#8217;une tasklet ; en revanche une même tasklet peut être exécutée par plusieurs steps.</p>
<p align="LEFT">Spring-batch propose un certain nombre de tasklets pré-définies, orientées par exemple vers la lecture et l’écriture de fichiers. Il est cependant possible de créer ses propres tasklets, c&#8217;est ce que nous allons faire au cours de cet article.</p>
<h3 align="LEFT">Problématique</h3>
<p align="LEFT">Supposons que le batch à écrire doive réaliser les opérations suivantes :</p>
<ul>
<li>download de fichiers JPG d&#8217;un serveur FTP vers un répertoire temporaire ;</li>
<li>redimensionnement des images et transfert dans un répertoire local ;</li>
<li>enregistrement de métadonnées concernant ces images dans une base de données.</li>
</ul>
<h3 align="LEFT">Implémentation</h3>
<p align="LEFT">La première étape consiste à identifier quels sont les concepts utilisés ici, afin de mettre en place notre architecture de batch et de définir les interactions entre ces composants (enchaînements, conditions d&#8217;arrêt&#8230;)</p>
<p align="LEFT">L’ensemble du processus décrit ci-dessus correspond à un job ; en effet tous ces traitements sont liés et seule l’exécution de tous ces traitements permet de réaliser la tâche attendue.</p>
<p>Ce processus peut facilement être décomposé en plusieurs tâches élémentaires, qui seront représentées sous la forme de steps : chacune des trois opérations décrites sera configurée comme étant un step dans l&#8217;application context.</p>
<p align="LEFT">Chaque step sera par la suite implémentée sous la forme d’une tasklet. Toute tasklet utilisée dans un projet Spring-batch est une classe implémentant l’interface <em>org.springframework.batch.core.step.tasklet.Tasklet</em>.</p>
<h4 align="LEFT">Création de l&#8217;applicationContext</h4>
<p>Nous allons commencer par créer un fichier applicationContext.xml basique, contenant le minimum de beans qui nous permettront ensuite au batch de s&#8217;exécuter. Trois beans doivent être créés :</p>
<ul>
<li>Un job launcher : Bean permettant l&#8217;exécution d&#8217;un batch. Dans notre cas, nous allons utiliser son implémentation la plus basique, SimpleJobLauncher.</li>
<li>Un transaction manager : Dans notre cas, nous n&#8217;utilisons pas de transactions, mais tout job launcher doit posséder un composant permettant de gérer les transactions qui lui sont associées. Nous allons donc utiliser ici un ResourcelessTransactionManager, ce point n&#8217;étant pas le thème principal de l&#8217;article.</li>
<li>Un job repository : Bean permettant de gérer la persistance des méta-données liées au batch.</li>
</ul>
<p>Le fichier applicationContext.xml minimum sera donc le suivant :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;beans<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;bean</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;transactionManager&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;org.springframework.batch.support.transaction.ResourcelessTransactionManager&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">&lt;!-- In memory job repository --&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;bean</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;jobRepository&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;property</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;transactionManager&quot;</span> <span style="color: #000066;">ref</span>=<span style="color: #ff0000;">&quot;transactionManager&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/bean<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">&lt;!-- Job launcher --&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;bean</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;jobLauncher&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;org.springframework.batch.core.launch.support.SimpleJobLauncher&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;property</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;jobRepository&quot;</span> <span style="color: #000066;">ref</span>=<span style="color: #ff0000;">&quot;jobRepository&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/bean<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/beans<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<h4 align="LEFT">Tasklet de download FTP : FTPTasklet</h4>
<p align="LEFT">Cette tasklet a simplement pour objectif de télécharger via FTP les fichiers se trouvant dans un répertoire distant vers un répertoire local.</p>
<p>Aucune des tasklets pré-définies de Spring-Batch ne permet de mettre en place directement ce traitement, nous allons donc devoir écrire une tasklet spécifique pour cette opération. Nous allons donc implémenter notre propre classe <em>FTPTasklet</em> à partir de l’interface <em>Tasklet</em> proposée par Spring-Batch.</p>
<p align="LEFT">L’interface <em>Tasklet</em> est la plus générique possible pour implémenter une tâche de batch : elle ne possède qu’une seule méthode, <em>execute</em>, destinée à effectuer les traitements attendus :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RepeatStatus execute <span style="color: #009900;">&#40;</span>StepContribution contribution, ChunkContext chunkContext<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span></div></td></tr></tbody></table></div>
<p align="LEFT">La classe <em>FTPTasklet</em> est donc construite suivant le modèle ci-dessous :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> FTPTasklet <span style="color: #000000; font-weight: bold;">implements</span> Tasklet <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> Logger log <span style="color: #339933;">=</span> Logger.<span style="color: #006633;">getLogger</span><span style="color: #009900;">&#40;</span>FTPTasklet.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// FTP Server connection properties</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> ipAddress<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> login<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> password<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> localRep<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #008000; font-style: italic; font-weight: bold;">/**<br />
&nbsp; &nbsp; &nbsp;* Define getters and setters here<br />
&nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> RepeatStatus execute<span style="color: #009900;">&#40;</span>StepContribution contribution,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ChunkContext chunkContext<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">IOException</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Connect to FTP server</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Check connection</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Retrieve files</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>Le code complet se trouve dans l&#8217;archive du projet liée à cet article. Le traitement de download est implémenté dans la méthode <em>execute</em>, en utilisant les informations de connexions injectées via IOC.</p>
<p align="LEFT">Une fois cette tasklet écrite, nous pouvons modifier le fichier applicationContext.xml pour définir un batch minimal l&#8217;utilisant. Cela consiste à modifier le job pour lui ajouter un step exécutant <em>FTPTasklet</em> :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #808080; font-style: italic;">&lt;!-- Job definition --&gt;</span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;batch:job</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;myJob&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">&lt;!-- Step definition --&gt;</span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;batch:step</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;ftpDownloadStep&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;batch:tasklet<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;bean</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;com.excilys.tuto.batch.tasklet.FTPTasklet&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;">&lt;!-- Properties of FTPTasklet bean --&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;property</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;localRep&quot;</span> <span style="color: #000066;">value</span>=<span style="color: #ff0000;">&quot;/dev/workspaces/spring_batch/batch-itemreader/src/main/resources/input/&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;property</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;ipAddress&quot;</span> <span style="color: #000066;">value</span>=<span style="color: #ff0000;">&quot;127.0.0.1&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;property</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;login&quot;</span> <span style="color: #000066;">value</span>=<span style="color: #ff0000;">&quot;testuser&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;property</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;password&quot;</span> <span style="color: #000066;">value</span>=<span style="color: #ff0000;">&quot;testuser&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/bean<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/batch:tasklet<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/batch:step<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/batch:job<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p align="LEFT">Le batch peut alors être exécuté en utilisant la classe main <em>org.springframework.batch.core.launch.support.CommandLineJobRunner</em>, qui prend en paramètre le path du fichier applicationContext et le nom du job à lancer (ici myJob).</p>
<h4 align="LEFT">Lecture et redimensionnement des images : JPEGFileResizeTasklet</h4>
<p align="LEFT">La deuxième tâche à créer pour notre batch est la tâche chargée du redimensionnement des fichiers JPEG précédemment récupérés.</p>
<p align="LEFT">On pourrait créer la tasklet chargée de lire et de redimensionner les images comme la tasklet de download FTP, en écrivant une classe implémentant l&#8217;interface <em>Tasklet</em> :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> JPEGFileResizeTasklet <span style="color: #000000; font-weight: bold;">implements</span> Tasklet <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> RepeatStatus execute <span style="color: #009900;">&#40;</span>StepContribution contribution, ChunkContext chunkContext<span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Read local directory</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Resize images</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Write images to output directory</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>La configuration de la step correspondante serait alors la suivante :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;batch:step</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;step1&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;batch:tasklet<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;bean</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;com.excilys.tuto.batch.tasklet.JPEGFileResizeTasklet&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;property</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;localRep&quot;</span> <span style="color: #000066;">value</span>=<span style="color: #ff0000;">&quot;/dev/workspaces/spring_batch/batch-itemreader/src/main/resources/input/&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/bean<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/batch:tasklet<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/batch:step<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p align="LEFT">Cependant, Spring-batch propose des classes prédéfinies permettant de réaliser ce traitement de manière beaucoup plus succinte.</p>
<p align="LEFT">En effet, la documentation met l’accent sur les classes permettant de parser des fichiers plats ou de lire des données dans une base afin d’en extraire des &#8220;items&#8221;. Ces items sont ensuite traités en &#8220;chunks&#8221; (ensembles d&#8217;items).</p>
<p align="LEFT">Les traitements effectués sur des chunks de données sont réalisés en implémentant trois interfaces fournies par Spring-batch :</p>
<ul>
<li><em>ItemReader</em> : interface prenant en charge la lecture des chunks d&#8217;items à traiter ;</li>
<li><em>ItemProcessor</em> : traitement des items ;</li>
<li><em>ItemWriter</em> : écriture des items.</li>
</ul>
<p>Les implémentations de ces trois interfaces sont ensuite enchaînées pour réaliser le traitement à effectuer sur les items (lecture &#8211; traitement &#8211; écriture).</p>
<p align="LEFT">Pour mettre en place notre traitement, il serait intéressant d’implémenter ces interfaces : de la même manière que ces classes sont par exemple implémentées par des classes du framework permettant de parser un fichier XML et d&#8217;en extraire des objets, nous pouvons les utiliser pour lire le contenu du répertoire d’entrée et d’en extraire des objets Resource.</p>
<h5 align="LEFT">Une implémentation d’<em>ItemReader</em> pour lire les fichiers d’entrée</h5>
<p align="LEFT">Pour mettre en place notre tasklet traitant des chunks de fichiers JPEG, le premier composant à écrire est donc la classe de lecture des fichiers JPEG, implémentant l&#8217;interface <em>ItemReader</em>.</p>
<p align="LEFT">Spring-batch propose une implémentation particulière de l&#8217;interface <em>ItemReader</em>, <em>ResourcesItemReader</em>, permettant de lire des objets <em>Resource</em> dans un répertoire à partir d’un pattern de nom de fichier. Il n&#8217;est donc pas nécessaire de créer notre propre implémentation d&#8217;<em>ItemReader</em> ; il suffit de déclarer dans l&#8217;applicationContext de Spring un bean de classe <em>ResourcesItemReader</em>, qui lira les fichiers jpg placés dans un répertoire d&#8217;entrée. Dans notre cas, ce bean sera donc configuré de la façon suivante :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;bean</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;itemReader&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;org.springframework.batch.item.file.ResourcesItemReader&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;property</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;resources&quot;</span> <span style="color: #000066;">value</span>=<span style="color: #ff0000;">&quot;/dev/workspaces/spring_batch/batch-itemreader/src/main/resources/input/*.jpg&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/bean<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p align="LEFT">Et c’est tout ! Il nous reste à câbler cet item reader avec un <em>itemProcessor</em> qui traitera les items (ici le redimensionnement des images), puis avec un <em>itemWriter</em> pour insérer en base de données les informations concernant les images importées.</p>
<h5 align="LEFT">Une implémentation d’<em>ItemProcessor</em> pour redimensionner les images</h5>
<p align="LEFT">Notre <em>ItemProcessor</em> va prendre en entrée les fichiers lus par l&#8217;<em>itemReader</em>, redimensionner les images et créer les objets métier <em>Image</em> correspondants.</p>
<p align="LEFT">Le fonctionnement d’un <em>ItemProcessor</em> étant spécifique au domaine du batch (ici le redimensionnement d’images JPEG), nous devons l’implémenter nous-mêmes. Dans notre cas, la méthode process de l’<em>ItemProcessor</em> prendra en entrée un objet de classe <em>Resource</em> (correspondant à un fichier lu par l&#8217;<em>itemReader</em>) pour créer une instance d&#8217;une classe de notre package model, représentant les données concernant l&#8217;image que nous enregistrerons ensuite dans la base de données :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> ImageProcessor <span style="color: #000000; font-weight: bold;">implements</span> ItemProcessor <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> outputFolder<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> setOutputFolder<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> outputFolder<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">outputFolder</span> <span style="color: #339933;">=</span> outputFolder<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Image</span> process<span style="color: #009900;">&#40;</span>Resource item<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003399;">BufferedImage</span> src <span style="color: #339933;">=</span> ImageIO.<span style="color: #006633;">read</span><span style="color: #009900;">&#40;</span>item.<span style="color: #006633;">getURL</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Resize image</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Write file</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Create an Image object and return it</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; ImageDTO image <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> ImageDTO<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// ...</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">return</span> image<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p align="LEFT">Nos images JPEG sont maintenant redimensionnées. Il ne nous reste plus qu&#8217;à insérer les données les concernant dans la base de données en utilisant une implémentation d&#8217;<em>ItemWriter</em>.</p>
<h5 align="LEFT">Une implémentation d’<em>ItemWriter</em> pour insérer les images en base de données</h5>
<p align="LEFT">L’interface <em>ItemWriter</em> de Spring ne contient qu’une méthode, <em>write</em>, prenant en entrée une liste d’objets.</p>
<p align="LEFT">Notre itemWriter est très simple : il se contente de récuperer les instances de <em>ImageDTO</em> créées par le processor et d&#8217;insérer les enregistrements correspondants en base de données.</p>
<p align="LEFT">Ce batch étant lié à un application web utilisant Spring IOC, il suffit d&#8217;injecter dans <em>l&#8217;itemWriter</em> le service déjà créé pour l&#8217;application web et de faire appel à sa méthode d&#8217;insertion afin d&#8217;alimenter la base de données.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> ImageWriter <span style="color: #000000; font-weight: bold;">implements</span> ItemWriter <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> ImageService imageService<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> write<span style="color: #009900;">&#40;</span>List<span style="color: #339933;">&lt;?</span> <span style="color: #000000; font-weight: bold;">extends</span> Image<span style="color: #339933;">&gt;</span> items<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">for</span><span style="color: #009900;">&#40;</span><span style="color: #003399;">Image</span> item <span style="color: #339933;">:</span> items<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; imageService.<span style="color: #006633;">insertImage</span><span style="color: #009900;">&#40;</span>item<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; ...<br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p align="LEFT">Notre batch est à présent terminé. Seuls les traitements spécifiques à ce batch ont fait l&#8217;objet d&#8217;une implémentation spécifique : les services et DAOs précédemment développés dans le cadre de l&#8217;application web ont pu être réutilisés.</p>
<h3 align="LEFT">Conclusion</h3>
<p align="LEFT">Cet article ne présente que quelques possibilités offertes par Spring-Batch que j’ai été amené à utiliser dans le cadre du projet sur lequel j’ai travaillé. Cependant, ce framework est très riche ; je n’ai par exemple pas évoqué les possibilités qu’il offre dans la gestion des transactions, le traitement par lots, un contrôle plus fin de l’enchaînement des tâches&#8230; Pour ces aspects, la documentation officielle est simple à prendre en main et des exemples plus détaillés sont disponibles dans le package de Spring-batch, je vous invite donc à aller y jeter un œil si le sujet vous intéresse.</p>
<p align="LEFT">Le projet Eclipse contenant le code utilisé dans cet article se trouve ici : <a href="http://blog.excilys.com/2013/01/03/introduction-a-spring-batch/spring_batch_tuto-2/" rel="attachment wp-att-4933"> spring_batch_tuto</a></p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2013/01/03/introduction-a-spring-batch/" data-text="Introduction à Spring-Batch"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2013/01/03/introduction-a-spring-batch/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2013%2F01%2F03%2Fintroduction-a-spring-batch%2F&amp;title=Introduction%20%C3%A0%20Spring-Batch" id="wpa2a_10"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/AHYnC9GNZiw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2013/01/03/introduction-a-spring-batch/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2013/01/03/introduction-a-spring-batch/</feedburner:origLink></item>
		<item>
		<title>Optimiser l’ergonomie de son application mobile</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/PJTQzriXxF0/</link>
		<comments>http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/#comments</comments>
		<pubDate>Tue, 18 Dec 2012 13:56:07 +0000</pubDate>
		<dc:creator>Julien BRICHE</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[bonnes pratiques]]></category>
		<category><![CDATA[ergonomie]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=5343</guid>
		<description><![CDATA[Introduction Dans un précédent article, je vous avais donné quelques conseils pour améliorer l&#8217;expérience utilisateur de votre application web. Depuis, le monde des Interfaces Homme-Machine a totalement été chamboulé par l’avènement des interfaces mobiles à travers notamment les formats téléphone, tablette et hybride. Ces nouvelles interfaces apportent avant toute chose de nouveaux modes d’interaction mais [...]]]></description>
				<content:encoded><![CDATA[<h3>Introduction</h3>
<p><a href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/intro/" rel="attachment wp-att-5350"><img class="alignright  wp-image-5350" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/11/intro.jpg" width="258" height="162" /></a>Dans un <a href="http://blog.excilys.com/2010/09/13/optimiser-lergonomie-de-son-application-web/" target="_blank">précédent article</a>, je vous avais donné quelques conseils pour améliorer l&#8217;expérience utilisateur de votre application web.</p>
<p>Depuis, le monde des <a href="http://fr.wikipedia.org/wiki/Interface_homme-machine">Interfaces Homme-Machine</a> a totalement été chamboulé par l’avènement des interfaces mobiles à travers notamment les formats téléphone, tablette et hybride.</p>
<p>Ces nouvelles interfaces apportent avant toute chose de nouveaux modes d’interaction mais elles s&#8217;accompagnent également de nouvelles contraintes qui viennent perturber votre démarche ergonomique jusqu&#8217;alors si bien huilée (n&#8217;est-ce pas).</p>
<p>Cependant l’enjeu reste strictement le même, à savoir<strong> satisfaire rapidement et efficacement le besoin des utilisateurs</strong>. Je vous propose donc de voir dans cet article quelques conseils spécifiques à ces nouvelles interfaces. <span id="more-5343"></span></p>
<h3>Les interfaces mobiles, des interfaces avant tout</h3>
<p>Pour commencer, une interface mobile reste avant toute chose une IHM. Ainsi, tous les principes généraux énoncés dans mon précédent article restent valables. A savoir :</p>
<ul>
<li>Les personas</li>
<li>La loi de proximité</li>
<li>La loi de similarité</li>
<li>La loi de Fitts</li>
<li>Les tests utilisateurs</li>
</ul>
<p>De plus, je vais m&#8217;intéresser ici uniquement à l&#8217;interface et à l&#8217;ergonomie des applications mobiles, les règles et conseils énoncés ci-dessous s&#8217;appliquent donc à n&#8217;importe qu&#8217;elle application mobile, qu&#8217;elle soit développée en code natif ou à l&#8217;aide d&#8217;une technologie cross-platform.</p>
<h3>Dis-moi comment tu utilises ton mobile, je te dirai qui tu es</h3>
<p>Pour rappel, la <strong>première étape</strong> du travail d&#8217;ergonome est d&#8217;identifier correctement et avec précision les utilisateurs cibles de son application.<br />
Avant de passer aux conseils pratiques, nous allons donc voir dans cette première partie ce qui va caractériser de manière générale les utilisateurs d&#8217;une application mobile.</p>
<h4>Des utilisateurs dans un monde de contraintes</h4>
<p>Comme je le disais dans l&#8217;introduction, les interfaces mobiles ont apporté un grand nombre d&#8217;innovations mais également de nouvelles contraintes que les interfaces de bureau par exemple n&#8217;avaient pas.</p>
<p>Voici les plus significatives :</p>
<ul>
<li>Connexion internet peu fiable</li>
<li>Performances limitées</li>
<li>Taille d&#8217;écran réduite</li>
<li>Une utilisation possible <strong>partout</strong> et à <strong>tout moment</strong></li>
</ul>
<p>Ces deux derniers points vont nous intéresser particulièrement d&#8217;un point de vue de la démarche ergonomique (les deux premiers points relevant plus de la technique).</p>
<h4><strong>Utilisateur mobile = un doigt + un oeil</strong></h4>
<p>Contrairement aux utilisateurs des applications accessibles depuis un poste fixe, assis tranquillement sur leur chaise devant leur écran, les utilisateurs des applications mobiles peuvent bien souvent se trouver au travail, dans une file d&#8217;attente, au super-marché en train de faire leurs courses ou encore devant la télévision.</p>
<p>Ce qui signifie que dans la plupart des situations, vous n&#8217;aurez pas toute l&#8217;attention de vos utilisateurs et que ceux-ci ne seront pas en mesure d&#8217;interagir pleinement avec votre interface.</p>
<p>Ce constat général sur les utilisateurs mobiles, <a href="http://www.lukew.com/about/">Luke Wroblewski</a> l&#8217;a résumé dans son livre<em> Mobile First</em> en utilisant la métaphore suivante : <a href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/oeil_doigt/" rel="attachment wp-att-5380"><img class="aligncenter size-full wp-image-5380" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/11/oeil_doigt.jpg" width="600" height="191" /></a><br />
<strong>Un doigt :</strong> car l&#8217;utilisateur tiendra généralement son mobile d&#8217;une seule main et qu&#8217;il utilisera un seul doigt (surement deux dans le cas des tablettes) avec plus ou moins de précision pour interagir avec votre interface.</p>
<p><strong>Un œil :</strong> car votre utilisateur pourra se trouver un peu n&#8217;importe où avec de nombreuses sources de distraction aux alentours, vous n&#8217;aurez alors qu&#8217;une attention partielle de sa part.</p>
<p lang="fr-FR"><span style="color: #000000">C&#8217;est donc la première chose à garder à l&#8217;esprit quand on conçoit une application mobile.</span></p>
<h4><strong>Les trois comportements mobiles type<br />
</strong></h4>
<p>Pour terminer avec ce qui caractérise les utilisateurs mobiles d&#8217;une manière générale, voici les trois comportements ou besoins mobiles que <a href="http://globalmoxie.com/about/">Josh Clark</a> a identifiés dans son livre <em>Tapworthy</em>, avec pour chacun le conseil à suivre :</p>
<h5>&#8220;I&#8217;m local&#8221;</h5>
<ul>
<li><strong>Explication :</strong> Je suis à la recherche d&#8217;une information et j&#8217;en ai besoin MAINTENANT</li>
<li><strong>Exemple :</strong>Recherche d&#8217;une station de vélib, d&#8217;une adresse, du distributeur de votre banque préférée le plus proche</li>
<li><strong>Conseil :</strong> Donner rapidement l&#8217;information, le contenu doit prendre le pas sur la navigation. Miser également sur les accélérateurs, en effet le mobile est l&#8217;écran personnel par excellence, vous pouvez alors proposer la dernière recherche effectuée, enregistrer les préférences.</li>
</ul>
<h5>Micro-tasking</h5>
<ul>
<li><strong>Explication :</strong> Je veux rester à la page des mises-à-jour de certaines données / j&#8217;ai besoin que quelque chose soit fait maintenant et cela ne peut pas attendre</li>
<li><strong>Exemple :</strong> Réseaux sociaux (twitter, Facebook), virement bancaire</li>
<li><strong>Conseil :</strong> Tout ce qui est du ressort de l&#8217;efficience a énormément d&#8217;importance : les utilisations étant répétées, la moindre action en trop se ressent rapidement et gêne le confort d&#8217;usage donc attention aux pages inutiles (ex. pas d&#8217;écran promotionnel intempestif)</li>
</ul>
<h5>&#8220;I&#8217;m bored&#8221;</h5>
<ul>
<li><strong>Explication :</strong> Je m&#8217;ennuie, je souhaite tuer le temps</li>
<li><strong>Exemple :</strong> Articles, réseaux sociaux (oui encore) et surtout les fameux petits jeux vidéos</li>
<li><strong>Conseil :</strong> Ce cas est un  peu plus flexible que les deux précédents vu que l&#8217;utilisateur n&#8217;a rien d&#8217;autre à faire. Vous pouvez donc par exemple vous permettre d&#8217;avoir une navigation plus touffue ou des écrans moins essentiels (oui de la pub si vous voulez). Cependant ce n&#8217;est pas une raison pour négliger le confort d&#8217;utilisation ou d&#8217;oublier ce que l&#8217;utilisateur est venu chercher : une source de distraction.</li>
</ul>
<p>Voilà, si vous ne perdez pas de vue que vos utilisateurs finaux ne vous donneront qu&#8217;une attention partielle, que leur interaction avec l&#8217;interface de votre application tactile pourra être paradoxalement limitée et qu’ils s&#8217;inscriront potentiellement dans l’un de ces comportements, vous aurez déjà pas mal débroussaillé leur profil type. Pour le reste il faudra faire plus ample connaissance, à l&#8217;aide des personas par exemple.</p>
<h3>Conseils en vrac spécifiques aux interfaces mobiles</h3>
<p>Maintenant que vous avez identifié un peu mieux les utilisateurs de votre application mobile, place à la liste (non exhaustive) des améliorations ergonomiques possibles :</p>
<h4>Zone de sélection</h4>
<p>Pour les éléments interactifs de votre interface, toujours prévoir une zone de sélection suffisamment grande pour les doigts de vos utilisateurs. Attention, comme le montre cet exemple, la représentation graphique n&#8217;a pas besoin d&#8217;être aussi grande, seule la zone de sélection réelle doit l&#8217;être. <a href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/zone_selection/" rel="attachment wp-att-5364"><img class="aligncenter size-full wp-image-5364" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/11/zone_selection.jpg" width="600" height="167" /></a></p>
<h4> Zone de confort / zone d&#8217;inconfort</h4>
<p>On appelle zone de confort la partie d&#8217;une interface graphique qui est aisément atteignable par l&#8217;utilisateur.<br />
A l&#8217;inverse, la zone d&#8217;inconfort est la partie de l&#8217;interface qui demandera à l&#8217;utilisateur un effort supplémentaire pour atteindre un point précis.<br />
Pour les interfaces sur écran fixe, manipulées à l&#8217;aide d&#8217;une souris, la zone de confort correspond aux quatre coins et au centre de l&#8217;écran. <a href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/zone_de_confort3/" rel="attachment wp-att-5376"><img class="aligncenter size-full wp-image-5376" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/11/zone_de_confort3.jpg" width="600" height="200" /></a>Pour les interfaces mobiles, ces zones dépendent du format de l&#8217;écran et de son mode d&#8217;affichage. Ainsi, en mode paysage, l&#8217;utilisateur arrivera à atteindre plus facilement les parties latérales de l&#8217;interface alors qu&#8217;en mode portrait ça sera plutôt le coin inférieur gauche pour un téléphone et les coins inférieur droit et supérieur gauche pour une tablette tactile (pour un droitier).<br />
Une fois ces différentes surfaces identifiées, il est conseillé de placer dans la zone de confort les éléments importants de votre interface, typiquement les éléments interactifs qui subiront des actions répétées et à l&#8217;inverse, on placera dans la zone d&#8217;inconfort des éléments interactifs dont l&#8217;utilisateur doit réfléchir à deux fois avant de déclencher leur action, comme un bouton de suppression par exemple.</p>
<p style="text-align: center"><a href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/zone_de_confort2/" rel="attachment wp-att-5359"><img class="size-full wp-image-5359 aligncenter" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/11/zone_de_confort2.jpg" width="473" height="671" /></a></p>
<p>&nbsp;</p>
<h4>Aspect et présentation des éléments interactifs</h4>
<ul>
<li>Soigner l&#8217;affordance (voir mon article précédent pour la définition de ce terme) des éléments interactifs de votre interface. En effet contrairement aux interfaces web par exemple les utilisateurs ne peuvent obtenir d&#8217;indices de la part des infobulles ou du survol de leur souris sur un élément de l&#8217;interface. Ainsi au premier coup d’œil, l&#8217;utilisateur doit savoir quelles zones sont cliquables.</li>
<li>Essayer au possible d&#8217;avoir un nombre réduit de boutons par écran afin d&#8217;éviter une sélection erronée à cause d&#8217;une source de distraction extérieure.</li>
<li>S&#8217;assurer d&#8217;avoir un espace suffisant entre les éléments interactifs rapprochés de vos écrans afin d&#8217;empêcher une éventuelle erreur de sélection. Par exemple, dans le cas ci-dessous les boutons <em>Annuler</em> et <em>Valider</em> sont bien trop proches alors qu&#8217;ils déclenchent des actions <strong>radicalement</strong> différentes !</li>
</ul>
<div id="attachment_5371" class="wp-caption aligncenter" style="width: 130px"><a href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/cancelorvalid/" rel="attachment wp-att-5371"><img class="size-full wp-image-5371" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/11/cancelOrValid.jpg" width="120" height="75" /></a><p class="wp-caption-text">Exemple d&#8217;espaces insuffisants entre des boutons</p></div>
<ul>
<li>Soigner l&#8217;intitulé des boutons de navigation pour ne pas trop solliciter la mémoire de l&#8217;utilisateur. Par exemple, plutôt qu&#8217;un libellé <em>Retour</em> (retour vers quoi ?), il est préférable d&#8217;indiquer le nom de la partie de l&#8217;application vers laquelle l&#8217;utilisateur va revenir (par exemple <em>Accueil</em>).</li>
</ul>
<h4>Pour gagner de la place dans les formulaires</h4>
<p>L&#8217;espace horizontal pouvant faire défaut sur les interfaces mobiles, il est parfois difficile de mettre en place un formulaire, voici quelques clés pour gagner de l&#8217;espace :</p>
<ul>
<li>Inclure l&#8217;intitulé des champs de saisie au sein du composant (intitulé qui disparaît dès que l&#8217;utilisateur saisit quelque chose) :</li>
</ul>
<p><a href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/form2/" rel="attachment wp-att-5368"><img class="aligncenter size-full wp-image-5368" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/11/form2.jpg" width="506" height="104" /></a></p>
<ul>
<li>Préférer une présentation verticale plutôt qu&#8217;horizontale :</li>
</ul>
<p><a href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/form1/" rel="attachment wp-att-5367"><img class="aligncenter  wp-image-5367" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/11/form1.jpg" width="600" height="81" /></a></p>
<ul>
<li>Prendre en compte la hauteur du clavier virtuel quand celui-ci est visible (environ la moitié de l&#8217;écran) et bien prendre soin de laisser visible le champ qui est en train d&#8217;être rempli pour ne pas perturber l&#8217;utilisateur.</li>
</ul>
<ul>
<li>La saisie par un clavier virtuel pouvant s&#8217;avérer fastidieuse sur ce type d&#8217;interfaces, il est préférable de  :
<ul>
<li> Réduire au minimum le nombre d&#8217;informations que l&#8217;on demande à l&#8217;utilisateur.</li>
<li>Proposer des champs de saisie pré-remplis plutôt que des champs vides.</li>
<li>Proposer des mécanismes d&#8217;aide à la saisie comme de l&#8217;auto-complétion ou encore si le champ de saisie s&#8217;attend à recevoir un nombre, directement proposer le clavier virtuel numérique à l&#8217;utilisateur.</li>
</ul>
</li>
</ul>
<h3>Conclusion</h3>
<p>Voilà ce qu&#8217;il faut retenir de cet article : pour optimiser l&#8217;ergonomie de votre application mobile vous devez :</p>
<ul>
<li>Plus que jamais prendre en compte comment vos utilisateurs utilisent leur mobile et pourquoi.</li>
<li>Mettre l&#8217;accent sur le contenu plutôt que sur la navigation.</li>
<li>Maintenir un certain niveau de clarté et de concentration.</li>
</ul>
<p>A bientôt pour la prochaine révolution dans le monde des IHM (peut-être une interface incorporée à l&#8217;oeil ^^)</p>
<p style="text-align: center"><a href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/4782567057_e7e6f9d63b/" rel="attachment wp-att-5655"><img class="size-full wp-image-5655 aligncenter" alt="" src="http://blog.excilys.com/wp-content/uploads/2012/12/4782567057_e7e6f9d63b.jpg" width="500" height="313" /></a></p>
<p>&nbsp;</p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/" data-text="Optimiser l&#8217;ergonomie de son application mobile"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2012%2F12%2F18%2Foptimiser-lergonomie-de-son-application-mobile%2F&amp;title=Optimiser%20l%E2%80%99ergonomie%20de%20son%20application%20mobile" id="wpa2a_12"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/PJTQzriXxF0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2012/12/18/optimiser-lergonomie-de-son-application-mobile/</feedburner:origLink></item>
		<item>
		<title>Le Cloud : Quels sont les impacts pour le développeur ?</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/Dev0zu6dbJo/</link>
		<comments>http://blog.excilys.com/2012/12/14/le-cloud-quels-sont-les-impacts-pour-le-developpeur/#comments</comments>
		<pubDate>Fri, 14 Dec 2012 10:24:48 +0000</pubDate>
		<dc:creator>Nicolas DUSSART</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[cloud]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=5679</guid>
		<description><![CDATA[Tous les jours on trouve de nouveaux articles dans la presse spécialisée vantant les services d’un nouveau fournisseur de cloud ou encore des études sur les avantages et la réduction des coûts que le cloud computing apporte. En effet le cloud computing aujourd’hui, c’est à la mode. Mais en mettant tout ce buzz de côté, [...]]]></description>
				<content:encoded><![CDATA[<p>Tous les jours on trouve de nouveaux articles dans la presse spécialisée vantant les services d’un nouveau fournisseur de cloud ou encore des études sur les avantages et la réduction des coûts que le cloud computing apporte. En effet le cloud computing aujourd’hui, c’est à la mode. Mais en mettant tout ce buzz de côté, une question demeure : comment le changement vers le cloud va impacter le développeur qui code des applications d’entreprise (en JAVA ou pas d’ailleurs) ?</p>
<p>Pour comprendre ce changement, revenons sur la manière traditionnelle dont les équipes de développement codent les applications.</p>
<p><span id="more-5679"></span></p>
<p>&nbsp;</p>
<h3>Le développement d’application d’entreprise de manière traditionnelle</h3>
<p>&nbsp;</p>
<p>Dans un projet classique, le développeur doit savoir</p>
<ul>
<li>développer une application comprenant x entités ; définir des relations entre ces entités</li>
<li>afficher des sous-ensembles de données et pouvoir les mettre à jour</li>
<li>implémenter et déployer une application accessible par des utilisateurs appartenant à différents rôles</li>
<li>restreindre l’accès à certaines données pour des utilisateurs n’ayant pas les autorisations suffisantes</li>
<li>notifier tel module si telle action est faite</li>
<li>effectuer des actions de manière récurrente (tous les jours / semaines)</li>
<li>rendre l’application extensible et paramétrable</li>
</ul>
<p>Les challenges ci-dessus resteront toujours à relever. Par contre ce qui suit va probablement changer :</p>
<ul>
<li>installer un outil de versioning tel que git, svn.</li>
<li>mettre en place un outil d’intégration continue</li>
<li>configurer un serveur d’applications (tomcat, jboss)</li>
<li>installer une base de données</li>
<li>mettre en place un système de messaging</li>
</ul>
<p>Je vous entends déjà : mais c’est pas aux développeurs de savoir faire ça. Pourtant dans bien des cas pour développer et tester votre application, vous devez savoir mettre en place ces outils.</p>
<p>Ces dernières tâches peuvent prendre beaucoup de temps : il faut trouver des machines virtuelles pour héberger les différents outils, les sauvegarder, se documenter sur la configuration, rendre accessible ces outils à tous les collaborateurs par l’intermédiaire d’un vpn ou autre. Du coup c’est moins de temps passé sur la logique métier de votre application et donc une perte de productivité.</p>
<p>&nbsp;</p>
<h3>Qu’est ce que l’on trouve dans le cloud</h3>
<p><a href="http://blog.excilys.com/2012/12/14/le-cloud-quels-sont-les-impacts-pour-le-developpeur/cloudservices-2/" rel="attachment wp-att-5857"><img class="alignnone size-full wp-image-5857" src="http://blog.excilys.com/wp-content/uploads/2012/12/cloudservices1.png" alt="" width="597" height="404" /></a></p>
<p>Il y a  trois concepts bien distincts de cloud service. Ceux qui nous intéressent en tant que développeur sont le PaaS et l’ IaaS.</p>
<p>Avec une solution de type Insfrastructure as a service, la création de machine virtuelle se fait à la volée. Ensuite on est libre de faire ce que l’on veut avec. On peut créer des machines virtuelles pour faire office de production, de pré-production, de recette mais également pour héberger les outils de versioning, l’intégration continue, etc&#8230;</p>
<p>Avec un fournisseur de services type Platform as a service, le but est d’être capable de développer et déployer des applications très rapidement et de les rendre disponibles de manière fiable. On peut par exemple partir avec AmazonWS, Google App Engine, Force.com, Windows Azure, et bien d’autres encore. Le tout étant de choisir son fournisseur en fonction des technologies que l’on va utiliser.</p>
<p>Un des outils fondamentaux pour le développeur est son IDE. Amazon et Google proposent des plug-in Eclipse très pratiques permettant de développer, tester et déployer des applications dans leur cloud respectif. Il existe aussi des IDEs accessibles directement depuis un navigateur internet comme Cloud IDE ou bien Cloud9 IDE. Tout comme un IDE en local, il y a la complétion syntaxique, l’intégration du gestionnaire de source mais aussi le partage de workspace, un chat afin de faciliter le travail collaboratif dans une équipe de développement. La plupart des éditeurs proposent une application en local permettant de travailler en mode offline.</p>
<p>Le PaaS s’inscrit parfaitement dans la stratégie “DevOps”, éliminant la frontière qui existe entre développement et exploitation. Il n’y a plus besoin de trouver et de mettre en place un environnement de test, les développeurs n’ont plus besoin d’aller voir l’équipe d’exploitation avant de lancer les campagnes de tests et grâce à l’intégration continue, les développeurs peuvent maintenant tester à mesure qu’ils développent sans perdre de temps.</p>
<p>&nbsp;</p>
<h3>Qu’elles sont les limites du cloud ?</h3>
<p>&nbsp;</p>
<p>Tout n’est pas virtualisable. En effet, des composants qui génèrent d’important I/O peuvent être bridés par la virtualisation étant donnée que l’accès aux ressources physiques n’est plus direct mais géré par un hyperviseur. Par exemple, il est préférable de ne pas virtualiser les bases de données car elles génèrent beaucoup d’I/O disque quand elles sont grandement sollicitées.</p>
<p>Il y a également un manque de standardisation. En effet, de nombreux fournisseurs donnent accès à des API propriétaires qui couplent une partie du code au service utilisé. Plus l’application utilise les services mis à disposition, plus la probabilité qu’elle devienne complètement dépendante de celle-ci augmente. Il faut donc faire attention à bien prendre en compte dès le début la portabilité des applications d’une plateforme à l’autre pour ne pas être coincé chez un fournisseur.</p>
<p>La gestion du offline peut aussi être problématique. Si toutes les données et tous les services sont en ligne, que se passe-t-il lorsqu’il n’y a plus d’accès à internet ?  Il faut penser à prévoir la possibilité de rapatrier des données en local.</p>
<p>Enfin, le principal reproche fait à ces services est la gestion des données. Toutes les entreprises ne sont pas prêtes à externaliser leurs données sur les serveurs d’un tiers. Cependant les solutions de cloud privé ou même hybride permettent de palier à ces problématiques.</p>
<p>&nbsp;</p>
<p>En conclusion, on voit bien que le cloud computing va faire évoluer les challenges du développeur. De part la facilité de mise en place d’environnements de développement, de test et de production, l’amélioration du travail collaboratif, le développeur perd moins de temps dans la configuration et se concentre sur le métier et l’expérience utilisateur. De plus, les applications dans le cloud sont par définition accessibles à travers tous terminaux reliés à internet : pc, tablette et smartphone. Le développeur doit donc être capable d’interfacer les applications avec ces terminaux.</p>
<p>&nbsp;</p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2012/12/14/le-cloud-quels-sont-les-impacts-pour-le-developpeur/" data-text="Le Cloud : Quels sont les impacts pour le développeur ?"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2012/12/14/le-cloud-quels-sont-les-impacts-pour-le-developpeur/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2012%2F12%2F14%2Fle-cloud-quels-sont-les-impacts-pour-le-developpeur%2F&amp;title=Le%20Cloud%20%3A%20Quels%20sont%20les%20impacts%20pour%20le%20d%C3%A9veloppeur%20%3F" id="wpa2a_14"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/Dev0zu6dbJo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2012/12/14/le-cloud-quels-sont-les-impacts-pour-le-developpeur/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2012/12/14/le-cloud-quels-sont-les-impacts-pour-le-developpeur/</feedburner:origLink></item>
		<item>
		<title>CQRS – Lire ou écrire il faut choisir !</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/690DR10fQJY/</link>
		<comments>http://blog.excilys.com/2012/12/11/cqrs-lire-ou-ecrire-il-faut-choisir/#comments</comments>
		<pubDate>Tue, 11 Dec 2012 09:40:34 +0000</pubDate>
		<dc:creator>Charles KAYSER</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[axon]]></category>
		<category><![CDATA[cqrs]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=5487</guid>
		<description><![CDATA[Introduction Nous pouvons distinguer deux types de requêtes reçues par nos sites web, celles demandant de la restitution d&#8217;information (GET) et celles désirant altérer l&#8217;état de l&#8217;application (POST, PUT, DELETE). D&#8217;un côté &#8220;on lit&#8221;, d&#8217;un autre &#8220;on écrit&#8221;. Qu&#8217;est-ce que votre application fait le plus avec les données, en lire ou en écrire ? Pour [...]]]></description>
				<content:encoded><![CDATA[<h3>Introduction</h3>
<p>Nous pouvons distinguer deux types de requêtes reçues par nos sites web, celles demandant de la restitution d&#8217;information (GET) et celles désirant altérer l&#8217;état de l&#8217;application (POST, PUT, DELETE). D&#8217;un côté &#8220;on lit&#8221;, d&#8217;un autre &#8220;on écrit&#8221;.<br />
Qu&#8217;est-ce que votre application fait le plus avec les données, en lire ou en écrire ?<br />
Pour un blog par exemple, la lecture d&#8217;articles est largement plus demandée par les utilisateurs que la soumission de nouveaux articles à publier, survenant moins souvent.<br />
Cette observation pourrait-elle influer sur la façon de coder nos applications ?</p>
<p><span id="more-5487"></span></p>
<p>En général lorsque nous concevons une application, nous veillons soigneusement à ce que son code soit organisé quel que soit le traitement demandé, lecture ou écriture. Ainsi une requête pour afficher un article de blog manipulera certainement un objet de domaine Article dont un ou plusieurs champs seront utilisés pour afficher le contenu à lire à l&#8217;écran.<br />
D&#8217;un autre côté lorsque l&#8217;utilisateur créera un nouvel article ce sera également ce même objet du modèle qui sera manipulé mais peut-être en utilisant d&#8217;autres champs non utilisés en lecture.<br />
Pour quelle raison abstraire la donnée dans un objet unique si les usages sont différents ?</p>
<p>Le modèle d&#8217;architecture que je vous propose de découvrir est né de l&#8217;idée qu&#8217;il ne faut pas un mais plusieurs modèles pour abstraire un domaine métier complexe et que ce choix favorise la performance et la maintenance des applications. En particulier il préconise de séparer les composants de lecture de ceux d&#8217;écriture: le modèle de lecture n&#8217;est pas le même que celui utilisé pour l&#8217;écriture.</p>
<h3>Le modèle d&#8217;architecture Command Query Responsibility Segregation (CQRS)</h3>
<p>La définition de <em>CQRS</em> est plutôt simple, c&#8217;est un modèle d&#8217;architecture séparant en deux couches distinctes les traitements de restitution de données (<em>Queries</em>) de ceux les modifiants (<em>Commands</em>).</p>
<p>Le schéma suivant illustre la différence entre ce modèle et une architecture 3-tiers beaucoup utilisée en entreprise:</p>
<p><a href="http://blog.excilys.com/2012/12/11/cqrs-lire-ou-ecrire-il-faut-choisir/ckayser_cqrs_vs_3tiers-2/" rel="attachment wp-att-5497"><img class="aligncenter size-full wp-image-5497" src="http://blog.excilys.com/wp-content/uploads/2012/12/ckayser_cqrs_vs_3tiers1.png" alt="Architecture 3-tiers vs CQS" width="711" height="230" /></a></p>
<p>Là où les traitements de lecture et d&#8217;écriture passent par les mêmes couches sur une architecture 3-tiers, étudions ce que ça nous apporte de les séparer.</p>
<h4>Des Queries pour restituer les données</h4>
<div>
<div id="attachment_5501" class="wp-caption alignleft" style="width: 339px"><a href="http://blog.excilys.com/2012/12/11/cqrs-lire-ou-ecrire-il-faut-choisir/ckayser_cqrs_query-2/" rel="attachment wp-att-5501"><img class="size-full wp-image-5501" src="http://blog.excilys.com/wp-content/uploads/2012/12/ckayser_cqrs_query1.png" alt="Diagramme &quot;Queries&quot;" width="329" height="297" /></a><p class="wp-caption-text">Diagramme &#8220;Queries&#8221;</p></div></p>
<p>L&#8217;accès direct aux données depuis la couche de Présentation permet de récupérer, via des <em>queries</em> spécialisées par écran, les données à afficher et seulement celles-ci.</p>
</div>
<div>L&#8217;idée est d&#8217;être très performant en lecture en réduisant le nombre de traitements qui s&#8217;accumulent lors d&#8217;une requête d&#8217;affichage. Le plus souvent il s&#8217;agit de la conversion des données récupérées par des requêtes sql (multi fetch joins, plusieurs select, full scan de tables volumineuses, etc.) ou de la manipulation d&#8217;objets stockés en cache</div>
<div>CQRS saute cette étape de conversion et rapproche l&#8217;utilisateur de la donnée à afficher en ne récupérant que ce qui doit être affiché.</div>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;">// Avant: JPQL compliqué</span><br />
<span style="color: #003399;">List</span> transactions <span style="color: #339933;">=</span> entityManager.<span style="color: #006633;">createQuery</span><span style="color: #009900;">&#40;</span><br />
<span style="color: #0000ff;">&quot;select t from PaymentTransaction t<br />
&nbsp; &nbsp; fetch join t.participant p<br />
&nbsp; &nbsp; fetch join p.bank b<br />
&nbsp; &nbsp; where b.bankBic = :creditorBic<br />
&nbsp; &nbsp; and t.classification = 'DirectDebit'<br />
&nbsp; &nbsp; and t.outbound = false&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #666666; font-style: italic;">// Après: Par exemple en requêtant une vue sql</span><br />
<span style="color: #666666; font-style: italic;">// des données affichables</span><br />
<span style="color: #003399;">ResultSet</span> transactions <span style="color: #339933;">=</span> entityManager.<span style="color: #006633;">createQuery</span><span style="color: #009900;">&#40;</span><br />
<span style="color: #0000ff;">&quot;select view from ReceivedDirectDebitTransactionsView view<br />
&nbsp; &nbsp; where creditorBic= :creditorBic&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>Le stockage se doit donc d&#8217;être <strong>dé-normalisé</strong> pour refléter au plus près le format de la donnée affichable et limiter les transformations applicatives. Des vues SQL ou une base noSQL sont par exemple tout à fait adaptées à cette utilisation.</p>
<p>Ce modèle de lecture est mis à jour lorsque l&#8217;état de l&#8217;application change, concrètement lorsque l&#8217;application reçoit des <em>commandes</em>.</p>
<h4>Des Commandes pour modifier l&#8217;état de l&#8217;application</h4>
<div id="attachment_5504" class="wp-caption alignright" style="width: 245px"><a href="http://blog.excilys.com/2012/12/11/cqrs-lire-ou-ecrire-il-faut-choisir/ckayser_cqrs_command-2/" rel="attachment wp-att-5504"><img class="size-full wp-image-5504" src="http://blog.excilys.com/wp-content/uploads/2012/12/ckayser_cqrs_command1.png" alt="Diagramme &quot;Commands&quot;" width="235" height="393" /></a><p class="wp-caption-text">Diagramme &#8220;Commands&#8221;</p></div>
<p>Dans la couche domaine on maintient l&#8217;état de l&#8217;application dans une base de stockage pouvant être différente de celle utilisée par les <em>queries</em>, comme le montre le diagramme à droite.</p>
<p>Pour altérer cet état la couche présentation soumet des commandes au domaine en encapsulant le changement demandé.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;">// un appel de méthode de service classique</span><br />
bankService.<span style="color: #006633;">performTransfer</span><span style="color: #009900;">&#40;</span>debitedAccountId, creditedAccountId, amount<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #666666; font-style: italic;">// devient une soumission de commande à la couche métier avec CQRS</span><br />
bankCommandBus.<span style="color: #006633;">dispatch</span><span style="color: #009900;">&#40;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">new</span> PerformTransferCommand<span style="color: #009900;">&#40;</span>debitedAccountId, creditedAccountId, amount<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>Dans l&#8217;exemple ci-dessus, la commande PerformTransferCommand, qui est un simple POJO, place les trois paramètres dans un contexte propre à l&#8217;action à réaliser exprimée par le nom de la classe de commande: effectuer un transfert d&#8217;argent compte à compte. Le nom de classe est un verbe conjugué à l&#8217;impératif pour bien signaler qu&#8217;il s&#8217;agit d&#8217;un ordre donné au système pour effectuer un traitement.</p>
<p>L&#8217;intention utilisateur est transportée à la différence d&#8217;un appel de méthode de service où les données ne sont pas liées à un contexte particulier. Le code gagne ainsi en clarté et la maintenance est facilitée.</p>
<p>L&#8217;usage de la commande peut également permettre d&#8217;améliorer la réactivité de l&#8217;application. En effet si besoin est, désynchroniser le travail métier de celui de la couche présentation est facilement envisageable en rendant le command bus asynchrone sans avoir à modifier le code métier ou le controller.</p>
<p style="padding-left: 30px"><img class="alignleft size-full wp-image-5622" src="http://blog.excilys.com/wp-content/uploads/2012/12/AxonFramework_logo.png" alt="" width="56" height="49" />Pour faciliter encore plus l&#8217;intégration des composants CQRS comme le command bus nous disposons en java du framework <a title="Axon framework" href="http://www.axonframework.org" target="_blank">Axon</a>. Celui-ci à la particularité de s&#8217;intégrer rapidement dans les projets Spring grâce au namespace <code class="codecolorer text default"><span class="text">axon</span></code> proposé.</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;beans</span> ... <span style="color: #000066;">xmlns:axon</span>=<span style="color: #ff0000;">&quot;http://www.axonframework.org/schema/core&quot;</span> ... <span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;axon:command-bus</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;bankCommandBus&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;axon:annotation-config</span> <span style="color: #000066;">command-bus</span>=<span style="color: #ff0000;">&quot;bankCommandBus&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/beans<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
</p>
<p style="padding-left: 30px">Ici on déclare le command bus qui sera géré comme un bean Spring et donc injectable dans tous nos controlleurs. La seconde ligne &lt;axon:annotation-config /&gt; active un BeanPostProcessor qui permet de configurer les command handler par annotation.</p>
<p>Lorsque la commande est envoyée depuis la couche présentation, un command handler la réceptionne et applique la logique métier en persistant les modifications d&#8217;états du modèle d&#8217;écriture.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@<span style="color: #003399;">Component</span><br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> PerformTransferCommandHandler <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; ...<br />
&nbsp; &nbsp; @CommandHandler<br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">void</span> handlePerformTransfer<span style="color: #009900;">&#40;</span>PerformTransferCommand performTransferCommand<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Account debitedAccount <span style="color: #339933;">=</span> accountRepository.<span style="color: #006633;">load</span><span style="color: #009900;">&#40;</span>command.<span style="color: #006633;">getDebitedAccountId</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Account creditedAccount <span style="color: #339933;">=</span> accountRepository.<span style="color: #006633;">load</span><span style="color: #009900;">&#40;</span>command.<span style="color: #006633;">getCreditedAccountId</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; PaymentTransaction debitTransaction <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> PaymentTransaction<span style="color: #009900;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; debitedAccount, <span style="color: #339933;">-</span>command.<span style="color: #006633;">getAmount</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>, <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Date</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; PaymentTransaction creditTransaction <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> PaymentTransaction<span style="color: #009900;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; creditedAccount, command.<span style="color: #006633;">getAmount</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>, <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Date</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; repository.<span style="color: #006633;">add</span><span style="color: #009900;">&#40;</span>debitTransaction<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; repository.<span style="color: #006633;">add</span><span style="color: #009900;">&#40;</span>creditTransaction<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; ...<br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>Contrairement à la partie query le domaine a tout intérêt à être modélisé dans une structure <strong>normalisée</strong> (une base de données relationnelle par exemple) car il assure la cohérence de l&#8217;état de l&#8217;application.<br />
Ce modèle utilisé pour l&#8217;écriture est donc différent de celui utilisé en lecture par les <em>queries</em>. Il nous reste à voir comment les deux modèles synchronisent leurs données.</p>
<h4>Des évènements de domaine pour mettre à jour les données utilisées par les Queries</h4>
<div id="attachment_5503" class="wp-caption aligncenter" style="width: 617px"><a href="http://blog.excilys.com/2012/12/11/cqrs-lire-ou-ecrire-il-faut-choisir/ckayser_cqrs_events/" rel="attachment wp-att-5503"><img class="size-full wp-image-5503" src="http://blog.excilys.com/wp-content/uploads/2012/12/ckayser_cqrs_events.png" alt="Diagramme évènements" width="607" height="309" /></a><p class="wp-caption-text">Diagramme évènements</p></div>
<p>Pour maintenir la synchronisation entre les données de lecture et l&#8217;état réel de l&#8217;application, le domaine prend la responsabilité supplémentaire d&#8217;émettre des évènements notifiant que l&#8217;état de l&#8217;application a changé. La couche d&#8217;accès aux données réceptionne alors les évènements et met à jour la base de lecture en conséquence.</p>
<p style="padding-left: 30px"><img class="alignleft size-full wp-image-5622" src="http://blog.excilys.com/wp-content/uploads/2012/12/AxonFramework_logo.png" alt="" width="56" height="49" />Axonframework propose que les entités du domaine étendent la classe abstraite AbstractAnnotatedAggregateRoot pour bénéficier de la méthode apply() qui permet d&#8217;émettre les évènements vers la couche query:</p>
<p style="padding-left: 30px">
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> PaymentTransaction <span style="color: #000000; font-weight: bold;">extends</span> AbstractAnnotatedAggregateRoot <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; ...<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> PaymentTransaction<span style="color: #009900;">&#40;</span>Account account, <span style="color: #003399;">BigDecimal</span> amount, <span style="color: #003399;">Date</span> date<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Emission d'un évènement de domaine notifiant qu'une opération de paiement a été créée</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; apply<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> PaymentTransactionCreatedEvent<span style="color: #009900;">&#40;</span>account, amount, date<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; ...<br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
</p>
<p style="padding-left: 30px">Et l&#8217;annotation @EventHandler permet d&#8217;attraper simplement les évènements côté query afin de mettre à jour le modèle de lecture en fonction des demandes réalisées:</p>
<p style="padding-left: 30px">
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> PaymentTransactionUpdater <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; @EventHandler<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> handlePaymentTransactionCreatedEvent<span style="color: #009900;">&#40;</span>PaymentTransactionCreatedEvent event<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// Ajout des données dé-normalisées dans une collection MongoDB </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003399;">String</span> date <span style="color: #339933;">=</span> event.<span style="color: #006633;">getDate</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">toString</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003399;">String</span> account <span style="color: #339933;">=</span> event.<span style="color: #006633;">getCreditedAccount</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">getNumber</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">int</span> amount <span style="color: #339933;">=</span> event.<span style="color: #006633;">getAmount</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; BasicDBObject newTransactionEntry <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> BasicDBObject<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;date&quot;</span>, date<span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #006633;">append</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;account&quot;</span>, account<span style="color: #009900;">&#41;</span>.<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006633;">append</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;amount&quot;</span>, amount<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; DBCollection transactionEntries <span style="color: #339933;">=</span> db.<span style="color: #006633;">getCollection</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;paymentTransactionEntries&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; transactionEntries.<span style="color: #006633;">insert</span><span style="color: #009900;">&#40;</span>newTransactionEntry<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
</p>
<p style="padding-left: 30px">Lorsque beaucoup d&#8217;évènements sont générés, Axon propose <a href="http://www.axonframework.org/docs/0.7.x/repositories-and-event-stores.html#using-snapshot-events">un mécanisme de snapshots</a> qui permet de grouper plusieurs évènements en un seul afin d&#8217;alléger le nombre d&#8217;échanges avec la couche de lecture.</p>
<p>Dans certains cas il n&#8217;est pas nécessaire de mettre à jour en temps réel les données de lecture et l&#8217;utilisation d&#8217;évènements permet de paramétrer cela facilement. Par exemple un évènement pourrait être placé dans une file d&#8217;attente et être traité à une heure où l&#8217;activité de l&#8217;application est au plus bas afin de conserver la performance de lecture en horaire de pointe.</p>
<h3>Conclusion</h3>
<p>Grâce à un accès rapide aux données affichables et préparées en amont de toute demande utilisateur, la force de CQRS est particulièrement révélée pour les applications dont la sollicitation en lecture est plus élevée qu&#8217;en écriture. La partie lecture pouvant être isolée dans son propre composant, elle pourrait facilement être instanciée sur plusieurs serveurs load-balancés afin d&#8217;assurer le traitement d&#8217;un nombre important de demandes d&#8217;affichage. L&#8217;architecture nous fait ainsi bénéficier d&#8217;une scalabilité horizontale intéressante et c&#8217;est cette performance que vise CQRS en premier lieu.</p>
<p>De par les commandes et les évènements qui enrichissent le domaine, CQRS tend agréablement à compléter l&#8217;approche <a href="http://domaindrivendesign.org/resources/what_is_ddd">Domain Driven Design</a> et nous aide à concevoir des applications dont le code colle encore plus au métier, ce qui augmente aussi la qualité de l&#8217;application.<br />
En effet, côté écriture, l&#8217;usage de commandes contextualisées facilite la maintenance pour réaliser des évolutions ou des correctifs. Lorsqu&#8217;une action change, l&#8217;impact est localisé sur son <em>command handler</em> et les contentions avec les autres actions sont faibles.</p>
<p>Mais malgré ses atouts le modèle CQRS reste très peu utilisé en entreprise. Son principe n&#8217;est pas aussi compliqué qu&#8217;il n&#8217;y paraît au premier abord mais il change tout de même radicalement la façon de travailler de beaucoup de développeurs habitués au modèle en couches traditionnel. Du coup l&#8217;utiliser sur un projet nécessite une certaine période d&#8217;adaptation et d&#8217;apprentissage du concept. Heureusement Axonframework aide beaucoup à faire le premier pas grâce au support qu&#8217;il apporte, surtout si vous utilisez déjà Spring sur votre projet.</p>
<h5>Pour aller plus loin</h5>
<ul>
<li><a href="http://cqrs.wordpress.com/documents/events-as-storage-mechanism/">Stocker le modèle d&#8217;écriture dans un event store, &#8220;l&#8217;event sourcing&#8221;</a></li>
<li><a href="http://codebetter.com/gregyoung/2010/02/20/cqrs-and-cap-theorem/">Des transactions BASE côté écriture et ACID côté lecture</a></li>
<li><a href="https://github.com/BottegaIT/ddd-cqrs-sample">Exemple d&#8217;application DDD CQRS</a></li>
<li><a href="http://fr.slideshare.net/jeremiechassaing/prsentation-cqrs-devoxxfr">La présentation de Jérémie Chassaing à devoxx france</a></li>
</ul>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2012/12/11/cqrs-lire-ou-ecrire-il-faut-choisir/" data-text="CQRS &#8211; Lire ou écrire il faut choisir !"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2012/12/11/cqrs-lire-ou-ecrire-il-faut-choisir/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2012%2F12%2F11%2Fcqrs-lire-ou-ecrire-il-faut-choisir%2F&amp;title=CQRS%20%E2%80%93%20Lire%20ou%20%C3%A9crire%20il%20faut%20choisir%20%21" id="wpa2a_16"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/690DR10fQJY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2012/12/11/cqrs-lire-ou-ecrire-il-faut-choisir/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2012/12/11/cqrs-lire-ou-ecrire-il-faut-choisir/</feedburner:origLink></item>
		<item>
		<title>Devoxx 2012 et Android</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/_3MLrfBXW2g/</link>
		<comments>http://blog.excilys.com/2012/12/10/devoxx-2012-et-android/#comments</comments>
		<pubDate>Mon, 10 Dec 2012 09:54:01 +0000</pubDate>
		<dc:creator>Alexandre THOMAS</dc:creator>
				<category><![CDATA[J'y étais !]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[devoxx]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=5334</guid>
		<description><![CDATA[Si vous n&#8217;avez eu la chance de participer au Devoxx à Anvers cette année, voici un petit retour sur ce qui s&#8217;est dit sur Android pendant l&#8217;événement. &#160; Avant de rentrer dans le vif du sujet, voyons quelques chiffres clés de l’événément. Devoxx 2012 c&#8217;est : 3400 participants, un peu plus de 190 speakers, 188 [...]]]></description>
				<content:encoded><![CDATA[<p>Si vous n&#8217;avez eu la chance de participer au Devoxx à Anvers cette année, voici un petit retour sur ce qui s&#8217;est dit sur Android pendant l&#8217;événement.</p>
<p><a href="http://blog.excilys.com/2012/11/30/devoxx-2012-et-android/devoxx-logo-2/" rel="attachment wp-att-5416"><img class="aligncenter size-full wp-image-5416" src="http://blog.excilys.com/wp-content/uploads/2012/11/devoxx-logo1.png" alt="" width="600" height="225" /></a></p>
<p>&nbsp;</p>
<p>Avant de rentrer dans le vif du sujet, voyons quelques chiffres clés de l’événément.</p>
<p>Devoxx 2012 c&#8217;est :</p>
<ul>
<li><strong>3400</strong> participants,</li>
<li>un peu plus de <strong>190</strong> speakers,</li>
<li><strong>188</strong> slots dont 16 University, 24 Tools in action, 10 Labs, 25 BOF, 24 Quickies et 89 Conferences (sans compter les keynotes, code story, hackergaarten…)</li>
</ul>
<p><span id="more-5334"></span></p>
<p>Cette année seulement <strong>6%</strong> des talks traitaient d&#8217;Android contrairement à l&#8217;an passé (2011) où l&#8217;OS représentait <strong>9%</strong> des sujets. Un chiffre en baisse mais tout de même plus important que Devoxx France 2012 où seulement <strong>3%</strong> des sujets concernaient Android. On espère voir davantage de bonhommes verts lors du Devoxx UK 2013, dont l&#8217;annonce a été faite lors de la keynote de bienvenue, et Devoxx France 2013.</p>
<h3>Conférences</h3>
<p>Au total, <strong>11</strong> slots étaient consacrés à Android : 2 BOF, 3 Tools in Action, 1 University, 1 Labs et 4 Conférences. Les talks étaient principalement animés par des googlers dont Richard Hyndman, Nick Butcher, Xavier Ducrohet ou encore Romain Guy et Chet Haase&#8230;</p>
<h4>Le duo de choc : Romain Guy et Chet Haase</h4>
<p>Vous avez probablement déjà entendu parler de Romain Guy et de Chet Haase. Ils font tous deux partie chez Google de l&#8217;équipe en charge de l&#8217;UI toolkit d&#8217;Android. Leurs talks sont toujours très agréables à suivre et très intéressants.<br />
Ils nous ont proposé 3 talks au total pour ce Devoxx, 2 techniques et un plus orienté marketing avec présentation du smartphone Nexus 4, de la tablette Nexus 10 et des nouveautés de la version 4.2 d’Android, qu&#8217;ils ont d&#8217;ailleurs officiellement sortie pendant l&#8217;événement.<br />
L&#8217;un des 2 talks techniques n&#8217;était autre que &#8220;For butter and worse&#8221; qu&#8217;ils avaient déjà présenté lors du Google I/O en avril dernier, durant laquelle ils présentent les améliorations et optimisations qu&#8217;ils ont apportées à l’UI pour qu’elle soit plus réactive et plus fluide dans la version 4.1 (Jelly Bean) de l’OS. Ils exposent également des bonnes pratiques à respecter pour garder une application aussi fluide que possible et les outils utiles au débuggage de l’UI. La présentation du Google I/O est d’ores et déjà <a href="http://www.youtube.com/watch?v=Q8m9sHdyXnE" target="_blank">disponible</a>. Pour la version Devoxx, il va falloir attendre encore un peu.</p>
<h4>Open sourcez vos projets!</h4>
<p>C’est l’un des messages que Jake Wharton, créateur d’ActionBarSherlock et développeur chez Square*, a essayé de faire passer lors de sa conférence “Bootstrapping Android Apps with open source”.</p>
<p>Il nous a fait part d’un très grand nombre de librairies open source sur lesquelles Square s’est appuyé pour développer leur(s) application(s) et qui ont, pour la majeure partie, été développées par leurs soins.</p>
<p>Voici la liste des librairies en question :</p>
<ul>
<li><a href="http://actionbarsherlock.com/" target="_blank">ActionBarSherlock</a> : rétro-compatibilité de l&#8217;action bar apparue avec la version 3.0 (Honeycomb)</li>
<li><a href="http://nineoldandroids.com/" target="_blank">NineOldAndroids</a> : rétro-compatibilité de l&#8217;API d&#8217;animation apparue avec Honeycomb,</li>
<li><a href="https://github.com/square/retrofit" target="_blank">Retrofit</a> : client REST type-safe pour Android et Java,</li>
<li><a href="http://square.github.com/tape/" target="_blank">Tape</a> :  une FIFO basée sur les fichiers pour Android et Java,</li>
<li><a href="https://github.com/square/dagger" target="_blank">Dagger</a> : système d&#8217;injection de dépendances pour Android et Java,</li>
<li><a href="http://pivotal.github.com/robolectric/" target="_blank">Roboelectric</a> : exécutait les tests unitaires sur votre JVM pour gagner du temps,</li>
<li><a href="http://code.google.com/p/robotium/" target="_blank">Robotium</a> : tests d&#8217;intégration à la Selenium,</li>
<li><a href="http://viewpagerindicator.com/" target="_blank">ViewPagerIndicator</a>, <a href="https://github.com/square/okhttp" target="_blank">OkHttp</a>, <a href="http://square.github.com/pollexor/" target="_blank">Pollexor</a>, <a href="http://code.google.com/p/mockwebserver/" target="_blank">MockWebServer</a>, PonyCrossing, Fest, Mockito, Spoon (pas encore disponible mais qui permettrait de faciliter l’exécution des tests d’intégration).</li>
</ul>
<p><em>*Square propose un système de paiement par carte à partir d’un smartphone.</em></p>
<h4>Diet Driven Development avec AndroidAnnotations</h4>
<p>Ce n’est pas en spectacteur mais en speaker que j’ai pu participer à ce “Tools in action” consacré à <a href="http://androidannotations.org/">AndroidAnnotations</a>. Avec Pierre-Yves Ricau avons construit le talk autour de 2 parties, la première était consacrée à la présentation du projet et les motivations de sa création et la deuxième à un live refactoring d’une application existante à la façon AndroidAnnotations. Vous trouverez le <a href="https://github.com/pyricau/DevoxxBeerCounter">code source</a> de l’application refactorée sur github.</p>
<h3>Rendez-vous sur Parleys</h3>
<p>Toutes les vidéos des talks seront disponibles sur <a href="http://www.parleys.com/">Parleys</a>, vous pourrez ainsi les (re)voir. Il va tout de même falloir être patient car les vidéos sont rarement disponibles avant 1,5 ou 2 mois après l&#8217;événement. En attendant, vous pouvez toujours aller jeter un oeil aux interviews des &#8220;googlers&#8221; dont celle de <a href="http://www.youtube.com/watch?v=w_WX5lZqpRM">Chet Haase et Romain Guy</a> enregistrée lors de l&#8217;évenement.</p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2012/12/10/devoxx-2012-et-android/" data-text="Devoxx 2012 et Android"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2012/12/10/devoxx-2012-et-android/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2012%2F12%2F10%2Fdevoxx-2012-et-android%2F&amp;title=Devoxx%202012%20et%20Android" id="wpa2a_18"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/_3MLrfBXW2g" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2012/12/10/devoxx-2012-et-android/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2012/12/10/devoxx-2012-et-android/</feedburner:origLink></item>
		<item>
		<title>Retour sur la conférence DotJs 2012</title>
		<link>http://feedproxy.google.com/~r/blogexcilyscom/~3/DJWYBZ4Et0M/</link>
		<comments>http://blog.excilys.com/2012/12/07/retour-sur-la-conference-dotjs-2012/#comments</comments>
		<pubDate>Fri, 07 Dec 2012 09:46:37 +0000</pubDate>
		<dc:creator>Arnaud GOURLAY</dc:creator>
				<category><![CDATA[J'y étais !]]></category>
		<category><![CDATA[conférence]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://blog.excilys.com/?p=5528</guid>
		<description><![CDATA[Le 30 novembre 2012, j’ai eu la chance d’assister à la première du dotJs, la plus grande conférence Javascript de France d’après ses organisateurs. Se déroulant sur une unique journée dans un cadre superbe et original, le théatre des variétés en plein Paris, le programme de la journée s’annonçait prometteur avec onze speakers de qualité. [...]]]></description>
				<content:encoded><![CDATA[<p style="text-align: justify">Le 30 novembre 2012, j’ai eu la chance d’assister à la première du <a title="dotJs" href="http://www.dotjs.eu/">dotJs</a>, la plus grande conférence Javascript de France d’après ses organisateurs. Se déroulant sur une unique journée dans un cadre superbe et original, le théatre des variétés en plein Paris, le programme de la journée s’annonçait prometteur avec onze speakers de qualité.</p>
<div class="wp-caption aligncenter" style="width: 550px"><a href="http://svay.com/photos/2012-11-30_dotjs/"><img class=" " src="http://svay.com/photos/2012-11-30_dotjs/conference/2012-11-30_10-17-17.jpg" alt="" width="540" height="360" /></a><p class="wp-caption-text">Credit photo : dotJs</p></div>
<p style="text-align: justify"><span id="more-5528"></span></p>
<p style="text-align: justify">Dès l’introduction, nous avons été prévenus que le dotJs n’était pas une simple conférence technique mais que celle-ci s’apparentait plus au style des conférences <a href="http://www.ted.com/">TED</a> où la transmission d’idée et de passion prime sur la technique. Cela s’est vérifié dans l’après midi car la matinée fut assez technique pour le plus grand bonheur des développeurs présents.</p>
<p style="text-align: justify">Je vais à présent revenir sur les présentations qui m’ont le plus marqué.</p>
<h3 style="text-align: justify">Addy Osmani &#8211;  Chrome développeur relation pour Google.</h3>
<p style="text-align: justify">La présentation à débuté par une démonstration des outils disponibles  dans Google Chrome pour faciliter le développement d&#8217;application web à travers une étude de cas d’optimisation d’un site web. Malgré les bonnes idées et techniques pour trouver de potentielles fuites mémoires et fluidifier le rendu des pages, j’ai été un peu déçu car cela correspondait exactement à une excellente présentation de Patrick Dubroy déjà disponible <a href="http://vimeo.com/53073654">ici</a>.</p>
<p style="text-align: justify">Le temps passé sur cette première partie, ainsi que les problèmes de wifi ralentissant la démonstration, n’ont laissé que très peu de temps pour présenter l’outil Yeoman dont il est le créateur.</p>
<p style="text-align: justify">Yeoman est un outil qui peut notamment:</p>
<ul style="text-align: justify">
<li>‘scaffolder’ rapidement une application moderne avec <a title="HTML5BP" href="http://html5boilerplate.com/">HTML5BP</a>, TwitterBootstrap&#8230;</li>
<li>gérer les dépendances externes pour en finir avec le copier coller manuel.</li>
<li>compiler automatiquement Coffeescript et <a title="Compass" href="http://compass-style.org/">Compass</a></li>
</ul>
<p style="text-align: justify">Pour plus d’information, voir le site officiel de <a href="http://yeoman.io/">Yeoman</a> ainsi que l’excellent blog de <a href="http://addyosmani.com/blog/yeoman-at-your-service/">Addy Osmani</a>.</p>
<h3 style="text-align: justify"><strong>Jeremy Ashkenas - Créateur de <a href="http://documentcloud.github.com/backbone/">Backbone.js</a>,<a href="http://documentcloud.github.com/underscore/">Underscore.js</a> &amp; <a href="http://coffeescript.org/">CoffeeScript</a></strong></h3>
<p style="text-align: justify">Jeremy Ashkenas a parlé des langages symbiotiques, sujet qu’il connait parfaitement en tant que créateur de CoffeeScript. Un langage symbiotique peut être défini comme un langage étant &#8220;compilé&#8221; sous forme de code source d’un autre langage, donc dans le cas du CoffeeScript vers le javascript.</p>
<p style="text-align: justify">Cependant, dans le cas du CoffeeScript on parle plus de “transpiling” que de compilation car le CoffeeScript reste très proche du javascript et en conserve la sémantique. Une liste plutôt exhaustive des langages compilés/transpilés vers javascript est disponible sur le wiki de Coffeescript <a href="https://github.com/jashkenas/coffee-script/wiki/List-of-languages-that-compile-to-JS">ici</a>. De nombreux projets récents comme <a title="Dart" href="http://www.dartlang.org/">Dart</a> de Google ou encore <a title="Typescript" href="http://www.typescriptlang.org/">TypeScript</a> de Microsoft tentent de construire des langages de plus haut niveau dont la syntaxe et la sémantique s’éloignent du javascript et sont plutôt prometteurs.</p>
<p style="text-align: justify">Jeremy termine sa présentation par une remarque intéressante à garder en tête lors de l’utilisation de langages symbiotiques, ceux-ci ont deux audiences. Le code généré pour la plateforme cible, ici le javascript, n’est pas seulement destiné à une machine. En effet il doit aussi pouvoir être compris par un développeur javascript.</p>
<h3 style="text-align: justify"></h3>
<h3 style="text-align: justify">Brian Leroux &#8211; Membre de l&#8217;équipe <a href="http://www.phonegap.com/">PhoneGap</a></h3>
<p style="text-align: justify" dir="ltr">Une fois de plus, Brian Leroux à mis en lumière les bizarries du langage Javascript dans une présentation pleine d’humour. Retrouvez <a href="http://brian.io/slides/dotjs-2012/">ici</a> les assertions que Brian Leroux à présentées et testez les vous même dans votre console javascript.</p>
<p style="text-align: justify">Pour les faineants, voici quelques une de mes bizarreries préférés:</p>
<h4 style="text-align: justify">A propos des opérations sur les nombres décimaux</h4>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">0.1 + 0.2 === 0.3<br />
resultat : false</div></td></tr></tbody></table></div>
<h4 style="text-align: justify">Les associations String &#8211; Number</h4>
<p style="text-align: justify">
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&quot;2&quot; +1<br />
resultat : &quot;21&quot;<br />
&quot;2&quot; -1<br />
resultat :1</div></td></tr></tbody></table></div>
</p>
<h4><span style="text-align: justify">Sans oublier les tableaux</span></h4>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[] == []<br />
resultat : false<br />
[] == ![]<br />
resultat : true<br />
[] == 0<br />
resultat : true</div></td></tr></tbody></table></div>
<h3 style="text-align: justify"></h3>
<h3 style="text-align: justify">Vojta Jína &#8211; Membre de l’équipe <a href="http://angularjs.org/">AngularJS</a> et créateur de <a href="http://vojtajina.github.com/testacular/">Testacular</a></h3>
<p style="text-align: justify">La plupart des spectateurs attendait une présentation technique sur Angular.js ou encore sur Testacular. Mais Vojta Jina à décidé de présenter trois <em>grands développeurs</em> pour nous montrer comment nous pouvons nous en inspirer pour réaliser nos projets.</p>
<p style="text-align: justify">A travers l’histoire de Ryan Dahl (créateur de NodeJs), John Resig (créateur de Jquery) et Dennis Ritchie (créateur de Unix et du C), Vojta Jina à dégagé trois idées importantes:</p>
<ul style="text-align: justify">
<li>réutiliser l’existant en assemblant et en s’inspirant du travail des autres.</li>
<li>résoudre ses propres problèmes d’abord car il y a de grandes chances qu’ils soient partagés par d’autres personnes.</li>
<li>et bien sur le classique: être passionné.</li>
</ul>
<h3 style="text-align: justify"></h3>
<h3 style="text-align: justify">Jacob Thornton (fat) &#8211; Co-createur de <a href="http://twitter.github.com/bootstrap/">Twitter Bootstrap</a></h3>
<p style="text-align: justify">Jacob Thornton à lui aussi décidé de ne pas faire de présentation technique mais à décidé de retracer avec beaucoup d’humour l’histoire de l’Open Source. De Richard Stallman à Linus Torvald, Jacob finit par introduire un nouveau concept issue de son expérience personnelle avec les projets Open Source.</p>
<blockquote>
<p style="text-align: center"><strong>“The cute puppy syndrome”</strong></p>
</blockquote>
<p>&nbsp;</p>
<p style="text-align: justify">Le “syndrome du chiot mignon” pour les francophones, correspond à la sensation que l’on a au début de chaque projet: on aime travailler sur son projet, d’autres personnes s’y investissent également, tout se passe bien. Mais au bout d’un certain temps, ce petit projet mignon se transforme en énorme <del>Saint-Bernard</del> projet dont il faut prendre soin constamment. Il devient envahissant en terme de temps passé à faire du support, correction de bugs …</p>
<p style="text-align: justify">On a soudain l’envie d’abandonner son gros projet à quelqu’un d’autre et d’adopter un nouveau petit chiot. Je vous laisse réfléchir sur cette métaphore canine et je précise que je ne cautionne pas les abandons d’animaux <img src='http://blog.excilys.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
<p style="text-align: justify">Cette présentation au format One-man show est arrivée à point nommé en cloture d’une longue journée riche en apprentissage.</p>
<p style="text-align: justify">Pour conclure cette article, bien que j’aurais aimé davantage de présentations techniques, le DotJs a tenu toutes ses promesses et je remercie toute l’équipe pour son organisation. Pour info, suite au succès de cette année une nouvelle édition sera organisée en 2013.</p>
<p style="text-align: justify">Toutes les photos de la journée sont disponibles. <a href="http://svay.com/photos/2012-11-30_dotjs/">http://svay.com/photos/2012-11-30_dotjs/</a></p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://blog.excilys.com/2012/12/07/retour-sur-la-conference-dotjs-2012/" data-text="Retour sur la conférence DotJs 2012"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://blog.excilys.com/2012/12/07/retour-sur-la-conference-dotjs-2012/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.excilys.com%2F2012%2F12%2F07%2Fretour-sur-la-conference-dotjs-2012%2F&amp;title=Retour%20sur%20la%20conf%C3%A9rence%20DotJs%202012" id="wpa2a_20"><img src="http://blog.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/blogexcilyscom/~4/DJWYBZ4Et0M" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.excilys.com/2012/12/07/retour-sur-la-conference-dotjs-2012/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.excilys.com/2012/12/07/retour-sur-la-conference-dotjs-2012/</feedburner:origLink></item>
	</channel>
</rss>
