<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>Le Touilleur Express &#187; Web 2.0</title>
	<atom:link href="http://www.touilleur-express.fr/category/web20/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.touilleur-express.fr</link>
	<description>Blog sur Java, le métier de développeur et la vie de freelance par Nicolas Martignole</description>
	<lastBuildDate>Wed, 08 Feb 2012 11:54:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Play 2.0 announced at Devoxx 2011</title>
		<link>http://www.touilleur-express.fr/2011/11/26/play-2-0-announced-at-devoxx-2011/</link>
		<comments>http://www.touilleur-express.fr/2011/11/26/play-2-0-announced-at-devoxx-2011/#comments</comments>
		<pubDate>Sat, 26 Nov 2011 13:29:07 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Perso]]></category>
		<category><![CDATA[Web 2.0]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=5939</guid>
		<description><![CDATA[ 
Guillaume Bort and Sadek Drobi made two big announcements last Wednesday, 16th November at the Devoxx 2011 conference. First off, Play! 2.0 is available in beta form for download  on the Play! Framework web site. Within hours, there were over 4,000 downloads (see Twitter here).
Secondly is that Play Framework is now the official web stack of the Typesafe products, Typesafe being the company created by Martin Odersky, creator of Scala. And last but not least, Guillaume Bort is joining the company’s Advisory Board, alongside James Gosling and Doug ...]]></description>
			<content:encoded><![CDATA[<p><img class="alignright size-medium wp-image-5929" title="Logo_play2" src="http://www.touilleur-express.fr/wp-content/uploads/2011/11/Logo_play2-300x154.png" alt="" width="300" height="154" /> </p>
<p>Guillaume Bort and Sadek Drobi made two big announcements last Wednesday, 16th November at the <a href="http://www.devoxx.com/">Devoxx</a> 2011 conference. First off, Play! 2.0 is available in beta form for download <a href="http://download.playframework.org/releases/play-2.0-beta.zip"> on the Play! Framework web site</a>. Within hours, there were over 4,000 downloads (<a href="https://twitter.com/#!/search/sebcreme">see Twitter here</a>).<br />
Secondly is that Play Framework is now the official web stack of the <a href="http://www.typesafe.com/">Typesafe</a> products, Typesafe being the company created by Martin Odersky, creator of Scala. And last but not least, Guillaume Bort is joining the company’s Advisory Board, alongside James Gosling and Doug Lea.</p>
<p>Nice, hey?</p>
<p>But let&#8217;s back to the Devoxx presentation and what I thought of it. I&#8217;ll try to not be too enthusiastic, but this will be hard (as I am a Play Framework evangelist as part of my daily work).</p>
<p>Sadek Drobi is Zenexity&#8217;s CTO. Guillaume Bort is the co-founder of the company, and the original author of Play! Framework. As Sadek explained, Play! was born because of internals needs for their company, as a secret weapon to rapidly develop Java applications. The framework has been open-source as of the end of 2008. But right from the beginning, it&#8217;s was a framework designed for &laquo;&nbsp;real-life projects&nbsp;&raquo; for &laquo;&nbsp;real-life customers&nbsp;&raquo;. Not as a project you use to solve non-existant problems.</p>
<p><a href="http://www.touilleur-express.fr/wp-content/uploads/2011/11/DSC_3233.jpg"><img class="alignnone size-medium wp-image-5934" title="DSC_3233" src="http://www.touilleur-express.fr/wp-content/uploads/2011/11/DSC_3233-300x199.jpg" alt="" width="300" height="199" /></a><br />
(Sadek with its hat and Guillaume)</p>
<p>I remember when Play! Framework was presented at a BOF at Devoxx 2009. 2 years later, there&#8217;s 3 conferences at Devoxx and one BOF. I&#8217;m looking at Devoxx 2011&#8242;s schedule, I don&#8217;t see any other web framework that had such exposure this week. This makes some of my friends cringe, but it definitely shows the interest the community has for this framework.</p>
<h2>Why Play 2.0 ?</h2>
<p>Since the beginning, <a href="http://www.zenexity.com/">Zenexity</a> worked closely with the community. Play 1.x is now a very popular web framework, being simple and productive. However, adding functionalities is like creating a mega sandwich (or burger&#8230; or whatever) : that is, it becomes less maintainable and harder to patch. Plus, there&#8217;s a lot of “magic” going on in the core of Play 1.x (Maybe a little too much).</p>
<p>Second point is that meanwhile, the Web is evolving. People, especially in the Java community, are discovering a new world where a stateful approach might not be the only way to build web applications. Sadek and Guillaume say that we can really feel that architectures are changing, and this will have an impact on web frameworks. The very first Web frameworks were based on a static page approach. A single page generated by Perl, PHP or simple Java techniques. And this is still OK&#8230; if your content is static.</p>
<p>Web architectures on the server side needed to adapt to be able to work asynchronously with web clients. For instance, Ajax and related techniques like partial page refreshes gave birth to great web applications such as GMail. From a static page view, we moved to a dynamic approach on the server. Web frameworks were proud of this&#8230; especially JSF folks. One needs to understand that when usages changes, all web frameworks are impacted. Some are good for AJAX, some suck&#8230; I won&#8217;t give any names, but you see what I mean.</p>
<p>Today in 2011, the new challenge is easy web service integration and asynchronous processing. On a lot of web applications, you have to be able to link and reuse APIs such as Facebook API, Twitter OAuth, Google OpenID, or maybe your company SSO system that cost a tank&#8230; We know that to build a modern web application, the web framework has to be able to glue all these services together. And it has to be able to handle huge amounts of data, and the huge amount of things that can go wrong. Maybe tomorrow, a really good Web Developer will be the person that has a strong knowledge on Web APIs (OAuth, Github API, OpenID), and that is able to create value-added services from these external APIs.</p>
<p>From a technical point of view, Sadek explains that the Java Servlet API is very limited. We hope that Java EE 7 will propose a more modern approach. Java Servlet is just too&#8230; low-level. One important issue is that it&#8217;s impossible to split a task and configure threads, memory and CPU in a Web application. If you visit a page, you go through a Servlet. If you upload a file, another Servlet. Wouldn&#8217;t it be interesting to route the content in your application (and not in a crappy “web.xml”) and really focus on the business part ? It&#8217;s not normal that your application’s home page can blow up if someone else starts uploading a large file in another part of your application.</p>
<p>Web frameworks have been created to be the central point of the architecture, whereas Sadek and Guillaume think that being Client-focused (as in, clients of your application) and having an adaptive server are more important.</p>
<p>Basically, “server” should be written with a lower-case “s” and “Client” with an upper-case “C”.</p>
<p>What is also important to keep in mind is that the amount of data and the flow of data between Clients (browsers, other web apps, your iPhone&#8230;) and the server are evolving at a very fast pace. It&#8217;s not at all easy to write concurrent code, and this was something that Play 1.x was not designed for. Play 2.0 is not here as an evolution or replacement of Play 1.x. It&#8217;s here because we&#8217;re moving forward, baby. We have to adapt and be ready for these new kinds of applications we haven&#8217;t thought of before. You see what I mean ?</p>
<p>Sadek Drobi goes on to say that when a Java developer creates an application for the Web, he tends to have a sort of “automatic behaviours”. The first one, regarding the datastore, is to pick an ORM. And this is stupid. What for ? Because you can forget SQL ? ORM are a perfect tool&#8230; when you need it. And no, you don&#8217;t need an ORM every time, every where.</p>
<p>Take a look at your application. First, do you use honestly use advanced relational mapping in the database ? Yes ? And is it really useful ? How much time did you lose on Play 1.x with @OneToMany, @ManyToOne, @MaybeOneOrTwoToTooMany  annotations ?</p>
<p>On the client side, especially in the browser, we have also new web technologies that ease the web app development. Personally (and I usually blog about this in French), I think that if you (yes, you there in the back) don&#8217;t want to learn HTML/CSS/HTTP, then you are in serious trouble. OK, let me be honest : you suck. Either learn basic web stuff or go back to your Cobol stuff.</p>
<p>Now, back to the presentation. So we have these new tools such as <a href="http://www.sass-lang.com">Sass</a>, <a href="http://jashkenas.github.com/coffee-script/">CoffeeScript</a>, <a href="http://lesscss.org/">Less</a> or <a href="http://www.jquery.com/">jQuery</a>. Guillaume explains that it would be also interesting to integrate these tools in Play 2.0 so that you get compilation and validation for CoffeeScript or Sass, for instance.</p>
<p>In a sense, <a href="http://www.playframework.org/2.0">Play 2.0</a> was born because they (Sadek and Guillaume) wanted to create not a v2 of Play, but a v2011 version of it. Something that is more mature, less magic, more type-safe, more solid. Something that is able to handle all the new use cases they talked about.</p>
<p>This is important point : <a href="http://www.playframework.org/2.0">Play 2.0</a> is more of a whole new framework that just an evolution of Play 1.x.</p>
<p>When we explain what is Play! Framework to someone for the very first time, especially to Java developers, we present it as a full-stack framework. You write java code in your editor, you switch to your web browser, hit refresh, and it works.</p>
<p>Java developer : <strong>No more console and maven hell.</strong> Play 1.x relies on Eclipse’s IDT compiler, the one we&#8217;ve been using for years : it just works.</p>
<p>Java developer : <strong>No more crappy logs in a console</strong> Play 1.x reports errors in the web browser. If there&#8217;s a compilation error, well, you see it in the browser. Forget about looking at logs files like a teenager with an adult magazine : no more logs.</p>
<p>What is also important is that Play 1.x is not a stateless web framework, but a &laquo;&nbsp;<em>framework sans état conversationnel du côté serveur</em>&nbsp;&raquo; wich translate into English as &laquo;&nbsp;<em>a framework that respects web standard, has no conversation, thus no state on the server, as HTTP is from the beginning&#8230;</em>&nbsp;&raquo;</p>
<p>The code editor and a web browser. That is all you need to work. Even better, you can use VI/Emacs/TextMate or any basic text editor, since all the work is done by Play! Framework. However I recommend IntelliJ for its good web support (JS/HTML/CSS/Java/bla bla bla).</p>
<p>Guillaume worked really hard to keep exactly the same spirit in Play 2.0 : productive, simple, no logs for dummies, no maven/gradle stuff, no xml&#8230; just being productive (and having fun, but that is another story).</p>
<p><a href="http://www.playframework.org/2.0">Play 2.0</a> is a web framework that is able to compile, deploy, download dependencies, launch asynchronous jobs, runs tests, send emails and even (but we&#8217;re not 100% sure about this feature) prepare a good coffee if you need it.</p>
<h2>Démonstration</h2>
<p>Guillaume did a good demonstration of Play 2.0. Firstly : no more Python for the tooling and no more Groovy for the templating (compared to Play 1.x). Play 2.0’s engine is Scala. But hold on&#8230; don’t run away&#8230; no, you <i>won&#8217;t</i> have to learn Scala. You can write your code in Java or Scala, Play doesn&#8217;t care. The template language is Scala. But it&#8217;s very simple and there&#8217;s only a few tags to learn. It took me 1 day to understand how it works. And I&#8217;m happy to be able to do advanced stuff with Scala, when needed, in the view.</p>
<p>For Java, Guillaume removed Hibernate and took <a href="http://www.avaje.org/">Avajé EBean</a>. It&#8217;s a lightweight stateless ORM, that is less powerful than Hibernate. But it&#8217;s perfect for Play, it&#8217;s JPA compatible if I&#8217;m remember correctly. For the Scala part, Sadek implemented a simple SQL engine called Anorm. I started to use Anorm in May, spent some time with Sadek in August to fix some bugs, and I like it. Simple, easy to learn, not too much &laquo;&nbsp;<i>scaletist</i>&nbsp;&raquo; and just fine for a Scala beginner like me. You can also use some other Scala/SQL libraries such as <a href="http://scalaquery.org/">ScalaQuery</a> : it&#8217;s very easy to flip from one lib to another.</p>
<p>The very first characteristic of Play 2.0 is that it&#8217;s based on SBT, Simple Build Tool. As you can imagine, SBT is now the one in charge of dependencies, incremental compilation, tests, packaging, documentation and maybe (but not sure about this one) preparing a good coffee. It&#8217;s even easy to launch SBT and import Play commands into SBT. You also get tooling support for Eclipse and IntelliJ with SBT.</p>
<p>The template engine is new in Play 2.0. This is maybe the biggest difference between the 2 versions of the framework. I know that one of the Play core developer is working on porting the Groovy engine to Play 2.0. It will work, but does it makes sense ? Not sure about that.</p>
<p>Templates are HTML files with very little Scala code in them. Everything is strongly typed. Less errors, more validation at compile time, and maybe fewer tests since there&#8217;s less dynamic in the code. Each page declares the list of parameters, everything is transformed to Scala then compiled to byte-code. In terms of performance, the new template engine is 15 times faster than the previous one.</p>
<p>One important point : in order to write a view page with <a href="http://www.playframework.org/2.0">Play 2.0</a>’s new template language, you don&#8217;t have to be a Scala Jedi Master. The syntax is easy to learn, and you have access to the power of Scala when needed. I also think that this is a good way to discover and learn Scala, if you only use Java for the controller side.</p>
<p>I had a really nice use-case recently, where Scala was really powerful. I wrote a Contact application. There&#8217;s a web page with a list of people ordered by last name. Here is an extract of one of my templates for Play 2.0 :</p>
<p><script src="https://gist.github.com/1376491.js?file=gistfile1.html"></script></p>
<h2>Play 2.0 in details</h2>
<p>Sadek and Guillaume then go on and explain what is really new in Play 2.0. First, existing solutions in the Java world suffers from a very low-level approach. Let&#8217;s say you want to implement a file upload functionality. With the traditional approach, based on java.io.InputStream, it&#8217;s very complex to correctly write an application that does not block on I/O. The read() method in InputStream reads one byte after another. This is OK if the server is able to handle the reception of the byte stream. However, it tends to create resource deadlocks, where the server hangs and does not respond to incoming requests anymore. Sadek, who is also a very good Haskell developer, explains that they decided to adapt the <a href="http://www.haskell.org/haskellwiki/Enumerator_and_iteratee">Enumerator and Iteratee</a> pattern in Play 2.0</p>
<p>Play 2.0 has a completely new approach, it applies inversion of control on the way the controller and the client exchange information. What is really awesome is the way it handles an incoming request. First, in the connection layer, Play 2.0 is like Play 1.x : a single thread listens for and dispatches any incoming requests (just like Node.JS, for instance). The request is then dispatched to either a limited pool of workers. If you annotate a part of your code in the controller with the @Asynchronous annotation, Play is then able to suspend your request, thus freeing one thread, while an asynchronous activity does something. Enumerator and Iteratee is a nice pattern where the asynchronous task tells the Enumerator when it completes its work, and that it can resume. It is much more efficient than the classical Future + Pooling approach we had in Play 1.x. And this is also why you now understand that Play 2.0 doesn&#8217;t care about the Servlet API, it just wouldn&#8217;t be able to work like this.</p>
<p>An Enumerator sends data to an Iteratee. The Iteratee executes a function, does something, and sends a signal back to the Enumerator with a status. See also the <a href="http://en.wikibooks.org/wiki/Haskell/Continuation_passing_style">Continuation passing style</a>&nbsp;&raquo; that is explained in Haskell documentation.</p>
<p>Well, you may say : why is this interesting ? Why should I care ? Well my friend, now you can start to write asynchronous web servers &laquo;&nbsp;à la Node.JS&nbsp;&raquo; (this is French) but with Scala or Java. Play 2.0 is much more functional. The code is easier to read, it&#8217;s also easy to split up long processing operations to speed-up your server.</p>
<p>To orchestrate everything, Guillaume and Sadek decided to use Akka. It&#8217;s a very powerful and simple Actor-based framework. I wrote an application for a startup, where we had to post-process uploaded images and generate PDFs : Akka is great. The code is easy to test, there&#8217;s no middleware stuff, ant it’s simple and very easy to develop in. </p>
<p><a href="http://akka.io/">Akka</a> is a powerful tool that lets you write asynchronous code very easily in your application. What I particularly like about it is that the thread pool and all the general fine-tuning is managed outside of your code. If there’s a new framework you need to look at, definitely check out Akka. Great tool.</p>
<p>Here is a sample from Play 2.0 that Guillaume showed at Devoxx. The search function receive a keyword. The AsyncResult is interesting because it tells Play to handle the code as an asynchronous function. While the search operation is done, the server frees up the resource and let the system handles another request. The Iteratee pattern is then used : when the search result is available, the html.results template is executed to generate the HTML response. The double-bang (!!) means : send the &laquo;&nbsp;keywords&nbsp;&raquo; to the searchInternet&#8217;s Actor. As you can see, the searchInternet could be anything, there&#8217;s no functional code here.</p>
<pre class="brush:scala">def search(keywords: String) = Action {
	 AsyncResult {
		(searchInternet !! keywords).map{
			found =&gt; Ok(html.results(found))
		}
	}
}</pre>
<p>Sadek talked also a little about the data layer in Play 2.0 : either a relational database or NoSQL databases like MongoDB, etc. Play 2.0 does not try to hide the fact that there&#8217;s a data layer in your application. If you want to use Neo4J or PostgresSQL, well you can. </p>
<p>In Play 2.0, Guillaume decided to drop Hibernate 3 in favour of EBean. Why ? Well, Hibernate is a great tool and all, but it&#8217;s just too powerful for what we usually need for Play. It was really hard to set up the asynchronous part with Hibernate sessions. On the other hand, the java community really wants an ORM in Play for the Java part. EBean is simple to use, and offers good compatibility. There&#8217;s also some Spring 3 in Play 2.0 for the Bean binding and JSR-303. So EBean is simpler and better-adapted to Play 2.0. </p>
<p>For Scala, Sadek wrote Anorm (An Not ORM). Based on Scala’s complex parser, it&#8217;s a very simple parser technology that removes the JDBC boilerplate and offers a nice match/case engine. But if you don&#8217;t like Anorm, you can use ScalaQuery or whatever your favorite Scala technology is. </p>
<p>Guillaume also demonstrated that Play 2.0 is bundled with support for client-side tools such as CoffeeScript, Less or SASS. Play is a full stack framework. So it will compiles your CoffeeScript to Javascript. And last but not least, it is also capable of reporting errors in a CoffeeScript page for instance. This is just great. </p>
<p>With regards to “Cloud computing” support, Play 2.0 works on <a href="http://www.heroku.com/">Heroku</a> and soon, will also work on CloudBees. </p>
<p>Thanks to Akka, it will also be easy to distribute a work on multiple Play Servers. For instance, it will be possible to have a Play server as some sort of Front controller, that dispatches to another Play Server the &laquo;&nbsp;Image resize&nbsp;&raquo; operation (for example), and to another server the &laquo;&nbsp;Generate a PDF&nbsp;&raquo;&#8230; I don&#8217;t know if you realize how powerful this is. It will be easy to split up and create a flexible architecture. Something Java J2EE tried to sell 8 years ago. </p>
<p>The actual Play 2.0 core is not very complex. I spent a day with Guillaume and was able to understand how it works. In fact, there&#8217;s x4 less code. It&#8217;s thus easier to find your way around in your code. </p>
<p>On the performance side, Play 2.0 is 2 to 3 times faster than Play 1.x. On a MacBook Pro with an i7 processor with 8 cores, a simple Apache bench command shows that Play 2.0 manages just fine 40 000 requests per seconds. The network stack is JBoss Netty, one of the most powerful stacks for Asynchronous NIO in Java.</p>
<p>On the memory footprint side, their tests show that after a full GC, the heap is back to 2 MB&#8230; </p>
<p>Personally, if I have a negative feedback to give, it’s that the compilation is much slower with Scala. You lose the very quick turn-around/compilation experience we had with Play 1.x. Yes, it&#8217;s slower. Scala being more complex, it takes more time to generate the code and compile it. </p>
<h2>Type safe</h2>
<p>Play 2.0 also brings strong typing to the HTML template part. The first reflex is : damn, I prefer the groovy way. But bit by bit, I moved to Scala and started to really appreciate the type safe template engine. </p>
<p>The &laquo;&nbsp;routes&nbsp;&raquo; file is also compiled. Everything is compiled in Play 2.0. This is less error-prone. If you update a function in your controller and you forget to rename it in the “routes” file, Play will properly report the error. No more &laquo;&nbsp;404 What the hell&nbsp;&raquo; exception. </p>
<p>To wrap things up, Guillaume did a nice presentation of the 3 applications they wrote in September. You have a complete CRUD application, an advanced HelloWorld and a complete application with authentication. I was happy to see that Guillaume took into consideration a suggestion I had back in September : don&#8217;t bother to give a stupid helloworld example. Give me a real application so that I can start to learn the framework, see how to set-up authentication, start to read some nice code&#8230; And please : no pet-store and no stupid hotel booking application. </p>
<p>By the way, dear reader, did you know that the Pet-store was actually a joke ? The idea to create a Petstore was suggested by a guy at 4:00 am after too many beers at SUN. Stop and think about this for a minute : would you <i>really</i> buy a bird on the Internet and have it shipped in a little box ? Come on&#8230; You can&#8217;t be serious&#8230; But apparently nobody was really surprised and we still believe that we can buy birds with a J2EE application&#8230; Ah ah ah.</p>
<h2>What you won&#8217;t like in Play 2.0</h2>
<p>Now my friends, let&#8217;s me be honest : Play 2.0 is not for dummies. A lot of people want to port their code and every day, they post a bunch of questions about migration patches and yada yada yada on the mailing-list. The question is : do you really need to migrate ? Do you really need to use Play 2.0 ? Maybe <i>ya do</i>, maybe <i>ya don’t</i>.</p>
<p>The templates are different, but it&#8217;s not too hard to port your code. The controller side is completely new. You can write it in Java or Scala. So don&#8217;t be scared : you don&#8217;t have to be a Scala developer (yet). </p>
<p>Play 2.0 was created to solve new issues and new architecture patterns. It&#8217;s more like a cousin than the little brother of Play 1.x. </p>
<p>However, and please read this carefully : Play 1.x is a community project and will be maintained by its core contributors (Guillaume Bort, Nicolas Leroux from Lunatech, Peter Hausel, Morten Kjetland, Erwan Loisant, Peter Hilton&#8230;). Zenexity has a lot of customers with live apps in production using Play 1.x. So Play 1.x is here to stay and is still a very interesting solution to look at. </p>
<p>If you want to learn Scala, Play 2.0 is really a great tool. I started to learn Scala in May with Play 1.x and play-scala, and it took me one month to start, maybe 2 months to really understand Scala and appreciate the language. As a Java developer with no functional background, I really like this language. Less verbose and not that hard to learn. Don&#8217;t be afraid of the fact that there’s are some hippie-guru-functional-heretics that talk about Scala like it’s their girlfriend. If you&#8217;re just a java developer, trust me : it&#8217;s easy to learn and to work with. Don&#8217;t give a shit about people that try to look smart with &laquo;&nbsp;Monad or higher-order function&nbsp;&raquo;. Soon you will be writing more Scala code than those pseudo-evangelists ever have&#8230; </p>
<p>One important point is that Play 2.0 is more like a new framework. If you want to write rock-solid applications and you need asynchronous/flexibility, then Play 2.0 is your guy.</p>
<p>I talked with Guillaume and at one point, I suggested that they change its name of the Framework to Play<sup>2</sup> ([plèy square]).</p>
<p>But we will keep Play 2.0, that&#8217;s already a big evolution.</p>
<p><strong>Other interesting and great resources:</strong><br />
- <a href="http://www.playframework.org/2.0">Play 2.0 official web site</a><br />
- Another <a href="http://raibledesigns.com/rd/entry/play_2_0_a_web">great article by Matt Raible</a>. I hope to see him in Paris for Devoxx France 2012<br />
- James Ward also wrote <a href="http://www.jamesward.com/">great articles</a> about Scala, Play and Heroku<br />
- Play! Framework also has a Twitter account : <a href="http://www.twitter.com/playframework">@PlayFramework</a></p>
<blockquote><p>This article was reviewed by Roland MALAPRADE (<a href="http://www.twitter.com/rmalaprade" target="_blank">@rmalaprade</a>). </p></blockquote>
<blockquote><p>Nicolas Martignole is a freelance developer. He lives in France, in Paris. He is a Play evangelist. He created <a href="http://www.express-board.fr/" target="_blank">the eXpress-Board</a>, a job board for enthusiastic developers, with Play 1.2. He is also a member of <a href="http://www.parisjug.org">the Paris JUG</a> team and he&#8217;s one of the 4 crazy geeks that decided to organize <a href="http://www.devoxx.fr">Devoxx France</a>, a conference for developers created by freelance developers, from the 18th to the 20th of April 2012. You can follow him on Twitter <a href="http://www.twitter.com/nmartignole" target="_blank">@nmartignole</a>.</p></blockquote>
<p>Original article in French <a href="http://www.touilleur-express.fr/2011/11/18/play-2-0-annonce-a-devoxx-2011/">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2011/11/26/play-2-0-announced-at-devoxx-2011/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Comment tester son site sur les différentes versions d&#8217;Internet Explorer</title>
		<link>http://www.touilleur-express.fr/2011/11/03/comment-tester-son-site-sur-les-differentes-versions-dinternet-explorer/</link>
		<comments>http://www.touilleur-express.fr/2011/11/03/comment-tester-son-site-sur-les-differentes-versions-dinternet-explorer/#comments</comments>
		<pubDate>Thu, 03 Nov 2011 08:06:08 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=5793</guid>
		<description><![CDATA[Je ne sais pas pour vous, mais moi Windows j&#8217;ai dit bye-bye en 2008 pour prendre un Mac. Alors on peut dire ce que l&#8217;on veut d&#8217;Apple, râler très fort en disant que c&#8217;est cher, mais il y a un truc qui échappe encore au monde de Windows : ça marche. 
En même temps on a un vrai métier &#171;&#160;dans la vraie vie&#160;&#187; comme disent les gens. Or dans ma vraie vie de développeur indépendant, je code des sites Internet avec Play! Framework pour des clients. Et les sites de ...]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.touilleur-express.fr/wp-content/uploads/2011/11/tag_web.jpg" alt="" title="tag_web" width="150" height="150" class="alignleft size-full wp-image-5795" />Je ne sais pas pour vous, mais moi Windows j&#8217;ai dit bye-bye en 2008 pour prendre un Mac. Alors on peut dire ce que l&#8217;on veut d&#8217;Apple, râler très fort en disant que c&#8217;est cher, mais il y a un truc qui échappe encore au monde de Windows : ça marche. </p>
<p>En même temps on a un vrai métier &laquo;&nbsp;<em>dans la vraie vie</em>&nbsp;&raquo; comme disent les gens. Or dans ma vraie vie de développeur indépendant, je code des sites Internet avec Play! Framework pour des clients. Et les sites de mes clients doivent fonctionner sur le navigateur de Microsoft, à savoir Microsoft Internet Explorer. La question aujourd&#8217;hui, cher public, c&#8217;est comment tester facilement que le fruit de votre travail fonctionne sur IE7, IE8 et IE9 ? Et bien voici la réponse : Virtual Box et des VMs, un script intelligent et quelques heures de patience. </p>
<p>Je commence par la fin, pour montrer à la salle qu&#8217;il n&#8217;y a aucuns trucages :<br />
<a href="http://www.touilleur-express.fr/wp-content/uploads/2011/11/windows_vm_virtualbox.png"><img src="http://www.touilleur-express.fr/wp-content/uploads/2011/11/windows_vm_virtualbox-300x228.png" alt="" title="windows_vm_virtualbox" width="300" height="228" class="alignnone size-medium wp-image-5794" /></a> </p>
<p>Le site Artstechnica montre <a href="http://arst.ch/rg6">dans cet article</a> que pour la première fois, Internet Explorer est passé sous la barre des 50% des navigateurs utilisés dans le monde, en octobre 2011. Bref si IE6 est presque mort, il faut bien voir qu&#8217;IE8 est actuellement le navigateur le plus utilisé dans le monde. </p>
<p>Alors voyons comment configurer des machines virtuelles rapidement avec IE7, IE8 et IE9. Pour IE7 nous allons utiliser Windows Vista. Et pour IE8/IE9 nous prendrons Windows 7 Ultimate Edition. Ce qui suit fonctionne pour Mac OS X et pour Linux. </p>
<p><strong>Virtual Box</strong><br />
Commencez par installer <a href="http://virtualbox.org/">Virtualbox</a>, une machine virtuelle gratuite et géniale, développée par SUN Microsystems et qui appartient maintenant à Oracle. Sur Mac OS X avec un processeur Intel, ceci vous permet d&#8217;avoir un Windows fonctionnel et qui tourne parfaitement. </p>
<p><strong>Les machines virtuelles Microsoft</strong><br />
Microsoft propose des machines virtuelles pour les développeurs Webs, afin de tester leurs sites webs facilement sur les différents navigateurs <a href="http://www.microsoft.com/download/en/details.aspx?id=11575">depuis août dernier</a>. Ces images sont même patchées avec les dernières corrections de sécurité. Cependant vous ne pourrez pas activer l&#8217;image virtuelle. Ce ne sera donc pas une installation de Windows sur laquelle vous pourrez installer d&#8217;autres logiciels. Tous les 30 jours il faudra revenir à l&#8217;image de départ, mais je vais vous expliquer cela plus loin. </p>
<blockquote><p>In order to help web designers and web developers test their websites in older versions of Internet Explorer, we&#8217;ve provided the following VHD with Windows set up with the specified version of Internet Explorer. The images are patched with the latest security updates and are otherwise clean installs of the operating system with very few modifications. </p>
<p>This download page contains different VPC images, depending on what you want to test.  These images are specifically designed to run on Microsoft Virtual PC, and may or may not work in other hosting environments. </p>
<p>Due to the size of the Windows 7 and Windows Vista VHDs, it is split across several files, you&#8217;ll need to download all files for that version of the Internet Explorer and uncompress them to the same directory to unpack the VHD file. </p>
<p>The password to login to all of these images is <strong>Password1</strong> and the username will be present when you first login. </p>
<p>*Note: You may be required to activate the OS as the product key has been deactivated. This is the expected behavior. The VHDs will not pass genuine validation. Immediately after you start the Windows 7 or Windows Vista images they will request to be activated. You can cancel the request and it will login to the desktop. You can activate up to two “rearms” (type slmgr –rearm at the command prompt) which will extend the trial for another 30 days each time OR simply shutdown the VPC image and discard the changes you’ve made from undo disks to reset the image back to its initial state.  By doing either of these methods, you can technically have a base image which never expires although you will never be able to permanently save any changes on these images for longer than 90 days.
</p></blockquote>
<p>Le nom d&#8217;utilisateur sera Admin et le mot de passe par défaut est Password1. </p>
<p><strong>Je veux un script</strong><br />
Je ne sais pas pour vous, mais moi je ne me vois pas télécharger chacune des VMs et ensuite configurer pas à pas les installations. En cherchant un peu sur Github, j&#8217;ai trouvé un script développé par Greg Thornton qui marche super bien sur Mac OS X Lion. Allez voir <a href="https://github.com/xdissent/ievms">https://github.com/xdissent/ievms</a>. </p>
<p>Pour lancer le téléchargement et l&#8217;installation des VMs, pour peu que Virtual Box soit déjà installé, comptez 3 à 4 heures avec la commande suivante :<br />
<code>
<pre>
curl -s https://raw.github.com/xdissent/ievms/master/ievms.sh | bash
</pre>
<p></code></p>
<p>Voilà, vous aurez ensuite 3 VMs sur VirtualBox configurées avec IE7, IE8 et IE9.</p>
<p><strong>Oui mais le numéro de série ?</strong><br />
Pas besoin de numéro de série. Il suffit de ne pas activer la version de Windows, et de reprendre à chaque fois l&#8217;image de base sans sauvegarder sur VirtualBox. Une autre possibilité plus compliquée est expliquée <a href="http://www.microsoft.com/download/en/details.aspx?id=11575">sur le site de Microsoft</a>.</p>
<p>Au final, cela vous donne un moyen simple et rapide de tester avec IE7, IE8 et IE9 votre site Internet.</p>
<p><strong>D&#8217;autres manières de tester</strong></p>
<p>Via Twitter (@nmartignole) j&#8217;ai eu d&#8217;autres suggestions d&#8217;outils et de liens intéressants. Voici ce que j&#8217;ai reçu : </p>
<p>- <a href="http://www.browserstack.com">http://www.browserstack.com</a> est une solution dans le cloud qui permet aussi de tester un site en local sur votre machine, via une Applet Java. Vous pouvez tester le service gratuitement 60mn, sinon c&#8217;est payant. Outil proposé par @alanzirek. </p>
<p>- <a href="http://saucelabs.com/ondemand">SauceOnDemand</a> permet de tester un site via des VMs hostées dans le Cloud. Intéressant, il est possible de faire tourner des tests Selenium afin de vous notifier lorsqu&#8217;une des versions pour un navigateur donné est cassée. Merci @fcamblor du BordeauxJUG pour l&#8217;info. </p>
<p>[Update]<br />
Après plusieurs essais, la solution la plus pratique est <a href="http://www.browserstack.com/">Browserstack</a>. Cet outil vous permet de tester via votre navigateur puis des machines virtuelles, l&#8217;ensemble des navigateurs du marché. A découvrir d&#8217;urgence. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2011/11/03/comment-tester-son-site-sur-les-differentes-versions-dinternet-explorer/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Scala et Play! Framework</title>
		<link>http://www.touilleur-express.fr/2011/06/06/scala-et-play-framework/</link>
		<comments>http://www.touilleur-express.fr/2011/06/06/scala-et-play-framework/#comments</comments>
		<pubDate>Mon, 06 Jun 2011 08:48:41 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Perso]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[playframework]]></category>
		<category><![CDATA[scala]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=5244</guid>
		<description><![CDATA[La conférence ScalaDays 2011 s&#8217;est déroulée la semaine dernière à Stanford, au sud de San Francisco. Guillaume Bort et Sadek Grobi étaient là-bas pour présenter la dernière version de l&#8217;approche Scala proposée par Play! Framework. Play! Scala permet d&#8217;utiliser Scala tout en conservant l&#8217;approche simple et puissante de Play! Framework. Je ne présente plus Play! Framework, car avec 260 000 téléchargements à ce jour on ne parle plus d&#8217;un épiphénomène, mais bien d&#8217;un outil de plus en plus populaire.
Après avoir installé Play! Framework v1.2.2RC1, l&#8217;installation du module s&#8217;effectue en quelques ...]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.touilleur-express.fr/wp-content/palette-couleur-300x199.jpg" alt="" title="Couleurs" width="300" height="199" class="alignleft size-medium wp-image-4601" />La conférence ScalaDays 2011 s&#8217;est déroulée la semaine dernière à Stanford, au sud de San Francisco. Guillaume Bort et Sadek Grobi <a href="http://days2011.scala-lang.org/node/255">étaient là-bas</a> pour présenter la dernière version de l&#8217;approche Scala proposée par Play! Framework. <a href="http://scala.playframework.org/">Play! Scala</a> permet d&#8217;utiliser <a href="http://www.scala.org/">Scala</a> tout en conservant l&#8217;approche simple et puissante de Play! Framework. Je ne présente plus Play! Framework, car avec <a href="http://www.playframework.org/download">260 000 téléchargements à ce jour</a> on ne parle plus d&#8217;un épiphénomène, mais bien d&#8217;un outil de plus en plus populaire.</p>
<p>Après avoir installé Play! Framework <a href="http://download.playframework.org/releases/play-1.2.2RC1.zip">v1.2.2RC1</a>, l&#8217;installation du module s&#8217;effectue en quelques instants avec la commande &laquo;&nbsp;play install&nbsp;&raquo;, qui permet de télécharger et de configurer les modules complémentaires de Play! </p>
<pre class="brush:html">
$ play install scala-0.9.1
$ play new myScalaWebApp --with scala
$ play run myScalaWebApp
</pre>
<h3>Un moteur de template plus simple</h3>
<p>Première différence avec la version Java, le moteur de template. Tout en conservant le nom des tags de la version actuelle, la nouvelle version Scala propose un moteur de template ultra-simple. L&#8217;idée est empruntée à Razor, le nouveau système de templating que l&#8217;on retrouve dans la toute dernière version du framework web de Microsoft. L&#8217;idée ? Au lieu d&#8217;encadrer avec le principe d&#8217;une balise ouvrante/fermante le code dynamique, il suffit de placer directement les tags, un peu finalement comme des Macros :</p>
<p>La version avec le moteur Groovy :</p>
<pre class="brush:html">
&lt;UL&gt;
#{list products, as:'p'}
 &lt;LI&gt;${p.name} (${p.price})&lt;/LI&gt;
#{/list}
&lt;/UL&gt;
</pre>
<p>La nouvelle version avec le nouveau moteur Scala : </p>
<pre class="brush:html">
&lt;ul&gt;
@for(p &lt;- products) {
    &lt;li&gt;@p.name ($@p.price)&lt;/li&gt;
}
&lt;/ul&gt;
</pre>
<p>Ce nouveau moteur de template permet d&#8217;écrire le code de la partie vue, de manière plus fluide et plus simple, comme une suite de macro appliquée dans un template.</p>
<p>Chaque template dans cette nouvelle version est une fonction. Il devient donc nécessaire de préciser les paramètres en entrée de chaque template, ce qui diffère de l&#8217;approche actuelle Java+Groovy. En quelques sortes, vos templates de page dans cette version deviennent des templates typés fortement, les paramètres attendus doivent être explicitement déclarés.</p>
<p>Il devient aussi facile de faire du pattern matching. Remarquez aussi le mélange des balises HTML, traitées comme du XML par Scala, et la partie purement dynamique : </p>
<pre class="brush:html">
@connected match {

    case Admin(name) => {
        &lt;span class="admin"&gt;Connected as admin (@name)&lt;/span&gt;
    }

    case User(name) => {
        &lt;span&gt;Connected as @name&lt;/span&gt;
    }

}
</pre>
<p>Les Tags dans la version Groovy/Java sont des macros qui permettent de réutiliser à différents endroits, des bouts de code. Dans la version Scala, une nouveauté apparait implicitement : il devient possible de passer des paramètres à un Tag, mais il devient aussi possible de construire ce type d&#8217;appel :</p>
<p>Définissons d&#8217;abord un tag notice &laquo;&nbsp;views/tags/notice.scala.html&nbsp;&raquo;<br />
Ce template prend 2 paramètres : un paramètre level de type String, dont la valeur par défaut sera error, ainsi qu&#8217;un bloc html :</p>
<pre class="brush:html">
@(level:String = "error")(body: (String) => Html)

@level match {

    case "success" => {
        &lt;p class="success"&gt;
            @body("green")
        &lt;/p&gt;
    }

    case "warning" => {
        &lt;p class="warning"&gt;
            @body("orange")
        &lt;/p&gt;
    }

    case "error" => {
        &lt;p class="error"&gt;
            @body("red")
        &lt;/p&gt;
    }

}
</pre>
<p>Si level vaut &laquo;&nbsp;error&nbsp;&raquo;, nous affichons un bloc span avec pour style css &laquo;&nbsp;error&nbsp;&raquo;, puis nous rappelons le template body en passant en paramètre la valeur &laquo;&nbsp;red&nbsp;&raquo;.<br />
Voici comment l&#8217;utiliser dans une page : </p>
<pre class="brush:html">
@import views.tags.html._

@notice("error") { color =&gt;
    Oops, something is <span style="color:@color">wrong</span>
}
</pre>
<p>Dans notre page principale, @notice est notre template définit comme un tag. Nous passons &laquo;&nbsp;error&nbsp;&raquo;. Cette fonction s&#8217;exécute et appelle ensuite body, en passant un paramètre de type String. Dans la page principale, je déclare une fonction qui prend un paramètre color, et qui retourne alors notre message d&#8217;erreur, avec la couleur red&#8230; </p>
<p>Cet exemple illustre simplement un nouveau principe disponible uniquement grâce à cette approche, où comme dirait quelqu&#8217;un l&#8217;an dernier, &laquo;&nbsp;<a href="http://www.touilleur-express.fr/2010/03/22/scala-case-option-none-et-some/">tout est objet et fonction en même temps</a>&laquo;&nbsp;. </p>
<p>Cette approche a aussi une conséquence sur la façon d&#8217;écrire et d&#8217;architecturer son application. Je pense qu&#8217;elle demandera vraiment une remise en question de notre habitude très &laquo;&nbsp;MVC&nbsp;&raquo;, sagement appliquée ces 10 dernières années. Ne partez pas, on en reparle à la fin. </p>
<h3>Anorm, un &laquo;&nbsp;Another NOT ORM&nbsp;&raquo; framework</h3>
<p>Parlons un petit peu de JPA et de son utiliastion avec Scala auparavant. Techniquement, en utilisant l&#8217;implémentation JPA d&#8217;Hibernate, cela fonctionne. Je crois que Scala 2.8 ne supporte pas les annotations imbriquées comme @JoinTable. Il serait par exemple impossible d&#8217;écrire ce code en Scala :</p>
<pre class="brush:java">
// note : c'est du Java mon ami
 @JoinTable(
        name="CUST_PHONE",
        joinColumns=
            @JoinColumn(name="CUST_ID", referencedColumnName="ID"),
        inverseJoinColumns=
            @JoinColumn(name="PHONE_ID", referencedColumnName="ID")
    )
</pre>
<p>Deuxième question sur le concept de l&#8217;ORM et son utilisation avec Scala. Si je cherche à privilégier une approche immutable de mon domaine de données, est-ce que l&#8217;ORM reste intéressant ? Si je souhaite avoir un domaine objet pour travailler, et utiliser la base de données relationnelles comme mode de persistence, l&#8217;approche JPA+Java est parfaite. Si par contre je décide que mon domaine est avant tout en base, que le coeur de mon application travaille sur une base de données (relationnelle ou non) alors l&#8217;ORM perd de son intérêt. </p>
<p>Pour bien comprendre ce que j&#8217;essaye de dire, essayez d&#8217;imaginer où doit se situer la &laquo;&nbsp;golden source&nbsp;&raquo; de votre donnée. Est-ce que c&#8217;est en mémoire, dans votre serveur d&#8217;application ? Ou est-ce que votre donnée de référence doit être dans votre base de données ? Cela semble trivial, mais lorsque l&#8217;on prend l&#8217;approche sans état conversationnel, il devient plus simple de ne PAS utiliser d&#8217;ORM. </p>
<p>La question devient alors : quel est le meilleur DSL pour accéder à une base de données ? Les développeurs de Play! Framework se sont creusés la tête, et Sadek Grobi a proposé une approche très simple et très puissante basée sur SQL. </p>
<p><a href="http://scala.playframework.org/documentation/scala-0.9.1/anorm">Anorm</a> (Anorm is Not a Object Relational Mapper) est un nouveau moteur très puissant qui tire partie de la richesse de Scala. Je retrouve un des points forts de Play! Framework, que l&#8217;on pourrait résumer par l&#8217;approche &laquo;&nbsp;<em>tu es un p$$tain de développeur oui ou merde ? </em>&nbsp;&raquo; . Or comme chaque développeur, tu as fais du SQL. Cela tombe bien, Anorm ne propose pas un nouveau DSL, il se base sur SQL. Oui tu m&#8217;as bien entendu, tu vas faire du bon vieux SQL qui marche depuis la nuit des temps. Et c&#8217;est pareil pour la couche web : tu ne vas pas décrire ta page web en Java, tu vas la coder en HTML. Tu sais ce que c&#8217;est le HTML non ?<br />
Bref le retour à la source. </p>
<p>Cela s&#8217;accorde bien lorsqu&#8217;en face vous cherchez (et vous trouvez) des problèmes inhérents au mélange du monde relationnel et du monde objet. Prenez par exemple une relation de type @OneToMany de type lazy. Disons une liste de Pays et de Villes, un Pays pouvant contenir plusieurs Villes.  Avec une relation de type @OneToMany, si vous souhaitez construire votre ensemble Pays/Villes, vous vous retrouvez par défaut avec <a href="http://stackoverflow.com/questions/2593029/how-can-i-resolve-the-n1-selects-problem">le problème du N+1 SELECT</a>. Il faut alors passer par un left join dans vos @NamedQuery. Vas-y que je te rappelle que tu travailles sur un modèle relationnel, dans ton beau modèle tout objet&#8230; (voir aussi <a href="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html#performance-fetching">Hibernate Performance Fetching</a>). </p>
<p>Ici il n&#8217;est plus question d&#8217;abstraction, puisque vous travaillez directement sur votre base de données. Un point faible de cette approche  semble être la dépendance du code par rapport au schéma. Si je décide que ma base de données est la source de référence, il faudra donc que je corrige mes requêtes si le schéma change. Mais dans l&#8217;approche JPA, lorsque mon domaine change, je dois aussi reprendre le code de mon service. Bref il y a toujours quelque chose à reprendre si votre Domaine (avec un grand D) évolue. Que ce soit dans le monde objet, ou dans le monde relationnel. Comprenez une chose : soit vous ignorez la base de données, soit vous en faite une amie.</p>
<p>Voici un exemple tiré de la documentation de l&#8217;API, qui montre comment récupérer une liste de pays, puis ensuite construire une nouvelle collection pour la vue :</p>
<pre class="brush:scala">
// Create an SQL query
val selectCountries = SQL("Select * from Country")

// Transform the resulting Stream[Row] as a List[(String,String)]
val countries = selectCountries().map(row =>
    row[String]("code") -> row[String]("name")
).toList
</pre>
<p>Un autre exemple tiré de l&#8217;exemple Yabe :</p>
<pre class="brush:scala">
val postsWithAuthor:List[(Post~User)] =
    SQL(
        """
            select * from Post p join User u on
            p.author_id = u.id order by p.postedAt desc
        """
    ).as( Post ~< User * )
</pre>
<p>Vous pouvez aussi l'écrire de cette façon dans la class Post :
</pre>
<pre class="brush:scala">
def allWithAuthor:List[(Post,User)] =
    SQL(
        """
            select * from Post p join User u on p.author_id = u.id
            order by p.postedAt desc
        """
    ).as( Post ~< User ^^ flatten * )
</pre>
<p>Puis vous pourrez vous en servir dans une action dans votre controller de cette façon :
</pre>
<pre class="brush:scala">
import models._

object Application extends Controller {

def index = {
    val allPosts = Post.allWithAuthor
    html.index(
        front = allPosts.headOption,
        older = allPosts.drop(1)
    )
}

}
</pre>
<p><em>headOption</em> récupère le post le plus récent pour l&#8217;afficher complètement, <em>older</em> est l&#8217;ensemble des Posts moins le post le plus récent que nous venons d&#8217;afficher. Pratique non ? </p>
<p>Nul doute qu&#8217;il sera nécessaire de bien comprendre et de challenger cette approche. Mais elle vient remettre en question le mouvement &laquo;&nbsp;tout-orm&nbsp;&raquo; du monde Java, avec une petite brise fraiche qui plaît bien. </p>
<h3>Du code plus lisible et bien typé</h3>
<p>L&#8217;approche Scala pour Play! est intéressante car elle rend le code plus simple et plus facile à lire. Finalement, dans l&#8217;esprit nous pouvons voir une requête Web comme l&#8217;enchainement de différentes fonctions de transformation. Soit un état de départ donné n, si j&#8217;applique ma fonction f(n) sur mon ensemble, je dois retrouver toujours le même résultat. C&#8217;est exactement la définition même d&#8217;une URI : pour un espace d&#8217;adressage donné, je retourne un ensemble de méta-données. Scala avec son approche fonctionnelle est particulièrement intéressant pour construire une application web.  </p>
<p>C&#8217;est vraiment un pas en avant vers une architecture où le client tient les informations d&#8217;état (appelez cela la session si vous voulez) et où le serveur est complètement sans état conversationnel. Il peut cependant très bien avoir un cache de données, mais il n&#8217;y a plus la notion de session utilisateur du côté serveur. Il y a la notion de session serveur du côté client. Que ce soit dès aujourd&#8217;hui avec un Cookie, ou demain avec HTML5, c&#8217;est une approche assez différente. Je n&#8217;ai pas dit mieux ou moins bien, j&#8217;ai bien dit : une approche différente. </p>
<h3>Prévu dans la version 1.0</h3>
<p>La version 0.9.1 est sortie juste à temps pour Scala Days. La version 1.0 apportera d&#8217;autres concepts dans <a href="http://scala.playframework.org/documentation/scala-0.9.1/roadmap">la roadmap</a> :</p>
<pre>
Before completing the 1.0 version of Play Scala we need to work on the following aspects:

- Rework the MVC layer to make it easier to compose Actions.
- Add proper JSON support — Probably using Lift JSON
- Provide an API to work with the Play async features.
- Integrate Akka actors
- Provide a type safe way to express routes directly in Scala
So probably move to Scala 2.9.
</pre>
<h3>Conclusion</h3>
<p>Intéressant et très poussé, Play! Scala offre un moteur Web plus simple à utiliser que Lift pour la plateforme Scala. A titre personnel, je vais travailler dessus pendant 2 mois. Je vous ferai part de ce qui m&#8217;a plu et ce que j&#8217;ai trouvé délicat. Allez y jeter un oeil par curiosité, c&#8217;est facile à tester, <a href="http://scala.playframework.org/documentation/scala-0.9.1/guide1">le tutorial Yabe</a> permet de se lancer rapidement, cela ne vous demandera que quelques heures.</p>
<p>A suivre donc.</p>
<p><strong>Références :</strong><br />
Le site de Play! Framework : <a href="http://scala.playframework.org/">http://scala.playframework.org/</a><br />
Le souci des requêtes N+1 <a href="http://stackoverflow.com/questions/2593029/how-can-i-resolve-the-n1-selects-problem">sur StackOverflow</a><br />
<a href="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html#performance-fetching">Guide des stratégies de chargement d&#8217;Hibernate</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2011/06/06/scala-et-play-framework/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Envoi de courrier électronique en masse</title>
		<link>http://www.touilleur-express.fr/2011/06/01/envoi-de-courrier-electronique-en-masse/</link>
		<comments>http://www.touilleur-express.fr/2011/06/01/envoi-de-courrier-electronique-en-masse/#comments</comments>
		<pubDate>Wed, 01 Jun 2011 15:49:17 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Perso]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[express-board]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=5235</guid>
		<description><![CDATA[
Une goutte de sueur froide dans le café
Petite sueur froide ce matin. Notre but est de calculer au plus juste les différents prix pour les annonceurs pour l&#8217;eXpress-Board. Pour cela, nous avons besoin de quelques statistiques. Il est 08h32, je commence à coder quelques lignes. Quelques minutes plus tard, j&#8217;ai une belle page, avec de beaux chiffres&#8230; et de nouveaux soucis que je viens de m&#8217;ajouter.
Il y a eu 456 &#171;&#160;UserRequest&#160;&#187; depuis que j&#8217;ai lancé ce système en septembre 2010. Il s&#8217;agit des demandes de mise en relation de la ...]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.touilleur-express.fr/wp-content/2011/06/mailchimp-300x222.jpg" alt="" title="mailchimp" width="300" height="222" class="alignleft size-medium wp-image-5239" /><br />
<h3>Une goutte de sueur froide dans le café</h3>
<p>Petite sueur froide ce matin. Notre but est de calculer au plus juste les différents prix pour les annonceurs pour <a href="http://www.express-board.fr/">l&#8217;eXpress-Board</a>. Pour cela, nous avons besoin de quelques statistiques. Il est 08h32, je commence à coder quelques lignes. Quelques minutes plus tard, j&#8217;ai une belle page, avec de beaux chiffres&#8230; et de nouveaux soucis que je viens de m&#8217;ajouter.</p>
<p>Il y a eu 456 &laquo;&nbsp;UserRequest&nbsp;&raquo; depuis que j&#8217;ai lancé ce système en septembre 2010. Il s&#8217;agit des demandes de mise en relation de la part des recruteurs, à l&#8217;intention des candidats. Sur ce nombre, il y a eu 164 demandes acceptées (35.3%), 113 demandes refusées (24.72%) et surtout&#8230; petite sueur froide&#8230; 180 demandes en attente (39.8%). Comment se fait-il que les candidats ne donnent pas suite ? Ok dans le lot, il y a ceux qui ne répondent pas tout de suite. Mais c&#8217;est un chiffre un peu trop important.</p>
<p>Le souci n&#8217;est pas lié uniquement aux candidats qui ne répondent pas. Il est aussi lié à l&#8217;infrastructure technique que j&#8217;utilise. Les emails aujourd&#8217;hui sont envoyés via la passerelle SMTP de Gandi, qui est aussi le hosteur du domaine express-board.fr. Or cette passerelle limite l&#8217;envoi par heure, afin d&#8217;éviter de se retrouver marqué comme &laquo;&nbsp;spammeur&nbsp;&raquo;. Bref il est possible qu&#8217;un certain nombre de mes emails&#8230; ne soient pas envoyés correctement.  </p>
<p>La goutte est tombée dans le café lorsque j&#8217;ai retrouvé un gros paquet d&#8217;erreurs sur PlayApps, lors de l&#8217;envoi d&#8217;emails à toi, cher Candidat. Gandi et sa passerelle SMTP me tappe, pour surcharger l&#8217;utilisation du serveur SMTP&#8230; Allez, on va faire un peu de DNS et de configuration ce matin&#8230; </p>
<h3>La configuration des serveurs SMTP pour les nuls</h3>
<p>Il est facile de configurer un serveur SMTP, mais bien plus compliqué de le rendre &laquo;&nbsp;présentable&nbsp;&raquo; pour les passerelles de filtrage. En fait, sans expérience en marketing web, sans connaisance des termes white-list, DKIM ou SPF, et bien tu ne vas pas très loin. Allez, je suis presque sûr que tu ne connais pas DKIM&#8230; fait pas ton timide.</p>
<h3>SMTP </h3>
<p>Petit rappel : SMTP (Simple Mail Transfer Protocol) est un protocole vieux comme le monde, qui permet de transférer des messages électroniques vers un serveur. Il ne permet pas de récupérer du courrier électronique. Pour cela vous utiliserez POP ou IMAP. On pourrait penser que le protocole a été inventé en même temps que les pigeons voyageurs, mais non. C&#8217;est plus récent.</p>
<h3>DKIM</h3>
<p>DKIM (DomainKeys Identified Mail) est une norme d&#8217;identification fiable des noms de domaine, qui date de 2004. Expliqué  dans la <a href="http://www.ietf.org/rfc/rfc4871.txt">RFC 4871</a>, ce système permet en fait à l&#8217;aide d&#8217;un système de clé publique/clé privée, de garantir l&#8217;authenticité du domaine expéditeur. Elle permet aussi à des intermédiaires techniques de prendre la responsabilité de l&#8217;envoi. C&#8217;est exactement ce que je cherche : déléguer cette certification à un tiers, qui lui, est connu de vos fournisseurs d&#8217;accès.</p>
<p>Le système est assez simple. Les messages sont signés avec une clé RSA, une signature est ensuite communiquée à votre message de cette manière :<br />
- tout d&#8217;abord le corps du message est signé, afin bien entendu d&#8217;éviter qu&#8217;un email modifié passe à travers la grille.<br />
- il est possible ensuite d&#8217;ajouter d&#8217;autres headers SMTP dans la signature<br />
- un nouveau champ SMTP appelé DKIM-Signature est ajouté, avec la liste des headers signés, différentes informations pour valider le domaine, le nom de l&#8217;algo utilisé, le hash du corps du message, le tout est empaqueté.<br />
- les champs de l&#8217;entête ainsi que le champ DKIM-Signature sont ensuite normalisés (<a href="http://en.wikipedia.org/wiki/Canonicalization">canonicalized</a> en Anglais) et hashés.<br />
- une signature RSA est calculé sur ce hash, et cette signature est ajoutée dans le champ DKIM-Signature</p>
<p>L&#8217;ensemble est donc un espèce de timbre poste méga-sécurisé, qui valide bien l&#8217;expéditeur, et/ou la plateforme technique ayant fait passer le message. Par contre, pour mettre cela en place, il faut avoir soit son propre serveur SMTP, soit croiser les doigts pour que votre solution actuelle l&#8217;utilise. Je crois (sans avoir vérifié) que Gandi propose ce système lorsque vous utilisez leurs relais SMTP&#8230; Donc à priori j&#8217;ai déjà. Cool.</p>
<h3>SPF</h3>
<p>SPF (<a href="http://fr.wikipedia.org/wiki/Sender_Policy_Framework">Sender Policy Framewor</a>k) est plutôt un moyen de limiter le spam en déclarant dans votre DNS quelles sont les sources valides dans votre domaine, qui ont le droit d&#8217;envoyer des emails. Par exemple, si un email arrive chez vous en provenance de 217.10.11.12 , alors que je n&#8217;ai dit que seul telle autre IP a le droit d&#8217;envoyer un email, il sera marqué comme spam. Ce n&#8217;est pas LA solution, mais un moyen simple d&#8217;améliorer aussi la réputation de son domaine.</p>
<h3>Sinon il y a d&#8217;autres solutions</h3>
<p>Si vous souhaitez envoyer une newsletter (une lettre de publi-information) il est peut-être plus intéressant de passer par des solutions comme <a href="http://mailchimp.com/">MailChimp</a> par exemple. Une API type JSON/REST permet d&#8217;intégrer dans son application tout ce qu&#8217;il faut pour créer votre newsletter, mettre à jour la liste des personnes intéressées et gérer aussi les désinscriptions. Par ailleurs la plateforme propose des outils marketing (taux d&#8217;ouverture, fréquence de lecture par jour, etc). Bref cela pourra être intéressant plus tard si je souhaite mettre en place une liste de diffusion sur l&#8217;eXpress-Board.</p>
<p><a href="http://sendgrid.com/">SendGrid</a> est une autre solution, plus orientée du côté de l&#8217;envoi massif d&#8217;emails. Premier service intéressant : c&#8217;est votre postier. Il agit comme un tiers de confiance vis-à-vis des destinataires des emails. Techniquement, vous avez plus de chances que votre email atteigne sans soucis son destinataire, qu&#8217;en se reposant sur votre propre configuration SMTP par défaut. Ensuite, et cela peut devenir intéressant, c&#8217;est un système capable de diffuser rapidement un volume important. Ils envoient plus de 45 millions d&#8217;emails par jour pour le compte de 23 000 entreprises&#8230; La solution de base à 0.10 USD pour 1000 est ultra-compétitif. Mais pour ce prix, votre email ne sera pas reçu immédiatement par le destinataire. C&#8217;est un peu ennuyant lorsqu&#8217;il s&#8217;agit d&#8217;envoyer un rappel de mot de passe. Votre client risque de ne pas revenir sur votre site, s&#8217;il reçoit 3 heures plus tard son mot de passe. Il faut alors prendre une solution un peu plus chère, mais le tout reste raisonnable. Comptez <a href="http://sendgrid.com/pricing.html">79 USD par mois</a>, pour débuter.</p>
<p><a href="http://www.critsend.com/">CritSend</a> est un poil plus technique. Le système gère aussi l&#8217;envoi, il offre un système de désabonnement automatiquement. Par ailleurs, il stocke les emails incorrects. Cela me permettrait de nettoyer par exemple chaque nuit les comptes invalides sur l&#8217;eXpress-Board. Le prix est compétitif : 20 000 emails pour 10$, bref de quoi travailler tranquillement sans soucis. </p>
<h3>Conclusion</h3>
<p>Je pense que je vais tester <strong>MailChimp</strong>. Le service est gratuit jusqu&#8217;à 2000 emails par moi. Il va me servir d&#8217;abord de relai SMTP. Je verrai plus tard pour ce qui est de l&#8217;envoi des newsletters.<br />
Sinon à plus court terme, j&#8217;ai nettoyé mes enregistrements DNS, mais je crois que je vais basculer vers une solution plus pro.</p>
<p>Merci à @framiere, @Baztoune, @jsevellec, @cestpasdur, @ProjectBleuet, @ThibaudVibes, @fabriceclaeys et @evenisse pour vos informations cet après-midi sur Twitter. </p>
<h3>Ressources</h3>
<p>Voici d&#8217;autres solutions proposées. </p>
<p>Si votre besoin est plutôt de l&#8217;envoi d&#8217;emails transactionnels (mot de passe oublié, création de compte, facture)<br />
- <a href="http://www.mailjet.com/">MailJet</a> propose une offre gratuite jusqu&#8217;à 6000 emails par mois (merci Bruno S.)<br />
- <a href="http://postmarkapp.com/">Postmark</a> propose une offre à 1.5 $ pour 1000 emails envoyés</p>
<p>Si votre besoin est plutôt du type lettre d&#8217;information<br />
- <a href="http://www.mailchimp.com">MailChimp</a><br />
- <a href="http://www.campaignmonitor.com/">Campaign Monitor</a> est plus marketing/suivi des emails lus</p>
<p>Pour tester la configuration SPF d&#8217;un domaine : <a href="http://www.myiptest.com/staticpages/index.php/check-spf-Sender-Policy-Framework">MyIPTest</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2011/06/01/envoi-de-courrier-electronique-en-masse/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>CSS3 : découvrez les ombres et la lumière</title>
		<link>http://www.touilleur-express.fr/2011/02/13/css3-html5/</link>
		<comments>http://www.touilleur-express.fr/2011/02/13/css3-html5/#comments</comments>
		<pubDate>Sat, 12 Feb 2011 23:16:12 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Perso]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[css3]]></category>
		<category><![CDATA[html5]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=4891</guid>
		<description><![CDATA[
HTML5 et CSS3 arrivent à grand pas, il est temps de s&#8217;y mettre sérieusement. En fait si vous avez un téléphone Android ou un iPhone dans la poche, vous avez déjà un appareil tout à fait capable d&#8217;utiliser ces nouvelles spécifications. Mais concernant les navigateurs un peu plus ancien, où en sommes-nous vraiment ?
Internet Explorer 6 est mort depuis des années
Si votre DSI vous impose encore l&#8217;usage de cette bouse de pseudo-navigateur, envoyez-lui un faire-part lui annonçant que son précieux est mort depuis 10 ans. IE6 est sorti en août ...]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.touilleur-express.fr/wp-content/HTML5_sticker.png"><img src="http://www.touilleur-express.fr/wp-content/HTML5_sticker-300x150.png" alt="" title="HTML5_sticker" width="300" height="150" class="alignleft size-medium wp-image-4893" /></a></p>
<p><strong>HTML5 et CSS3 arrivent à grand pas, il est temps de s&#8217;y mettre sérieusement. En fait si vous avez un téléphone Android ou un iPhone dans la poche, vous avez déjà un appareil tout à fait capable d&#8217;utiliser ces nouvelles spécifications. Mais concernant les navigateurs un peu plus ancien, où en sommes-nous vraiment ?</strong></p>
<h3>Internet Explorer 6 est mort depuis des années</h3>
<p>Si votre DSI vous impose encore l&#8217;usage de cette bouse de pseudo-navigateur, envoyez-lui un faire-part lui annonçant que son précieux est mort depuis 10 ans. IE6 est sorti en août 2001. IE8 date de mars 2009 et c&#8217;est le premier navigateur utilisé. <a href="http://windows.microsoft.com/fr-FR/internet-explorer/products/ie-9/home">IE9 est disponible</a> en Release Candidate au moment où j&#8217;écris ces lignes.</p>
<p>Concernant les parts de marché des navigateurs webs, le leader en cette début d&#8217;année 2011 est Internet Explorer 8 avec 34.17%, puis Firefox 3.6 avec 18.55% et en 3ème position notre bon vieux Internet Explorer 6 avec seulement 11.43% de part de marché. Vous trouverez d&#8217;autres chiffres <a href="http://www.netmarketshare.com/browser-market-share.aspx?qprid=2">sur le site de Net MarketShare</a>. </p>
<p>Regardons plutôt vers l&#8217;avenir et vers ce qu&#8217;il est possible d&#8217;utiliser dès maintenant sur beaucoup de navigateurs. Croyez-moi, il est temps de se mettre à niveau si vous n&#8217;avez jamais touché une balise HTML de votre vie. </p>
<h3>Le mythe du Web Designer</h3>
<p>Dans la communauté Java, les développeurs imaginent parfois qu&#8217;ils vont travailler avec des &laquo;&nbsp;<em>Web Designers</em>&laquo;&nbsp;. Un jour, quelqu&#8217;un leur a expliqué qu&#8217;il fallait qu&#8217;ils fassent la partie &laquo;&nbsp;serveur&nbsp;&raquo; et qu&#8217;un gars allait faire la partie Web. </p>
<p>Hé les gars&#8230; on s&#8217;est moqué de vous&#8230; c&#8217;était une blague&#8230; comme le papa Noël, il faut arrêter d&#8217;y croire.</p>
<p>C&#8217;est culturel et je vais expliquer pourquoi. Il y a quelques années, les développeurs d&#8217;application de gestion utilisaient des clients lourds. Or lorsque vous développez en C/C++ avec Qt ou en Java avec Swing, vous êtes habitué à utiliser des composants. La création d&#8217;une interface utilisateur n&#8217;est pas un souci. Cela fait 20 ans que nous sommes capables de construire des applications de gestion sans soucis.<br />
Le problème s&#8217;est posé lorsque nous avons dû commencer à faire du Web. Plus exactement, du client riche (RIA si vous voulez vous la raconter). Or le Web est un milieu bien plus jeune et en constante évolution, là où du côté du client lourd nous étions dans un environnement stable.<br />
Pour bien maîtriser le développement Web, il faut d&#8217;abord comprendre son architecture. Puis ensuite de nouveaux concepts comme les états sans session transactionnelle du côté serveur.<br />
Au lieu de chercher à comprendre, nous avons voulu calquer ce que nous savions faire dans la bonne grosse application type client lourd.</p>
<p>Grave erreur.</p>
<p>Cela donne des applications qui ne sont pas des applications webs. A la limite, rendez-vous service : ne faîtes pas de Web. Vous étiez meilleurs avant.</p>
<p>J&#8217;appelle donc les développeurs Java qui pensent que l&#8217;on peut faire du Web avec un W majuscule : le web designer n&#8217;existe pas. C&#8217;est toi mon ami qui doit apprendre à construire l&#8217;interface en comprenant ce que fait un navigateur. Et ne pense pas te cacher derrière un framework (GWT/JSF/Flex) au lieu de prendre quelques heures pour découvrir ce que le navigateur peut faire. </p>
<p>Je te le promets, c&#8217;est fun et c&#8217;est simple. Plus que Scala, plus que Clojure et plus qu&#8217;Haskell. Et cela te sera certainement plus utile à mon avis.</p>
<p>On appelle cela HTML5 et CSS3&#8230;</p>
<h3>Dessinons un cadre</h3>
<p>Prenez un simple DIV pour commencer, avec un fond orange et une taille fixée. Une page en HTML5 se déclare aussi simplement que cela : </p>
<pre class="brush:html">
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
  &lt;meta charset="utf-8"&gt;
  &lt;title&gt;My first HTML5 page&lt;/title&gt;
  &lt;link rel="stylesheet" href="demo.css"&gt;
	&lt;style type="text/css"&gt;
	#demo{
		background-color: #f37350;
		color: #fff;
		width:200px;
		height:80px;
	}
	&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt; 

		&lt;div id="demo"&gt;

		&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Ce qui donne : </p>
<style type="text/css">
	#demo1{
		background-color: #f37350; 
		color: #fff; 
		width:200px; 
		height:80px;
    margin-bottom:10px;
	}
	</style>
<div id="demo1">
</div>
<h3>Ajoutons de l&#8217;ombre avec la propriété box-shadow</h3>
<p>Pour ajouter une ombre sur votre objet, CSS3 introduit la propriété box-shadow. Voyons comment ajouter une ombre simple sur notre boîte. Tout d&#8217;abord le résultat :</p>
<style type="text/css">
	#demo2{
		background-color: #f37350; 
		color: #fff; 
		width:200px; 
		height:80px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
    margin-bottom:10px;
	}
	</style>
<div id="demo2">
</div>
<p>Le code est assez simple :</p>
<pre class="brush:html">
&lt;style type="text/css"&gt;
	#demo2{
		background-color: #f37350;
		color: #fff;
		width:200px;
		height:80px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
	}
	&lt;/style&gt;
&lt;div id="demo2"&gt;
&lt;/div&gt;
</pre>
<p>Notez que si votre navigateur ne reconnait pas cette propriété, il se contentera d&#8217;afficher un cadre orange. Nous appelons cette notion le dégradage progressif. Cela ne fait pas planter le navigateur, il est toujours en mesure d&#8217;afficher la sémantique de la page. La mise en forme sera simplement moins riche. </p>
<p>Pour aller plus loin avec les ombres : <a href="http://www.css3.info/preview/box-shadow/">http://www.css3.info/preview/box-shadow/</a></p>
<h3>Arrondir les bords</h3>
<p>La propriété border-radius permet d&#8217;arrondir simplement les bords de votre cadre. Opera 10.5+, Safari 5+, IE9 et Chrome 5+ reconnaissent cette propriété nativement. Firefox 3.6+ doit par contre utiliser la propriété -moz-border-radius, jusqu&#8217;à ce qu&#8217;une version plus récente de Firefox reconnaisse cette propriété nativement. Ce sera le cas de Firefox 4. </p>
<style type="text/css">
	#demo3{
		background-color: #f37350; 
		color: #fff; 
		width:200px; 
		height:80px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
               border-radius: 5px;
               -moz-border-radius: 5px;
              margin-bottom:10px;
	}
	</style>
<div id="demo3">
</div>
<p>Le code source complet:</p>
<pre class="brush:html">
<!-- Demo 3 -->
&lt;style type="text/css"&gt;
	#demo3{
		background-color: #f37350;
		color: #fff;
		width:200px;
		height:80px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;

                border-radius: 5px;
               -moz-border-radius: 5px;
	}
	&lt;/style&gt;
&lt;div id="demo3"&gt;
&lt;/div&gt;
</pre>
<p>Il est bien entendu possible de définir pour chaque coin des paramètres différents.</p>
<style type="text/css">
	#demo4{
		background-color: #f37350; 
		color: #fff; 
		width:200px; 
		height:80px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
               border-radius: 5px;
-moz-border-radius-bottomright: 50px 25px;
border-bottom-right-radius: 50px 25px;
margin-bottom:10px;
	}
	</style>
<div id="demo4">
</div>
<p>Plus de détails : <a href="http://www.css3.info/preview/rounded-border/">http://www.css3.info/preview/rounded-border/</a></p>
<h3>Dégradé de couleur</h3>
<p>CSS3 permet aussi de créer des dégradés. <a href="http://fr.wikipedia.org/wiki/WebKit">Webkit</a>, le moteur de Safari, Chrome ou Konqueror, est présent sur Android et sur IPhone. Par ailleurs si vous surfez avec Safari sur Mac ou Chrome sur PC, vous utilisez Webkit sans le savoir. Autant dire que ce que vous allez voir est déjà utilisable sur un bon nombre de plateforme. </p>
<p>Pour construire une barre de navigation dans un menu ou pour ajouter un dégradé à votre tableau, la propriété gradient est parfaite. Elle est arrivée <a href="http://webkit.org/blog/175/introducing-css-gradients/">fin 2008</a> sur Webkit. Le moteur Mozilla a ensuite ajouté sa version, plus proche de la spécification CSS3. C&#8217;est un sujet encore très récent car <a href="http://dev.w3.org/csswg/css3-images/#gradients">le dernier draft sur le site du W3C</a> date du 12 février 2011&#8230; </p>
<p>Du côté Microsoft, IE7 et IE8 permettent de faire des gradients en utilisant des propriétés propriétaires. Je ne vous en parlerai donc même pas. Ce que vous allez voir ne marche que sur Mozilla et WebKit. </p>
<p>Voyons d&#8217;abord un simple petit dégradé sur notre cadre :</p>
<style type="text/css">
	#demo5{</p>
<p>		color: #fff; 
		width:200px; 
		height:80px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
               border-radius: 5px;
               border-radius: 5px;
               -moz-border-radius: 5px;
             margin-bottom:10px;
              background-image: -webkit-gradient(linear, left top, left bottom, from(#f37350), to(#fcdfd7));
              background-image: -moz-linear-gradient(top,#f37350,#fcdfd7);
              background-color: #ff0000; 
	}
	</style>
<div id="demo5">
</div>
<p>Si vous voyez un cadre rouge, votre navigateur ne reconnait pas la propriété CSS <em>-webkit-gradient</em> ou <em>-moz-linear-gradient</em>. </p>
<p>Pour afficher ce dégradé linéaire, j&#8217;ai simplement définit plusieurs fois la propriété background de mon div :</p>
<pre class="brush:html">

&lt;style type="text/css"&gt;
	#demo5{

		color: #fff;
		width:200px;
		height:80px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
               border-radius: 5px;
               border-radius: 5px;
               -moz-border-radius: 5px;
             margin-bottom:10px;
              background-image: -webkit-gradient(linear, left top, left bottom, from(#f37350), to(#fcdfd7));
              background-image: -moz-linear-gradient(top,#f37350,#fcdfd7);
              background-color: #ff0000;
	}
	&lt;/style&gt;
&lt;div id="demo5"&gt;
&lt;/div&gt;
</pre>
<p>- Le site <a href="http://gradients.glrzad.com/">CSS3 Gradient Generator</a> permet de générer de beaux dégradés.<br />
- <a href="http://www.colorzilla.com/gradient-editor/">Ultimate CSS Gradient Generator</a> est très pratique car il donne des gradients préconfigurés pour débuter rapidement. </p>
<h3>Transition</h3>
<p>Jusqu&#8217;ici, nous avons simplement ajouté des styles sur notre élément. Il est maintenant temps de vous montrer une version animée de notre cadre. Il serait sympa d&#8217;animer la couleur de fond lorsque l&#8217;utilisateur survole un cadre par exemple. </p>
<p><strong>Solution 1</strong><br />
Je vais d&#8217;abord faire en sorte que le dégradé de fond soit différent lorsque l&#8217;utilisateur passe la souris au dessus du DIV. Pour détecter ceci, j&#8217;utilise :hover sur le DIV :</p>
<style type="text/css">
	#demo6,#demo7 {
		color: #ccc; 
		width:120px; 
		height:50px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
               border-radius: 5px;
               border-radius: 5px;
               -moz-border-radius: 5px;
               margin-bottom:10px;
              background-image: -webkit-gradient(linear, left top, left bottom, from(#f37350), to(#fcdfd7));
              background-image: -moz-linear-gradient(top,#f37350,#fcdfd7);
              background-color: #f37350;
	}
      div#demo6:hover,div#demo7:hover{
              background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#cdd));
              background-image: -moz-linear-gradient(top,#fff,#cdd);
               background-color: #fff; 
        }
	</style>
<div id="demo6">
Survolez-moi 1
</div>
<div id="demo7">
Survolez-moi
</div>
<p>Le code source :</p>
<pre class="brush:html">
&lt;style type="text/css"&gt;
	#demo6,#demo7 {
		color: #ccc;
		width:120px;
		height:50px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
               border-radius: 5px;
               border-radius: 5px;
               -moz-border-radius: 5px;
               margin-bottom:10px;
              background-image: -webkit-gradient(linear, left top, left bottom, from(#f37350), to(#fcdfd7));
              background-image: -moz-linear-gradient(top,#f37350,#fcdfd7);
              background-color: #f37350;
	}
      div#demo6:hover,div#demo7:hover{
              background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#cdd));
              background-image: -moz-linear-gradient(top,#fff,#cdd);
               background-color: #fff;
        }
	&lt;/style&gt;
&lt;div id="demo6"&gt;
Survolez-moi 1
&lt;/div&gt;
&lt;div id="demo7"&gt;
Survolez-moi
&lt;/div&gt;
</pre>
<p><strong>Solution 2</strong><br />
La solution 1 n&#8217;est pas optimale. Il faut calculer les dégradés à la main, clairement ce n&#8217;est pas très pratique. Et si nous pouvions jouer sur certaines propriétés comme l&#8217;opacité ? Le tout avec une belle transition ? </p>
<p>CSS3 permet d&#8217;ajouter <a href="http://www.w3.org/TR/css3-transitions/">des transitions</a> très facilement. Sur <a href="http://www.express-board.fr/">l&#8217;eXpress-Board</a> par exemple, si vous survolez le menu de navigation en haut, vous verrez qu&#8217;un petit effet de transition est ajouté lorsque vous passez la souris sur les options. Une transition est une opération qui s&#8217;applique sur une ou plusieurs propriétés CSS. Par exemple vous pouvez appliquer une transition sur la propriété background-color ou sur la propriété opacity. C&#8217;est exactement ce que nous allons faire ici.</p>
<p>Tout d&#8217;abord le résultat final, observez ce que fait le navigateur lorsque vous survolez le cadre : </p>
<style type="text/css">
	#demo8,#demo9 {
		color: #ccc; 
		width:120px; 
		height:50px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
               border-radius: 5px;
               border-radius: 5px;
               -moz-border-radius: 5px;
               margin-bottom:10px;
              background-color: #f37350;
              opacity: 0.4;
			  -webkit-transition-property: opacity;
			  -webkit-transition-duration: 1s;
			  -moz-transition-property: opacity;
			  -moz-transition-duration: 1s;
			  transition-property: opacity;
			  transition-duration: 1s;
	}
      div#demo8:hover,div#demo9:hover{
              opacity:1;
			  -webkit-transition-property: opacity;
			  -webkit-transition-duration: 0.4s;
			  -moz-transition-property: opacity;
			  -moz-transition-duration: 0.4s;
			  transition-property: opacity;
			  transition-duration: 0.4s;
        }
	</style>
<div id="demo8">
Survolez-moi 8
</div>
<div id="demo9">
Survolez-moi 9
</div>
<p>Concernant Mozilla, les transitions ne sont supportées qu&#8217;à partir de la version 3.6.4 ou dans la bêta de Firefox 4. Par contre, du côté de Chrome ou de Safari, webkit supporte très bien celles-ci depuis bientôt 2 ans. </p>
<p>Voyez le code source nécessaire pour animer simplement la propriété opacity :</p>
<pre class="brush:html">
&lt;style type="text/css"&gt;
	#demo8,#demo9 {
		color: #ccc;
		width:120px;
		height:50px;
               -moz-box-shadow: 5px 5px 5px #888;
               -webkit-box-shadow: 5px 5px 5px #888;
               box-shadow: 5px 5px 5px #888;
               border-radius: 5px;
               border-radius: 5px;
               -moz-border-radius: 5px;
               margin-bottom:10px;
              background-color: #f37350;
              opacity: 0.4;
			  -webkit-transition-property: opacity;
			  -webkit-transition-duration: 1s;
			  -moz-transition-property: opacity;
			  -moz-transition-duration: 1s;
			  transition-property: opacity;
			  transition-duration: 1s;
	}
      div#demo8:hover,div#demo9:hover{
              opacity:1;
			  -webkit-transition-property: opacity;
			  -webkit-transition-duration: 0.4s;
			  -moz-transition-property: opacity;
			  -moz-transition-duration: 0.4s;
			  transition-property: opacity;
			  transition-duration: 0.4s;
        }
	&lt;/style&gt;
&lt;div id="demo8"&gt;
Survolez-moi 8
&lt;/div&gt;
&lt;div id="demo9"&gt;
Survolez-moi 9
&lt;/div&gt;
</pre>
<p>Pour aller plus loin et découvrir d&#8217;autres effets très puissants avec les transitions, voici quelques sites :</p>
<p>- <a href="http://www.w3.org/TR/css3-transitions/">la norme W3C</a> explique la syntaxe<br />
- <a href="http://css3.bradshawenterprises.com/">Using CSS3 Transitions, Transforms and Animation</a> : ce site vous montre toutes les transitions possibles<br />
- Gaetan Renaudeau explique <a href="http://www.zengularity.com/item/1583389563/les-transitions-css3-un-exemple-de-diaporama-dimages">les transitions CSS3 sur Zengularity</a>. Je l&#8217;ai mis en oeuvre <a href="http://www.express-board.fr/">sur la page d&#8217;accueil de l&#8217;eXpress-Board</a>.<br />
- Un effet <a href="http://www.creativejuiz.fr/trytotry/lightbox-with-tabindex-and-css3/">LightBox</a> en CSS3</p>
<h3>Conclusion</h3>
<p>Vous avez découvert les propriétés CSS3 suivants :<br />
 &#8211; box-shadow pour dessiner une ombre portée<br />
 &#8211; border-radius pour arrondir les angles<br />
 &#8211; gradient pour faire un dégradé<br />
 &#8211; transition pour passer d&#8217;une valeur à une autre pour une propriété CSS.</p>
<p>Ce n&#8217;est que 2% de ce qu&#8217;il est possible de faire simplement de nos jours. Souvenez-vous de cet article lorsqu&#8217;il s&#8217;agira de rendre agréable l&#8217;interface de votre logiciel&#8230; Sortons un peu de la culture &laquo;&nbsp;ingénieur&nbsp;&raquo; du côté serveur et n&#8217;oublions pas que nous codons des logiciels qui doivent être agréable à utiliser. C&#8217;est bien de faire 3 séparations en couche. C&#8217;est mieux de penser qu&#8217;il y a un utilisateur qui va cliquer dans votre interface. C&#8217;est bien de rêver la nuit de Lombok. C&#8217;est encore mieux de penser que trouver 3 belles couleurs ne demande que <a href="http://kuler.adobe.com/">5 minutes d&#8217;efforts</a>&#8230; </p>
<p>Rêvons un peu, sortons de l&#8217;ombre et allons vers la lumière.</p>
<p><strong>Ressources</strong><br />
- <a href="http://initializr.com/">Initializr.com</a> : pour construire en 2mn un projet HTML5 propre, qui fonctionne aussi avec les navigateurs anciens.<br />
- <a href="http://www.colorzilla.com/gradient-editor/">Ultimate CSS Gradient Editor</a> : trouver des gradients propres<br />
- <a href="http://www.modernizr.com/">Modernizr</a> : librairie JS pour simplifier la gestion des tags HTML5<br />
- <a href="http://code.google.com/p/html5shim/">HTML5shim</a> permet d&#8217;ajouter des fonctions HTML5 à des versions antérieurs à IE9 si vous devez coder pour les navigateurs Microsoft.<br />
- <a href="https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills">Liste de librairies JS et CSS</a> pour ajouter tout un ensemble de fonctionnalités comme les formulaires, les styles ou le local-storage.<br />
- <a href="http://code.google.com/p/webforms2/">WebForms2</a> est une implémentation cross-browser qui descend jusqu&#8217;à IE6 de la spécification <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/">WHATWG Web Forms 2.0</a>. Cette spécification donne une vision de ce qui sera disponible au delà d&#8217;HTML5.<br />
- <a href="http://peter.sh/experiments/vendor-prefixed-css-property-overview/">Vendor prefixed CSS properties</a> liste à jour de l&#8217;ensemble des propriétés CSS3 implémentés par moteur de rendu. Gecko pour Firefox, Webkit pour Chrome/Safari, Presto pour Opera et Trident pour Microsoft.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2011/02/13/css3-html5/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Devops expliqué simplement</title>
		<link>http://www.touilleur-express.fr/2010/12/04/prod/</link>
		<comments>http://www.touilleur-express.fr/2010/12/04/prod/#comments</comments>
		<pubDate>Sat, 04 Dec 2010 14:30:13 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Perso]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[devops]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=4706</guid>
		<description><![CDATA[Une histoire fictive de la réalité
J&#8217;imagine votre joie en ce beau vendredi. Il est 17h50 et vous venez juste de réussir à construire votre projet avec Maven. Après 3 jours d&#8217;efforts surhumains, vous avez enfin terminé le packaging de votre logiciel/projet/truc-du-moment. Comme le projet était à mettre en prod il y a 2 jours, vous êtes largement dans les temps avec seulement 48 heures de retard. Donc tout va bien, vous êtes prêt à passer en prod.
Et il est maintenant temps d&#8217;appeler le gars de l&#8217;exploite.
Pour ceux qui ne connaissent ...]]></description>
			<content:encoded><![CDATA[<h3>Une histoire fictive de la réalité</h3>
<div id="attachment_4712" class="wp-caption alignright" style="width: 241px"><img src="http://www.touilleur-express.fr/wp-content/2322020454_c7549c0330_z.jpg" alt="Chabal" title="Chabaaal" width="231" height="242" class="size-full wp-image-4712" /><p class="wp-caption-text">Credit photo Steve Chilton http://www.flickr.com/photos/steve_chilton/2322020454/sizes/z/in/photostream/</p></div>
<p>J&#8217;imagine votre joie en ce beau vendredi. Il est 17h50 et vous venez juste de réussir à construire votre projet avec Maven. Après 3 jours d&#8217;efforts surhumains, vous avez enfin terminé le packaging de votre logiciel/projet/truc-du-moment. Comme le projet était à mettre en prod il y a 2 jours, vous êtes largement dans les temps avec seulement 48 heures de retard. Donc tout va bien, vous êtes prêt à passer en prod.</p>
<p>Et il est maintenant temps d&#8217;appeler <em>le gars de l&#8217;exploite</em>.</p>
<p>Pour ceux qui ne connaissent pas <em>le gars de l&#8217;exploite</em>, c&#8217;est ce bonhomme qui ne vit que la nuit, qui passe son temps à installer, configurer, patcher, arrêter, relancer et surtout installer les logiciels que vous développpez. </p>
<p>En règle générale vous n&#8217;avez que de brefs échanges au téléphone. Souvent des Onomatopées pour sa part comme &laquo;&nbsp;Mouais, mmmfff, grrr, cool&nbsp;&raquo;. Alors que de votre côté vous avez vos phrases toutes faites auxquelles il ne réagit plus comme ma favorite &laquo;&nbsp;<em>Mais sur mon poste ça marchaaaiiit</em>&nbsp;&raquo; ou &laquo;&nbsp;<em>Ben l&#8217;intégration continue elle est verte, alors ça doit marchéeeee</em>&nbsp;&raquo; et encore une autre : &laquo;&nbsp;<em>Je comprends, chez moi ça marche avec H2 in-memory et Tomcat. Pourquoi cela ne marcherait-il pas avec Websfear et Ourahcleu ?</em>&nbsp;&raquo;</p>
<p>Nous avons donc à notre gauche un expert système aussi sexy que Sébastien Chabal et à notre droite un développeur protégé par son bouclier &laquo;&nbsp;Maven+&nbsp;&raquo;. Et vous, chef de projet/architecte/femme au foyer devez faire cohabiter ces 2 espèces&#8230; </p>
<p>Le développeur a une fâcheuse tendance à ne pas penser qu&#8217;un jour, peut-être, son logiciel sera mis en production. L&#8217;exploitant système d&#8217;autre part a une tendance ennuyante à demander des conditions de prods, quant bien même l&#8217;installation n&#8217;est prévue que pour 2 testeurs. </p>
<p>Bref cela donne beaucoup d&#8217;échanges intéressants. </p>
<h3>Heureusement que l&#8217;on fait pas ça hein&#8230;</h3>
<p>Il y a heureusement un mouvement intéressant qui prend de l&#8217;ampleur, et dont nous aurons l&#8217;occasion de parler dans les mois qui viennent : <strong>DevOps</strong></p>
<p>J&#8217;ai entendu parler de <strong>DevOps</strong> à Devoxx 2010 pour la première fois. Il s&#8217;agit d&#8217;un ensemble de techniques collaboratives pour que les développeurs, les personnes de l&#8217;exploitation système et les équipes de tests travaillent plus efficacement ensembles. C&#8217;est aussi un mot pour regrouper plusieurs idées brillantes dont la première serait &laquo;&nbsp;<em>et si on arretait de travailler comme des bourrins</em>&laquo;&nbsp;. </p>
<p>Il y a le commerce équitable où tu respectes le gars qui fait du café en Colombie. </p>
<p>Il y a Devops pour envisager de manière différente la relation <em>développeur-testeur-gars de l&#8217;exploitation</em>.</p>
<p>Basé sur des valeurs Agiles, DevOps propose de livrer souvent, simplement et par petits incréments votre logiciel. Cela semble logique dans la suite du mouvement amorcé dans le développement, avec l&#8217;essor des méthodes Agiles comme Scrum par exemple. </p>
<p>Le petit plus du mouvement DevOps : il est initié par les experts systèmes des grands du Web. Il est plus centré sur la mise en production et l&#8217;assurance qualité. Ne cherchez pas de certification DevOps, nous parlons bien d&#8217;une manière de travailler différemment. </p>
<p>De la même manière que vous avez de l&#8217;intégration continue, vous pourriez avoir aussi une mise en production continue. L&#8217;essor du Cloud et des systèmes de virtualisation expliquent aussi l&#8217;apparition de ce mouvement. Il est désormais possible non pas de mettre en production un logiciel, mais carrément une image virtuelle complète et validée. Le temps de l&#8217;installation d&#8217;une application sur un serveur d&#8217;app est peut-être même terminé. Il sera demain plus simple d&#8217;installer et de démarrer une machine virtuelle, plutôt que de perdre du temps avec un serveur d&#8217;application Java.</p>
<h3>So-li-da-ri-té</h3>
<p>DevOps c&#8217;est aussi un ensemble de pratiques afin de favoriser le travail entre les développeurs, les testeurs et les équipes systèmes. Cela va à l&#8217;encontre des silos érigés dans les DSI ces dernières années. Il est plus efficace de co-localiser un développeur, une personne de la production et un ingénieur qualité, que de les disperser dans 3 sites différentes. Retirez aussi les micro-managers qui ne servent à rien et laissez-les travailler. Bon, du coup la DSI ne sert plus à grand chose, mais qui en doute encore hein ? </p>
<h3>Automatisation</h3>
<p>Pour être efficace, une démarche &laquo;&nbsp;DevOps&nbsp;&raquo; doit aussi viser l&#8217;hyper industrialisation des processus. Oh que j&#8217;aime cette phrase de consultant&#8230;. Attendez j&#8217;en essaye une autre&#8230; <em>l&#8217;automatisation des tâches répétitives permet de réduire les facteurs de coûts et d&#8217;améliorer la livraison des incréments de manière itérative</em>&#8230;. ça se voit que j&#8217;ai vendu du SOA.</p>
<p>Blague à part, vous l&#8217;aurez compris, pour être efficace il faut travailler avec les exploitants systèmes et les équipes qualités. En France les équipes qualités ne sont pas valorisées. Elles sont même absentes de certains projets. Lorsque j&#8217;étais chez Reuters, c&#8217;était le chef QA qui décidait ou non de la sortie de notre logiciel. Imaginez un instant que ce ne soit pas le chef de projet&#8230; Respirez&#8230; pensez-y encore&#8230;. voilà&#8230; vous pensez DevOps. </p>
<h3>Développer c&#8217;est aussi mettre en production</h3>
<p>Etre développeur dans une approche DevOps, c&#8217;est aller voir les personnes de l&#8217;exploitation. C&#8217;est passer une journée avec eux afin de comprendre les problèmes rencontrés. Ce sera très enrichissant et cela vous permettra de voir ce que font réellement les clients. </p>
<p>Chaque développeur devrait être capable de construire l&#8217;ensemble du projet puis de l&#8217;installer. Il n&#8217;est pas normal qu&#8217;une seule personne soit en charge de packager et livrer l&#8217;application. Nous devons être tous responsable du logiciel. </p>
<p>Les développeurs sont en général assez mauvais sous Unix. Allez je vais essayer de voir si tu sais répondre à cette série de questions simples : </p>
<blockquote><p>
- Que se passe-t-il si je fais un kill -3 sur le pid d&#8217;un process Java sous Unix ?<br />
- A quoi sert la commande jps livrée dans le JDK 6 ?<br />
- Comment remplacer le caractère ^M avec vi ?
</p></blockquote>
<p>Si tu réponds à ces 3 questions, tu es un maître. Si tu connais au moins l&#8217;une des réponses, c&#8217;est pas mal. Sinon&#8230; et bien sinon c&#8217;est pas grave, tu es comme la majorité des personnes. </p>
<h3>L&#8217;effet silo</h3>
<p>Soyons réaliste : beaucoup de projets informatiques actuels ne sont pas adaptés aux idées du mouvement DevOps. Nous avons mis en place des départements isolés. L&#8217;équipe métier, l&#8217;équipe technique, l&#8217;assurance qualité et l&#8217;équipe d&#8217;exploitation. Chaque silo est bien séparé. La communication s&#8217;effectue par l&#8217;intermédiaire de &laquo;&nbsp;réunions&nbsp;&raquo; avec des &laquo;&nbsp;phones-calls&nbsp;&raquo;, alors que parfois les équipes sont dans le même bâtiment&#8230; Très rapidement cela conduit à une polarisation des relations entre les équipes. C&#8217;est &laquo;&nbsp;eux&nbsp;&raquo; contre &laquo;&nbsp;nous&nbsp;&raquo;&#8230;<br />
Alors qu&#8217;un chef courageux prenne ces équipes et les mettent dans un même espace. Du métier à l&#8217;exploitation. Voyons ce que cela donne, ayons le courage d&#8217;aller à l&#8217;envers de ce qui coûte de plus en plus cher au fil du temps&#8230; C&#8217;est ça DevOps.</p>
<p>Je pense aussi qu&#8217;il y a un effet génération. Les 40 ans et plus ont une vision des années 90, sans Twitter et sans Facebook. Alors que la tranche 25-40 ans ne comprend pas pourquoi la mise en production d&#8217;un projet doit rester héroïque. Si les choses changent, c&#8217;est aussi parce que nous en avons marre de devoir envoyer 3 emails pour relancer &laquo;&nbsp;dans l&#8217;urgence&nbsp;&raquo; un serveur&#8230; </p>
<h3>Conclusion</h3>
<p>DevOps est un nouveau buzz word appuyé cependant par des acteurs du Webs. Une fois encore, ils viennent bousculer le monde de l&#8217;entreprise avec une approche Agile des relations développeurs/assurance qualité/production. C&#8217;est aussi une nouvelle fracture entre l&#8217;approche classique, majoritaire dans les DSI aujourd&#8217;hui, et l&#8217;approche Web mise en oeuvre par les petites structures. </p>
<p>C&#8217;est un mouvement lancé par des personnes qui pensent qu&#8217;il faut construire des logiciels avec de bonnes équipes, de bonnes relations inter-équipes et de bonnes technologies. Le plus important c&#8217;est le dernier mètre, les derniers instants avant que votre plateforme soit lancé. C&#8217;est aussi une vision où par exemple la mise en production n&#8217;a pas lieu dans 20 mois mais dès le premier mois. C&#8217;est une vision où le développeur prend conscience des problèmes d&#8217;exploitations. Plutôt que de s&#8217;auto-congratuler sur une approche DAO/Service, il pensera à faire une couche de logging efficace et pratique à utiliser. </p>
<p>Je pense que nous aurons l&#8217;occasion d&#8217;en parler dans les mois qui viennent. Surveillez les blogs des voisins comme <a href="http://blog.xebia.fr">Xebia</a>, <a href="http://blog.octo.com">Octo</a> ou <a href="http://blog.ippon.fr/">Ippon Technologies</a>, je pense que le sujet est intéressant à débattre.</p>
<h3>Ressources</h3>
<p><a href="http://www.jedi.be/blog/">Blog de Patrick Debois</a> (cité par Sébastien ci-dessous dans les commentaires)<br />
Très bonne présentation sur DevOps sur son son blog. Le slide sur Kanban avec l&#8217;abattage à la chaîne est énorme&#8230; Et la métaphore du barbecue, j&#8217;ai adoré. A invité d&#8217;urgence à Agile France. Vous trouverez pleins d&#8217;astuces, comme l&#8217;appel de l&#8217;API Rest de Nexus via des scripts. Cela vous permet de récupérer la dernière SNAPSHOT de votre projet pour l&#8217;installer (dédicace à Emmanuel Servent de Xebia, il comprendra). </p>
<p>Blog de Stephen Nelson-Smith, <a href="http://agilesysadmin.net/">Agile Sysadmin</a>. Beaucoup d&#8217;articles intéressants comme la notion de <a href="http://agilesysadmin.net/imaging-or-configuration-management">golden image</a></p>
<p><a href="http://continuousdelivery.com/">Continuous Delivery</a>, le blog de Jez Humble et Dave Farley est intéressant. Jez est product Manager de <a href="http://www.thoughtworks-studios.com/go-agile-release-management">Go</a>, un logiciel de ThoughtWorks Studios. Go permet de gérer la compilation, l&#8217;installation et le lancement de campagne de tests pour différents environnements (UAT, Prod, Dev) très simplement. C&#8217;est un outil d&#8217;industrialisation intéressant avec une édition community.</p>
<p>Il y a un même un <a href="http://twitter.com/DEVOPS_BORAT">DEVOPS_Borat</a> sur Twitter qui balance pas mal de trucs marrants.</p>
<h3>Réponses aux questions</h3>
<p>1) Sous Unix pour générer une Stack Trace d&#8217;un processus Java vous pouvez envoyer le signal QUIT à un processus Java. Par exemple kill -3
<pid>.<br />
Sous Windows il suffit de sélectionner la fenêtre où tourne votre application et de faire Ctrl-Break.<br />
2) La commande jps que peu de monde connaît, alors qu&#8217;elle fait partie de Java 6, permet de lister les processus Javas qui tournent sur votre machine.<br />
3) Pour remplacer le terminateur de fin de ligne Windows lorsque vous êtes sous Unix, avec vi voici la suite de touche à jouer : <code>[Esc] : % s / [Ctrl-V] [Ctrl-M] / / g </code>. Il y a aussi sinon la commande dos2unix et des milliers d&#8217;autres façons de faire</pid>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2010/12/04/prod/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>L&#039;eXpress-Board bascule sur la plateforme PlayApps.net</title>
		<link>http://www.touilleur-express.fr/2010/06/24/lexpress-board-bascule-sur-la-plateforme-playapps-net/</link>
		<comments>http://www.touilleur-express.fr/2010/06/24/lexpress-board-bascule-sur-la-plateforme-playapps-net/#comments</comments>
		<pubDate>Thu, 24 Jun 2010 07:38:41 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Perso]]></category>
		<category><![CDATA[Web 2.0]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=3870</guid>
		<description><![CDATA[
Play! Framework est un framework Java dont je vous parle depuis quelques temps. Comme vous le savez, j&#8217;ai lancé un site d&#8217;offres d&#8217;emplois pour passionnés, l&#8217;eXpress-Board.fr, réalisé justement avec ce framework Web il y a 2 mois. Le site marche bien. J&#8217;ai reçu hier soir le témoignage de Fabrice, qui a signé grâce à l&#8217;eXpress-Board.fr
Zenexity est une société Française dont le directeur technique, Guillaume Bort, est aussi le fondateur du framework Play! Zenexity a lancé un nouveau service en bêta il y a quelques jours : PlayApps.net. Il s&#8217;agit d&#8217;une ...]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.touilleur-express.fr/wp-content/playappsnet.jpg"><img src="http://www.touilleur-express.fr/wp-content/playappsnet-300x142.jpg" alt="" title="playappsnet" width="300" height="142" class="alignright size-medium wp-image-3884" /></a><br />
<a href="www.playframework.org/" target="new1">Play! Framework</a> est un framework Java dont je vous parle depuis quelques temps. Comme vous le savez, j&#8217;ai lancé un site d&#8217;offres d&#8217;emplois pour passionnés, <a href="http://www.express-board.fr/">l&#8217;eXpress-Board.fr</a>, réalisé justement avec ce framework Web il y a 2 mois. Le site marche bien. J&#8217;ai reçu hier soir le témoignage de Fabrice, qui a signé grâce à l&#8217;eXpress-Board.fr</p>
<p><a href="http://www.zenexity.fr/" target="new2">Zenexity</a> est une société Française dont le directeur technique, Guillaume Bort, est aussi le fondateur du framework Play! Zenexity a lancé un nouveau service en bêta il y a quelques jours : <a href="http://www.playapps.net" target="new2">PlayApps.net</a>. Il s&#8217;agit d&#8217;une solution PaaS (Platform as a service) construite en partenariat avec la société <a href="http://www.gandi.net/" target="new2">Gandi</a>.</p>
<p>Ce service permet :<br />
- d&#8217;installer une application Play! Framework en quelques minutes<br />
- de configurer la quantité de CPU et de mémoire selon l&#8217;usage de son application<br />
- de gérer les sauvegardes de la base comme de l&#8217;application facilement<br />
- d&#8217;effectuer l&#8217;installation d&#8217;une nouvelle version en sauvegardant l&#8217;ancienne<br />
- d&#8217;afficher des statistiques sur l&#8217;application (trafic, mémoire, réseau)<br />
- de suivre les performances dans le temps<br />
- de gérer les bases de données simplement<br />
- de ne pas perdre de temps avec la configuration système<br />
- de se connecter via SSH si nécessaire pour pouvoir travailler avec l&#8217;application directement</p>
<p><img alt="" src="https://www.playapps.net/public/images/screenshot.png" title="PlayApps.net" class="alignnone" width="400" height="246" /></p>
<p>L&#8217;installation est relativement simple. Pour tout vous dire, j&#8217;ai migré en 30 minutes mon application existante ainsi que la base de données vers le nouveau serveur, sachant que le plus long a été de me souvenir des commandes MySQL pour exporter/importer la base <img src='http://www.touilleur-express.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> . Je ne m&#8217;attendais pas à ce que cela soit aussi simple et rapide.</p>
<p>Playapps.net est en bêta, mais il y a déjà 71 applications installées sur la plate-forme. Son accès se fait sur invitation seulement pour l&#8217;instant. Si ce projet vous intéresse, vous pouvez demander un accès sur le site PlayApps.net.</p>
<p>A terme, je gagne un système qui me fait gagner du temps, offre un service de qualité à mes clients, et me permet de me concentrer sur le coeur de ma solution, sans devoir bidouiller des serveurs. Par rapport à Google App Engine, sur lequel il est possible de faire tourner Play!, je préfère finalement payer quelques euros par mois pour avoir une vraie plateforme, sur laquelle je peux travailler.<br />
La liste de diffusion de Play! Framework montre que beaucoup de personnes rencontrent des difficultés liées aux limitations de la JVM sur la plateforme Google.</p>
<p>Côté performance, des tests conduits par Guillaume sur l&#8217;express-board avec Apache Bench montre que l&#8217;application tourne sans soucis à 80 visiteurs par seconde, et que la page d&#8217;accueil a une latence moyenne de 57ms avec 20 threads seulement. Le tout avec une JVM qui n&#8217;utilise que 64Mo. Oui je sais, ça fait bizarre lorsque l&#8217;on pense qu&#8217;une application Java peut fonctionner avec si peu de mémoire&#8230; C&#8217;est 4 fois moins de mémoire que mon iPhone. Quand on utilise pas 10 000 frameworks, croyez-moi ça tourne.</p>
<p>Ce nouveau service ouvre la voie au développement Web léger en Java, ainsi qu&#8217;à l&#8217;hébergement. Il sera intéressant de revenir sur les différentes architectures Webs et de montrer à quoi peut répondre Play! Framework. Après les plateformes L.A.M.P (Linux Apache MySql Php) nous allons passer aux plateformes <strong>LiPlayFraMy </strong>(Linux/Play! Framework/MySql) pour un prix similaire, mais sur une JVM.</p>
<p>Moi je dis, ce que font ces gars là est à suivre.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2010/06/24/lexpress-board-bascule-sur-la-plateforme-playapps-net/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Spring Faces+WebFlow+Java classique comparé à Play! Framework</title>
		<link>http://www.touilleur-express.fr/2010/05/13/spring-faceswebflowjava-classique-compare-a-play-framework/</link>
		<comments>http://www.touilleur-express.fr/2010/05/13/spring-faceswebflowjava-classique-compare-a-play-framework/#comments</comments>
		<pubDate>Thu, 13 May 2010 13:23:31 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[jsf]]></category>
		<category><![CDATA[play]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=3716</guid>
		<description><![CDATA[
Dans cet article, je vous propose de regarder 2 approches différentes pour résoudre un même problème : construire une application web sécurisée, moderne et Ajaxisé. Je vais vous présenter les différences de conceptions sur la partie Java essentiellement. Cet article ne sera pas une introduction à l&#8217;une ou l&#8217;autre des technologies. Je souhaite vous éclairer sur quelques concepts différents dans Play! par rapport à l&#8217;approche classique.
Pour comparer 2 choses, il faut un référentiel comparable. JBoss Seam propose sur cette page plusieurs versions d&#8217;une application de réservation de chambres d&#8217;hôtels. La ...]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.touilleur-express.fr/wp-content/uploads/2010/05/springwebflow-versus-Play1.jpg"><img src="http://www.touilleur-express.fr/wp-content/uploads/2010/05/springwebflow-versus-Play1.jpg" alt="" title="springwebflow-versus-Play" width="500" height="500" class="alignnone size-full wp-image-3719" /></a><br />
Dans cet article, je vous propose de regarder 2 approches différentes pour résoudre un même problème : construire une application web sécurisée, moderne et Ajaxisé. Je vais vous présenter les différences de conceptions sur la partie Java essentiellement. Cet article ne sera pas une introduction à l&#8217;une ou l&#8217;autre des technologies. Je souhaite vous éclairer sur quelques concepts différents dans Play! par rapport à l&#8217;approche classique.</p>
<p>Pour comparer 2 choses, il faut un référentiel comparable. JBoss Seam propose <a href="http://demo.flamingo.exadel.com/booking/">sur cette page</a> plusieurs versions d&#8217;une application de réservation de chambres d&#8217;hôtels. La version originale basée sur JSF 1.2 n&#8217;est plus toute jeune mais fonctionne très bien. <a href="http://www.touilleur-express.fr/2006/12/12/jboss-seam-linteret-de-la-conversation/">J&#8217;en avais parlé sur le Touilleur Express il y a 3 ans et demie&#8230;</a> Quand je me dis que cela fait plus de 3 ans que j&#8217;ai vu cela, et qu&#8217;à l&#8217;époque je vous avais convaincu de regarder JSF par rapport à Struts, je me dis qu&#8217;il faut que je réussisse dans cet article à vous montrer la nouvelle approche, celle de 2010.</p>
<p>Nous allons prendre Spring Faces et Spring Webflow, une approche que je trouve très intéressante et qui permet de réaliser des applications Webs avec les technologies de Spring.</p>
<h2>I. Le match : télécharger, installer et lancer la démo</h2>
<p>Voyons tout d&#8217;abord comment télécharger, compiler et lancer la démonstration avec chacun des environnements. Je souhaite évaluer la rapidité de prise en main. Je souhaite que le projet soit configuré dans IDEA IntelliJ ou Eclipse pour travailler, et je vais évaluer la facilité à monter chacun des environnements. Je regarderai aussi le temps de démarrage des serveurs Webs testés.</p>
<h3>I.1) Préparation de l&#8217;espace de démo pour Spring</h3>
<p>SpringSource propose &laquo;&nbsp;<a href="http://richweb.springframework.org/swf-booking-faces/spring/intro">Spring Travel</a>&laquo;&nbsp;, une version basée sur <a href="http://www.springsource.org/faces">Spring Faces</a>, Spring WebFlow 2.0.9 et une librairie JSF avancée pour le rendu. J&#8217;ai regardé précisément le code &laquo;&nbsp;booking-faces&nbsp;&raquo; dans le répertoire projects/spring-webflow-samples pour écrire cet article. Si vous souhaitez tester par vous même :<br />
- téléchargez <a href="http://www.springsource.org/download#webflow">la version Spring WebFlow 2.0.9</a> sur le site de SpringSource<br />
- décompressez l&#8217;archive<br />
- ouvrez le répertoire projects/spring-webflow-samples/booking-faces<br />
- tapez <em>mvn eclipse:eclipse</em> pour créer un nouveau projet<br />
- ou sinon tapez <em>mvn idea:idea</em> pour créer un projet IDEA IntelliJ<br />
- ou enfin, si vous avez IDEA IntelliJ, celui-ci importe sans problème le pom.xml et vous prépare un projet configuré&#8230; Pas besoins de plugins.</p>
<p><strong>Faire tourner la démo en local</strong><br />
Pour faire fonctionner la version Spring en local, éditez le pom.xml et ajoutez Jetty dans la balise build/plugins comme ici:</p>
<pre class="brush:xml">   ...
   &lt;build&gt;
        &lt;finalName&gt;swf-booking-faces&lt;/finalName&gt;
        &lt;plugins&gt;
        	...

            &lt;plugin&gt;
                &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
                &lt;artifactId&gt;maven-jetty-plugin&lt;/artifactId&gt;
            &lt;/plugin&gt;

        &lt;/plugins&gt;
    &lt;/build&gt;
    &lt;properties&gt;
        &lt;spring.version&gt;2.5.6.SEC01&lt;/spring.version&gt;
        &lt;jsf.version&gt;1.2.0.09&lt;/jsf.version&gt;
    &lt;/properties&gt;
&lt;/project&gt;</pre>
<p>Vous pourrez alors lancer la démo avec mvn jetty:run dans le répertoire de la démo de Spring.</p>
<h3>I.2) Configuration de la version Play! Framework</h3>
<p>Play! Framework se télécharge <a href="http://www.playframework.org/">sur le site de Play!</a>. Voici comment suivre mes explications pour la deuxième partie :<br />
- décompressez le zip dans un répertoire C:\Soft\play par exemple<br />
- ajoutez C:\Soft\play à votre PATH</p>
<p>Pour lancer l&#8217;application :<br />
- allez dans le répertoire C:\Soft\play\samples-and-tests\booking<br />
- tapez &laquo;&nbsp;<em>play run</em>&nbsp;&raquo; et connectez-vous sur http://localhost:9000/</p>
<p>Pour travailler avec Eclipse ou IDEA IntelliJ<br />
- allez dans le répertoire C:\Soft\play\samples-and-tests\booking<br />
- tapez <em>play eclipsify</em> pour créer un projet Eclipse<br />
- OU tapez <em>play idealize</em> pour créer un module IDEA IntelliJ<br />
c&#8217;est tout !</p>
<p><strong>I.3) Résultat du match :</strong></p>
<p>Facilité d&#8217;installation : match nul<br />
Qualité de la doc sur le site : match nul<br />
Création du projet dans IDEA IntelliJ : match nul<br />
Serveur : la démo Spring n&#8217;embarque pas de serveur, il a fallut ajouter Jetty pour tester alors que Play! Framework dispose de son propre serveur<br />
Temps de démarrage : avantage Play! qui démarre très rapidement en 2 secondes.</p>
<p><strong>Vainqueur : Play! Framework d&#8217;une courte longueur </strong></p>
<h2>II. Le modèle</h2>
<h3>II.1) Spécification fonctionnelle</h3>
<p>L&#8217;objectif de l&#8217;application est de réaliser un site de réservation d&#8217;hôtel. Pour cela, l&#8217;utilisateur peut chercher un hôtel, puis ensuite créer une réservation (Booking en Anglais).</p>
<p>L&#8217;utilisateur doit s&#8217;authentifier avant de pouvoir terminer sa réservation. Une entité User permet donc de gérer la sécurité facilement avec un moteur de persistence.</p>
<p>Nous utiliserons JPA et une base en mémoire pour les 2 versions. L&#8217;essentiel est que Play! Framework comme la version Spring, utilisent JPA. Donc les entités sont presque identiques.</p>
<h3>II.2) Une entité simple : l&#8217;Hotel</h3>
<p>Prenons tout d&#8217;abord l&#8217;entité Hotel qui représente un hôtel. Voici la version de la démo Spring Webflow/JSF :</p>
<pre class="brush:java">
package org.springframework.webflow.samples.booking;

import java.io.Serializable;
import java.math.BigDecimal;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

/**
 * Version Spring Web Flow. La sérialisation est nécessaire pour JSF.
 */
@Entity
public class Hotel implements Serializable {
    private static final long serialVersionUID = 4011346719502656269L;
    private Long id;
    private String name;
    private String address;
    private String city;
    private String state;
    private String zip;
    private String country;
    private BigDecimal price;

    @Id
    @GeneratedValue
    public Long getId() {
	return id;
    }

    public void setId(Long id) {
	this.id = id;
    }

    public String getName() {
	return name;
    }

    public void setName(String name) {
	this.name = name;
    }

    public String getAddress() {
	return address;
    }

    public void setAddress(String address) {
	this.address = address;
    }

    public String getCity() {
	return city;
    }

    public void setCity(String city) {
	this.city = city;
    }

    public String getZip() {
	return zip;
    }

    public void setZip(String zip) {
	this.zip = zip;
    }

    public String getState() {
	return state;
    }

    public void setState(String state) {
	this.state = state;
    }

    public String getCountry() {
	return country;
    }

    public void setCountry(String country) {
	this.country = country;
    }

    @Column(precision = 6, scale = 2)
    public BigDecimal getPrice() {
	return price;
    }

    public void setPrice(BigDecimal price) {
	this.price = price;
    }

    public Booking createBooking(User user) {
	return new Booking(this, user);
    }

    @Override
    public String toString() {
	return "Hotel(" + name + "," + address + "," + city + "," + zip + ")";
    }
}</pre>
<p>Voici la version Play! Framework :</p>
<pre class="brush:java">package models;

import play.db.jpa.*;
import play.data.validation.*;
import javax.persistence.*;
import java.math.*;

/**
 * Version Play! Framework
 */
@Entity
public class Hotel extends play.db.jpa.Model {

    @Required
    @MaxSize(50)
    public String name;

    @MaxSize(100)
    public String address;

    @Required
    @MaxSize(40)
    public String city;

    @Required
    @MaxSize(6)
    @MinSize(2)
    public String state;

    @Required
    @MaxSize(6)
    @MinSize(5)
    public String zip;

    @Required
    @MaxSize(40)
    @MinSize(2)
    public String country;

    @Column(precision=6, scale=2)
    public BigDecimal price;

    public String toString() {
        return "Hotel(" + name + "," + address + "," + city + "," + zip + ")";
    }

}</pre>
<p>Les différences entre les 2 versions :<br />
- Play! Framework étend la classe &laquo;&nbsp;play.db.jpa.Model&nbsp;&raquo; qui fournit le support JPA.<br />
- Les attributs sont publics dans la version Play!, ce qui logiquement évite des getters/setters comme dans la version Spring classique. Moi depuis que l&#8217;encapsulation n&#8217;est plus à la mode en soirée, j&#8217;ai arrêté.<br />
- La syntaxe JPA est un peu plus poussée dans la version Play! Nous verrons si cela permet d&#8217;améliorer l&#8217;expérience utilisateur dans l&#8217;interface graphique ou non.</p>
<p><del datetime="2010-05-14T05:57:30+00:00">Le bean de Spring n&#8217;étend pas une classe de base contrairement à Play!, ce qui permet d&#8217;étendre éventuellement Hotel. Mais est-ce que c&#8217;est quelque chose que nous faisons pour des Entities ? Je me pose la question. Perso je décourage l&#8217;héritage avec JPA. FBI = Fausse bonne idée.</del></p>
<p>[Update] Avec Play! Framework vous pouvez étendre la classe Model afin de simplifier la gestion JPA de l&#8217;entité. Cela fait partie des bonnes pratiques comme expliqué par Nicolas Leroux dans les commentaires. Pour en savoir plus, regardez <a href="http://www.playframework.org/documentation/1.0.2.1/jpa">la doc de la partie JPA de Play!<br />
</a></p>
<p>Play! gère l&#8217;identité dans la class Model avec un attribut Id de type Long par défaut. Si vous souhaitez gérer votre propre id, vous <del datetime="2010-05-14T05:57:30+00:00">devez</del> <em>pouvez</em> par exemple étendre la class <strong>JPASupport</strong> de Play!, puis simplement définir un attribut @Id comme d&#8217;habitude avec JPA. C&#8217;est tout à fait possible.</p>
<p>On voit que dans la version Spring, il y a une méthode &laquo;&nbsp;createBooking&nbsp;&raquo; qui permet de créer au niveau de l&#8217;Hotel une réservation.</p>
<p>Il n&#8217;y a donc pas de vainqueur dans cette partie pour moi. Les 2 approches se valent je pense. Je préfère la simplicité de Play! Framework qui remet en question quelques habitudes du monde Java, comme l&#8217;encapsulation systématique.</p>
<p>Par contre je n&#8217;aime pas trop l&#8217;approche de placer les annotations JPA sur les getters/setters plutôt que sur les attributs de la classe dans la version Spring. On perd beaucoup en lisibilité.</p>
<h3>II.3) Une entité plus compliquée : Booking</h3>
<p>Une réservation est l&#8217;association d&#8217;un User, d&#8217;un Hotel, d&#8217;une date d&#8217;arrivée et de départ et de différents autres attributs. Je vais faire exprès de vous donner la version complète des 2 frameworks, afin de vous montrer les différences flagrantes entre les 2 approches.</p>
<p>La version de la démo Spring WebFlow telle qu&#8217;elle est livrée, sans trucages, sans effets spéciaux :</p>
<pre class="brush:java">package org.springframework.webflow.samples.booking;

import java.io.Serializable;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;

import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.binding.validation.ValidationContext;

/**
 * A Hotel Booking made by a User.
 */
@Entity
public class Booking implements Serializable {

    private static final long serialVersionUID = 1171567558348174963L;

    private Long id;
    private User user;
    private Hotel hotel;
    private Date checkinDate;
    private Date checkoutDate;
    private String creditCard;
    private String creditCardName;
    private int creditCardExpiryMonth;
    private int creditCardExpiryYear;
    private boolean smoking;
    private int beds;
    private Amenity amenities;   // Absent de la version Play!

    public Booking() {
    }

    public Booking(Hotel hotel, User user) {
        this.hotel = hotel;
        this.user = user;
        Calendar calendar = Calendar.getInstance();
        setCheckinDate(calendar.getTime());
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        setCheckoutDate(calendar.getTime());
    }

    @Transient
    public BigDecimal getTotal() {
        return hotel.getPrice().multiply(new BigDecimal(getNights()));
    }

    @Transient
    public int getNights() {
        if (checkinDate == null || checkoutDate == null) {
            return 0;
        } else {
            return (int) (checkoutDate.getTime() - checkinDate.getTime()) / 1000 / 60 / 60 / 24;
        }
    }

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Basic
    @Temporal(TemporalType.DATE)
    public Date getCheckinDate() {
        return checkinDate;
    }

    public void setCheckinDate(Date datetime) {
        this.checkinDate = datetime;
    }

    @ManyToOne
    public Hotel getHotel() {
        return hotel;
    }

    public void setHotel(Hotel hotel) {
        this.hotel = hotel;
    }

    @ManyToOne
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Basic
    @Temporal(TemporalType.DATE)
    public Date getCheckoutDate() {
        return checkoutDate;
    }

    public void setCheckoutDate(Date checkoutDate) {
        this.checkoutDate = checkoutDate;
    }

    public String getCreditCard() {
        return creditCard;
    }

    public void setCreditCard(String creditCard) {
        this.creditCard = creditCard;
    }

    @Transient
    public String getDescription() {
        DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
        return hotel == null ? null : hotel.getName() + ", " + df.format(getCheckinDate()) + " to "
                + df.format(getCheckoutDate());
    }

    public boolean isSmoking() {
        return smoking;
    }

    public void setSmoking(boolean smoking) {
        this.smoking = smoking;
    }

    public int getBeds() {
        return beds;
    }

    public void setBeds(int beds) {
        this.beds = beds;
    }

    public String getCreditCardName() {
        return creditCardName;
    }

    public void setCreditCardName(String creditCardName) {
        this.creditCardName = creditCardName;
    }

    public int getCreditCardExpiryMonth() {
        return creditCardExpiryMonth;
    }

    public void setCreditCardExpiryMonth(int creditCardExpiryMonth) {
        this.creditCardExpiryMonth = creditCardExpiryMonth;
    }

    public int getCreditCardExpiryYear() {
        return creditCardExpiryYear;
    }

    public void setCreditCardExpiryYear(int creditCardExpiryYear) {
        this.creditCardExpiryYear = creditCardExpiryYear;
    }

    @Transient
    public Amenity getAmenities() {
        return amenities;
    }

    public void setAmenities(Amenity amenities) {
        this.amenities = amenities;
    }

    public void validateEnterBookingDetails(ValidationContext context) {
        MessageContext messages = context.getMessageContext();
        if (checkinDate.before(today())) {
            messages.addMessage(new MessageBuilder().error().source("checkinDate").code(
                    "booking.checkinDate.beforeToday").build());
        } else if (checkoutDate.before(checkinDate)) {
            messages.addMessage(new MessageBuilder().error().source("checkoutDate").code(
                    "booking.checkoutDate.beforeCheckinDate").build());
        }
    }

    private Date today() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        return calendar.getTime();
    }

    @Override
    public String toString() {
        return "Booking(" + user + "," + hotel + ")";
    }

}</pre>
<p>Voyons la version Play! Framework de l&#8217;entité Booking maintenant :</p>
<pre class="brush:java">package models;

import play.db.jpa.*;
import play.data.validation.*;
import javax.persistence.*;
import java.util.*;
import java.text.*;
import java.math.*;

@Entity
public class Booking extends Model {

    @Required
    @ManyToOne
    public User user;

    @Required
    @ManyToOne
    public Hotel hotel;

    @Required
    @Temporal(TemporalType.DATE)
    public Date checkinDate;

    @Required
    @Temporal(TemporalType.DATE)
    public Date checkoutDate;

    @Required(message="Credit card number is required")
    @Match(value="^\\d{16}$", message="Credit card number must be numeric and 16 digits long")
    public String creditCard;

    @Required(message="Credit card name is required")
    @MinSize(value=3, message="Credit card name is required")
    @MaxSize(value=70, message="Credit card name is required")
    public String creditCardName;
    public int creditCardExpiryMonth;
    public int creditCardExpiryYear;
    public boolean smoking;
    public int beds;

    public Booking(Hotel hotel, User user) {
        this.hotel = hotel;
        this.user = user;
    }

    public BigDecimal getTotal() {
        return hotel.price.multiply( new BigDecimal( getNights() ) );
    }

    public int getNights() {
        return (int) ( checkoutDate.getTime() - checkinDate.getTime() ) / 1000 / 60 / 60 / 24;
    }

    public String getDescription() {
        DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
        return hotel==null ? null : hotel.name +
            ", " + df.format( checkinDate ) +
            " to " + df.format( checkoutDate );
    }

    public String toString() {
        return "Booking(" + user + ","+ hotel + ")";
    }

}</pre>
<p>Tout d&#8217;abord il manque des choses dans la version Play! Framework, et on ne compare donc plus exactement la même chose. Notez l&#8217;absence de l&#8217;entité Amenity. Ensuite, la version Spring utilise une approche différente pour l&#8217;attribut @Id et spécifie le générateur alors que la version Play! hérite de ce que la super-classe Model propose.</p>
<p>Ce qui me dérange dans la version Spring c&#8217;est la présence de la méthode <em>validateEnterBookingDetails</em>. Celle-ci est intéressante fonctionnellement, mais dépose une dépendance vers MessageContext un peu trop forte à mon goût.</p>
<p>Regardez aussi la différence sur la validation du numéro de carte de paiement. La version Play! utilise un moteur simple similaire à Hibernate Validation, mais propriétaire à Play! Au contraire la version Spring ne précise rien, et on imagine donc que la validation des 16 digits se fera dans la vue&#8230; et pas dans le modèle.</p>
<p>Conclusion : j&#8217;ai de plus en plus de mal avec la sauce des getters/setters, surtout lorsque les entités dépasse les &laquo;&nbsp;HelloWorld&nbsp;&raquo; et que l&#8217;on commence à sortir des cas d&#8217;usages compliqués. Souvenez-vous de ce que je vous disais : pour résoudre des problèmes compliqués il faut des solutions simples&#8230;</p>
<h2>III. Le contrôleur</h2>
<p>Après avoir regardé 2 entités différentes pour le modèle, voyons maintenant la différence d&#8217;approche entre Spring WebFlow et Play! Framework. Je vais étudier le cas de l&#8217;affichage de la liste des Hôtels, lorsque l&#8217;utilisateur clique sur le bouton Search par exemple.</p>
<h3>III.1) Spring WebFlow</h3>
<p>L&#8217;approche de Spring WebFlow est la suivante : afin d&#8217;éviter d&#8217;écrire un controller classique, Spring propose de définir les chemins logiques de navigation en XML, et d&#8217;associer directement des méthodes sur des services pour récupérer la partie Model. Cette approche a l&#8217;avantage de simplifier le développement de workflow webs complexes. Je vais vous présenter le concept.</p>
<p>Pour commencer, nous définissions un flow en XML pour l&#8217;action qui affiche la liste des Hotel correspondant à un critère de recherche. Je ne vous montre que la view-state &laquo;&nbsp;reviewHotels&nbsp;&raquo; afin de ne se concentrer que sur l&#8217;exemple :</p>
<p>&laquo;&nbsp;<em>main-flow.xml</em>&nbsp;&raquo;</p>
<pre class="brush:xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;

	&lt;var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" /&gt;

	&lt;view-state id="enterSearchCriteria"&gt;
		// Effacé pour simplifier l'exemple...
	&lt;/view-state&gt;

	&lt;view-state id="reviewHotels"&gt;
		&lt;on-render&gt;
			&lt;evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" result-type="dataModel" /&gt;
		&lt;/on-render&gt;
        &lt;transition on="sort"&gt;
            &lt;set name="searchCriteria.sortBy" value="requestParameters.sortBy" /&gt;
            &lt;render fragments="hotels:searchResultsFragment" /&gt;
        &lt;/transition&gt;
		&lt;transition on="previous"&gt;
			&lt;evaluate expression="searchCriteria.previousPage()" /&gt;
			&lt;render fragments="hotels:searchResultsFragment" /&gt;
		&lt;/transition&gt;
		&lt;transition on="next"&gt;
			&lt;evaluate expression="searchCriteria.nextPage()" /&gt;
			&lt;render fragments="hotels:searchResultsFragment" /&gt;
		&lt;/transition&gt;
		&lt;transition on="select" to="reviewHotel"&gt;
			&lt;set name="flowScope.hotel" value="hotels.selectedRow" /&gt;
		&lt;/transition&gt;
		&lt;transition on="changeSearch" to="changeSearchCriteria" /&gt;
	&lt;/view-state&gt;</pre>
<p><strong>Le point fort de Spring WebFlow/JSF ici :</strong> il sera possible de ne redessiner qu&#8217;une partie de l&#8217;interface. En effet, vous avez noté le tag &lt;render fragments=&nbsp;&raquo;"/&gt; ? Grâce à ce système très pratique avec Spring WebFlow et JSF, si vous avez une partie cliente en Ajax il devient alors possible de ne redéfinir qu&#8217;une partie de l&#8217;interface.</p>
<p>Dans la démo Play! Booking, ce mécanisme est implémenté &laquo;&nbsp;à la main&nbsp;&raquo; dans la page index.html. Tout est fait avec du jQuery et le tag jsAction de Play!, qui appelle des actions du controller Play. Celui-ci retourne alors des fragments de HTML, ce qui fait que la version Play! est aussi ajaxisé. Je reconnais que c&#8217;est moins évident et facile, mais les 2 se discutent.</p>
<p>Il y a donc un BookingService avec une méthode <strong>findHotels</strong> pour rechercher la liste des hôtels, d&#8217;après le Flow XML. Il s&#8217;agit d&#8217;une interface, ce qui permet de changer l&#8217;implémentation. Dans notre cas nous n&#8217;aurons que la version JPA. Si vous avez une forte envie de faire une version <em>PasJPA</em> notez que c&#8217;est super utile&#8230;</p>
<pre class="brush:java">public interface BookingService {
   // j'ai effacé les autres méthodes ici pour ne garder que l'essentiel

    public List&lt;hotel&gt; findHotels(SearchCriteria criteria);

}</pre>
<p>Le service implémente l&#8217;interface, j&#8217;ai supprimé une bonne partie du code afin de ne vous laissez que ce qui nous intéresse. Mais croyez-moi, la version complète est assez verbeuse. Ce qui nous intéresse ici c&#8217;est uniquement la méthode <em>findHotels</em></p>
<pre class="brush:java">@Service("bookingService")
@Repository
public class JpaBookingService implements BookingService {

    private EntityManager em;

    @PersistenceContext
    public void setEntityManager(EntityManager em) {
        this.em = em;
    }

   // Note : j'ai effacé du code ici pour ne garder que quelques méthodes

    @Transactional(readOnly = true)
    @SuppressWarnings("unchecked")
    public List findHotels(SearchCriteria criteria) {
        String pattern = getSearchPattern(criteria);
        return em.createQuery(
                "select h from Hotel h where lower(h.name) like " + pattern + " or lower(h.city) like " + pattern
                        + " or lower(h.zip) like " + pattern + " or lower(h.address) like " + pattern + " order by h."
                        + criteria.getSortBy()).setMaxResults(criteria.getPageSize()).setFirstResult(
                criteria.getPage() * criteria.getPageSize()).getResultList();
    }

   // Note 2 : j'ai effacé du code ici pour ne garder que quelques méthodes

   // helpers
   private String getSearchPattern(SearchCriteria criteria) {
        if (StringUtils.hasText(criteria.getSearchString())) {
            return "'%" + criteria.getSearchString().toLowerCase().replace('*', '%') + "%'";
        } else {
            return "'%'";
        }
    }

}</pre>
<p>Le plus intéressant est donc la méthode findHotels. Elle permet d&#8217;effectuer la recherche, de retourner une List d&#8217;Hotel à la vue, que nous verrons tout à l&#8217;heure si vous êtes toujours motivé.</p>
<h3>III.2) Le contrôleur version Play! Framework</h3>
<p>L&#8217;approche de Play! est un peu plus simple mais demande un peu plus de code Java.<br />
Je précise tout de suite : j&#8217;ai modifié l&#8217;exemple afin de retirer la partie Authentification et pour ne me concentrer que sur le Use-Case de récupérer la liste des Hotel correspondant à un critère entré dans l&#8217;interface utilisateur.</p>
<pre class="brush:java">public class Hotels extends Controller {

    public static void list(String search, Integer size, Integer page) {
        List hotels = null;
        page = page != null ? page : 1;
        if(search.trim().length() == 0) {
            hotels = Hotel.all().fetch(page, size);
        } else {
            search = search.toLowerCase();
            hotels = Hotel.find("lower(name) like ? OR lower(city) like ?", "%"+search+"%", "%"+search+"%").fetch(page, size);
        }
        render(hotels, search, size, page);
    }
   ...
    // Note : code effacé pour ne conserver que la méthode list
   ...
}</pre>
<p>L&#8217;approche de Play! Framework est donc la suivante :<br />
- écrire une classe dans le répertoire &laquo;&nbsp;controllers&nbsp;&raquo; qui étend la class Controller de Play!<br />
- écrire une méthode public static avec en argument les paramètres de la vue. Play! se charge de vous récupérer ce que l&#8217;utilisateur a entré. Notez ensuite l&#8217;appel pour récupérer la liste des Hotels :</p>
<pre class="brush:java">            search = search.toLowerCase();
            hotels = Hotel.find("lower(name) like ? OR lower(city) like ?", "%"+search+"%", "%"+search+"%").fetch(page, size);</pre>
<p>La méthode find est une méthode statique définie dans la classe JPASupport. Elle permet d&#8217;exécuter des requêtes JPA. Vous pouvez aussi écrire une méthode dans l&#8217;entité Hotel pour éviter de placer ici du code technique par exemple. Les 2 approches se valent.</p>
<h3>III.3) Différence d&#8217;approche</h3>
<p>La version Spring WebFlow demande plus de XML, ce qui personnellement me dérange. Je trouve que la définition des flows n&#8217;est pas simple. C&#8217;est même une fausse bonne idée. Je préfère une approche fortement typée en Java, où par contre pour que la magie opère, je dois étendre des classes spécialisées de Play! Framework.</p>
<p>Côté test, il est assez simple de <a href="http://static.springsource.org/spring-webflow/docs/2.0.x/reference/htmlsingle/spring-webflow-reference.html#testing">tester Spring WebFlow</a>, mais je n&#8217;ai pas testé personnellement.</p>
<p>Je vais être méchant avec Spring WebFlow : je trouve cela bien compliqué pour résoudre un problème simple. Ici quel est l&#8217;apport du XML ? Des annotations ? Est-ce que notre vie est plus simple grâce à ce système ? Permettez-moi d&#8217;en douter.</p>
<p>C&#8217;est peut-être une révolution par rapport à l&#8217;approche Struts, mais tant qu&#8217;à faire la révolution, il ne faut pas s&#8217;arrêter à un concept basé sur un fichier XML pour définir la navigation d&#8217;une application Web.</p>
<h3>III. 4) Synthèse</h3>
<h4>Avantages de Spring Web Flow</h4>
<p>- possibilité de déclarer en quelques lignes un bout de code pour que la vue récupère le Model sans devoir définir un controller.<br />
- gestion de la navigation de l&#8217;utilisateur fine dans le site<br />
- se repose sur des Services facile à tester pour s&#8217;assurer que le Model est correct<br />
- bien meilleur que l&#8217;approche classique où les DAO dans les Services ne servent à rien<br />
- Spring MVC récupère les paramètres de la vue et les dénormalise, ce qui est très pratique.<br />
- bonne intégration de la sécurité</p>
<h4>Inconvénients de Spring Web Flow</h4>
<p>- clairement plus compliqué que Play! sur ce point<br />
- XML tu aimes ou tu n&#8217;aimes pas, mais c&#8217;est <strong>ton</strong> environnement de développement Web<br />
- gestion pas facile pour les débutants entre les view-states, les action-states, les transitions etc.<br />
- l&#8217;EntityManager à la main dans le service : pas top. Mais c&#8217;est quelque chose qui est souvent factorisé dans des classes de GenericService par exemple, avec un DAO&#8230; Bref encore 3 ou 4 classes Java en plus.</p>
<h4>Avantages de Play! Framework :</h4>
<p>- le controlleur déclare des méthodes static et public qui deviennent autant d&#8217;URL dans la vue<br />
- le framework se charge de récupérer et dénormaliser les paramètres de la vue<br />
- approche full java<br />
- les entités ont un moteur comme GORM en Grails mais plus simple. Cela permet d&#8217;avoir des finders comme méthode statique sur l&#8217;entité Hotel. C&#8217;est cependant statique et plus limité que Grails/GORM.</p>
<h4>Inconvénients de Play! Framework :</h4>
<p>- le controller de base étend une class de Play! (si la spécialisation vous dérange plutôt que la composition)<br />
- la gestion des workflows compliqué demande un effort de développement par rapport à Spring WebFlow, qui sera capable de retomber sur ses pieds.</p>
<h2>IV. La Vue</h2>
<p>Je vais maintenant évaluer plusieurs critères :<br />
- la création d&#8217;une page pour chacune des technos<br />
- la qualité de l&#8217;expérience utilisateur avec l&#8217;utilisation d&#8217;Ajax<br />
- la taille des pages webs et les temps de chargement</p>
<h3>IV.1) Créer une page avec Spring Faces</h3>
<p>Basé sur JSF, cette partie sera donc essentiellement une discussion sur JSF plutôt que sur Spring. Je parlerai un peu de la taglibs de Spring pour la partie validation du côté client, que je trouve très bien fait. Mais l&#8217;essentiel du débat porte plus sur JSF ici.</p>
<p>Dans le projet Spring Webflow, les pages sont construites à partir d&#8217;un template, avec la technologie Facelets. Très pratique, cela vous permet de construire le canevas de votre site, puis de spécialiser vos vues en écrivant peu de code.</p>
<p>Le template standard.xhtml :</p>
<pre class="brush:html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;f:view xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
	  xmlns:c="http://java.sun.com/jstl/core"
	  xmlns:sf="http://www.springframework.org/tags/faces"
	  contentType="text/html" encoding="UTF-8"&gt;
&lt;html&gt;
&lt;head&gt;
	&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
	&lt;title&gt;Spring Faces: Hotel Booking Sample Application&lt;/title&gt;

	&lt;sf:includeStyles/&gt;
	&lt;sf:resourceGroup&gt;
		&lt;sf:resource path="/css-framework/css/tools.css"/&gt;
		&lt;sf:resource path="/css-framework/css/typo.css"/&gt;
		&lt;sf:resource path="/css-framework/css/forms.css"/&gt;
		&lt;sf:resource path="/css-framework/css/layout-navtop-localleft.css"/&gt;
		&lt;sf:resource path="/css-framework/css/layout.css"/&gt;
	&lt;/sf:resourceGroup&gt;
	&lt;sf:resource path="/styles/booking.css"/&gt;
	&lt;ui:insert name="headIncludes"/&gt;
&lt;/head&gt;
&lt;body class="tundra spring"&gt;
&lt;div id="page"&gt;
	&lt;div id="header" class="clearfix spring"&gt;
		&lt;div id="welcome"&gt;
			&lt;div class="left"&gt;Spring Travel: A Spring Faces Reference Application&lt;/div&gt;
			&lt;div class="right"&gt;
				&lt;c:if test="${not empty currentUser.name}"&gt;
	            	Welcome, ${currentUser.name} | &lt;a href="${request.contextPath}/spring/logout"&gt;Logout&lt;/a&gt;
				&lt;/c:if&gt;
				&lt;c:if test="${empty currentUser.name}"&gt;
	            	&lt;a href="${request.contextPath}/spring/login"&gt;Login&lt;/a&gt;
				&lt;/c:if&gt;
			&lt;/div&gt;
		&lt;/div&gt;
		&lt;div id="branding" class="spring"&gt;
			&lt;a href="#{request.contextPath}"&gt;&lt;img src="${request.contextPath}/resources/images/header.jpg" alt="Spring Travel"/&gt;&lt;/a&gt;
		&lt;/div&gt;
	&lt;/div&gt;
	&lt;div id="content" class="clearfix spring"&gt;
		&lt;div id="local" class="spring"&gt;
			&lt;a href="http://www.thespringexperience.com"&gt;
				&lt;img src="${request.contextPath}/resources/images/diplomat.jpg" alt="generic hotel" /&gt;
			&lt;/a&gt;
			&lt;a href="http://www.thespringexperience.com"&gt;
				&lt;img src="${request.contextPath}/resources/images/tse.gif" alt="The Spring Experience" /&gt;
			&lt;/a&gt;
			&lt;p&gt;
			&lt;/p&gt;
		&lt;/div&gt;
		&lt;div id="main"&gt;
			&lt;ui:insert name="content"/&gt;
		&lt;/div&gt;
	&lt;/div&gt;
	&lt;div id="footer" class="clearfix spring"&gt;
		&lt;a href="http://www.springframework.org"&gt;&lt;img src="${request.contextPath}/resources/images/powered-by-spring.png" alt="Powered by Spring" /&gt;&lt;/a&gt;
	&lt;/div&gt;
&lt;/div&gt;

&lt;/body&gt;
&lt;/html&gt;
&lt;/f:view&gt;</pre>
<p>La page &laquo;&nbsp;reviewHotels.xhtml&nbsp;&raquo; avec 52 balises HTML+JSF permet d&#8217;afficher la liste des Hôtels :</p>
<pre class="brush:html">&lt;!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;ui:composition xmlns="http://www.w3.org/1999/xhtml"
	    		xmlns:ui="http://java.sun.com/jsf/facelets"
	  			xmlns:h="http://java.sun.com/jsf/html"
	  			xmlns:f="http://java.sun.com/jsf/core"
	  			xmlns:sf="http://www.springframework.org/tags/faces"
				template="/WEB-INF/layouts/standard.xhtml"&gt;

&lt;ui:define name="content"&gt;

&lt;h:form id="hotels"&gt;
	&lt;div class="section"&gt;
		&lt;h2&gt;Hotel Results&lt;/h2&gt;
		&lt;p&gt;
			&lt;sf:commandLink value="Change Search" action="changeSearch"/&gt;
		&lt;/p&gt;
		&lt;ui:fragment id="searchResultsFragment"&gt;
			&lt;div id="searchResults"&gt;
				&lt;h:outputText id="noHotelsText" value="No Hotels Found" rendered="#{hotels.rowCount == 0}"/&gt;
				&lt;h:dataTable id="hotels" styleClass="summary" value="#{hotels}" var="h" rendered="#{hotels.rowCount &gt; 0}"&gt;
					&lt;h:column&gt;
						&lt;f:facet name="header"&gt;
							&lt;sf:commandLink id="sortByNameLink" action="sort" value="Name" processIds="*"&gt;
								&lt;f:param name="sortBy" value="name" /&gt;
							&lt;/sf:commandLink&gt;
						&lt;/f:facet&gt;
						#{h.name}
					&lt;/h:column&gt;
					&lt;h:column&gt;
						&lt;f:facet name="header"&gt;Address&lt;/f:facet&gt;
						#{h.address}
					&lt;/h:column&gt;
					&lt;h:column&gt;
						&lt;f:facet name="header"&gt;
							&lt;sf:commandLink id="sortByCity" action="sort" value="City, State" processIds="*" &gt;
								&lt;f:param name="sortBy" value="city" /&gt;
							&lt;/sf:commandLink&gt;
						&lt;/f:facet&gt;
						#{h.city}, #{h.state}, #{h.country}
					&lt;/h:column&gt;
					&lt;h:column&gt;
						&lt;f:facet name="header"&gt;Zip&lt;/f:facet&gt;
						#{h.zip}
					&lt;/h:column&gt;
					&lt;h:column&gt;
						&lt;f:facet name="header"&gt;Action&lt;/f:facet&gt;
						&lt;sf:commandLink id="viewHotelLink" value="View Hotel" action="select"/&gt;
					&lt;/h:column&gt;
				&lt;/h:dataTable&gt;
				&lt;div class="buttonGroup"&gt;
					&lt;sf:commandLink id="previousPageLink" value="Previous results" action="previous" rendered="#{searchCriteria.page &gt; 0}"/&gt;
					&lt;sf:commandLink id="nextPageLink" value="More Results" action="next" rendered="#{not empty hotels and hotels.rowCount == searchCriteria.pageSize}"/&gt;
				&lt;/div&gt;
			&lt;/div&gt;
		&lt;/ui:fragment&gt;
	&lt;/div&gt;
&lt;/h:form&gt;
&lt;/ui:define&gt;
&lt;/ui:composition&gt;</pre>
<p>Ce que j&#8217;apprécie à titre personnel avec JSF :<br />
- possibilité d&#8217;écrire du code de mise en forme sans avoir besoin de faire trop de CSS<br />
- moteur de construction de page assez proche d&#8217;une vue client lourd<br />
- possibilité d&#8217;écrire avec la syntaxe JSF et d&#8217;avoir un rendu riche sans se prendre la tête avec du Javascript ou des feuilles de style.<br />
&lt;HUMOUR&gt;De toutes les façons, je suis pas développeur Web. Donc mort aux CSS&#8230;&lt;/HUMOUR&gt;</p>
<p>Ce qui me fait un peu tiquer, c&#8217;est que l&#8217;on dit au développeur Java : tu n&#8217;auras pas besoin d&#8217;apprendre à faire une mise en page compliquée en HTML et CSS&#8230;</p>
<p>Mais bon, vous vous foutez pas un peu de nous là ?</p>
<p>La page demande 52 balises. Et ce dataTable pourrait très bien être un simple TABLE en HTML avec une CSS non ? Ce qui d&#8217;ailleurs est le cas au final non ?</p>
<h3>IV.2) Créer une page avec Play! Framework</h3>
<p>Attention cela pique les yeux. Comme JSF ou Wicket, vous pouvez utiliser un éditeur HTML pour construire votre page. En effet, le format des pages de Play! Framework est très original <strong>puisque c&#8217;est du HTML</strong> ni plus, ni moins. Il y a cependant l&#8217;utilisation de Groovy qui risque de faire peur à quelques uns. Je note cela comme un inconvénient par rapport à JSF.</p>
<p>La version livrée avec Play! utilise jQuery et Ajax à fond. Il y a en fait une page principale, puis un ensemble de fonction Javascript pour ne charger rapidement que ce qui change dans un DIV au format HTML. C&#8217;est radicalement différent, et peut-être moins facile d&#8217;abord pour des gens n&#8217;ayant jamais fait de Web. Je vous montre donc plutôt ici ma version &laquo;&nbsp;<em>gars qui a fait du Java et du Web et qui découvre Play</em>&nbsp;&raquo;</p>
<p>Play! Framework dispose aussi d&#8217;un moteur de template. Je vais donc vous montrer 2 fichiers. Le fichier main.html est un template de base que j&#8217;utilise pour le site :</p>
<pre class="brush:html">&lt;!DOCTYPE html&gt;

&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;#{get 'title' /}&lt;/title&gt;
        &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;
        &lt;link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/main.css'}"&gt;
        #{get 'moreStyles' /}
        &lt;link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}"&gt;
        &lt;script src="@{'/public/javascripts/jquery-1.3.2.min.js'}" type="text/javascript" charset="utf-8"&gt;&lt;/script&gt;
        &lt;script src="@{'/public/javascripts/sessvars.js'}" type="text/javascript" charset="utf-8"&gt;&lt;/script&gt;
        #{get 'moreScripts' /}
    &lt;/head&gt;
    &lt;body&gt;

        &lt;div id="header"&gt;
            &lt;h1&gt;play framework booking demo&lt;/h1&gt;
            #{if user?.id}
                &lt;div id="options"&gt;
                    Connected as ${user.username}
                    |
                    &lt;a href="@{Hotels.index()}"&gt;Search&lt;/a&gt;
                    |
                    &lt;a href="@{Hotels.settings()}"&gt;Settings&lt;/a&gt;
                    |
                    &lt;a href="@{Application.logout()}"&gt;Logout&lt;/a&gt;
                &lt;/div&gt;
            #{/if}
        &lt;/div&gt;

        &lt;div id="content"&gt;
            #{if flash.error}
                &lt;p class="fError"&gt;
                    &lt;strong&gt;${flash.error}&lt;/strong&gt;
                &lt;/p&gt;
            #{/if}
            #{if flash.success}
                &lt;p class="fSuccess"&gt;
                    &lt;strong&gt;${flash.success}&lt;/strong&gt;
                &lt;/p&gt;
            #{/if}

            #{doLayout /}

        &lt;/div&gt;

        &lt;div id="footer"&gt;
            Created with &lt;a href="http://www.playframework.org"&gt;play framework&lt;/a&gt; and really inspirated from the booking sample application provided by &lt;a href="http://seamframework.org/"&gt;seam framework&lt;/a&gt;
        &lt;/div&gt;

    &lt;/body&gt;
&lt;/html&gt;</pre>
<p>Voici la page qui affiche la liste des Hotels. Le fichier s&#8217;appelle &laquo;&nbsp;list.html&nbsp;&raquo; comme le nom de la méthode <em>list</em> du Controller Hotels que nous avons vu plus haut. Il est bien entendu possible de déclarer un nom différent et de configurer une autre route avec Play! Framework.</p>
<p>Le fichier est assez simple : il reçoit un objet List&lt;Hotel&gt;, il itère en Groovy avec un tag spécifique à Play et il affiche le résultat dans un tableau HTML :</p>
<pre class="brush:html">
#{extends 'main.html' /}
#{set title:'Search result'/}

#{if hotels}
&lt;table&gt;
    &lt;thead&gt;
    &lt;tr&gt;
        &lt;th&gt;Name&lt;/th&gt;
        &lt;th&gt;Address&lt;/th&gt;
        &lt;th&gt;City, State&lt;/th&gt;
        &lt;th width="8%"&gt;Zip&lt;/th&gt;
        &lt;th width="13%"&gt;Action&lt;/th&gt;
    &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
    #{list hotels, as:'hotel'}
    &lt;tr&gt;
        &lt;td&gt;${hotel.name}&lt;/td&gt;
        &lt;td&gt;${hotel.address}&lt;/td&gt;
        &lt;td&gt;${hotel.city}, ${hotel.state}, ${hotel.country}&lt;/td&gt;
        &lt;td&gt;${hotel.zip}&lt;/td&gt;
        &lt;td&gt;
            &lt;a href="@{show(hotel.id)}"&gt;View Hotel&lt;/a&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    #{/list}
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;
    &lt;a id="nextPage" href="${page+1}"&gt;More results&lt;/a&gt;
&lt;/p&gt;
#{/if}

#{else}
&lt;p&gt;
    No more results
&lt;/p&gt;
#{/else}</pre>
<p>La version Play! est très dépouillée. L&#8217;inconvénient : il sera nécessaire dans les 2 cas (JSF comme Play!) d&#8217;apprendre la technologie de rendu et les tags pour travailler efficacement. Mais Play! n&#8217;est pas orienté composant comme JSF. Il n&#8217;y a pas d&#8217;utilisation de l&#8217;Expression Language, ici c&#8217;est Groovy. Ce moteur Groovy est un poil plus lent que <a href="http://www.playframework.org/modules/japid">Japid</a>, un autre moteur Java qui est disponible pour Play!</p>
<p>Regardez aussi <a href="http://www.playframework.org/modules">la liste des modules de Play! Framework</a>. Il y a du lourd comme le support de Scala, le moteur NoSQL MongoDB, Guice, GWT ou le module Search avec Lucene (comme Hibernate Search en plus simple).</p>
<p>Un inconvénient de Play! pour les personnes qui n&#8217;ont jamais fait de Web : la mise en page et la partie CSS. Lorsque je monte un projet avec Play! Framework, j&#8217;utilise par exemple <a href="http://960.gs/">CSS 960 grid system</a>. Je connais ces systèmes car j&#8217;ai un gros background Web. Pour le CSS, pour sélectionner des couleurs qui ne font pas vomir l&#8217;utilisateur, j&#8217;ai pleins d&#8217;outils comme <a href="http://kuler.adobe.com/">Kuler</a> d&#8217;Adobe pour préparer des styles&#8230; Donc pour moi qui suit très Web : je peux faire un truc joli avec Play! Framework. Je pourrai styliser mes tableaux sans soucis. Je pense que ce qui est bien avec JSF, c&#8217;est que cela vous aide à monter vos pages facilement, à construire vos formulaires ou vos tableaux sans devoir devenir une bête en HTML/CSS. Mais c&#8217;est un peu dommage, lorsque l&#8217;on voit le potentiel d&#8217;HTML 5 pour les années qui viennent.</p>
<h3>IV.3) Expérience utilisateur</h3>
<p>J&#8217;ai lancé les 2 applications et j&#8217;ai effectué une comparaison en tant qu&#8217;utilisateur. Je donne l&#8217;avantage à la version Spring Faces. Je trouve que la librairie de Spring de validation du côté client est très sympa.</p>
<p>En terme de rapidité, la version Play! Framework est vraiment plus réactive. Si je clique comme un dingue avec un script Selenium, la version Spring WebFlow est à la peine alors que la version Play! se tourne les pouces sans s&#8217;essoufler.</p>
<h3>IV.5) Taille des pages Webs</h3>
<p>La page d&#8217;affichage de la liste des Hôtels sur Spring WebFlow (cliquez sur l&#8217;image pour l&#8217;afficher en grand) :<br />
<a href="http://www.touilleur-express.fr/wp-content/page_spring_webflow_result.jpg"><img class="alignnone size-medium wp-image-3739" title="page_spring_webflow_result" src="http://www.touilleur-express.fr/wp-content/page_spring_webflow_result-300x236.jpg" alt="" width="300" height="236" /></a></p>
<p>La version Play! Framework :<br />
<a href="http://www.touilleur-express.fr/wp-content/page_hotels_play.jpg"><img class="alignnone size-medium wp-image-3740" title="page_hotels_play" src="http://www.touilleur-express.fr/wp-content/page_hotels_play-300x261.jpg" alt="" width="300" height="261" /></a></p>
<p>Mes tests avec YSlow en effectuant 10 chargements de la page puis en prenant la moyenne :</p>
<p><strong>Moyenne des temps de chargement de la page liste des Hotels pour 10 hotels:</strong><br />
- Play! Framework en mode Dev ou Prod : 0,234 secondes<br />
- Spring WebFlow avec Jetty : 1.286 secondes<br />
- Spring WebFlow avec Tomcat 6.0.20 : 1.092 secondes</p>
<p>Le mode Prod de Play! ne prend qu&#8217;un peu plus de temps à démarrer, mais je n&#8217;ai pas constaté de différence une fois le serveur lancé.</p>
<p><strong>Volume de la partie HTML sans CSS/Images et temps de chargement :</strong><br />
Si je ne regarde que la partie HTML sans prendre en compte le temps de chargement des images, je teste réellement que la partie serveur.</p>
<p>Play! Framework : chargement de 3.1 kb en un temps moyen de 14ms<br />
Tomcat+Spring : chargement de 14kb en un temps moyen de 52 ms</p>
<p>Donc pour un affichage similaire, vous voyez le souci ? <strong>la version Spring Faces de cette démonstration est 5 fois plus lourde et demande 4 à 5 fois plus de temps</strong>. Je ne dis pas qu&#8217;en général c&#8217;est le cas, mais pour notre exemple oui c&#8217;est le cas. Et nous sommes sur le même cahier des charges : afficher dans un tableau une liste d&#8217;Hôtel.</p>
<h2>V. Conclusion</h2>
<p>Si vous avez pris le temps de lire cet énorme article jusqu&#8217;au bout, merci. Revenez voir le code des deux version en détail. Téléchargez chacune des versions et donnez vos propres arguments techniques en faveur de l&#8217;un ou de l&#8217;autre.</p>
<p>J&#8217;ai envie de taper du poing sur la table : <strong>il est temps de revoir le développement Web en Java</strong>.<br />
Le développement Web demande des compétences. Si vous ne les avez pas, ne prenez pas une techno pour couvrir vos lacunes. Apprenez à faire du Web. Désolé, le XML ne m&#8217;excite pas. Play! est plus facile à apprendre que toute la stack Spring que je vous ai présenté. Car elle est plus simple et tout aussi puissante.</p>
<p>Pour construire une application Web compliquée ou d&#8217;entreprise, est-ce que dans 5 ans nous ferrons toujours du JSF ? ou est-ce que nous ferrons du HTML+CSS+jQuery ? Je pose la question. Evidemment, Play! Framework doit encore faire ses preuves et on peut me retourner l&#8217;argument. Mais c&#8217;est plus simple, plus réaliste et plus facile à maintenir.</p>
<p>Le point faible de Play! par rapport à un framework comme Spring Faces+Spring WebFlow+JSF c&#8217;est qu&#8217;il faut plus d&#8217;efforts pour construire ses composants dans la partie vue. D&#8217;un autre côté, lorsque vous avez un framework aussi puissant que JSF, vous avez envie de prendre des tableaux avancés, des richTables, ou des composants plus puissants. Cela entraîne parfois le risque de prendre des composants qui font trop par rapport à un tableau simple. D&#8217;ailleurs, si vous ne preniez pas ces composants, quel intérêt à utiliser une librairie JSF ? Je pose la question.</p>
<p>Un risque est donc d&#8217;utiliser des composants riches trop facilement dans une application Web avec JSF. Vous vous retrouvez avec un tableau avec des colonnes triables, alors que vous n&#8217;en n&#8217;avez pas besoin. Et votre page pèse 15-20kb au lieu de peser 2-5kb, et demande de la mémoire et du CPU du côté serveur.</p>
<p>Le site &laquo;&nbsp;<a href="http://www.express-board.fr/">eXpress-Board</a>&nbsp;&raquo; tourne sur la même machine que le blog &laquo;&nbsp;Le Touilleur Express&nbsp;&raquo; avec 3 parts chez Gandi, soit 768Mo. Le process Java tourne avec 128 Mo de mémoire, et prend entre 5 et 16% de l&#8217;utilisation de la machine, bien loin de MySQL qui est très sollicité par WordPress pour le Blog.</p>
<p>Je vais me faire taper par les maîtres Javas qui ne vont pas être d&#8217;accord. Mais c&#8217;est la première fois où j&#8217;en ai rien à faire, car je suis sûr de mes arguments.</p>
<p>Allez SuperTroll, sort du bois, je t&#8217;attends.</p>
<p>On va bien s&#8217;amuser&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2010/05/13/spring-faceswebflowjava-classique-compare-a-play-framework/feed/</wfw:commentRss>
		<slash:comments>44</slash:comments>
		</item>
		<item>
		<title>Java et les frameworks Webs : une longue histoire compliquée</title>
		<link>http://www.touilleur-express.fr/2010/05/01/java-et-les-frameworks-webs-une-longue-histoire-compliquee/</link>
		<comments>http://www.touilleur-express.fr/2010/05/01/java-et-les-frameworks-webs-une-longue-histoire-compliquee/#comments</comments>
		<pubDate>Sat, 01 May 2010 21:45:38 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=3648</guid>
		<description><![CDATA[&#160;
Imaginons que vous êtes votre framework Web Java favori&#8230; Disons que vous habitez dans une maison, qui représente votre framework Web Java.
Fermez les yeux&#8230; visualisez une petite usine qui fume avec 345 tuyaux, plusieurs portes d&#8217;entrées, des grillages à certains endroits, une pelouse fatiguée, un panneau &#171;&#160;Attention, Chien Dangereux (et Con)&#171;&#160;, des panneaux solaires qui ne sont pas raccordés, un autocollant &#171;&#160;Java 7 Rulez&#160;&#187; et des empilements de briques&#8230; Il y a souvent des coupures, des problèmes d&#8217;allocations de ressources, des débordements de pile. Lorsque vous voulez fermer les volets, ...]]></description>
			<content:encoded><![CDATA[<p>&nbsp;</p>
<div id="attachment_5445" class="wp-caption alignleft" style="width: 250px"><a href="http://www.touilleur-express.fr/wp-content/uploads/2010/05/complexity.jpg"><img class="size-full wp-image-5445" title="complexity" src="http://www.touilleur-express.fr/wp-content/uploads/2010/05/complexity.jpg" alt="" width="240" height="240" /></a><p class="wp-caption-text">Crédit photo michael.heiss Licence Commons Creatives 2.0</p></div>
<p>Imaginons que vous êtes votre framework Web Java favori&#8230; Disons que vous habitez dans une maison, qui représente votre framework Web Java.</p>
<p>Fermez les yeux&#8230; visualisez une petite usine qui fume avec 345 tuyaux, plusieurs portes d&#8217;entrées, des grillages à certains endroits, une pelouse fatiguée, un panneau &laquo;&nbsp;<em>Attention, Chien Dangereux (et Con)</em>&laquo;&nbsp;, des panneaux solaires qui ne sont pas raccordés, un autocollant &laquo;&nbsp;<em>Java 7 Rulez</em>&nbsp;&raquo; et des empilements de briques&#8230; Il y a souvent des coupures, des problèmes d&#8217;allocations de ressources, des débordements de pile. Lorsque vous voulez fermer les volets, vous devez relancer le groupe électrogène. Pour ajouter une chambre supplémentaire, vous avez fait poser une extension sur le toit&#8230;</p>
<p>Un jour, un gars vient sonner chez vous. Vous ouvrez la porte et il vous dit : &laquo;&nbsp;<em>Bonjour, moi je m&#8217;appelle PHP, j&#8217;ai l&#8217;air moins sérieux que toi, mais je fais tourner de gros sites Webs</em>&laquo;&nbsp;. Et toi, tu le regardes comme un hippie Baba-Cool bien gentil, mais au final tu lui lances un &laquo;&nbsp;<em>t&#8217;es incapable de monter en charge</em>&laquo;&nbsp;.<br />
Vlam, tu refermes la porte.</p>
<p>Le lendemain, un autre gars vient te voir et te tape sur l&#8217;épaule : &laquo;&nbsp;<em>Bonjour, moi c&#8217;est Ruby on Rails. Je t&#8217;éclate en productivité et en plus mes développeurs s&#8217;amusent</em>&laquo;&nbsp;. Là tu lui ris très fort au nez et tu lui dis : &laquo;&nbsp;<em>Ouais mais Ruby c&#8217;est pas comme le Latin ? Une langue morte ?</em>&laquo;&nbsp;. Vlam, et tu lâches le chien pour voir s&#8217;il a vraiment des soucis de montée en charge.</p>
<p>Le surlendemain, un mec qui semble avoir fumé un arbre sonne à ta porte. Il porte un teeshirt &laquo;&nbsp;NE TIREZ PAS : JE TOURNE SUR LA JVM&nbsp;&raquo;.<br />
C&#8217;est Mr Grails.<br />
Fier comme un pape : &laquo;&nbsp;<em>Salut, moi c&#8217;est Grails. Je suis enfin là pour t&#8217;apporter de la productivité, je suis en Groovy et je tourne sur la JVM&#8230; Gentil&#8230; pas tirer&#8230;</em>&laquo;&nbsp;. Et toi de répondre : &laquo;&nbsp;<em>Grails c&#8217;est pas si performant en production, il faut apprendre Groovy, et en plus tu portes un teeshirt moche</em>&laquo;&nbsp;. Le gars a juste le temps de reculer lorsque tu refermes la porte.</p>
<p>Quelques jours plus tard, un gars en costard-cravate, look banal, sonne à ta porte&#8230; &laquo;&nbsp;<em>Salut, moi c&#8217;est JSF2. Je suis un standard, j&#8217;ai plusieurs implémentations possibles, je suis là pour tuer Struts2. Comme je suis un standard, je suis bon pour toi et pour ton CV. Par contre pour avoir des composants intéressants il faudra que tu prennes RichFaces/IceFaces ou autre. Et pour ce qui est du REST, on prendra mon pote JAX-RS&#8230; et pour la mise en prod on prendra le serveur Trucmuche de 80mo&#8230; et blablablabla&#8230;</em>&laquo;&nbsp;.<br />
Bon là c&#8217;est facile, le bonhomme se taille en 4 tout seul. Refermons la porte poliment et retournons à TéléFoot.</p>
<p>&nbsp;</p>
<div id="attachment_5447" class="wp-caption alignleft" style="width: 250px"><a href="http://www.touilleur-express.fr/wp-content/uploads/2010/05/factory.jpg"><img class="size-full wp-image-5447" title="factory" src="http://www.touilleur-express.fr/wp-content/uploads/2010/05/factory.jpg" alt="" width="240" height="160" /></a><p class="wp-caption-text">Licence Commons Creatives, crédit photo Kenichi Nobusue</p></div>
<p><a href="http://www.flickr.com/photos/antman-87/"><br />
</a><strong>Quelques idées sérieuses</strong><br />
Le développement Web fait-il partie du génome des développeurs Java ?<br />
Autant PHP me semble fortement lié à une culture Web, autant Java n&#8217;est pas forcément associé au Web. Lors des présentations de Play! Framework, Nicolas Leroux et Guillaume Bort demandaient à la salle du Paris JUG :<br />
<strong>Qui est développeur Web ?</strong><br />
Quelques mains timides se lèvent sur les quelques 450 personnes présentes. Guillaume demande ensuite qui a déjà fait des JSPs ou des Servlets. Et là, presque tout le monde lève sa main&#8230; Donc si nous comprenons bien ce qui se passe, vous n&#8217;êtes pas développeur Web, mais pourtant vous faîtes des JSPs, des Servlets et du Struts&#8230;<br />
Ce qui est dommage si nous continuons sur cette idée, c&#8217;est que du coup, vous n&#8217;avez pas forcément de connaissances en HTML, en CSS, en JavaScript. Si je vous dis que le protocole HTTP est sans état, cela vous semble-t-il alors normal de voir des frameworks massivement avec état, comme le principe même de JSF par exemple ?</p>
<p>Le Web est un vaste espace documentaire. Chaque ressource doit disposer d&#8217;une adresse unique, d&#8217;une URI. Les répertoires virtuels d&#8217;un site doivent être autant de conteneur, pour classer et ranger vos objets, par catégorie. Le protocole Web dispose depuis 15 ans des verbes d&#8217;action pour manipuler les documents : GET,POST,DELETE et PUT essentiellement. Saviez-vous que JSF 1.x ne faisait que des POSTs pour échanger des données avec le serveur ? Qui imagine avoir des URLs bookmarkable avec ce type de framework ?</p>
<p>Chaque URL affichée par votre navigateur représente finalement une vue. Les premiers frameworks du monde Java, Struts 1.x pour n&#8217;en citer qu&#8217;un, ont été construit par dessus l&#8217;API des Servlets. Intéressant, lorsque l&#8217;on pense qu&#8217;une Servlet ajoute la notion de session du côté du serveur, concept qui va rendre la vie des développeurs Javas un peu plus compliqué que prévu. Tout d&#8217;un coup, un navigateur qui n&#8217;est pas connecté au serveur Web en permanence, demande au serveur de maintenir une session. Le concept magique de session, qui permet d&#8217;acheter des oiseaux dans un magasin, devient la référence pour les développeurs Webs. Cette démonstration a eu l&#8217;effet d&#8217;un lampadaire sur les moustiques : tout le monde a plongé dans le monde formidable de la session. J&#8217;y reviens plus loin dans quelques lignes.</p>
<p>Le jour où Gavin King a annoncé Seam, il explique entre autre avec fierté que son framework gère le bouton &laquo;&nbsp;Back&nbsp;&raquo; du navigateur&#8230; Chose qui me semble normal, et que nous savions gérer en 1997 avec du Perl, et un peu d&#8217;huile de coude.</p>
<p>La session dans les frameworks Webs Java est un objet assez mal compris. L&#8217;implémentation de certains frameworks Webs, que j&#8217;appelle &laquo;&nbsp;avec session&nbsp;&raquo; comme Tapestry, Struts ou Wicket, utilisent un Cookie pour identifier la session du navigateur avec une session en mémoire sur le serveur. Ce ticket permet de stocker sur le serveur des objets volumineux. Je vois alors le framework Web comme une sorte de Mont de Piété. Vous déposez vos valeurs, on vous donne un ticket. Lorsque vous revenez, vous récupérez alors vos valeurs, votre session contre ce ticket. Ce mécanisme est nécessaire car il n&#8217;est pas souhaitable de sérialiser vos données et de les passer au client. Le souci : cela entraîne une consommation de mémoire du côté serveur car le développeur Java aura tendance à mettre en session tout et n&#8217;importe quoi.</p>
<p>Il y a eu une tentative de faire travailler le navigateur Web, et de lui donner alors toutes les données de session. L&#8217;avantage ? Les Serveurs sans état peuvent alors être monté en cluster, et le navigateur passe les données de session de page en page. Cela fonctionne avec de la réécriture d&#8217;URL lorsque vous faîtes des GET ou en postant les données dans des champs cachés si vous faîtes du POST. Cela marche très bien, c&#8217;est le principe d&#8217;eBay. Il y a cependant quelques limitations, comme la taille des URIs par exemple. Vous ne vous êtes pas demandé quel serait la taille maximale d&#8217;une URI ?<br />
Avant tout, la RFC 2616 (Hypertext Transfer Protocol — HTTP/1.1) précise dans la section 3.2.1 qu&#8217;il n&#8217;y a pas de limite sur la taille d&#8217;une URI. En théorie, il serait donc possible de passer des données de session énorme. Dans les faits, c&#8217;est faux.<a href="http://support.microsoft.com/kb/208427"> Internet Explorer ne dépasse pas 2083 caractères</a>, Firefox pousse jusqu&#8217;à 65565 caractères. Mais les serveurs Webs comme Apache limitent à 4000 caractères par défaut, comme parfois les proxy qui se chargent de faire de la redirection ou du load-balancing.</p>
<p>En général, les frameworks Webs évitent d&#8217;avoir une notion de session qui coûte de la mémoire côté serveur lorsque celle-ci n&#8217;est pas indispensable. Les frameworks Javas ont proposé très tôt ce concept de Session. Pour monter plus de Sessions, il faudra souvent avoir plus de mémoire. Personne n&#8217;aura expliqué au développeur Web Java comment calculer la taille de sa session.</p>
<p>Il y eu ensuite le souci de la durée de vie des Sessions. Soit aucune session, proche des EJB stateless, soit avec une session, on pense aux EJBs statefull. Mais ce concept métier ne fonctionnait pas bien avec le Web. Lorsque les premières architectures Webs pour la partie vue ont commencé à discuter avec des architectures métiers écrites sur la base des EJB 2.x, ce fut assez amusant. Pour que le monde du Web puisse afficher des données sur plusieurs écrans successifs, il fallait les stocker dans le &laquo;&nbsp;Web Tiers&nbsp;&raquo;. Nous avons utilisé des DTO, représentant des données venues de la partie métier. Puis quelqu&#8217;un a dit un jour que la séparation entre le Web tiers et l&#8217;EJB Tiers était une grosse bêtise inventée par des vendeurs de machines. Et nous avons tué cette idée aussi rapidement qu&#8217;elle s&#8217;était diffusée. Je rigole pas trop fort car je connais encore beaucoup de systèmes en production avec un tiers Web, et un tiers EJB. Il y a un firewall entre le monde extérieur et le Web tiers. Puis un deuxième firewall entre le web tiers et l&#8217;ejb tiers. Bien entendu, 4 machines pour le tout, afin de faire du load-balancing&#8230; J&#8217;imagine le sourire du commercial qui a vendu les machines à l&#8217;époque.</p>
<p>Seam a introduit le concept de conversation. <a href="http://www.touilleur-express.fr/2006/12/12/jboss-seam-linteret-de-la-conversation/" target="new2">En décembre 2006</a> j&#8217;en parlais avec passion. Cela a été repris dans CDI, dans JEE6. Et le concept de conversation Flash est aussi maintenant partie intégrante de Grails ou de Play! Framework par exemple. Preuve qu&#8217;il est possible de gérer de manière fine la durée des sessions.</p>
<p>Tout ceci pour dire que je crois que les frameworks Javas et le monde Web ne se sont pas vraiment compris. Nous avons souvent une complexité culturelle avec Java, qui n&#8217;est pas nécessaire lorsque l&#8217;on fait du Web (idée de Guillaume Bort, auteur de Play! Framework). Les développeurs Javas que je connais, même les meilleurs, n&#8217;ont pas nécessairement la culture Web. Avoir une culture Web pour moi, c&#8217;est connaître le protocole HTTP, HTML, CSS, REST et les possibilités des architectures sans états. Ce n&#8217;est pas imposer une vision middleware, qui ne fonctionne pas avec le Web. C&#8217;est certainement une chose sur laquelle nous devons encore travailler, nous qui sommes développeur Java. Le jour où SUN Microsystems, IBM et HP ont décidé que pour vendre de la machine, il fallait de la complexité, nous aurions dû faire attention. Pourquoi les autres langages comme PHP, Python, Perl ou C# ont-ils des frameworks Webs digne de ce nom ? Parce qu&#8217;ils n&#8217;ont pas cherché à en faire une longue histoire compliquée. Parce qu&#8217;ils ont respecté HTTP pour ce qu&#8217;il était, sans essayer une danse du ventre pour séduire les développeurs, pour finalement les planter quelques mois plus tard. Que celui qui n&#8217;a jamais entendu un AdminSys râler à propos de Tomcat et des serveurs d&#8217;applications lève la main&#8230;</p>
<p>J&#8217;écris cela car je pense que le Web et Java ont encore des efforts pour se comprendre. Je ne suis pas extasié devant GWT, inventé pour masquer la complexité de JavaScript. Je ne suis pas impressionné par Apache Wicket qui permet de coder &laquo;&nbsp;à la Swing&nbsp;&raquo; dans un monde massivement orienté objet une application Web, qui n&#8217;a que faire de l&#8217;orienté objet. Je ne suis pas impressionné par JSF 2 qui a rattrapé le retard, mais qui dispose de plusieurs implémentations, qui demande le support d&#8217;une librairie externe pour faire de l&#8217;AJAX, et qui demande des librairies pour faire de l&#8217;orienté composant&#8230;</p>
<p>Je suis impressionné par contre par la créativité des développeurs Javas, qui cherchent parfois à faire du client lourd dans un navigateur, en appelant cela &laquo;&nbsp;un client léger&nbsp;&raquo;. Bon sang, une application Web n&#8217;a rien à voir avec la vision à papa, avec des fenêtres et des boutons qui font pouet-pouet&#8230; Prenez dans ce cas une techno comme Flex par exemple, pour faire un client déporté</p>
<p>Je pense qu&#8217;il est temps de faire simple. Les vraies architectures Webs c&#8217;est pour maintenant. Allez lire <a href="http://iam.guillaume.bort.fr/">sur le blog de Guillaume Bort</a> pourquoi Play! Framework n&#8217;est pas basé sur l&#8217;API Servlet. C&#8217;est très intéressant.</p>
<p>Merci de penser que Java et les frameworks Webs c&#8217;est simple.</p>
<p><em><br />
</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2010/05/01/java-et-les-frameworks-webs-une-longue-histoire-compliquee/feed/</wfw:commentRss>
		<slash:comments>46</slash:comments>
		</item>
		<item>
		<title>L&#039;utilité de Twitter pour les Geeks</title>
		<link>http://www.touilleur-express.fr/2010/04/18/lutilite-de-twitter-pour-les-geeks/</link>
		<comments>http://www.touilleur-express.fr/2010/04/18/lutilite-de-twitter-pour-les-geeks/#comments</comments>
		<pubDate>Sun, 18 Apr 2010 16:00:37 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Perso]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=3582</guid>
		<description><![CDATA[Tout le monde se demande bien ce que l&#8217;on peut trouver à Twitter. Je ne sais pas si j&#8217;ai compris, mais en tous les cas, je peux au moins partager mon expérience. J&#8217;ai installé Twitter il y a 18 mois, sans franchement avoir une idée de son utilité. Le concept est simple : c&#8217;est un mégaphone placé dans les oreilles des gens qui souhaitent vous écouter. Pour peu que votre vie soit un peu intéressante, Twitter peut rendre de grands services, et surtout vous distraire.
Premier principe : les &#171;&#160;followers&#160;&#187; sont ...]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.touilleur-express.fr/wp-content/2511539541_b8c0356486_m.jpg" alt="" title="2511539541_b8c0356486_m" width="156" height="240" class="alignleft size-full wp-image-3607" />Tout le monde se demande bien ce que l&#8217;on peut trouver à Twitter. Je ne sais pas si j&#8217;ai compris, mais en tous les cas, je peux au moins partager mon expérience. J&#8217;ai installé Twitter il y a 18 mois, sans franchement avoir une idée de son utilité. Le concept est simple : c&#8217;est un mégaphone placé dans les oreilles des gens qui souhaitent vous écouter. Pour peu que votre vie soit un peu intéressante, Twitter peut rendre de grands services, et surtout vous distraire.</p>
<p>Premier principe : les &laquo;&nbsp;followers&nbsp;&raquo; sont des personnes qui s&#8217;abonnent à votre canal de diffusion Twitter. Plus vous avez de followers, plus vous êtes populaire. Cela me rappelle une phrase d&#8217;Audiard : <em>Lorsque les gens d&#8217;1m80 parlent, ceux d&#8217;1m60 écoutent&#8230;</em>&laquo;&nbsp;. Là c&#8217;est un peu la même chose : <em>lorsque les gens avec 1000 followers parlent, ceux avec 30 followers écoutent&#8230;</em>.</p>
<p>Twitter n&#8217;est pas un logiciel. C&#8217;est un service sur Internet. La société Twitter a ouvert l&#8217;API, ce qui a permis à beaucoup de développeurs d&#8217;implémenter différents logiciels. Sur Mac j&#8217;utilise <a href="http://www.atebits.com/tweetie-mac/">Tweetie</a> comme sur iPhone. Il a l&#8217;énorme avantage d&#8217;avoir un affichage par conversation, très pratique pour suivre le message d&#8217;origine lorsque quelqu&#8217;un vous répond. J&#8217;ai laissé tomber <a href="http://nambu.com/">Nambu</a>, mais c&#8217;est un bon client Mac. Sur Windows, <a href="http://tweetdeck.com/beta/">TweetDecks</a> est pas utilisé par les personnes que je followe. Il permet par exemple <a href="http://www.tweetdeck.com/features/increased-api-rate/index.html">d&#8217;ajuster son rate d&#8217;updates Twitter</a> par canal de diffusion : Mentions, DM, New Followers, Lists.<br />
La liste des applications est longue (Tweetr, Twibble, Seesmic). Je vous conseille de tester plusieurs clients.</p>
<p>J&#8217;utilise essentiellement Twitter sur mon iPhone. C&#8217;est un moyen de se tenir informé de ce que font vos amis, vos connaissances, et les bonhommes de la communauté Java pour moi. Ce matin je lisais les soucis du pauvre Sacha Labourey (@slabourey) qui tente de rejoindre Philadelphie en partant de Suisse. Il a conduit de nuit jusqu&#8217;à Barcelone, avant de prendre un avion de Barcelone à Madrid, puis les USA. On peut suivre aussi les conférences et se faire indiquer les sujets intéressants, tout comme les casseroles à éviter.<br />
Lorsque James Gosling a quitté SUN, nous l&#8217;avons su quelques heures plus tard, via Twitter. Je cherchais ce matin le nom de la voiture électrique très sportive, je pose la question sur Twitter, 12 personnes répondent. C&#8217;est la <a href="http://twitter.com/nmartignole/status/12395304426">Tesla Roadster</a>, une voiture qui passe de 0 à 100 en 3.9 secondes, fabriquée par Lotus en Angleterre puis équipée d&#8217;un moteur électrique par Tesla aux USA. <a href="http://twitter.com/nmartignole/status/12395664921">Cette petite vidéo</a> vous montrera les capacités de la bête.</p>
<p><strong>Twitter et d&#8217;autres services</strong><br />
Un grand nombre de services s&#8217;est lancé, afin de proposer des concepts au dessus de Twitter. Le site <a href="http://www.twitgraph.com/nmartignole/profile.aspx">Twitgraph.com</a> par exemple vous permet de regarder quelques statistiques sur votre activité sur Twitter. Le site <a href="http://foamee.com/">Foamee</a> vous rappelle lorsque vous devez une bière à quelqu&#8217;un. <a href="http://tweepletwak.com">Tweeple Twak</a> permet de suivre le nombre de gens qui arrêtent de vous suivre. Pratique pour les comptes Twitter &laquo;&nbsp;pro&nbsp;&raquo;.<br />
Le site <a href="http://twitterank.com/">Twitterank</a> est un peu le Top 50 de Twitter, avec des indications sur la popularité de votre compte.</p>
<p>Les sites de statistiques permettent de visualiser quelques statistiques comme la fréquence à laquelle vous Twittez sur <a href="http://www.twitteranalyzer.com/ms.aspx?userId=nmartignole">Twitter Analyzer</a>. Mon préféré est le site <a href="http://tweetstats.com/graphs/nmartignole">TweetStats</a>. Il donne des indicateurs précis sur la fréquence d&#8217;utilisation de Twitter, qui vous retweeter le plus, les mots clés que vous utilisez le plus.<br />
Voici par exemple mon hashtag cloud :</p>
<div class="tag-cloud"><span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#16</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#2</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#361</a></span> <span><a class="cloud_word" href="#" style="font-size: 16px; color: rgb(177, 204, 177);" style="font-size:16px;color:rgb(177,204,177);">#4</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#500</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#8217</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#agile</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#agileee</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#android</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#apple</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#barcamp</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#blague</a></span> <span><a class="cloud_word" href="#" style="font-size: 21px; color: rgb(108, 164, 108);" style="font-size:21px;color:rgb(108,164,108);">#citcon</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#concours</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#concoursletouilleur</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#concourstouilleur</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#culture</a></span> <span><a class="cloud_word" href="#" style="font-size: 30px; color: rgb(0, 102, 0);" style="font-size:30px;color:rgb(0,102,0);">#devoxx</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#dvcs</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#eclipse</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#envoyespecial</a></span> <span><a class="cloud_word" href="#" style="font-size: 16px; color: rgb(177, 204, 177);" style="font-size:16px;color:rgb(177,204,177);">#exo</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#expressboard</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#fatigue</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#fb</a></span> <span><a class="cloud_word" href="#" style="font-size: 18px; color: rgb(148, 187, 148);" style="font-size:18px;color:rgb(148,187,148);">#followfriday</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#frenchsug</a></span> <span><a class="cloud_word" href="#" style="font-size: 21px; color: rgb(114, 168, 114);" style="font-size:21px;color:rgb(114,168,114);">#gae</a></span> <span><a class="cloud_word" href="#" style="font-size: 16px; color: rgb(177, 204, 177);" style="font-size:16px;color:rgb(177,204,177);">#gaelyk</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#gandi</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#gatein</a></span> <span><a class="cloud_word" href="#" style="font-size: 21px; color: rgb(108, 164, 108);" style="font-size:21px;color:rgb(108,164,108);">#geek</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#geuledebois</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#gfxbuilder</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#git</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#google</a></span> <span><a class="cloud_word" href="#" style="font-size: 19px; color: rgb(137, 181, 137);" style="font-size:19px;color:rgb(137,181,137);">#grails</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#grogon</a></span> <span><a class="cloud_word" href="#" style="font-size: 17px; color: rgb(161, 195, 161);" style="font-size:17px;color:rgb(161,195,161);">#groovy</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#gwt</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#hibernate</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#infinispan</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#intellij</a></span> <span><a class="cloud_word" href="#" style="font-size: 16px; color: rgb(177, 204, 177);" style="font-size:16px;color:rgb(177,204,177);">#ipad</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#itablet</a></span> <span><a class="cloud_word" href="#" style="font-size: 18px; color: rgb(148, 187, 148);" style="font-size:18px;color:rgb(148,187,148);">#java</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#javafx</a></span> <span><a class="cloud_word" href="#" style="font-size: 18px; color: rgb(148, 187, 148);" style="font-size:18px;color:rgb(148,187,148);">#javaone</a></span> <span><a class="cloud_word" href="#" style="font-size: 27px; color: rgb(38, 124, 38);" style="font-size:27px;color:rgb(38,124,38);">#jazoon</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#jazoon2009</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#jboss</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#jbossworld</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#jduchess</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#jefusjeune</a></span> <span><a cla<br />
ss="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#jemelapeteensoiree</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#jesuisungeek</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#jsf2</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#kenschwaber</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#left4kdead</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#letouilleur</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#maven</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#maven3</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#nljug</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#nosql</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#nosqlfr</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#nostalgie</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#openweb</a></span> <span><a class="cloud_word" href="#" style="font-size: 27px; color: rgb(34, 122, 34);" style="font-size:27px;color:rgb(34,122,34);">#parisjug</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#parleys</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#phrase_a_la_con</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#play</a></span> <span><a class="cloud_word" href="#" style="font-size: 16px; color: rgb(177, 204, 177);" style="font-size:16px;color:rgb(177,204,177);">#playframework</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#qcon</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#raleur</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#rumeur</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#rumeurs</a></span> <span><a class="cloud_word" href="#" style="font-size: 19px; color: rgb(137, 181, 137);" style="font-size:19px;color:rgb(137,181,137);">#scala</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#securite</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#sfeir</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#socialcoding</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#soyezmoinsbete</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#spring</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#subversion</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#sug</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#superconnard</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#tapestry</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#tedxparis</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#tenyearsago</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#thecodersbreakfast</a></span> <span><a class="cloud_word" href="#" style="font-size: 16px; color: rgb(177, 204, 177);" style="font-size:16px;color:rgb(177,204,177);">#touilleurexpress</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#toutlemondesenfout</a></span> <span><a class="cloud_word" href="#" style="font-size: 17px; color: rgb(161, 195, 161);" style="font-size:17px;color:rgb(161,195,161);">#troll</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#tweetcloud</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#usi</a></span> <span><a class="cloud_word" href="#" style="font-size: 24px; color: rgb(71, 143, 71);" style="font-size:24px;color:rgb(71,143,71);">#usi2009</a></span> <span><a class="cloud_word" href="#" style="font-size: 14px; color: rgb(201, 218, 201);" style="font-size:14px;color:rgb(201,218,201);">#usi2010</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#volcan</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#wicket</a></span> <span><a class="cloud_word" href="#" style="font-size: 22px; color: rgb(98, 158, 98);" style="font-size:22px;color:rgb(98,158,98);">#xpday</a></span> <span><a class="cloud_word" href="#" style="font-size: 12px; color: rgb(0, 102, 0);" style="font-size:12px;color:rgb(0,102,0);">#zenika</a></span> </div>
<p>Enfin si vous souhaitez voir quels sont les personnes que vous suivez, qui ne vous suivent pas en retour, <a href="http://friendorfollow.com">un petit clic sur FriendOrFollow</a> et vous aurez la réponse à votre question.</p>
<p><strong>Références :</strong><br />
<a href="http://fr.mashable.com/2008/05/31/twitter-150-outilsservices-pour-aller-plus-loin/">Une longue liste de services Twitter pour aller plus loin</a><br />
<a href="http://smartech.blogetery.com/2008/05/16/69-twitter-web-services-you-should-know/">69 Twitter Web Services you should know</a> la liste complète et à jour des services pour Twitter.<br />
<a href="http://www.twitter.com/">Twitter</a> le site officiel.<br />
<a href="http://mashable.com/2009/06/27/twitter-desktop-apps/">19 Twitter Desktop Apps Compared</a> pour trouver une application pour votre PC ou votre Mac.</p>
<p>Crédit photo : <a href="http://www.flickr.com/photos/carrotcreative/">CarrotCreative</a> sur FlickR. Photo en licence Commons Creative.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2010/04/18/lutilite-de-twitter-pour-les-geeks/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

