<?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>Blog Technique d&#039;Infin-IT</title>
	<atom:link href="http://blog.infin-it.fr/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.infin-it.fr</link>
	<description>Le blog technique d&#039;Infin-It</description>
	<lastBuildDate>Fri, 15 Jul 2011 11:37:41 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>Git : comment passer à travers un proxy bloquant (push et pull)</title>
		<link>http://blog.infin-it.fr/2011/07/15/git-comment-passer-a-travers-un-proxy-bloquant-push-et-pull/</link>
		<comments>http://blog.infin-it.fr/2011/07/15/git-comment-passer-a-travers-un-proxy-bloquant-push-et-pull/#comments</comments>
		<pubDate>Fri, 15 Jul 2011 11:36:59 +0000</pubDate>
		<dc:creator>Jean-Philippe Briend</dc:creator>
				<category><![CDATA[Git]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[putty]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://blog.infin-it.fr/?p=366</guid>
		<description><![CDATA[Voici la configuration que j&#8217;applique lorsque mon poste de développement se situe derrière un proxy ultra-restrictif. Je pars du principe que vous êtes dans une entreprise, donc vous avez hélas un poste sous Windows. Le principe est d&#8217;utiliser plink au lieu de la commande ssh fournie par défaut par msysgit. Plink va permettre d&#8217;utiliser une [...]]]></description>
			<content:encoded><![CDATA[<p><img alt="Github" src="https://github.com/images/modules/contact/heartocat.png" title="Github" class="alignleft" width="230" height="230" /></p>
<p>Voici la configuration que j&#8217;applique lorsque mon poste de développement se situe derrière un proxy ultra-restrictif. Je pars du principe que vous êtes dans une entreprise, donc vous avez hélas un poste sous Windows.</p>
<p>Le principe est d&#8217;utiliser <em>plink</em> au lieu de la commande <em>ssh</em> fournie par défaut par <a href="http://code.google.com/p/msysgit/" title="msysgit" target="_blank">msysgit</a>.<br />
<em>Plink</em> va permettre d&#8217;utiliser une clef stockée par <em>pageant</em> et de faire un tunnel SSH vers le repository git.</p>
<p><br/><br />
<span id="more-366"></span></p>
<h3>1ère étape : Télécharger plink, pageant et putty</h3>
<p>Allez sur le site de <a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html" title="putty" target="_blank">putty</a> et téléchargez les exécutables suivants :</p>
<ul>
<li>putty.exe</li>
<li>plink.exe</li>
<li>pageant.exe</li>
<li>puttygen.exe</li>
</ul>
<h3>2ième étape : installer msysgit</h3>
<p>Téléchargez la dernière version de <em>msysgit</em> sur le <a href="http://code.google.com/p/msysgit/" title="msysgit" target="_blank">site officiel</a>.<br />
Suivez la procédure très bien décrite chez Github : <a href="http://help.github.com/win-set-up-git/" title="Setup Git for Windows" target="_blank">Setup Git for Windows</a>.<br />
Rien de particulier à signaler ici.</p>
<h3>3ième étape : changer l&#8217;agent SSH</h3>
<p>Nous allons maintenant changer l&#8217;agent SSH utilisé par msysgit : ajoutez la variable d&#8217;environnement <strong>GIT_SSH</strong> et mettez en valeur le chemin vers <strong>plink.exe</strong> (exemple : C:\Program Files (x86)\PuTTY\plink.exe).</p>
<h3>4ième étape : ppk-iser sa clé SSH</h3>
<p>Je pars du principe que vous avez une clé SSH générée. Elle se présente sous la forme d&#8217;une paire de fichiers (par défaut id_rsa et id_rsa.pub) contenant votre clé privée et la clé publique associée.<br />
A l&#8217;aide de <em>puttygen</em>, nous allons packager la clé au format .ppk (géré par pageant).</p>
<p>Lancez puttygen :<br />
<a href="http://blog.infin-it.fr/wp-content/uploads/2011/07/pageant_1.jpg"><img src="http://blog.infin-it.fr/wp-content/uploads/2011/07/pageant_1-300x288.jpg" alt="" title="pageant_1" width="300" height="288" class="aligncenter size-medium wp-image-380" /></a></p>
<p>Utilisez le menu <strong>Conversions</strong> puis <strong>Import key</strong>.<br />
Sélectionnez votre clé privée (par défaut id_rsa). Puttygen vous demande alors la passphrase pour accéder à votre clé.</p>
<p><a href="http://blog.infin-it.fr/wp-content/uploads/2011/07/pageant_2.jpg"><img src="http://blog.infin-it.fr/wp-content/uploads/2011/07/pageant_2-300x288.jpg" alt="" title="pageant_2" width="300" height="288" class="aligncenter size-medium wp-image-383" /></a></p>
<p>Cliquez sur le bouton <strong>Save private key</strong> pour sauvegarder votre clé au format ppk.</p>
<h3>5ième étape : charger sa clé dans pageant</h3>
<p>Pageant doit tourner pour que plink puisse l&#8217;utiliser.<br />
A chaque redémarrage du la machine, il faut rajouter la clé à pageant.</p>
<p>Lancer pageant : une icône apparait dans la barre des tâches.<br />
Un clic droit dessus vous permet de rajouter une clé (<strong>Add key</strong>).<br />
Sélectionnez votre clé au format ppk, entrez la passphrase et c&#8217;est tout.</p>
<h3>6ième étape : ajouter ssh.github.com à putty</h3>
<p>Maintenant, il faut configurer putty pour lui rajouter le site ssh.github.com.</p>
<p>Lancez putty et configurez le comme suit :</p>
<ul>
<li>Host name : <strong>ssh.github.com</strong></li>
<li>Port : <strong>443</strong></li>
<li>Dans l&#8217;onglet Proxy, entrez les informations relatives à votre proxy : hostname, port et éventuellement login/password.</li>
<li>Dans l&#8217;onglet <em>Connection &#8211; SSH &#8211; Auth</em>, chargez votre clé privé à l&#8217;aide du bouton <em>Browse</em></li>
<li>Revenez sur l&#8217;onglet Session et sauvegardez cette session avec un nom précis (par exemple <strong>gitproxy</strong>).</li>
</ul>
<p>La configuration dans putty suffit.<br />
Par contre, il faut se connecter une fois au serveur pour accepter la clé SSH du serveur : ouvrez une connection ssh avec putty grâce à la configuration précédemment créée et acceptez le certificat. Ceci n&#8217;est à faire qu&#8217;une seule fois.</p>
<h3>7ième étape : cloner un repo dans Git ou ajouter un repo distant</h3>
<p>La procédure est quasiment la même :</p>
<ul>
<li>Pour cloner un repository Git, récupérez l&#8217;URL du repository utilisant le protocole git (et non le http). Cette URL est au format : <em>git@github.com:username/myRepo.git</em>.</li>
<li>Exécutez la commande suivante pour faire le clone Git : <em>git clone git@<strong>gitproxy</strong>:username/myRepo.git</em>. Notez le remplacement de github.com par le nom de la configuration Putty.</li>
</ul>
<p>C&#8217;est tout : vous pouvez puller et pusher sur ce repository.<br />
N&#8217;oubliez pas que pour que ceci fonctionne, il faut que pageant tourne et ait votre clé privée chargée.</p>
<p>L&#8217;astuce est dans le fait que lors d&#8217;une commande Git, ce dernier va exécuter plink pour se connecter en SSH au repository distant. Plink va alors regarder l&#8217;adresse dans l&#8217;URL, et chercher dans putty si une configuration existe avec ce nom. Comme c&#8217;est le cas, il va utiliser la configuration putty pour se connecter.</p>
<p>Pour ajouter un repository distant, faites de même avec :<br />
<em>git remote add monRepo git@gitproxy:username/myRepo.git</em><br />
N&#8217;oubliez pas de substituer github.com par gitproxy.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.infin-it.fr/2011/07/15/git-comment-passer-a-travers-un-proxy-bloquant-push-et-pull/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Une application Play! sur Google AppEngine</title>
		<link>http://blog.infin-it.fr/2010/12/27/une-application-play-sur-google-appengine/</link>
		<comments>http://blog.infin-it.fr/2010/12/27/une-application-play-sur-google-appengine/#comments</comments>
		<pubDate>Mon, 27 Dec 2010 12:18:56 +0000</pubDate>
		<dc:creator>Jean-Philippe Briend</dc:creator>
				<category><![CDATA[Play!]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[play!]]></category>

		<guid isPermaLink="false">http://blog.infin-it.fr/?p=300</guid>
		<description><![CDATA[Dans cet article, je vais vous montrer comment créer une application Play! et comment la déployer sur Google AppEngine. Google AppEngine (GAE) propose une solution d&#8217;hébergement d&#8217;applications gratuites très pratiques pour des applications Java. La liste des fonctionnalités offertes par GAE peut-être consultée ici. Le framework Play! fournit un module extrêmement bien fait pour déployer [...]]]></description>
			<content:encoded><![CDATA[<p>Dans cet article, je vais vous montrer comment créer une application Play! et comment la déployer sur Google AppEngine.</p>
<p>Google AppEngine (GAE) propose une solution d&#8217;hébergement d&#8217;applications gratuites très pratiques pour des applications Java. La liste des fonctionnalités offertes par GAE peut-être consultée <a href="http://code.google.com/appengine/docs/whatisgoogleappengine.html">ici</a>.<br />
Le framework <a href="http://www.playframework.org/">Play!</a> fournit un <a href="http://www.playframework.org/modules/gae">module</a> extrêmement bien fait pour déployer des applications sur Gae et un autre module est disponible pour gérer le Datastore Gae : <a href="http://www.playframework.org/modules/siena">Siena</a>.<br />
<span id="more-300"></span><br />
<br/></p>
<p>
<center><img src="http://code.google.com/appengine/images/appengine-silver-120x30.gif" alt="Powered by Google App Engine" /></center>
</p>
<h1>Google AppEngine</h1>
<p>
Google fournit un SDK complet pour développer des applications destinées à être hébergées par GAE. Dans le cas d&#8217;une application Play!, ce SDK est intégré au module GAE et le SDK n&#8217;est nécessaire que pour le déploiement final.<br />
Il faut récupérer ce SDK à cette adresse : <a href="http://code.google.com/appengine/downloads.html">http://code.google.com/appengine/downloads.html</a>.<br />
La version Windows nécessite d&#8217;installer aussi Python (un lien est fourni via l&#8217;installeur du SDK).
</p>
<p>
Attaquons-nous à la partie applicative de cet article !
</p>
<p>
<center><img src="http://www.playframework.org/public/images/logo.png"/></center>
</p>
<h1>Play! Framework</h1>
<p>
Tout d&#8217;abord, il faut installer les modules GAE et Siena de Play! :<br />
<code><br />
play install gae<br />
play install siena<br />
</code><br />
Une fois ces installations terminées, créons notre future application <em>test-gae</em><br />
<code><br />
play new test-gae<br />
</code><br />
Enrichissons le <em>conf/application.conf</em> nouvellement créé avec les 2 modules précédents :<br />
<code><br />
# ---- MODULES ----<br />
module.gae=${play.path}/modules/gae-1.4<br />
module.siena=${play.path}/modules/siena-1.3<br />
</code>
</p>
<h3>Le Modèle</h3>
<p>
GAE sans stocker des données dans le Datastore, ça ne sert pas à grand chose.<br />
Nous allons donc créer un objet modèle très simple que l&#8217;on retrouve dans quasiment toutes les applications : <b>User</b> !</p>
<pre class="brush: java; title: ; notranslate">
package models;

import java.util.List;
import siena.Column;
import siena.Generator;
import siena.Id;
import siena.Max;
import siena.Model;
import siena.NotNull;
import siena.Query;

public class User extends Model {

    @Id(Generator.AUTO_INCREMENT)
    public Long id;

    @Column(&quot;login&quot;)
    @Max(15) @NotNull
    public String login;

    @Column(&quot;nickname&quot;)
    @Max(15) @NotNull
    public String nickname;
    ...
    ...
}
</pre>
<p>Il y a plusieurs choses importantes à noter :</p>
<ul>
<li>La classe User étend la classe <em>siena.Model</em> et non plus la classe <em>play.Model</em> du framework Play!.<br />
Logique : <em>play.Model</em> est destiné à JPA qui n&#8217;est pas utilisé par GAE.</li>
<li>Il faut mettre un <b>Id</b> ! L&#8217;annotation <b>@Id</b> est <u>obligatoire</u> et elle doit pointer sur une variable de type <b>Long</b> nommée <b>id</b>.</li>
<li>Les autres annotations sont optionnelles. Par défaut, la table créée prendra le nom de la classe et les colonnes prendront le nom des variables sur lesquelles elles pointent.</li>
</ul>
<p>
Il va maintenant falloir ajouter des méthodes à notre modèle afin de pouvoir récupérer des données :</p>
<pre class="brush: java; title: ; notranslate">
...
static Query&lt;User&gt; all() {
    return Model.all(User.class);
}

public static List&lt;User&gt; findAll() {
    return all().fetch();
}

public static User findById(Long id) {
    return all().filter(&quot;id&quot;, id).get();
}

public static User findByLogin(String login) {
    return all().filter(&quot;login&quot;, login).get();
}
</pre>
<p>Nous avons ajouté une méthode protected <em>all()</em> qui créé une <b>Query</b> qui ramènera tous les Users.<br />
A partir de cette méthode, nous allons appliquer différents filtres pour ramener les données qui nous intéressent :</p>
<ul>
<li><em>fetch()</em> pour ramener toutes les données d&#8217;une Query.</li>
<li><em>filter(&#8220;id&#8221;, id).get()</em> pour filtrer les données dont l&#8217;attribut <b>id</b> est égal à <em>id</em>.</li>
<li><em>filter(&#8220;login&#8221;, login).get()</em> pour filtrer les données dont l&#8217;attribut <b>login</b> est égal à <em>login</em>.</li>
</ul>
<p>
Nous avons maintenant un petit modèle. Attaquons-nous au contrôleur dédié à User !
</p>
<h3>Le Contrôleur</h3>
<p>
Pour faire le plus simple possible, utilisons le contrôleur créé par défaut par Play! : <b>Application</b>.</p>
<pre class="brush: java; title: ; notranslate">
package controllers;

import java.util.List;

import models.User;
import play.mvc.Controller;

public class Application extends Controller {

    public static void index() {
        List&lt;User&gt; users = User.findAll();
        render(users);
    }

    public static void add(String login, String nickname) {
        User add = new User();
        add.login = login;
        add.nickname = nickname;
        add.insert();

        index();
    }

    public static void delete(Long id) {
        notFoundIfNull(id);
        User toRemove = User.findById(id);
        toRemove.delete();

        index();
    }
}
</pre>
<p>Il s&#8217;agit d&#8217;un contrôleur tout ce qu&#8217;il y a de plus classique en Play!.<br />
Une des méthodes va ramener la liste des Users, une autre va permettre d&#8217;en ajouter un et enfin la dernière permet d&#8217;en supprimer un.
</p>
<p>
Notre contrôleur est prêt, ajoutons-lui une page html pour pouvoir jouer avec !
</p>
<h3>La Vue</h3>
<p>
Contentons-nous d&#8217;utiliser le fichier index.html du contrôleur Application :</p>
<pre class="brush: xml; title: ; notranslate">
#{extends 'main.html' /}
#{set title:'Home' /}

&lt;p&gt;
&lt;h2&gt;Liste des Users :&lt;/h2&gt;
  #{list items:users, as:'user'}
    &lt;div&gt;&lt;label&gt;[${user.id}] ${user.login} (${user.nickname})&lt;/label&gt;&lt;label&gt;#{a @Application.delete(user.id)}Delete#{/a}&lt;/label&gt;&lt;/div&gt;
  #{/list}
&lt;/p&gt;

&lt;p&gt;
#{form @Application.add()}
&lt;h2&gt;Add a User :&lt;/h2&gt;
&lt;div&gt;
  &lt;label&gt;Login :&lt;/label&gt;
  &lt;input type=&quot;text&quot; name=&quot;login&quot; /&gt;
&lt;/div&gt;

&lt;div&gt;
  &lt;label&gt;Nickname :&lt;/label&gt;
  &lt;input type=&quot;text&quot; name=&quot;nickname&quot; /&gt;
&lt;/div&gt;
&lt;div&gt;
  &lt;input type=&quot;submit&quot; value=&quot;Add&quot; /&gt;
&lt;/div&gt;
#{/form}
&lt;/p&gt;
</pre>
<p>Rien que du très classique Play! : une liste pour afficher tous les Users, un lien pour supprimer un User et enfin un formulaire pour créer un User.
</p>
<p>
Notre application est désormais prête à être déployée sur Google AppEngine !<br />
Vous pouvez la tester avec un <em>play run</em>, elle devrait tourner parfaitement.
</p>
<h1>Le déploiement sur Google AppEngine</h1>
<p>
Lorsque nous avons créé l&#8217;application avec un <em>play new test-gae</em>, le module GAE a créé un répertoire <b>war</b> à la racine de notre application.<br />
Celui-ci contient un fichier généré automatiquement : <b>web.xml</b>.<br />
Il va être important de le modifier pour pouvoir déployer correctement sur GAE.</p>
<pre class="brush: xml; title: ; notranslate">
&lt;appengine-web-app xmlns=&quot;http://appengine.google.com/ns/1.0&quot;&gt;
  &lt;application&gt;&lt;!-- ID GAE de votre application --&gt;&lt;/application&gt;
  &lt;version&gt;1&lt;/version&gt;
&lt;/appengine-web-app&gt;
</pre>
<p>La balise <b>application</b> doit contenir l&#8217;ID de votre application Google AppEengine.<br />
Pour le déterminer, il faut créer une application dans Google AppEngine.
</p>
<h3>Créer une application dans Google AppEngine</h3>
<p>
Rendez-vous sur <a href="https://appengine.google.com/">https://appengine.google.com/</a>.<br />
Vous devez vous identifier avec votre compte Gmail.<br />
Lors de la création de votre première application, il vous demandera sûrement un numéro de téléphone portable pour vous envoyer un code.<br />
Une fois cette étape passée, vous devriez pouvoir créer une application Google AppEngine :<br />
<img src="http://blog.infin-it.fr/wp-content/uploads/2010/12/Gae_Create.jpg" width="600" height="320"/><br />
C&#8217;est le champ <em>Application Identifier</em> qui contient l&#8217;id de votre application à mettre dans votre fichier <em>web.xml</em>.
</p>
<p>
Hop, votre application a dûe être déclarée dans Google AppEngine !
</p>
<h3>Déployer l&#8217;application sur Google AppEngine</h3>
<p>
Ma petite soeur peut le faire : en se plaçant dans le répertoire de notre application, il suffit de lancer :<br />
<code><br />
play gae:deploy --gae={path_to_gae_sdk}<br />
</code><br />
<em>path_to_gae_sdk</em> indique votre installation du SDK Google AppEngine.
</p>
</p>
<p>Vous devriez voir Play! précompiler les classes, assembler un war puis tenter d&#8217;uploader votre application.<br />
Il vous demanderas sûrement votre login et mot de passe Gmail pour vous identifier sur votre compte.<br />
Une fois l&#8217;upload terminé, vous pouvez administrer votre application grâce à la console AppEngine :<br />
<a href="https://appengine.google.com">https://appengine.google.com</a></p>
<p>Votre application est disponible à l&#8217;url indiquée par la console d&#8217;administration :<br />
<center><img src="http://blog.infin-it.fr/wp-content/uploads/2010/12/Gae_deployed.jpg" style="border: 1px solid black;"/></center>
</p>
<p>
Voici l&#8217;archive du projet, prête à l&#8217;emploi : <a href="http://blog.infin-it.fr/wp-content/uploads/2010/12/test-gae.zip">Zip</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.infin-it.fr/2010/12/27/une-application-play-sur-google-appengine/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Play! Framework : enrichir votre application avec un éditeur riche Textile</title>
		<link>http://blog.infin-it.fr/2010/12/16/play-framework-enrichir-votre-application-avec-un-editeur-riche-textile/</link>
		<comments>http://blog.infin-it.fr/2010/12/16/play-framework-enrichir-votre-application-avec-un-editeur-riche-textile/#comments</comments>
		<pubDate>Thu, 16 Dec 2010 17:34:37 +0000</pubDate>
		<dc:creator>Jean-Philippe Briend</dc:creator>
				<category><![CDATA[Play!]]></category>
		<category><![CDATA[markItUp]]></category>
		<category><![CDATA[play!]]></category>

		<guid isPermaLink="false">http://blog.infin-it.fr/?p=262</guid>
		<description><![CDATA[L'objectif est d'intégrer dans une page un éditeur de texte riche au format Textile. Cette syntaxe est très simple à appréhender et la génération de code HTML à partir de code Textile est native dans Play! grâce à la librairie Wikitext.]]></description>
			<content:encoded><![CDATA[<p>Voici un billet un peu plus pratique utilisant le framework <a href="http://www.playframework.org/">Play!</a>.<br />
Contrairement à mon <a href="http://blog.infin-it.fr/2010/12/15/play-framework-integration-continue-retour-dexperience/">billet précédent</a>, celui-ci s&#8217;adresse avant tout aux développeurs.</p>
<p>L&#8217;objectif est d&#8217;intégrer dans une page un éditeur de texte riche au format <a href="http://fr.wikipedia.org/wiki/Textile_(langage)">Textile</a>. Cette syntaxe est très simple à appréhender et la génération de code HTML à partir de code Textile est native dans Play! grâce à la librairie <a href="http://wiki.eclipse.org/Mylyn/Incubator/WikiText">Wikitext</a>.</p>
<p>En ce qui concerne l&#8217;éditeur en tant que tel, nous allons utiliser l&#8217;excellent <a href="http://markitup.jaysalvat.com">MarkItUp</a>. Il est basé sur <a href="http://jquery.com/">jQuery</a>, lui même nativement embarqué par Play!. MarkItUp propose un éditeur modulaire, permettant de gérer plusieurs syntaxes. L&#8217;ajout de syntaxes se fait par des <em>sets</em>.</p>
<p><img src="http://blog.infin-it.fr/wp-content/uploads/2010/12/markItUp.jpg" /><br />
<span id="more-262"></span></p>
<h1>MarkItUp</h1>
<p>Commençons par télécharger <em>MarkItUp! pack</em> sur la page des <a href="http://markitup.jaysalvat.com/downloads/">téléchargements</a>.<br />
Il faut aussi prendre le <em>Basic Textile Set</em>.<br />
L&#8217;archive MarkItUp contient un répertoire <em><strong>markitup</strong></em> : c&#8217;est celui-ci qu&#8217;il faut décompresser dans le répertoire <em><strong>public</strong></em> de votre application Play!.<br />
Il faut ensuite décompresser l&#8217;archive contenant le <em>Basic Textile Set</em> dans le répertoire <em><strong>public/markitup/sets</strong></em> : un nouveau répertoire <em><strong>textile</strong></em> a dû apparaître.</p>
<p>L&#8217;installation des fichiers de MarkItUp est désormais terminée. Nous reviendrons plus tard pour la configuration et la customisation de l&#8217;éditeur.</p>
<h1>Générateur Wikitext</h1>
<p>Nous allons avoir besoin de 2 méthodes :</p>
<ul>
<li>une 1ère méthode pour générer le code destiné à l&#8217;iframe Preview. Qui dit iframe dit qu&#8217;il va falloir rajouter les feuilles css au code HTML généré.</li>
<li>une 2ième méthode destinée à générer le code qui sera faire le rendu du code Textile saisi par l&#8217;utilisateur. Cette fois-ci, il ne faut pas que les css soient inclues dans le code html généré.</li>
</ul>
<p>J&#8217;ai décidé de créer ces 2 méthodes dans un Controler :</p>
<pre class="brush: java; title: ; notranslate">
package controllers;

import java.io.StringWriter;

import jj.play.org.eclipse.mylyn.wikitext.core.parser.MarkupParser;
import jj.play.org.eclipse.mylyn.wikitext.core.parser.builder.HtmlDocumentBuilder;
import jj.play.org.eclipse.mylyn.wikitext.core.parser.builder.HtmlDocumentBuilder.Stylesheet;
import jj.play.org.eclipse.mylyn.wikitext.textile.core.TextileLanguage;
import play.mvc.Controller;

public class Wikitext extends Controller {

    // Controller classique Play! qui sera appelé par l'iframe Preview de MarkItUp.
    public static void renderPreview(String wiki) {
        StringWriter writer = new StringWriter();
        HtmlDocumentBuilder builder = new HtmlDocumentBuilder(writer);

        // Ajout de mes 2 css.
        Stylesheet css = new Stylesheet(&quot;/public/stylesheets/main.css&quot;);
        builder.addCssStylesheet(css);
        css = new Stylesheet(&quot;/public/stylesheets/wiki.css&quot;);
        builder.addCssStylesheet(css);

        // Création du parser Textile
        MarkupParser parser = new MarkupParser(new TextileLanguage());
        parser.setBuilder(builder);
        parser.parse(wiki);

        String htmlContent = writer.toString();
        renderText(htmlContent);
    }

    // Méthode qui sera appelée par les templates Groovy de Play!.
    public static String render(String wiki) {
        StringWriter writer = new StringWriter();
        HtmlDocumentBuilder builder = new HtmlDocumentBuilder(writer);
        // Empêche la génération des balises html et body.
        builder.setEmitAsDocument(false);

        // Création du parser Textile
        MarkupParser parser = new MarkupParser(new TextileLanguage());
        parser.setBuilder(builder);
        parser.parse(wiki);

        return writer.toString();
    }
}
</pre>
<p>Ces 2 méthodes n&#8217;ont rien de spécial : elles utilisent Wikitext qui parse le code Textile de l&#8217;utilisateur et en fait un rendu HTML.<br />
Notez dans l&#8217;une l&#8217;ajout de mes 2 css alors que l&#8217;autre méthode ne génère pas les tags <em>html</em> et <em>body</em>.</p>
<p>Afin de rendre ce controller un peu plus sexy, ajoutons lui une route pour le rendu de Preview :</p>
<pre class="brush: plain; title: ; notranslate">

*       /wikitext/renderPreview                 Wikitext.renderPreview
</pre>
<h1>Installation de markItUp dans nos templates</h1>
<p>Tout d&#8217;abord, il faut ajouter dans la page que vous voulez enrichier de notre éditeur riche le code suivant qui chargera markItUp :</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!--  Loading Markitup --&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;@{'/public/markitup/skins/markitup/style.css'}&quot; /&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;@{'/public/markitup/jquery.markitup.js'}&quot;&gt;&lt;/script&gt;

&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;@{'/public/markitup/sets/textile/style.css'}&quot; /&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;@{'/public/markitup/sets/textile/set.js'}&quot;&gt;&lt;/script&gt;

&lt;script language=&quot;javascript&quot;&gt;
$(document).ready(function()    {
    $('#wiki').markItUp(mySettings);
});
&lt;/script&gt;
&lt;!-- End markitup --&gt;
</pre>
<p>Attention à modifier le code ci-dessus pour que l&#8217;identifiant &#8216;#wiki&#8217; pointe bien vers l&#8217;id du <em>textarea</em> que vous voulez enrichir avec markItUp.<br />
De plus, notez la variable <em>mySettings</em>. Celle-ci contient la configuration du plugin markItUp sous format JSON.</p>
<p>Voici un exemple de textarea qui va être enrichi par le plugin markItUp :</p>
<pre class="brush: xml; title: ; notranslate">
&lt;textarea id=&quot;wiki&quot; name=&quot;description&quot; cols=&quot;70&quot; rows=&quot;20&quot;&gt;
    Bla Bla ...
&lt;/textarea&gt;
</pre>
<h1>Configuration et customisation de markItUp</h1>
<p>Il va falloir maintenant configurer et customiser markItUp.</p>
<p>Pour cela, il va falloir commencer par éditer le fichier <em><strong>public/markitup/sets/textile/set.js</strong></em> :</p>
<pre class="brush: css; title: ; notranslate">
mySettings = {
	previewParserPath:	'/wikitext/renderPreview', // path to your Wiki parser
	previewParserVar:	'wiki',
	onShiftEnter:		{keepDefault:false, replaceWith:'\n\n'},
	markupSet: [
		{name:'Heading 1', key:'1', openWith:'h1(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
		{name:'Heading 2', key:'2', openWith:'h2(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
		{name:'Heading 3', key:'3', openWith:'h3(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
		{name:'Heading 4', key:'4', openWith:'h4(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
		{name:'Heading 5', key:'5', openWith:'h5(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
		{name:'Heading 6', key:'6', openWith:'h6(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
		{name:'Paragraph', key:'P', openWith:'p(!(([![Class]!]))!). '},
		{separator:'---------------' },
		{name:'Bold', key:'B', closeWith:'*', openWith:'*'},
		{name:'Italic', key:'I', closeWith:'_', openWith:'_'},
		{name:'Stroke through', key:'S', closeWith:'-', openWith:'-'},
		{separator:'---------------' },
		{name:'Bulleted list', openWith:'(!(* |!|*)!)'},
		{name:'Numeric list', openWith:'(!(# |!|#)!)'},
		{separator:'---------------' },
		{name:'Picture', replaceWith:'![![Source:!:http://]!]([![Alternative text]!])!'},
		{name:'Link', openWith:'&quot;', closeWith:'([![Title]!])&quot;:[![Link:!:http://]!]', placeHolder:'Your text to link here...' },
		{separator:'---------------' },
		{name:'Quotes', openWith:'bq(!(([![Class]!]))!). '},
		{name:'Code', openWith:'@', closeWith:'@'},
		{separator:'---------------' },
		{name:'Preview', call:'preview', className:'preview'}
	]
}
</pre>
<p>J&#8217;ai fais les modifications suivantes :</p>
<ul>
<li>Remplissage de la variable <strong>previewParserPath</strong> : on pointe vers le controller que nous avons écrit plus tôt.</li>
<li>Ajout de la variable <strong>previewParserVar</strong> pour donner le nom du paramètre HTTP contenant le code à envoyer au controller.</li>
</ul>
<p>Le reste du fichier sert à configurer l&#8217;apparence et les boutons de markItUp. Je vous laisse le soin de fouiller la documentation de ce plugin si vous le souhaitez.</p>
<p>Il ne reste qu&#8217;à terminer par un peu de customisation via une CSS.<br />
J&#8217;ai modifié le fichier <em><strong>public/markitup/sets/textile/style.css</strong></em> et j&#8217;ai rajouté les lignes suivantes :</p>
<pre class="brush: css; title: ; notranslate">
.markItUp  {
    width:500px;
}

.markItUpEditor {
    width:443px;
    height:220px;
}
</pre>
<p>Ces surcharges me servent uniquement à redimensionner markItUp.</p>
<p>Et voilà !<br />
En une petite dizaine de minutes, nous avons réussi à transformer un simple Textarea en un éditeur riche supportant Textile avec un rendu aux petits oignons. Soignez votre CSS de rendu et vous êtes le roi du pétrole !</p>
<p><strong>Bonus </strong>: Voici un projet Play! contenant le code de cet article. Lancez-le avec un <em>play run</em> et ça marche !<br />
<a href="http://blog.infin-it.fr/wp-content/uploads/2010/12/sample-markitup.zip">Télécharger le code</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.infin-it.fr/2010/12/16/play-framework-enrichir-votre-application-avec-un-editeur-riche-textile/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Play! Framework / Intégration Continue : retour d&#8217;expérience</title>
		<link>http://blog.infin-it.fr/2010/12/15/play-framework-integration-continue-retour-dexperience/</link>
		<comments>http://blog.infin-it.fr/2010/12/15/play-framework-integration-continue-retour-dexperience/#comments</comments>
		<pubDate>Wed, 15 Dec 2010 08:42:10 +0000</pubDate>
		<dc:creator>Jean-Philippe Briend</dc:creator>
				<category><![CDATA[Play!]]></category>
		<category><![CDATA["intégration continue"]]></category>
		<category><![CDATA[play!]]></category>

		<guid isPermaLink="false">http://blog.infin-it.fr/?p=224</guid>
		<description><![CDATA[Depuis le 2 novembre, la version 1.1 du framework Play! est disponible. Je ne vous ferai pas une présentation de ce framework, il en existe plusieurs dont celle du Touilleur Express (Play! Framework). Je vais plutôt vous faire un retour d&#8217;expérience de la mise en place des outils nécessaires pour intégrer des applications Play! à [...]]]></description>
			<content:encoded><![CDATA[<p>Depuis le 2 novembre, la version 1.1 du framework <a href="http://www.playframework.org">Play!</a> est disponible.<br />
Je ne vous ferai pas une présentation de ce framework, il en existe plusieurs dont celle du Touilleur Express (<a href="http://www.touilleur-express.fr/2010/02/12/version-play-de-lapplication-zencontact-de-zenika/">Play! Framework</a>).</p>
<p>Je vais plutôt vous faire un retour d&#8217;expérience de la mise en place des outils nécessaires pour intégrer des applications Play! à notre usine d&#8217;Intégration Continue.<br />
<span id="more-224"></span><br />
Commençons par la description de notre environnement, très classique :</p>
<ul>
<li>tous les projets Java sont mavenisés (<a href="http://maven.apache.org/">Maven 2</a>).</li>
<li>tous les projets définissent des tests unitaires (<a href="http://www.junit.org/">JUnit</a>).</li>
<li>certains projets ont des tests <a href="http://seleniumhq.org/">Selenium</a>.</li>
<li>tous les projets ont des builds lancés automatiquement par <a href="http://www.atlassian.com/software/bamboo/">Bamboo</a>.</li>
<li>tous les projets sont analysés par <a href="http://www.sonarsource.org/">Sonar</a> chaque nuit</li>
</ul>
<p>La tâche est de faire rentrer les futures applications écrites avec Play! dans cette chaîne d&#8217;Intégration Continue.</p>
<h2>Mavenisation d&#8217;une application Play!</h2>
<p>Il s&#8217;agit de l&#8217;étape qui fut la plus compliquée et surtout la plus longue. Le cahier des charges est le suivant :</p>
<ul>
<li>Gestion des dépendances applicatives (ie: remplir le répertoire /lib avec les dependencies Maven du projet).</li>
<li>Gestion des librairies inclues dans le framework Play! (afin que la phase <em>compile</em> se passe correctement).</li>
<li>Maven doit pouvoir faire exécuter les tests unitaires et en récupérer un rapport Surefire.</li>
<li>Maven doit pouvoir exécuter Cobertura sur l&#8217;application et en extraire un rapport (à destination de Sonar).</li>
<li>Une analyse Sonar doit être effectuée sur l&#8217;application.</li>
</ul>
<h3>Gestion des dépendances applicatives</h3>
<p>Avec un peu de configuration Maven, (<a href="http://maven.apache.org/plugins/maven-dependency-plugin/">plugin dependency</a>), il est assez facile de faire exporter les librairies applicatives dans le répertoires <em>lib</em> de l&#8217;application, ainsi que de les supprimer lors d&#8217;un <em>mvn clean</em>.</p>
<pre class="brush: xml; title: ; notranslate">
  &lt;plugin&gt;
        &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
        &lt;artifactId&gt;maven-dependency-plugin&lt;/artifactId&gt;
        &lt;version&gt;2.1&lt;/version&gt;
        &lt;executions&gt;
          &lt;execution&gt;
            &lt;id&gt;default-cli&lt;/id&gt;
            &lt;configuration&gt;
              &lt;includeClassifiers&gt;,sources&lt;/includeClassifiers&gt;
              &lt;outputDirectory&gt;lib/&lt;/outputDirectory&gt;
              &lt;excludeScope&gt;provided&lt;/excludeScope&gt;
              &lt;!-- To download sources jars without failing --&gt;
              &lt;failOnMissingClassifierArtifact&gt;false&lt;/failOnMissingClassifierArtifact&gt;
              &lt;excludeArtifactIds&gt;play-runtime,play&lt;/excludeArtifactIds&gt;
            &lt;/configuration&gt;
         ...
</pre>
<h3>Gestion des dépendances du framework Play!</h3>
<p>Là, le boulot est un peu plus long car j&#8217;ai dû référencer toutes les librairies du framework. Cependant les versions sont très standard (mis à part un patch sur Hibernate), donc le boulot se fait bien.<br />
Petit écueil : certaines librairies n&#8217;existent pas dans les repository standards. Il y en a donc certaines qui doivent être uploadées dans votre Nexus d&#8217;entreprise.</p>
<p>Ces 2 étapes doivent vous permettre de compiler un projet Play! sous Maven sans encombre.</p>
<p>Maintenant que la compilation fonctionne, préparons l&#8217;exécution des tests unitaires via le runtime de Play!</p>
<h3>Le Runtime de Play! pour les Tests unitaires</h3>
<p>Je sais que la pratique suivante fera bondir certains, mais le runtime de Play! est tout simplement beaucoup trop puissant pour que l&#8217;on puisse s&#8217;en passer !<br />
Nous ne voulions pas devoir gérer l&#8217;installation du runtime sur les différents environnements où les agents Bamboo sont déployés. Il faut donc le packager sous forme de ressources avec ses modules intégrés dans le repository Maven. Une tâche Ant se chargera de le dézipper afin que la commande <em>play</em> soit accessible.</p>
<pre class="brush: xml; title: ; notranslate">
  &lt;dependency&gt;
    &lt;groupId&gt;org.play&lt;/groupId&gt;
    &lt;artifactId&gt;play-runtime&lt;/artifactId&gt;
    &lt;version&gt;1.1&lt;/version&gt;
    &lt;type&gt;zip&lt;/type&gt;
  &lt;/dependency&gt;
</pre>
<h2>Les tests unitaires</h2>
<p>Les tests unitaires de Play! framework sont des tests JUnit mais lancés par le runtime Play!.</p>
<p>La commande <em>auto-test</em> permet de lancer les tests unitaires et les tests Selenium automatiquement. Un reporting sous forme de fichier html est généré. J&#8217;ai donc dû enrichir le framework pour générer un rapport au format <a href="http://maven.apache.org/plugins/maven-surefire-plugin/">Surefire</a>. Ce rapport contient le nombre de tests unitaires joués, le nombre d&#8217;erreurs, d&#8217;echecs, le temps passé ainsi qu&#8217;un détail des erreurs.<br />
Un script ant permet de faire lancer par Maven le framework Play! en mode <em>auto-test</em>.</p>
<p>Voici une exécution Maven du plugin mavent-antrun-plugin :</p>
<pre class="brush: xml; title: ; notranslate">
&lt;execution&gt;
  &lt;id&gt;UnitTests&lt;/id&gt;
  &lt;phase&gt;test&lt;/phase&gt;
  &lt;configuration&gt;
    &lt;target&gt;
      &lt;property name=&quot;maven.project.artifactId&quot; value=&quot;${project.artifactId}&quot; /&gt;
      &lt;property name=&quot;maven.project.version&quot; value=&quot;${project.version}&quot; /&gt;
      &lt;property name=&quot;maven.project.play-runtime&quot; value=&quot;play-runtime-${play.version}&quot; /&gt;

      &lt;property name=&quot;play.runtime.path&quot; value=&quot;${basedir}/target/dependency/${maven.project.play-runtime}&quot; /&gt;
      &lt;property name=&quot;play.runtime.file&quot; value=&quot;${basedir}/target/dependency/${maven.project.play-runtime}.zip&quot; /&gt;
      &lt;property name=&quot;play.runtime.unzip.path&quot; value=&quot;${basedir}/target/dependency&quot; /&gt;

      &lt;!-- Check zipped Play! runtime is present. Build fails if not present --&gt;
      &lt;available file=&quot;${play.runtime.file}&quot; property=&quot;play.runtime.present&quot; /&gt;
      &lt;echo message=&quot;Is Play! runtime present : ${play.runtime.present}&quot; /&gt;

      &lt;fail message=&quot;Error : Play! runtime could not be found at : ${play.runtime.file}&quot;&gt;
        &lt;condition&gt;
          &lt;not&gt;
            &lt;isset property=&quot;play.runtime.present&quot; /&gt;
          &lt;/not&gt;
        &lt;/condition&gt;
      &lt;/fail&gt;

      &lt;!-- Delete existing Play! runtime --&gt;
      &lt;echo message=&quot;Deleting any existing unzipped Play! runtime ...&quot; /&gt;
      &lt;delete dir=&quot;play.runtime.unzip.path&quot; failonerror=&quot;false&quot; verbose=&quot;true&quot; /&gt;

      &lt;!-- Unzip Play! runtime --&gt;
      &lt;echo message=&quot;Unzipping Play! runtime ...&quot; /&gt;
      &lt;unzip src=&quot;${play.runtime.file}&quot; dest=&quot;${play.runtime.unzip.path}&quot; /&gt;
      &lt;echo message=&quot;Play! runtime unzipped successfully.&quot; /&gt;
      &lt;chmod dir=&quot;${play.runtime.path}&quot; perm=&quot;ugo+x&quot; includes=&quot;play&quot; /&gt;

      &lt;!-- launch play mvn:up to retrieve libs in /lib folder --&gt;
      &lt;condition property=&quot;playExtension&quot; value=&quot;.bat&quot;&gt;
        &lt;and&gt;
          &lt;os family=&quot;windows&quot; /&gt;
        &lt;/and&gt;
      &lt;/condition&gt;
      &lt;condition property=&quot;playExtension&quot; value=&quot;&quot;&gt;
        &lt;and&gt;
          &lt;os family=&quot;unix&quot; /&gt;
        &lt;/and&gt;
      &lt;/condition&gt;
      &lt;exec executable=&quot;${play.runtime.unzip.path}/${maven.project.play-runtime}/play${playExtension}&quot;&gt;
        &lt;arg value=&quot;mvn:up&quot; /&gt;
      &lt;/exec&gt;

      &lt;!-- Launch Play war command --&gt;
      &lt;condition property=&quot;playExtension&quot; value=&quot;.bat&quot;&gt;
        &lt;and&gt;
          &lt;os family=&quot;windows&quot; /&gt;
        &lt;/and&gt;
      &lt;/condition&gt;
      &lt;condition property=&quot;playExtension&quot; value=&quot;&quot;&gt;
        &lt;and&gt;
          &lt;os family=&quot;unix&quot; /&gt;
        &lt;/and&gt;
      &lt;/condition&gt;
      &lt;exec executable=&quot;${play.runtime.unzip.path}/${maven.project.play-runtime}/play${playExtension}&quot;&gt;
        &lt;arg value=&quot;auto-test&quot; /&gt;
      &lt;/exec&gt;
    &lt;/target&gt;
  &lt;/configuration&gt;
  &lt;goals&gt;
    &lt;goal&gt;run&lt;/goal&gt;
  &lt;/goals&gt;
&lt;/execution&gt;
</pre>
<p>L&#8217;étape des Tests Unitaires est désormais ok !</p>
<h2>L&#8217;analyse Cobertura</h2>
<p>Que serait une analyse Sonar sans le plugin Cobertura !</p>
<p>Heureusement, il existe un module Cobertura pour la framework Play! Il est très bien intégré puisqu&#8217;il suffit de le configurer dans votre application.conf et il l&#8217;analyse Cobertura sera effectuée durant la phase des tests unitaires. Un rapport est généré. Le seul travail à faire est de référencer dans Sonar l&#8217;emplacement de ce rapport pour que ce dernier puisse l&#8217;exploiter.</p>
<p>Je vous conseille la documentation de ce très bon module Play! : <a href="http://www.playframework.org/modules/cobertura-2.0/home">Documentation module Cobertura</a>.</p>
<h2>Le packaging</h2>
<p>Un gros morceau là aussi.</p>
<p>Le packaging d&#8217;une application Play! doit être fait sous forme de war pour pouvoir être uploadé dans un repository Maven.<br />
L&#8217;idée retenue est que l&#8217;on va laisser Maven exécuter sa phase <em>package</em> qui buildera un war non fonctionnel, puis une tâche Ant branchée sur cette même phase va générer grâce à la commande <em>play war</em> un fichier war Play! fonctionnel. Le principal soucis rencontré fût l&#8217;exclusion du répertoire <em>target</em> du packaging fait par Play! (notre target contient notamment le runtime Play!). Un développement soumis sur Github nous a permis de rajouter un paramètre <em>&#8211;exclude</em> à la ligne de commande <em>play war</em> et d&#8217;exclure les répertoires que nous ne voulions pas builder.</p>
<p>Voici l&#8217;exécution Maven du plugin Antrun :</p>
<pre class="brush: xml; title: ; notranslate">
&lt;execution&gt;
  &lt;id&gt;Packaging&lt;/id&gt;
  &lt;phase&gt;package&lt;/phase&gt;
  &lt;configuration&gt;
    &lt;target&gt;
      &lt;property name=&quot;maven.project.artifactId&quot; value=&quot;${project.artifactId}&quot; /&gt;
      &lt;property name=&quot;maven.project.version&quot; value=&quot;${project.version}&quot; /&gt;
      &lt;property name=&quot;maven.project.play-runtime&quot; value=&quot;play-runtime-${play.version}&quot; /&gt;

      &lt;property name=&quot;play.runtime.path&quot; value=&quot;${basedir}/target/dependency/${maven.project.play-runtime}&quot; /&gt;
      &lt;property name=&quot;play.runtime.file&quot; value=&quot;${basedir}/target/dependency/${maven.project.play-runtime}.zip&quot; /&gt;
      &lt;property name=&quot;play.runtime.unzip.path&quot; value=&quot;${basedir}/target/dependency&quot; /&gt;

      &lt;!-- Check zipped Play! runtime is present. Build fails if not present --&gt;
      &lt;available file=&quot;${play.runtime.file}&quot; property=&quot;play.runtime.present&quot; /&gt;
      &lt;echo message=&quot;Is Play! runtime present : ${play.runtime.present}&quot; /&gt;

      &lt;fail message=&quot;Error : Play! runtime could not be found at : ${play.runtime.file}&quot;&gt;
        &lt;condition&gt;
          &lt;not&gt;
            &lt;isset property=&quot;play.runtime.present&quot; /&gt;
          &lt;/not&gt;
        &lt;/condition&gt;
      &lt;/fail&gt;

      &lt;!-- Delete existing Play! runtime --&gt;
      &lt;echo message=&quot;Deleting any existing unzipped Play! runtime ...&quot; /&gt;
      &lt;delete dir=&quot;play.runtime.unzip.path&quot; failonerror=&quot;false&quot; verbose=&quot;true&quot; /&gt;

      &lt;!-- Unzip Play! runtime --&gt;
      &lt;echo message=&quot;Unzipping Play! runtime ...&quot; /&gt;
      &lt;unzip src=&quot;${play.runtime.file}&quot; dest=&quot;${play.runtime.unzip.path}&quot; /&gt;
      &lt;echo message=&quot;Play! runtime unzipped successfully.&quot; /&gt;
      &lt;chmod dir=&quot;${play.runtime.path}&quot; perm=&quot;ugo+x&quot; includes=&quot;play&quot; /&gt;

      &lt;!-- launch play mvn:up to retrieve libs in /lib folder --&gt;
      &lt;condition property=&quot;playExtension&quot; value=&quot;.bat&quot;&gt;
        &lt;and&gt;
          &lt;os family=&quot;windows&quot; /&gt;
        &lt;/and&gt;
      &lt;/condition&gt;
      &lt;condition property=&quot;playExtension&quot; value=&quot;&quot;&gt;
        &lt;and&gt;
          &lt;os family=&quot;unix&quot; /&gt;
        &lt;/and&gt;
      &lt;/condition&gt;
      &lt;exec executable=&quot;${play.runtime.unzip.path}/${maven.project.play-runtime}/play${playExtension}&quot;&gt;
        &lt;arg value=&quot;mvn:up&quot; /&gt;
      &lt;/exec&gt;

      &lt;!-- Launch Play war command --&gt;
      &lt;condition property=&quot;playExtension&quot; value=&quot;.bat&quot;&gt;
        &lt;and&gt;
          &lt;os family=&quot;windows&quot; /&gt;
        &lt;/and&gt;
      &lt;/condition&gt;
      &lt;condition property=&quot;playExtension&quot; value=&quot;&quot;&gt;
        &lt;and&gt;
          &lt;os family=&quot;unix&quot; /&gt;
        &lt;/and&gt;
      &lt;/condition&gt;
      &lt;exec executable=&quot;${play.runtime.unzip.path}/${maven.project.play-runtime}/play${playExtension}&quot;&gt;
        &lt;arg value=&quot;war&quot; /&gt;
        &lt;arg value=&quot;${basedir}&quot; /&gt;
        &lt;arg value=&quot;-o&quot; /&gt;
        &lt;arg value=&quot;${basedir}/target/${maven.project.artifactId}-${maven.project.version}&quot; /&gt;
        &lt;arg value=&quot;--zip&quot; /&gt;
        &lt;arg value=&quot;--exclude&quot; /&gt;
        &lt;arg value=&quot;target&quot; /&gt;
      &lt;/exec&gt;
    &lt;/target&gt;
  &lt;/configuration&gt;
  &lt;goals&gt;
    &lt;goal&gt;run&lt;/goal&gt;
  &lt;/goals&gt;
&lt;/execution&gt;
</pre>
<p>Nous avons donc les tests unitaires, l&#8217;analyse Cobertura, le packaging en War et le déploiement dans un repo Maven qui fonctionnent. Il ne nous reste plus qu&#8217;à attaquer l&#8217;analyse Sonar.</p>
<h2>L&#8217;analyse Sonar</h2>
<p>Avec un peu de configuration (emplacement des sources, des fichiers Surefire, Cobertura, etc&#8230;), la configuration du plugin Sonar s&#8217;est passée sans encombres.</p>
<p>Nous avons donc réussi à intégrer les applications Play! à notre usine d&#8217;Intégration Continue.<br />
Les projets ont donc uniquement besoin d&#8217;un pom avec les bonnes dépendances pour pouvoir commencer leurs développements et qu&#8217;ils soient compatibles Usine de Développement.</p>
<p>Conclusion : malgré quelques difficultés, l&#8217;intégration du framework Play! s&#8217;est faite sans gros soucis. Nous avons ainsi pu rentrer dans le code du framework, autant du côté Python que du côté Java. Quelques Pull Request chez Github et nous sommes pleinement fonctionnels !</p>
<p>Merci à Play! framework et à toute son équipe !</p>
<p>Update 16/12/2010 :<br />
Suite aux commentaires, voici le contenu du pom parent pour la version 1.1 de Play! :</p>
<pre class="brush: xml; collapse: true; light: false; title: ; toolbar: true; notranslate">
&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;
  &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
  &lt;groupId&gt;org.play&lt;/groupId&gt;
  &lt;artifactId&gt;play-parent&lt;/artifactId&gt;
  &lt;version&gt;1.1&lt;/version&gt;
  &lt;packaging&gt;pom&lt;/packaging&gt;
  &lt;name&gt;Play's parent POM&lt;/name&gt;

  &lt;repositories&gt;
    &lt;!-- Repository containing Play! jars. If you have your own repo (ex: Nexus), upload missing libs in third-parties --&gt;
    &lt;repository&gt;
      &lt;id&gt;infin-it&lt;/id&gt;
      &lt;url&gt;http://nexus.infin-it.fr/content/groups/public&lt;/url&gt;
      &lt;releases&gt;
        &lt;enabled&gt;true&lt;/enabled&gt;
      &lt;/releases&gt;
      &lt;snapshots&gt;
        &lt;enabled&gt;false&lt;/enabled&gt;
      &lt;/snapshots&gt;
    &lt;/repository&gt;
  &lt;/repositories&gt;  

  &lt;properties&gt;
    &lt;play.version&gt;1.1&lt;/play.version&gt;
  &lt;/properties&gt;

  &lt;dependencies&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;javax.activation&lt;/groupId&gt;
      &lt;artifactId&gt;activation&lt;/artifactId&gt;
      &lt;version&gt;1.1.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;antlr&lt;/groupId&gt;
      &lt;artifactId&gt;antlr&lt;/artifactId&gt;
      &lt;version&gt;2.7.6&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;com.ning&lt;/groupId&gt;
      &lt;artifactId&gt;async-http-client&lt;/artifactId&gt;
      &lt;version&gt;1.2.0&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.bouncycastle&lt;/groupId&gt;
      &lt;artifactId&gt;bcprov-jdk15&lt;/artifactId&gt;
      &lt;version&gt;1.45&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;c3p0&lt;/groupId&gt;
      &lt;artifactId&gt;c3p0&lt;/artifactId&gt;
      &lt;version&gt;0.9.1.2&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;cglib&lt;/groupId&gt;
      &lt;artifactId&gt;cglib-nodep&lt;/artifactId&gt;
      &lt;version&gt;2.2&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;commons-beanutils&lt;/groupId&gt;
      &lt;artifactId&gt;commons-beanutils&lt;/artifactId&gt;
      &lt;version&gt;1.8.3&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;commons-codec&lt;/groupId&gt;
      &lt;artifactId&gt;commons-codec&lt;/artifactId&gt;
      &lt;version&gt;1.4&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;commons-collections&lt;/groupId&gt;
      &lt;artifactId&gt;commons-collections&lt;/artifactId&gt;
      &lt;version&gt;3.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
      &lt;artifactId&gt;commons-email&lt;/artifactId&gt;
      &lt;version&gt;1.2&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;commons-fileupload&lt;/groupId&gt;
      &lt;artifactId&gt;commons-fileupload&lt;/artifactId&gt;
      &lt;version&gt;1.2&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;commons-io&lt;/groupId&gt;
      &lt;artifactId&gt;commons-io&lt;/artifactId&gt;
      &lt;version&gt;1.4&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;commons-lang&lt;/groupId&gt;
      &lt;artifactId&gt;commons-lang&lt;/artifactId&gt;
      &lt;version&gt;2.5&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;commons-logging&lt;/groupId&gt;
      &lt;artifactId&gt;commons-logging&lt;/artifactId&gt;
      &lt;version&gt;1.1.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;dom4j&lt;/groupId&gt;
      &lt;artifactId&gt;dom4j&lt;/artifactId&gt;
      &lt;version&gt;1.6.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;net.sf.ehcache&lt;/groupId&gt;
      &lt;artifactId&gt;ehcache-core&lt;/artifactId&gt;
      &lt;version&gt;2.0.0&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;net.sf.ezmorph&lt;/groupId&gt;
      &lt;artifactId&gt;ezmorph&lt;/artifactId&gt;
      &lt;version&gt;1.0.3&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.codehaus.groovy&lt;/groupId&gt;
      &lt;artifactId&gt;groovy-all&lt;/artifactId&gt;
      &lt;version&gt;1.7.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;com.google.code.gson&lt;/groupId&gt;
      &lt;artifactId&gt;gson&lt;/artifactId&gt;
      &lt;version&gt;1.4&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
      &lt;artifactId&gt;hibernate-core&lt;/artifactId&gt;
      &lt;version&gt;3.5.6-Final&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
      &lt;artifactId&gt;hibernate-annotations&lt;/artifactId&gt;
      &lt;version&gt;3.5.6-Final&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
      &lt;artifactId&gt;hibernate-commons-annotations&lt;/artifactId&gt;
      &lt;version&gt;3.2.0.Final&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
      &lt;artifactId&gt;hibernate-entitymanager&lt;/artifactId&gt;
      &lt;version&gt;3.5.6-Final&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.hibernate.javax.persistence&lt;/groupId&gt;
      &lt;artifactId&gt;hibernate-jpa-2.0-api&lt;/artifactId&gt;
      &lt;version&gt;1.0.0.Final&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- This lib must be added to your repository (comes from Sourceforge)--&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;hsqldb&lt;/groupId&gt;
      &lt;artifactId&gt;hsqldb&lt;/artifactId&gt;
      &lt;version&gt;1.8.1.2&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- This lib must be added to your repository (comes from Sourceforge)--&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;com.jamonapi&lt;/groupId&gt;
      &lt;artifactId&gt;jamon&lt;/artifactId&gt;
      &lt;version&gt;2.7&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;javax.mail&lt;/groupId&gt;
      &lt;artifactId&gt;mail&lt;/artifactId&gt;
      &lt;version&gt;1.4.3&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;javassist&lt;/groupId&gt;
      &lt;artifactId&gt;javassist&lt;/artifactId&gt;
      &lt;version&gt;3.9.0.GA&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;jaxen&lt;/groupId&gt;
      &lt;artifactId&gt;jaxen&lt;/artifactId&gt;
      &lt;version&gt;1.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- This lib must be added to your repository --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.play.jj&lt;/groupId&gt;
      &lt;artifactId&gt;imaging&lt;/artifactId&gt;
      &lt;version&gt;1.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- This lib must be added to your repository --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.play.jj&lt;/groupId&gt;
      &lt;artifactId&gt;simple-captcha&lt;/artifactId&gt;
      &lt;version&gt;1.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- This lib must be added to your repository --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.play.jj&lt;/groupId&gt;
      &lt;artifactId&gt;textile&lt;/artifactId&gt;
      &lt;version&gt;1.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- This lib must be added to your repository --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.play.jj&lt;/groupId&gt;
      &lt;artifactId&gt;wikitext&lt;/artifactId&gt;
      &lt;version&gt;1.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;joda-time&lt;/groupId&gt;
      &lt;artifactId&gt;joda-time&lt;/artifactId&gt;
      &lt;version&gt;1.6&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- This lib must be added to your repository (comes from Sourceforge)--&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;jregex&lt;/groupId&gt;
      &lt;artifactId&gt;jregex&lt;/artifactId&gt;
      &lt;version&gt;1.2_01&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;net.sf.jsr107cache&lt;/groupId&gt;
      &lt;artifactId&gt;jsr107cache&lt;/artifactId&gt;
      &lt;version&gt;1.0&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;javax.transaction&lt;/groupId&gt;
      &lt;artifactId&gt;jta&lt;/artifactId&gt;
      &lt;version&gt;1.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;junit&lt;/groupId&gt;
      &lt;artifactId&gt;junit&lt;/artifactId&gt;
      &lt;version&gt;4.8.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;log4j&lt;/groupId&gt;
      &lt;artifactId&gt;log4j&lt;/artifactId&gt;
      &lt;version&gt;1.2.16&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- This lib must be added to your repository --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;net.spy&lt;/groupId&gt;
      &lt;artifactId&gt;memcached&lt;/artifactId&gt;
      &lt;version&gt;2.4.2&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;mysql&lt;/groupId&gt;
      &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
      &lt;version&gt;5.1.13&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.jboss.netty&lt;/groupId&gt;
      &lt;artifactId&gt;netty&lt;/artifactId&gt;
      &lt;version&gt;3.2.2.Final&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- This lib must be added to your repository --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.play&lt;/groupId&gt;
      &lt;artifactId&gt;org.eclipse.jdt.core&lt;/artifactId&gt;
      &lt;version&gt;3.6.0&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;net.sf.oval&lt;/groupId&gt;
      &lt;artifactId&gt;oval&lt;/artifactId&gt;
      &lt;version&gt;1.50&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.apache.geronimo.specs&lt;/groupId&gt;
      &lt;artifactId&gt;geronimo-servlet_2.5_spec&lt;/artifactId&gt;
      &lt;version&gt;1.2&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;oauth.signpost&lt;/groupId&gt;
      &lt;artifactId&gt;signpost-core&lt;/artifactId&gt;
      &lt;version&gt;1.2&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
      &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
      &lt;version&gt;1.6.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
      &lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;
      &lt;version&gt;1.6.1&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.yaml&lt;/groupId&gt;
      &lt;artifactId&gt;snakeyaml&lt;/artifactId&gt;
      &lt;version&gt;1.6&lt;/version&gt;
      &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;!-- Play! framework dependency --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.play&lt;/groupId&gt;
      &lt;artifactId&gt;play&lt;/artifactId&gt;
      &lt;version&gt;1.1&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
      &lt;groupId&gt;org.play&lt;/groupId&gt;
      &lt;artifactId&gt;play-runtime&lt;/artifactId&gt;
      &lt;version&gt;1.1&lt;/version&gt;
      &lt;type&gt;zip&lt;/type&gt;
    &lt;/dependency&gt;
  &lt;/dependencies&gt;

  &lt;build&gt;
    &lt;sourceDirectory&gt;app&lt;/sourceDirectory&gt;
    &lt;pluginManagement&gt;
      &lt;plugins&gt;
        &lt;plugin&gt;
          &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
          &lt;artifactId&gt;maven-war-plugin&lt;/artifactId&gt;
          &lt;version&gt;2.1.1&lt;/version&gt;
          &lt;configuration&gt;
            &lt;failOnMissingWebXml&gt;false&lt;/failOnMissingWebXml&gt;
          &lt;/configuration&gt;
        &lt;/plugin&gt;

        &lt;plugin&gt;
          &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
          &lt;artifactId&gt;maven-dependency-plugin&lt;/artifactId&gt;
          &lt;version&gt;2.1&lt;/version&gt;
          &lt;executions&gt;
            &lt;execution&gt;
              &lt;id&gt;default-cli&lt;/id&gt;
              &lt;configuration&gt;
                &lt;includeClassifiers&gt;,sources&lt;/includeClassifiers&gt;
                &lt;outputDirectory&gt;lib/&lt;/outputDirectory&gt;
                &lt;excludeScope&gt;provided&lt;/excludeScope&gt;
                &lt;!-- To download sources jars without failing --&gt;
                &lt;failOnMissingClassifierArtifact&gt;false&lt;/failOnMissingClassifierArtifact&gt;
                &lt;excludeArtifactIds&gt;play-runtime,play&lt;/excludeArtifactIds&gt;
              &lt;/configuration&gt;
            &lt;/execution&gt;
            &lt;execution&gt;
              &lt;id&gt;packaging&lt;/id&gt;
              &lt;phase&gt;package&lt;/phase&gt;
              &lt;goals&gt;
                &lt;goal&gt;copy-dependencies&lt;/goal&gt;
              &lt;/goals&gt;
              &lt;configuration&gt;
                &lt;includeArtifactIds&gt;play-runtime&lt;/includeArtifactIds&gt;
              &lt;/configuration&gt;
            &lt;/execution&gt;
          &lt;/executions&gt;
        &lt;/plugin&gt;

        &lt;plugin&gt;
          &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
          &lt;artifactId&gt;maven-antrun-plugin&lt;/artifactId&gt;
          &lt;version&gt;1.6&lt;/version&gt;
          &lt;executions&gt;
            &lt;execution&gt;
              &lt;phase&gt;package&lt;/phase&gt;
              &lt;configuration&gt;
                &lt;target&gt;
                  &lt;property name=&quot;maven.project.artifactId&quot; value=&quot;${project.artifactId}&quot; /&gt;
                  &lt;property name=&quot;maven.project.version&quot; value=&quot;${project.version}&quot; /&gt;
                  &lt;property name=&quot;maven.project.play-runtime&quot; value=&quot;play-runtime-${play.version}&quot; /&gt;

                  &lt;property name=&quot;play.runtime.path&quot; value=&quot;${basedir}/target/dependency/${maven.project.play-runtime}&quot; /&gt;
                  &lt;property name=&quot;play.runtime.file&quot; value=&quot;${basedir}/target/dependency/${maven.project.play-runtime}.zip&quot; /&gt;
                  &lt;property name=&quot;play.runtime.unzip.path&quot; value=&quot;${basedir}/target/dependency&quot; /&gt;

                  &lt;!-- Check zipped Play! runtime is present. Build fails if not present --&gt;
                  &lt;available file=&quot;${play.runtime.file}&quot; property=&quot;play.runtime.present&quot; /&gt;
                  &lt;echo message=&quot;Is Play! runtime present : ${play.runtime.present}&quot; /&gt;

                  &lt;fail message=&quot;Error : Play! runtime could not be found at : ${play.runtime.file}&quot;&gt;
                    &lt;condition&gt;
                      &lt;not&gt;
                        &lt;isset property=&quot;play.runtime.present&quot; /&gt;
                      &lt;/not&gt;
                    &lt;/condition&gt;
                  &lt;/fail&gt;

                  &lt;!-- Delete existing Play! runtime --&gt;
                  &lt;echo message=&quot;Deleting any existing unzipped Play! runtime ...&quot; /&gt;
                  &lt;delete dir=&quot;play.runtime.unzip.path&quot; failonerror=&quot;false&quot; verbose=&quot;true&quot; /&gt;

                  &lt;!-- Unzip Play! runtime --&gt;
                  &lt;echo message=&quot;Unzipping Play! runtime ...&quot; /&gt;
                  &lt;unzip src=&quot;${play.runtime.file}&quot; dest=&quot;${play.runtime.unzip.path}&quot; /&gt;
                  &lt;echo message=&quot;Play! runtime unzipped successfully.&quot; /&gt;
                  &lt;chmod dir=&quot;${play.runtime.path}&quot; perm=&quot;ugo+x&quot; includes=&quot;play&quot; /&gt;

                  &lt;!-- launch play mvn:up to retrieve libs in /lib folder --&gt;
                  &lt;condition property=&quot;playExtension&quot; value=&quot;.bat&quot;&gt;
                    &lt;and&gt;
                      &lt;os family=&quot;windows&quot; /&gt;
                    &lt;/and&gt;
                  &lt;/condition&gt;
                  &lt;condition property=&quot;playExtension&quot; value=&quot;&quot;&gt;
                    &lt;and&gt;
                      &lt;os family=&quot;unix&quot; /&gt;
                    &lt;/and&gt;
                  &lt;/condition&gt;
                  &lt;exec executable=&quot;${play.runtime.unzip.path}/${maven.project.play-runtime}/play${playExtension}&quot;&gt;
                    &lt;arg value=&quot;mvn:up&quot; /&gt;
                  &lt;/exec&gt;

                  &lt;!-- Launch Play war command --&gt;
                  &lt;condition property=&quot;playExtension&quot; value=&quot;.bat&quot;&gt;
                    &lt;and&gt;
                      &lt;os family=&quot;windows&quot; /&gt;
                    &lt;/and&gt;
                  &lt;/condition&gt;
                  &lt;condition property=&quot;playExtension&quot; value=&quot;&quot;&gt;
                    &lt;and&gt;
                      &lt;os family=&quot;unix&quot; /&gt;
                    &lt;/and&gt;
                  &lt;/condition&gt;
                  &lt;exec executable=&quot;${play.runtime.unzip.path}/${maven.project.play-runtime}/play${playExtension}&quot;&gt;
                    &lt;arg value=&quot;war&quot; /&gt;
                    &lt;arg value=&quot;${basedir}&quot; /&gt;
                    &lt;arg value=&quot;-o&quot; /&gt;
                    &lt;arg value=&quot;${basedir}/target/${maven.project.artifactId}-${maven.project.version}&quot; /&gt;
                    &lt;arg value=&quot;--zip&quot; /&gt;
                  &lt;/exec&gt;
                &lt;/target&gt;
              &lt;/configuration&gt;
              &lt;goals&gt;
                &lt;goal&gt;run&lt;/goal&gt;
              &lt;/goals&gt;
            &lt;/execution&gt;
          &lt;/executions&gt;
        &lt;/plugin&gt;

        &lt;plugin&gt;
          &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
          &lt;artifactId&gt;maven-clean-plugin&lt;/artifactId&gt;
          &lt;version&gt;2.4.1&lt;/version&gt;
          &lt;configuration&gt;
            &lt;filesets&gt;
              &lt;fileset&gt;
                &lt;directory&gt;${project.basedir}/lib&lt;/directory&gt;
                &lt;includes&gt;
                  &lt;include&gt;**/*.jar&lt;/include&gt;
                  &lt;include&gt;**/*.zip&lt;/include&gt;
                &lt;/includes&gt;
                &lt;followSymlinks&gt;false&lt;/followSymlinks&gt;
              &lt;/fileset&gt;
            &lt;/filesets&gt;
          &lt;/configuration&gt;
        &lt;/plugin&gt;

        &lt;plugin&gt;
          &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
          &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
          &lt;version&gt;2.3.1&lt;/version&gt;
          &lt;configuration&gt;
            &lt;source&gt;1.5&lt;/source&gt;
            &lt;target&gt;1.5&lt;/target&gt;
          &lt;/configuration&gt;
        &lt;/plugin&gt;

      &lt;/plugins&gt;
    &lt;/pluginManagement&gt;

  &lt;/build&gt;

&lt;/project&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.infin-it.fr/2010/12/15/play-framework-integration-continue-retour-dexperience/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Aggrégateur JMX</title>
		<link>http://blog.infin-it.fr/2010/08/05/aggregateur-jmx-2/</link>
		<comments>http://blog.infin-it.fr/2010/08/05/aggregateur-jmx-2/#comments</comments>
		<pubDate>Thu, 05 Aug 2010 10:28:54 +0000</pubDate>
		<dc:creator>Jean-Philippe Briend</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[JMX]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[JMX JSR-250]]></category>

		<guid isPermaLink="false">http://blog.infin-it.fr/?p=219</guid>
		<description><![CDATA[JMX (Java Management Extensions) est très pratique pour le monitoring d&#8217;une application. Malheureusement, lorsque l&#8217;application grandit et que, par exemple, des traitements nocturnes apparaissent, ces derniers tournent dans des machines virtuelles distinctes. A ce moment là, JMX pose problème pour la remontée d&#8217;informations. L&#8217;idée que je vais vous proposer est la suivante : Une JVM [...]]]></description>
			<content:encoded><![CDATA[<p>JMX (Java Management Extensions) est très pratique pour le monitoring d&#8217;une application.<br />
Malheureusement, lorsque l&#8217;application grandit et que, par exemple, des traitements nocturnes apparaissent, ces derniers tournent dans des machines virtuelles distinctes.</p>
<p>A ce moment là, JMX pose problème pour la remontée d&#8217;informations.</p>
<p>L&#8217;idée que je vais vous proposer est la suivante :</p>
<ul>
<li>Une JVM hébergeant un MBeanServer tourne en continu (éventuellement hébergé dans un serveur d&#8217;application).</li>
<li>Chacun des traitements se connectera à cette JVM en utilisant un <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jmx/tutorial/connectors.html">JMX Connector</a> et invoquera à distance des Notifications JMX à destination du MBeanServer.</li>
</ul>
<p><span id="more-219"></span></p>
<p>Grâce à cette architecture, les ports des JVM &#8216;clientes&#8217; ne sont plus un problème : on laisse le mécanisme par défaut de JMX se débrouiller pour en trouver un libre.</p>
<p>Les personnes souhaitant monitorer ces traitements n&#8217;ont qu&#8217;à lancer JConsole et se connecter sur la JVM &#8216;serveur&#8217; qui héberge le MBeanServer.</p>
<p>Le schéma suivant présente le principe :</p>
<p><a href="http://blog.infin-it.fr/wp-content/uploads/2010/03/Aggregateur-JMX.png"><img src="http://blog.infin-it.fr/wp-content/uploads/2010/03/Aggregateur-JMX-300x107.png" alt="" title="Aggregateur JMX" width="300" height="107" class="aligncenter size-medium wp-image-57" /><center>Cliquez pour agrandir</center></a></p>
<p>JMX Notifier va être un objet faisant appel au NotificationPublisher de JMX.<br />
Ce NotificationPublisher envoi des notifications au serveur MBean.</p>
<p>Le JMX Notifier va être exposé par des JMX Connectors aux clients et sera proxyfié.<br />
Le traitement peut alors utiliser de façon invisible cet objet pour envoyer des évènements à la JVM distante.</p>
<p>Passons maintenant au code !</p>
<p><u>JMX Notifier (serveur) :</u><br />
<font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
@ManagedResource(objectName=&quot;batch:name=BatchJMX&quot;, description=&quot;JMX Notifier&quot;)
public class JmxNotifierImpl implements JmxNotifier, NotificationPublisherAware {

  protected NotificationPublisher publisher;

  @ManagedOperation(description=&quot;Annonce Début batch.&quot;)
  @ManagedOperationParameters(
  @ManagedOperationParameter(name=&quot;id&quot;, description= &quot;Batch ID.&quot;))
  public void annonceDebut(String id) {
    super.publisher.sendNotification(
                    new Notification(&quot;Debut du Batch &quot; + id,
                                           this,
                                           0,
                                           Calendar.getInstance().getTimeInMillis()));
  }

  public void setNotificationPublisher(NotificationPublisher publisher) {
    this.publisher = publisher;
  }
}
</pre>
<p></font><br />
On notera l&#8217;utilisation de l&#8217;interface <a href="http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/jmx/export/notification/NotificationPublisherAware.html">NotificationPublisherAware</a> afin de se faire injecter le <a href="http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/jmx/export/notification/NotificationPublisher.html">NotificationPublisher</a> par Spring.</p>
<p><u>JMX Notifier Service (client) :</u><br />
<font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
public class JmxNotifierServiceImpl implements InitializingBean {

  private String objectName;
  private String url;
  private JMXConnector jmxConnector;
  private JmxNotifier notifier;

  public void afterPropertiesSet() {
    try {
      ObjectName objectName = new ObjectName(this.objectName);
      JMXServiceURL jmxUrl = new JMXServiceURL(this.url);
      this.jmxConnector = JMXConnectorFactory.connect(jmxUrl);
      MBeanServerConnection mbsc = this.jmxConnector.getMBeanServerConnection();

      this.notifier = (JmxNotifier)MBeanServerInvocationHandler.newProxyInstance(
                                                                                           mbsc,
                                                                                           objectName,
                                                                                           JmxNotifier.class,
                                                                                           false);
    } catch (MalformedObjectNameException e) {
      System.err.println(&quot;Warning : could not connect to
                                     JMX server. JMX Notifications will not be sent.&quot;);
      System.err.println(&quot;Reason : &quot; + e.getMessage());
    } catch (MalformedURLException e) {
      System.err.println(&quot;Warning : could not connect to
                                     JMX server. JMX Notifications will not be sent.&quot;);
      System.err.println(&quot;Reason : &quot; + e.getMessage());
    } catch (IOException e) {
     System.err.println(&quot;Warning : could not connect to
                                     JMX server. JMX Notifications will not be sent.&quot;);
     System.err.println(&quot;It seems that remote server is
                                     not up and not running.&quot;);
    }
  }
}

  public void close() throws IOException {
    if ( this.jmxConnector != null ) {
      this.jmxConnector.close();
  }

  public void annonceDebu(String id) {
    if ( this.notifier != null ) {
      this.notifier.annonceDebut(id);
    }
  }
}
</pre>
<p></font><br />
Une meilleure gestion des exceptions levées permettrait de gérer les cas où le serveur distant n&#8217;est pas disponible.</p>
<p>Côté serveur, il va vous falloir la configuration Spring suivante :<br />
<font size="3"></p>
<pre class="brush: xml; title: ; notranslate">
&lt;context:annotation-config /&gt;

&lt;bean id=&quot;jmxNotifier&quot; class=&quot;JmxNotifierImpl&quot; /&gt;

&lt;bean id=&quot;registry&quot; class=&quot;org.springframework.remoting.rmi.RmiRegistryFactoryBean&quot; /&gt;

&lt;bean id=&quot;mbeanServer&quot; class=&quot;org.springframework.jmx.support.MBeanServerFactoryBean&quot;&gt;
	&lt;property name=&quot;locateExistingServerIfPossible&quot; value=&quot;true&quot; /&gt;
&lt;/bean&gt;

&lt;bean id=&quot;serverConnector&quot; class=&quot;org.springframework.jmx.support.ConnectorServerFactoryBean&quot;&gt;
  &lt;property name=&quot;objectName&quot; value=&quot;connector:name=rmi&quot;/&gt;
  &lt;property name=&quot;serviceUrl&quot; value=&quot;service:jmx:rmi://localhost/jndi/rmi://localhost:1099/standaloneJmxServer&quot;/&gt;
  &lt;property name=&quot;threaded&quot; value=&quot;true&quot;/&gt;
	&lt;property name=&quot;daemon&quot; value=&quot;true&quot;/&gt;
&lt;/bean&gt;

&lt;bean class=&quot;org.springframework.jmx.export.MBeanExporter&quot; lazy-init=&quot;false&quot;&gt;
	&lt;property name=&quot;autodetect&quot; value=&quot;true&quot; /&gt;
	&lt;property name=&quot;namingStrategy&quot; ref=&quot;namingStrategy&quot; /&gt;
	&lt;property name=&quot;assembler&quot; ref=&quot;assembler&quot; /&gt;
&lt;/bean&gt;

&lt;bean id=&quot;attributeSource&quot; class=&quot;org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource&quot;/&gt;

&lt;bean id=&quot;assembler&quot; class=&quot;org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler&quot;&gt;
	&lt;property name=&quot;attributeSource&quot; ref=&quot;attributeSource&quot; /&gt;
&lt;/bean&gt;

&lt;bean id=&quot;namingStrategy&quot; class=&quot;org.springframework.jmx.export.naming.MetadataNamingStrategy&quot;&gt;
	&lt;property name=&quot;attributeSource&quot; ref=&quot;attributeSource&quot; /&gt;
&lt;/bean&gt;
</pre>
<p></font></p>
<p>Les points marquants sont la présence de la MBeanServerFactory, la ConnectorServerFactoryBean et la RMIRegistryFactoryBean.</p>
<p>Et c&#8217;est tout !<br />
Votre code n&#8217;a plus qu&#8217;à appeler JMXNotifierService.<br />
Après, à vous d&#8217;enrichir les 2 JMX Notifier en fonction du fonctionnel de votre application.</p>
<p>Les équipes responsables du monitoring n&#8217;ont qu&#8217;à connecter leur JConsole sur la JVM contenant le MBeanServer.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.infin-it.fr/2010/08/05/aggregateur-jmx-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sample : génération de document PDF avec iText (1ère partie)</title>
		<link>http://blog.infin-it.fr/2010/08/05/sample-generation-de-document-pdf-avec-itext-1ere-partie/</link>
		<comments>http://blog.infin-it.fr/2010/08/05/sample-generation-de-document-pdf-avec-itext-1ere-partie/#comments</comments>
		<pubDate>Thu, 05 Aug 2010 09:51:36 +0000</pubDate>
		<dc:creator>Jean-Philippe Briend</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[exemple]]></category>
		<category><![CDATA[itext]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[pdf]]></category>

		<guid isPermaLink="false">http://blog.infin-it.fr/?p=179</guid>
		<description><![CDATA[Après avoir étudié les différents frameworks Java en vue de la génération de documents PDF (ici), entrainons-nous maintenant 2 exemples simples d&#8217;implémentation de la librairie iText. Ce sample se divise en 2 parties : la 1ère montre un exemple de génération de document PDF from scratch. la 2ième partie va générer un document à partir [...]]]></description>
			<content:encoded><![CDATA[<p>Après avoir étudié les différents frameworks Java en vue de la génération de documents PDF (<a class="vt-p" href="http://blog.infin-it.fr/2010/07/20/comparatif-des-librairies-java-pour-pdf/" target="_blank">ici</a>), entrainons-nous maintenant 2 exemples simples d&#8217;implémentation de la librairie <a class="vt-p" href="http://www.itextpdf.com/" target="_blank">iText</a>.</p>
<p>Ce sample se divise en 2 parties :</p>
<ul>
<li>la 1ère montre un exemple de génération de document PDF from scratch.</li>
<li>la 2ième partie va générer un document à partir d&#8217;un template que nous allons créer dans Open-Office.</li>
</ul>
<p><span id="more-179"></span></p>
<h3><span style="text-decoration: underline;">Générer un document PDF &#8216;from scratch&#8217; :</span></h3>
<ul>
<li><span style="text-decoration: underline;">1ère étape : générer un projet Java.</span></li>
</ul>
<p>Pour cela, utilisons la commande <em>mvn archetype:generate</em> et choisissons de créer un <em>maven-archetype-quickstart</em>.<br />
Dans notre exemple, le projet est configuré comme suit :</p>
<p><font size="3"></p>
<pre class="brush: xml; title: ; notranslate">
  &lt;groupId&gt;fr.infinit.sample.generationpdf&lt;/groupId&gt;
  &lt;artifactId&gt;generationpdf&lt;/artifactId&gt;
  &lt;version&gt;1.0.0-SNAPSHOT&lt;/version&gt;
  &lt;name&gt;Sample Generation PDF&lt;/name&gt;
</pre>
<p></font><br />
<HR/></p>
<ul>
<li><span style="text-decoration: underline;">2ième étape : ajouter la dépendance Maven à iText.</span></li>
</ul>
<p>Ajoutons le repository iText ainsi que la dépendance pour iText :</p>
<p><font size="3"></p>
<pre class="brush: xml; title: ; notranslate">
&lt;repositories&gt;
  &lt;repository&gt;
    &lt;id&gt;itextpdf.com&lt;/id&gt;
    &lt;name&gt;Maven Repository for iText&lt;/name&gt;
    &lt;url&gt;http://maven.itextpdf.com/&lt;/url&gt;
  &lt;/repository&gt;
&lt;/repositories&gt;
</pre>
<pre class="brush: xml; title: ; notranslate">
&lt;dependencies&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;com.itextpdf&lt;/groupId&gt;
    &lt;artifactId&gt;itextpdf&lt;/artifactId&gt;
    &lt;version&gt;5.0.2&lt;/version&gt;
    &lt;scope&gt;compile&lt;/scope&gt;
  &lt;/dependency&gt;
&lt;/dependencies&gt;
</pre>
<p></font><br />
<HR/></p>
<ul>
<li><span style="text-decoration: underline;">3ième étape : Créer un document PDF vide.</span></li>
</ul>
<p>Nous allons utiliser la classe <strong>com.itextpdf.text.Document</strong> :</p>
<p><font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
    /**
     * Crée un Document vide.
     *
     * @param file Chemin du fichier à créer.
     * @return Document s'il n'y a pas eu d'erreur.
     * @throws IOException s'il y a eu une erreur avec le nom de fichier fourni.
     * @throws DocumentException s'il y a eu une erreur côté iText.
     */
    private Document creerDocument(String file) throws DocumentException, IOException {
        Document document = new Document();

        PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(file));
        pdfWriter.setPageEvent(this);
        // Préférence de lecture : 2 pages en colonne.
        pdfWriter.setViewerPreferences(PdfWriter.PageLayoutTwoColumnLeft);
        document.open();

        return document;
    }
</pre>
<p></font></p>
<p>Attention à ne pas oublier de fermer l&#8217;objet Document après avoir travaillé sur cet objet !<br />
<HR/></p>
<ul>
<li><span style="text-decoration: underline;">4ième étape : Ajouter des informations Metadata.</span></li>
</ul>
<p>L&#8217;ajout de ces informations se fait via la classe <strong>com.itextpdf.text.Document</strong> :</p>
<p><font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
    /**
     * Ajout de données de type Metadata au document.
     *
     * @param document Document auquel il faut rajouter les metadatas.
     */
    private void addMetaData(Document document) {
        document.addTitle(&quot;Sample PDF&quot;);
        document.addSubject(&quot;Utilisant iText&quot;);
        document.addKeywords(&quot;Java, PDF, iText&quot;);
        document.addAuthor(System.getProperty(&quot;user.name&quot;));
        document.addCreator(System.getProperty(&quot;user.name&quot;));
    }
</pre>
<p></font><br />
<HR/></p>
<ul>
<li><span style="text-decoration: underline;">5ième étape : Ajouter des lignes vides.</span></li>
</ul>
<p>L&#8217;ajout de lignes vides se fait en ajoutant des paragraphes avec des espaces :</p>
<p><font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
    /**
     * Ajoute une ligne vide number fois dans le Paragraph passé en paramètre.
     *
     * @param paragraph A remplir avec les lignes vides.
     * @param number Nombre de lignes vides à ajouter.
     */
    private void ajouterLigneVide(Paragraph paragraph, int number) {
        for (int i = 0; i &lt; number; i++) {
            paragraph.add(new Paragraph(&quot; &quot;));
        }
    }
</pre>
<p></font><br />
<HR/></p>
<ul>
<li><span style="text-decoration: underline;">6ième étape : Ajouter des paragraphes avec des polices différentes.</span></li>
</ul>
<p>Commençons par définir différentes polices :<br />
<font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
    /** Times Roman 18 Bold */
    private static final Font CATFONT = new Font(Font.getFamily(&quot;TIMES_ROMAN&quot;), 18, Font.BOLD);

    /** Times Roman 12 Normal */
    private static final Font REDFONT = new Font(Font.getFamily(&quot;TIMES_ROMAN&quot;), 12, Font.NORMAL);

    /** Times Roman 16 Bold */
    private static final Font SUBFONT = new Font(Font.getFamily(&quot;TIMES_ROMAN&quot;), 16, Font.BOLD);

    /** Times Roman 12 Bold */
    private static final Font SMALLBOLD = new Font(Font.getFamily(&quot;TIMES_ROMAN&quot;), 12, Font.BOLD);
</pre>
<p></font></p>
<p>Ensuite, ajoutons différents paragraphes à notre document :<br />
<font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
    /**
     * Ajoute 4 paragraphes / - Titre du document. - Rapport généré par ... - Ce
     * document décrit ... - Ce document est ...
     *
     * @param document Document à enrichir
     * @throws DocumentException Si un problème survient lors de l'ajout des
     *             données.
     */
    private void addTitlePage(Document document) throws DocumentException {
        Paragraph preface = new Paragraph();

        // Ajout d'une ligne vide
        ajouterLigneVide(preface, 1);

        // Ecriture du header
        preface.add(new Paragraph(&quot;Titre du document&quot;, CATFONT));
        ajouterLigneVide(preface, 1);

        preface.add(new Paragraph(&quot;Rapport généré par: &quot; + System.getProperty(&quot;user.name&quot;) + &quot;, &quot; + new Date(), SMALLBOLD));
        ajouterLigneVide(preface, 3);

        preface.add(new Paragraph(&quot;Ce document décrit quelque chose de très important &quot;, SMALLBOLD));
        ajouterLigneVide(preface, 8);

        preface.add(new Paragraph(&quot;Ce document est une version préliminaire et n'est sujet à aucune restriction.&quot;, REDFONT));
        document.add(preface);

        // Debut d'une nouvelle page
        document.newPage();
    }
</pre>
<p></font></p>
<p><HR/></p>
<ul>
<li><span style="text-decoration: underline;">7ième étape : Ajouter du contenu (Chapitre, Section, Sous-section, Anchor).</span></li>
</ul>
<p>Les objets Anchor, Chapter, Section permettent de définir différentes zones de contenu dans un document.<br />
<font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
Anchor anchor = new Anchor(&quot;Premier Chapitre&quot;, CATFONT);
anchor.setName(&quot;Premier Chapitre&quot;);

// Le 2ième paramètre est le numero du chapitre
Chapter catPart = new Chapter(new Paragraph(anchor), 1);

Paragraph subPara = new Paragraph(&quot;Sous-Catégorie 1&quot;, SUBFONT);
Section subCatPart = catPart.addSection(subPara);
subCatPart.add(new Paragraph(&quot;Coucou (pour ne pas mettre toto...).&quot;));

subPara = new Paragraph(&quot;Sous-catégorie 2&quot;, SUBFONT);
subCatPart = catPart.addSection(subPara);
subCatPart.add(new Paragraph(&quot;Paragraphe 1&quot;));
subCatPart.add(new Paragraph(&quot;Paragraphe 2&quot;));
subCatPart.add(new Paragraph(&quot;Paragraphe 3&quot;));
</pre>
<p></font></p>
<hr/>
<ul>
<li><span style="text-decoration: underline;">8ième étape : Ajouter un tableau.</span></li>
</ul>
<p>Un tableau se créé avec la classe <b>com.itextpdf.text.pdf.PdfPTable</b>.<br />
Il se compose de <b>com.itextpdf.text.pdf.PdfPCell</b> :<br />
<font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
/**
 * Crée un tableau de 3 colonnes et l'ajoute à la Section passée en
 * paramètre.
 *
 * @param subCatPart Section à remplir avec le tableau.
 * @throws BadElementException Si une erreur survient lors de l'ajout d'un
 *             élément dans la PdfPTable.
 */
private void createTable(Section subCatPart) throws BadElementException {
    // Creation d'une PdfPTable avec 3 colonnes
    final PdfPTable table = new PdfPTable(3);

    // Creation d'une PdfPCell avec un paragraphe
    final PdfPCell cell = new PdfPCell(new Paragraph(&quot;header with colspan 3&quot;));

    // Changement du colspan de la PdfCell
    cell.setColspan(3);

    // Ajout de la PdfCell custom à la PdfPTable
    table.addCell(cell);

    // Ajout d'objets String à la PdfPTable
    table.addCell(&quot;1.1&quot;);
    table.addCell(&quot;2.1&quot;);
    table.addCell(&quot;3.1&quot;);
    table.addCell(&quot;1.2&quot;);
    table.addCell(&quot;2.2&quot;);
    table.addCell(&quot;3.2&quot;);

    // Ajout d'un espace entre la PdfPTable et l'élément précédent.
    table.setSpacingBefore(15f);

    subCatPart.add(table);
}
</pre>
<p></font></p>
<hr/>
<ul>
<li><span style="text-decoration: underline;">9ième étape : Ajouter une liste à puce.</span></li>
</ul>
<p>Une liste à puce dans un document PDF se créé avec l&#8217;objet <b>com.itextpdf.text.List</b>.<br />
En voici un exemple :<br />
<font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
/**
 * Crée une liste et l'ajoute dans la Section passée en paramètre.
 *
 * @param subCatPart Section à remplir avec la liste.
 */
private void createList(Section subCatPart) {
    final List list = new List(true, false, 10);
    list.add(new ListItem(&quot;Premier point&quot;));
    list.add(new ListItem(&quot;Deuxième point&quot;));
    list.add(new ListItem(&quot;Troisième point&quot;));
    subCatPart.add(list);
}
</pre>
<p></font></p>
<hr/>
<ul>
<li><span style="text-decoration: underline;">10ième étape : Ajouter des numéros de pages.</span></li>
</ul>
<p>L&#8217;ajout des numéros de page se fait en utilisant la classe <b>com.itextpdf.text.pdf.PdfPageEventHelper</b>.<br />
Elle s&#8217;utilise en l&#8217;ajoutant comme PageEvent sur le PdfWriter (méthode setPageEvent).<br />
Il suffit ensuite de surcharger les méthodes que l&#8217;on souhaite utiliser : onOpenDocument, onEndPage, etc&#8230;</p>
<p>Dans notre exemple, la méthode onOpenDocument est utilisée pour créer un <b>com.itextpdf.text.pdf.PdfTemplate</b> qui contiendra le texte des numéros de page.<br />
<font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
/**
 * Création du template destiné à recevoir les numéros de page.
 *
 * @param writer PdfWriter à utiliser.
 * @param document Document à enrichir.
 */
@Override
public void onOpenDocument(PdfWriter writer, Document document) {
    total = writer.getDirectContent().createTemplate(100, 100);
    total.setBoundingBox(new Rectangle(-20, -20, 100, 100));
    try {
        helv = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
    } catch (DocumentException e) {
        LOG.error(e.getMessage(), e);
        // TODO : gérer l'exception.
    } catch (IOException e) {
        LOG.error(e.getMessage(), e);
        // TODO : gérer l'exception.
    }
}
</pre>
<p></font></p>
<p>Note : total est un <b>com.itextpdf.text.pdf.PdfTemplate</b>.</p>
<p>La méthode onEndPage est responsable du remplissage du PdfTemplate pour chaque page via un <b>com.itextpdf.text.pdf.PdfContentByte</b>.</p>
<p>Dans l&#8217;exemple suivant, les numéros de page paires sont écrits en bas à gauche et les impaires sont écrits en bas à droite :<br />
<font size="3"></p>
<pre class="brush: java; title: ; wrap-lines: false; notranslate">
/**
 * Ajoute des numeros de page automatiquement. Les numeros paires sont mis
 * en bas à gauche. Les numéros impaires sont mis en bas à droite.
 *
 * @param writer Pdfwriter à utiliser.
 * @param document Document à utiliser.
 */
@Override
public void onEndPage(PdfWriter writer, Document document) {
    LOG.info(&quot;Generation des numeros de page ...&quot;);
    final PdfContentByte directcontent = writer.getDirectContent();

    directcontent.saveState();
    final String text = &quot;Page &quot; + writer.getPageNumber();
    final float textBase = document.bottom() - 20;
    final float textSize = helv.getWidthPoint(text, 12);

    directcontent.beginText();
    directcontent.setFontAndSize(helv, 11);

    // Ecriture du numero de page a gauche pour les pages impaires.
    if ((writer.getPageNumber() % 2) == 1) {
        directcontent.setTextMatrix(document.left(), textBase);
        directcontent.showText(text);
        directcontent.endText();
        directcontent.addTemplate(total, document.left() + textSize, textBase);
    } else {
        // Ecriture du numero de page a droite pour les pages paires.
        final float adjust = helv.getWidthPoint(&quot;0&quot;, 12);
        directcontent.setTextMatrix(document.right() - textSize - adjust, textBase);
        directcontent.showText(text);
        directcontent.endText();
        directcontent.addTemplate(total, document.right() - adjust, textBase);
    }
    directcontent.restoreState();
}
</pre>
<p></font></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.infin-it.fr/2010/08/05/sample-generation-de-document-pdf-avec-itext-1ere-partie/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Comparatif des librairies PDF en Java</title>
		<link>http://blog.infin-it.fr/2010/07/20/comparatif-des-librairies-java-pour-pdf/</link>
		<comments>http://blog.infin-it.fr/2010/07/20/comparatif-des-librairies-java-pour-pdf/#comments</comments>
		<pubDate>Tue, 20 Jul 2010 18:25:49 +0000</pubDate>
		<dc:creator>Jean-Philippe Briend</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[aspose]]></category>
		<category><![CDATA[itext]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[pdf]]></category>
		<category><![CDATA[pdfbox]]></category>

		<guid isPermaLink="false">http://blog.infin-it.fr/?p=140</guid>
		<description><![CDATA[Il existe diverses outils pour générer ou travailler sur des fichiers PDF de nos jours. Intéressons-nous plus particulièrement aux librairies Java permettant de générer ou de parser des PDF sans avoir besoin d&#8217;un quelconque outil d&#8217;Adobe. Aspose Aspose est une société commerciale proposant 2 solutions payantes gravitant autour des problématiques PDF. La 1ère, Aspose.Pdf for [...]]]></description>
			<content:encoded><![CDATA[<p>Il existe diverses outils pour générer ou travailler sur des fichiers PDF de nos jours.<br />
Intéressons-nous plus particulièrement aux librairies Java permettant de générer ou de parser des PDF sans avoir besoin d&#8217;un quelconque outil d&#8217;<a href="http://www.adobe.com/">Adobe</a>.<br />
<span id="more-140"></span></p>
<hr/>
<h2><img class="alignleft size-full wp-image-151" title="fleche_droite" src="http://blog.infin-it.fr/wp-content/uploads/2010/07/fleche_droite.jpeg" alt="" width="24" height="24" /><a href="http://www.aspose.com/">Aspose</a></h2>
<p><a href="http://www.aspose.com/">Aspose</a> est une société commerciale proposant 2 solutions payantes gravitant autour des problématiques PDF.<br />
La 1ère, <em>Aspose.Pdf for Java</em>, conviendra a la plus part des utilisateurs. Cette librairie permet de lire, écrire ou manipuler des documents PDF. Celle-ci est limitée a la création de documents PDF : l&#8217;édition de documents existants n&#8217;est pas possible.<br />
La 2ième, <em>Aspose.Pdf.Kit for Java</em>, englobe les fonctionnalités de la précédente mais l&#8217;enrichit de fonctionnalités avancées : gestion des champs des formulaires embarqués dans des documents PDF, édition de documents existants, etc&#8230;</p>
<p>Ces 2 solutions s&#8217;inscrivent dans une gamme de produits plus larges (documents Word, documents Excel, etc ..).</p>
<p><strong><span style="text-decoration: underline;">Points positifs :</span></strong></p>
<ul>
<li>2 produits à des tarifs différents pour des utilisations différentes.</li>
<li>Intégration à une gamme de produits variés (Word, Excel, Powerpoint, etc&#8230;).</li>
</ul>
<p><strong><span style="text-decoration: underline;">Points négatifs :</span></strong></p>
<ul>
<li>Produits payants, surtout la version &#8216;basique&#8217; qui ne nécessite pas forcément une solution commerciale.</li>
</ul>
<hr/>
<h2><img class="alignleft size-full wp-image-151" title="fleche_droite" src="http://blog.infin-it.fr/wp-content/uploads/2010/07/fleche_droite.jpeg" alt="" width="24" height="24" /><a href="http://www.itextpdf.com">iText</a></h2>
<p><a href="http://www.itextpdf.com">iText</a> est une solution Open-Source par <a href="http://www.lowagie.com/">Bruno Lowagie</a>. Cette solution offre toutes les fonctionnalités classiques de génération de documents PDF, ainsi que la possibilité d&#8217;éditer des documents existants. Elle permet aussi de remplir les formulaires intégrés aux PDFs.<br />
<span style="text-decoration: underline;">Attention :</span> la licence d&#8217;<a href="http://www.itextpdf.com">iText</a> a changé depuis Décembre 2009 (version 5.0). Désormais iText est sous licence <a href="http://www.itextpdf.com/terms-of-use/index.php?page=AGPL">Affero General Public License (AGPL)</a>.</p>
<p><strong><span style="text-decoration: underline;">Points positifs :</span></strong></p>
<ul>
<li>Le produit permet de faire tout ce qu&#8217;on attend d&#8217;une API de gestion des documents PDF.</li>
<li>Produit ayant une grande expérience (1998).</li>
<li>iText est très bon pour la génération de documents.</li>
</ul>
<p><strong><span style="text-decoration: underline;">Points négatifs :</span></strong></p>
<ul>
<li>Documentation difficile à trouver: les tutoriaux ont été supprimés, désormais il faut acheter le livre &#8216;iText in Action&#8217;.</li>
<li>L&#8217;extraction de données n&#8217;est pas le fort d&#8217;iText.</li>
</ul>
<hr/>
<h2><img class="alignleft size-full wp-image-151" title="fleche_droite" src="http://blog.infin-it.fr/wp-content/uploads/2010/07/fleche_droite.jpeg" alt="" width="24" height="24" /><a href="http://pdfbox.apache.org">PDFBox</a></h2>
<p><a href="http://pdfbox.apache.org">PDFBox</a> est un projet de la fondation Apache. Il permet la création de nouveaux documents PDF, la manipulation de documents existants et la possibilité d&#8217;en extraire des données.<br />
Il ne permet pas de modifier ou de remplir les formulaires des documents PDF.</p>
<p><strong><span style="text-decoration: underline;">Points positifs :</span></strong></p>
<ul>
<li>Très bon en extraction de données (avec notamment une intégration Lucene Search Engine).</li>
<li><a href="http://www.apache.org/licenses/LICENSE-2.0">License Apache License v2.0</a>.</li>
<li>Documentation nettement plus fournie qu&#8217;iText.</li>
</ul>
<p><strong><span style="text-decoration: underline;">Points négatifs :</span></strong></p>
<ul>
<li>Ne gère pas les formulaires PDF.</li>
</ul>
<hr/>
<h2><img class="alignleft size-full wp-image-151" title="fleche_droite" src="http://blog.infin-it.fr/wp-content/uploads/2010/07/fleche_droite.jpeg" alt="" width="24" height="24" />Conclusion</h2>
<ul>
<li>Si votre projet utilise déjà un produit Aspose, autant garder votre environnement technique homogène.</li>
<li>Si votre principale activité est l&#8217;extraction de données (par exemple extraire des données de documents pour un moteur de recherche), alors jetez vous sur PDFBox qui est fait pour vous.</li>
<li>Si vous voulez rester ouvert à toutes les possibilités, prenez iText qui résoudras 95% de vous contraintes.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.infin-it.fr/2010/07/20/comparatif-des-librairies-java-pour-pdf/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Spring Batch : les pièges à éviter</title>
		<link>http://blog.infin-it.fr/2010/02/03/spring-batch-les-pieges-a-eviter/</link>
		<comments>http://blog.infin-it.fr/2010/02/03/spring-batch-les-pieges-a-eviter/#comments</comments>
		<pubDate>Wed, 03 Feb 2010 19:28:24 +0000</pubDate>
		<dc:creator>Jean-Philippe Briend</dc:creator>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[Spring Batch]]></category>

		<guid isPermaLink="false">http://blog.infin-it.fr/?p=28</guid>
		<description><![CDATA[Suite à la mise en place de Spring Batch 2.1 chez un client, voici quelques recommandations afin d&#8217;éviter une catastrophe technique. Est-ce que votre traitement est un Batch ? Il faut savoir que Spring Batch a été développé dans la logique des Batchs tels qu&#8217;ils existaient dans les gros systèmes : un Batch est une [...]]]></description>
			<content:encoded><![CDATA[<p>Suite à la mise en place de Spring Batch 2.1 chez un client, voici quelques recommandations afin d&#8217;éviter une catastrophe technique.</p>
<ol>
<li><span style="text-decoration: underline;"><strong>Est-ce que votre traitement est un Batch ?</strong></span></li>
<p>Il faut savoir que Spring Batch a été développé dans la logique des Batchs tels qu&#8217;ils existaient dans les gros systèmes : un Batch est une répétition en très grand nombre d&#8217;opérations unitaires (work unit).<br />
Le mode de fonctionnement de Spring Batch est très lié à ce modèle.<br />
Il est donc complètement inutile de se lancer dans l&#8217;implémentation de ce framework si l&#8217;organisation de votre traitement ne suit pas ce modèle.<br />
Je pense notamment à des traitements qui executeraient des updates SQL massifs : il ne sert à rien de construire une suite de Steps qui lanceront chacunes un update massif. Il faut construire une Step unique qui lancera un traitement complet sur un élément unitaire (appelé <em>chunk</em> dans le langage Spring Batch).</p>
<li><span style="text-decoration: underline;"><strong>Avez-vous une gestion de la Persistance &#8216;Maison&#8217; ?</strong></span></li>
<p>Pour bénéficier de la puissance de Spring Batch, il est important que votre couche de persistance soit homogène : elle doit être full Hibernate, full iBatis ou full Spring JDBC et ne pas comporter d&#8217;éléments &#8216;homemade&#8217;.<br />
La gestion des transactions dans le batch est déléguée au TransactionManager, le développeur n&#8217;a plus la main dessus.</p>
<li><span style="text-decoration: underline;"><strong>Envie de paralléliser des exécutions de requêtes SQL ?</strong></span></li>
<p>Attention !<br />
Si vous souhaitez multithreader des requêtes potentiellement longues avec Spring Batch, il vous faudra mettre en place du JTA. En effet, votre TransactionManager va devoir gérer une transaction &#8216;chapeau&#8217; qui gèrera elle-même une transaction par thread.<br />
D&#8217;où JTA. Ceci est dû au fait qu&#8217;une connection à la base de données ne peut pas être accédée simultanément par plusieurs threads (l&#8217;objet Connection n&#8217;est pas thread-safe).
</ol>
]]></content:encoded>
			<wfw:commentRss>http://blog.infin-it.fr/2010/02/03/spring-batch-les-pieges-a-eviter/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Importer plusieurs projets Maven dans Eclipse</title>
		<link>http://blog.infin-it.fr/2009/12/23/importer-plusieurs-projets-maven-dans-eclipse/</link>
		<comments>http://blog.infin-it.fr/2009/12/23/importer-plusieurs-projets-maven-dans-eclipse/#comments</comments>
		<pubDate>Wed, 23 Dec 2009 17:20:25 +0000</pubDate>
		<dc:creator>Jean-Philippe Briend</dc:creator>
				<category><![CDATA[Eclipse]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[maven-eclipse-plugin]]></category>

		<guid isPermaLink="false">http://blog.infin-it.fr/?p=7</guid>
		<description><![CDATA[Lorsque l’on utilise Eclipse et Maven, il est très courant d’utiliser le plugin Maven Eclipse. Je ne compte plus les fois où je dois travailler avec plusieurs versions d’un même projet Eclipse dans le même workspace Eclipse. Or Eclipse interdit d’importer 2 fois un projet du même nom. Voici un paramètre du plugin permettant de [...]]]></description>
			<content:encoded><![CDATA[<div>
<p>Lorsque l’on utilise Eclipse et Maven, il est très courant d’utiliser le plugin <a href="http://maven.apache.org/plugins/maven-eclipse-plugin/">Maven Eclipse</a>.</p>
<p>Je ne compte plus les fois où je dois travailler avec plusieurs versions d’un même projet Eclipse dans le même workspace Eclipse. Or Eclipse interdit d’importer 2 fois un projet du même nom.</p>
<p>Voici un paramètre du plugin permettant de choisir le nom du projet Eclipse généré à partir de Maven :</p>
<pre class="brush: bash; gutter: false; title: ; notranslate">
mvn -Declipse.projectNameTemplate=[artifactId]-1.3 eclipse:eclipse
</pre>
<p>Cette commande va créer un projet nommé <em><strong>toto-1.3</strong></em> où <strong>toto</strong> est le nom du projet maven.<br />
Vous pouvez aussi utiliser les balises <strong>[groupId]</strong> et <strong>[version]</strong>.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.infin-it.fr/2009/12/23/importer-plusieurs-projets-maven-dans-eclipse/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

