<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Excilys Labs</title>
	
	<link>http://labs.excilys.com</link>
	<description />
	<lastBuildDate>Mon, 14 May 2012 14:11:50 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ExcilysLabs" /><feedburner:info uri="excilyslabs" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Mon premier plugin Maven</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/JwU9gHTcAwY/</link>
		<comments>http://labs.excilys.com/2012/05/14/mon-premier-plugin-maven/#comments</comments>
		<pubDate>Mon, 14 May 2012 14:10:52 +0000</pubDate>
		<dc:creator>Bastien Jansen</dc:creator>
				<category><![CDATA[Maven]]></category>
		<category><![CDATA[Annotations]]></category>
		<category><![CDATA[Plugin]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=2679</guid>
		<description><![CDATA[Il y a des outils qui sont bien pratiques, voire parfois nécessaires à tout développeur qui se respecte. Un logiciel de gestion de build comme Ant ou Maven en fait partie. Outre l&#8217;avantage indéniable de permettre de télécharger la moitié &#8230; <a href="http://labs.excilys.com/2012/05/14/mon-premier-plugin-maven/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Il y a des outils qui sont bien pratiques, voire parfois nécessaires à tout développeur qui se respecte. Un logiciel de gestion de build comme Ant ou Maven en fait partie. Outre l&#8217;avantage indéniable de permettre de télécharger la moitié de l&#8217;internet lors du premier build sur une machine <em>clean</em>, Maven permet aussi via son système de plugins d&#8217;effectuer tout un tas d&#8217;actions allant plus loin que la compilation des classes et leur packaging dans un JAR. Je vous propose de regarder comment créer son premier plugin Maven.</p>
<h2>D&#8217;abord il faut un besoin</h2>
<p>Eh oui, parce que se contenter de créer un plugin qui affiche “Hello World” à chaque build, c&#8217;est pas très utile&#8230; Alors tant qu&#8217;à faire, mettons nous en situation réelle. J&#8217;aurais pu prendre comme exemple un plugin qui lance l&#8217;<a href="http://blog.excilys.com/2010/12/21/la-reponse-a-la-grande-question-sur-la-vie-les-annotation-processors-et-le-reste/">Annotation Processing Tool</a> à chaque compilation (par exemple pour <a href="https://github.com/excilys/androidannotations">Android Annotations</a>), mais je vais plutôt vous parler du premier plugin que j&#8217;ai eu à développer, qui permet de faire de la génération de code.</p>
<p>Ce générateur prend en entrée un modèle décrit dans un fichier XML, et produit en sortie des classes Java correspondant aux entités et aux DAO CRUD, ainsi qu&#8217;un script SQL de création de tables. Ces classes sont ensuite utilisables dans les services écrits à la main. Certaines parties du code généré sont configurables :</p>
<ul>
<li>le fichier XML décrivant le modèle</li>
<li>le package Java de base des classes générées</li>
<li>le nom du script SQL de créations de tables</li>
</ul>
<h2>Création du squelette de plugin Maven</h2>
<p>Un plugin Maven n&#8217;est rien d&#8217;autre qu&#8217;un projet Maven avec un <em>packaging</em> de type <strong>maven-plugin</strong> :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;?xml</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">&quot;1.0&quot;</span> <span style="color: #000066;">encoding</span>=<span style="color: #ff0000;">&quot;UTF-8&quot;</span><span style="color: #000000; font-weight: bold;">?&gt;</span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;project</span> <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span style="color: #000066;">xmlns:xsi</span>=<span style="color: #ff0000;">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> <span style="color: #000066;">xsi:schemaLocation</span>=<span style="color: #ff0000;">&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>com.excilys.labs.maven.plugin<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>generator-maven-plugin<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>1.0-SNAPSHOT<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;packaging<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>maven-plugin<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/packaging<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/project<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p>La convention veut que les plugins soient nommés <em>xyz-maven-plugin</em>, ce qui permet d&#8217;appeler les goals de la manière suivante lors d&#8217;un build :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ mvn xyz:some-goal</div></td></tr></tbody></table></div>
<p>Notez qu&#8217;il n&#8217;est pas nécessaire de spécifier le nom complet du plugin, Maven va compléter avec &#8220;-maven-plugin&#8221;, ça fait quelques caractères en moins à écrire <img src='http://labs.excilys.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> . Si la convention de nommage n&#8217;est pas suivie, il faut impérativement spécifier le nom complet du plugin dans la ligne de commande.</p>
<p>Ensuite, un plugin contient un ou plusieurs MOJO, qui vont exécuter le &#8220;vrai&#8221; code du plugin. Un MOJO correspond en fait à un <em>goal</em>. Par exemple, dans le plugin dependency-maven-plugin, on retrouve les goals &#8220;<a href="http://svn.apache.org/viewvc/maven/plugins/tags/maven-dependency-plugin-2.4/src/main/java/org/apache/maven/plugin/dependency/TreeMojo.java?view=markup">tree</a>&#8221; (<code class="codecolorer text default"><span class="text">mvn dependency:tree</span></code>) et &#8220;<a href="http://svn.apache.org/viewvc/maven/plugins/tags/maven-dependency-plugin-2.4/src/main/java/org/apache/maven/plugin/dependency/resolvers/ResolveDependenciesMojo.java?view=markup">resolve</a>&#8221; (<code class="codecolorer text default"><span class="text">mvn dependency:resolve</span></code>)</p>
<p>Créons donc un squelette de MOJO pour notre générateur de code :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">package</span> <span style="color: #006699;">com.excilys.labs.maven.plugin.generator</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">org.apache.maven.plugin.AbstractMojo</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">org.apache.maven.plugin.MojoExecutionException</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">org.apache.maven.plugin.MojoFailureException</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #008000; font-style: italic; font-weight: bold;">/**<br />
&nbsp;* A goal to generate code.<br />
&nbsp;*<br />
&nbsp;* @goal generate<br />
&nbsp;* @phase generate-sources<br />
&nbsp;*/</span><br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> CodeGeneratorMojo <span style="color: #000000; font-weight: bold;">extends</span> AbstractMojo <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> execute<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> MojoExecutionException, MojoFailureException <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; getLog<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Hello World!&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>Notre MOJO étend <code class="codecolorer text default"><span class="text">AbstractMojo</span></code>, qui fournit toute l&#8217;infrastructure utile à la création d&#8217;un goal. La méthode abstraite <code class="codecolorer java default"><span class="java">execute<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span></span></code> contient le code à exécuter, c&#8217;est donc à nous de l&#8217;implémenter. Pour l&#8217;instant elle ajoute une ligne de log saluant le monde (je sais, j&#8217;ai dit tout à l&#8217;heure qu&#8217;on ne se contenterait pas de faire un hello world&#8230;). La méthode <code class="codecolorer java default"><span class="java">getLog<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span></span></code> est un exemple de facilités fournies par l&#8217;AbstractMojo.</p>
<p>Notez également la Javadoc, qui contient deux tags particuliers. <code class="codecolorer text default"><span class="text">@goal</span></code> spécifie le nom du goal correspondant à notre MOJO, ce tag est obligatoire. <code class="codecolorer text default"><span class="text">@phase</span></code> permet de dire à Maven que notre plugin intervient durant la phase de génération des sources du <a href="http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference">lifecycle Maven</a> (avant la compilation des sources, donc).</p>
<p>Notre plugin est prêt à être exécuté. Configurons le pom.xml pour permettre l&#8217;exécution du plugin lors du build :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;build<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;plugins<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;plugin<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>com.excilys.labs.maven.plugin<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>generator-maven-plugin<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>1.0-SNAPSHOT<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/plugin<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/plugins<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/build<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p>Puis lançons un build Maven :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">mbp:GeneratorMavenPlugin bastien$ mvn generator:generate<br />
[INFO] Scanning for projects...<br />
[INFO] &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
[INFO] ------------------------------------------------------------------------<br />
[INFO] Building generator-maven-plugin 1.0-SNAPSHOT<br />
[INFO] ------------------------------------------------------------------------<br />
[INFO] <br />
[INFO] --- generator-maven-plugin:1.0-SNAPSHOT:generate (default-cli) @ generator-maven-plugin ---<br />
[INFO] Hello World!<br />
[INFO] ------------------------------------------------------------------------<br />
[INFO] BUILD SUCCESS<br />
[INFO] ------------------------------------------------------------------------<br />
[INFO] Total time: 0.322s<br />
[INFO] Finished at: Mon May 14 14:47:54 CEST 2012<br />
[INFO] Final Memory: 2M/81M<br />
[INFO] ------------------------------------------------------------------------</div></td></tr></tbody></table></div>
<h2>Ajout de paramètres au plugin</h2>
<p>Notre cahier des charges requière la possibilité de configurer certaines parties du générateur. Pour cela, ajoutons des paramètres à notre MOJO:</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> CodeGeneratorMojo <span style="color: #000000; font-weight: bold;">extends</span> AbstractMojo <span style="color: #009900;">&#123;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #008000; font-style: italic; font-weight: bold;">/**<br />
&nbsp; &nbsp; &nbsp;* @parameter alias=&quot;model.location&quot;<br />
&nbsp; &nbsp; &nbsp;* @required<br />
&nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> modelLocation<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #008000; font-style: italic; font-weight: bold;">/**<br />
&nbsp; &nbsp; &nbsp;* @parameter<br />
&nbsp; &nbsp; &nbsp;* @required<br />
&nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> basePackageName<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #008000; font-style: italic; font-weight: bold;">/**<br />
&nbsp; &nbsp; &nbsp;* @parameter default-value=&quot;${project.build.directory}/create.sql&quot;<br />
&nbsp; &nbsp; &nbsp;*/</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> sqlOutputLocation<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// ...</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>Nos trois attributs utilisent également des tags Javadoc pour indiquer que ce sont des paramètres du plugin. Les deux premiers sont obligatoires, le troisième est facultatif et prend une valeur par défaut. <code class="codecolorer text default"><span class="text">${project.build.directory}</span></code> sera remplacé au runtime par le chemin du dossier target.<br />
L&#8217;attribut &#8220;alias&#8221; de <code class="codecolorer text default"><span class="text">@parameter</span></code> permet de définir le nom du paramètre s&#8217;il est différent du nom de l&#8217;attribut Java. Ainsi, par défaut il y aura un paramètre nommé &#8220;basePackageName&#8221;, puisqu&#8217;on ne lui a pas donné d&#8217;alias. Une liste plus exhaustive des tags Javadoc est disponible sur le <a href="http://www.sonatype.com/books/mvnref-book/reference/writing-plugins-sect-mojo-params.html">site de Sonatype</a>.</p>
<p>Ouvrons à nouveau le pom.xml pour ajouter la configuration du plugin :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;build<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;plugins<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;plugin<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>com.excilys.labs.maven.plugin<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>generator-maven-plugin<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>1.0-SNAPSHOT<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;configuration<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;model.location<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>src/main/resources/model.xml<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/model.location<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;basePackageName<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>com.excilys.labs.generated<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/basePackageName<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/configuration<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/plugin<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/plugins<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/build<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p>Pour vérifier que la configuration est bien injectée au runtime, ajoutons un log :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">getLog<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Classes will be generated in package &quot;</span> <span style="color: #339933;">+</span> basePackageName<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">mbp:GeneratorMavenPlugin bastien$ mvn generator:generate<br />
...<br />
[INFO] --- generator-maven-plugin:1.0-SNAPSHOT:generate (default-cli) @ generator-maven-plugin ---<br />
[INFO] Hello World!<br />
[INFO] Classes will be generated in package com.excilys.labs.generated<br />
[INFO] ------------------------------------------------------------------------<br />
[INFO] BUILD SUCCESS<br />
...</div></td></tr></tbody></table></div>
<p>Maintenant que nous savons créer, configurer et exécuter un plugin Maven, il ne reste plus qu&#8217;à implémenter la méthode <code class="codecolorer java default"><span class="java">execute<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span></span></code> pour appeler les vraies classes de génération de code. Comme ce n&#8217;est pas vraiment l&#8217;objet de cet article, je ne détaillerai pas plus cette partie. Cependant un <code class="codecolorer text default"><span class="text">FakeGenerator</span></code> est disponibles dans les <a href="https://bitbucket.org/bjansen/generatormavenplugin/src">sources complètes</a>.</p>
<h2>J&#8217;aime pas les XDoclet !!</h2>
<p>Comme vous l&#8217;avez vu, la configuration du Mojo se fait par défaut dans des tags Javadoc (des sortes de <a href="http://xdoclet.sourceforge.net/xdoclet/index.html">XDoclet</a>). Ce n&#8217;est pas terrible dans la mesure où il est facile de faire une faute de frappe (ex <code class="codecolorer text default"><span class="text">@paramter</span></code> au lieu de <code class="codecolorer text default"><span class="text">@parameter</span></code>) qui ne sera pas détectée par le compilateur ni par Maven. Pour pallier ce problème, il existe un <em>set</em> d&#8217;annotations Java, le <a href="http://wiki.jfrog.org/confluence/display/OSS/Maven+Anno+Mojo">maven-plugin-anno</a>. Après avoir configuré la dépendance dans votre pom, il est ensuite possible de tout annoter :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;dependencies<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;dependency<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>org.jfrog.maven.annomojo<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>maven-plugin-anno<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>1.4.0<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/dependency<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/dependencies<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;build<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;plugins<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;plugin<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>org.apache.maven.plugins<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>maven-plugin-plugin<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>2.6<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;dependencies<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;dependency<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>org.jfrog.maven.annomojo<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/groupId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>maven-plugin-tools-anno<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/artifactId<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>1.4.0<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/version<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;scope<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>runtime<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/scope<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/dependency<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/dependencies<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/plugin<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/plugins<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/build<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;pluginRepositories<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;pluginRepository<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;id<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>jfrog-plugins<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/id<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>jfrog-plugins-dist<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;url<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>http://repo.jfrog.org/artifactory/plugins-releases-local<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/url<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;releases<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;enabled<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>true<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/enabled<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/releases<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/pluginRepository<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/pluginRepositories<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;repositories<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;repository<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;id<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>jfrog-plugins<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/id<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>jfrog-plugins-dist<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;url<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>http://repo.jfrog.org/artifactory/plugins-releases<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/url<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;layout<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>default<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/layout<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;releases<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;enabled<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>true<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/enabled<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/releases<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/repository<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/repositories<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@MojoGoal<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;generate-annotated&quot;</span><span style="color: #009900;">&#41;</span><br />
@MojoPhase<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;generate-sources&quot;</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> AnnotatedMojo <span style="color: #000000; font-weight: bold;">extends</span> AbstractMojo <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; @MojoParameter<span style="color: #009900;">&#40;</span>alias <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;model.location&quot;</span>, required <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> modelLocation<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; @MojoParameter<span style="color: #009900;">&#40;</span>required <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> basePackageName<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; @MojoParameter<span style="color: #009900;">&#40;</span>defaultValue <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;${project.build.directory}/create.sql&quot;</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> sqlOutputLocation<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> execute<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> MojoExecutionException, MojoFailureException <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// ...</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>Sachez également que le support des annotations est en train d&#8217;arriver nativement dans Maven (<a href="http://olamy.blogspot.fr/2012/05/java5-annotations-support-for-maven.html">Java5 annotations support for Maven plugins</a>).</p>
<h2>Conclusion</h2>
<p>Comme vous avez pu le voir, créer un plugin Maven n&#8217;est pas très compliqué. Les plugins sont suffisamment souples et configurables pour pouvoir exécuter du code déjà existant (par exemple remplacer un main() par un plugin Maven) sans trop de difficultés. La palette de plugins déjà disponibles est assez vaste, mais si un jour vous avez un besoin spécifique vous saurez que le coût de création d&#8217;un plugin n&#8217;est pas très élevé <img src='http://labs.excilys.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> .</p>
<h2>Liens utiles</h2>
<ul>
<li><a href="https://bitbucket.org/bjansen/generatormavenplugin">Les sources complètes du code de cet article</a></li>
<li><a href="http://maven.apache.org/guides/introduction/introduction-to-plugins.html">Introduction to Maven 2.0 Plugin Development</a></li>
<li><a href="http://www.sonatype.com/books/mvnref-book/reference/writing-plugins.html">Maven: The Complete Reference &#8211; Chapter 11. Writing Plugins</a></li>
</ul>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2012/05/14/mon-premier-plugin-maven/" data-text="Mon premier plugin Maven"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2012/05/14/mon-premier-plugin-maven/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2012%2F05%2F14%2Fmon-premier-plugin-maven%2F&amp;title=Mon%20premier%20plugin%20Maven" id="wpa2a_2"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/JwU9gHTcAwY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2012/05/14/mon-premier-plugin-maven/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2012/05/14/mon-premier-plugin-maven/</feedburner:origLink></item>
		<item>
		<title>Git-SVN – Soignez votre addiction à Git</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/1nsm0zpWpzU/</link>
		<comments>http://labs.excilys.com/2012/04/30/git-svn/#comments</comments>
		<pubDate>Mon, 30 Apr 2012 20:25:15 +0000</pubDate>
		<dc:creator>Jean Blanchard</dc:creator>
				<category><![CDATA[Outils]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=2654</guid>
		<description><![CDATA[Le vrai problème avec Git, c’est qu’une fois qu’on l’a utilisé c’est très dur de revenir vers un SCM non distribué comme Subversion, voire CVS. La solution évidente est de lancer une migration vers Git (ou un autre SCM distribué &#8230; <a href="http://labs.excilys.com/2012/04/30/git-svn/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Le vrai problème avec <a href="http://git-scm.com/">Git</a>, c’est qu’une fois qu’on l’a utilisé c’est très dur de revenir vers un <abbr title="Gestionnaire de versionnement de sources">SCM</abbr> non distribué comme <a href="http://subversion.tigris.org/">Subversion</a>, voire <a href="http://www.nongnu.org/cvs/">CVS</a>.<br />
La solution évidente est de lancer une migration vers Git (ou un autre SCM distribué comme <a href="http://mercurial.selenic.com/">Mercurial</a> ou <a href="http://bazaar.canonical.com/">Bazaar</a>, ne soyons pas sectaires). Toutefois cette migration  n’est pas toujours simple, voire possible, à mettre en place, pour diverses raisons qui n’ont habituellement rien à voir avec la technique.<br />
Et si on pouvait avoir les avantages de Git sur son poste de développeur, tout en gardant le repository SVN central historique du projet ? Et bien si vous n’avez pas encore vu le titre de cet article, apprenez donc que c’est justement ce que propose Git-SVN.</p>
<p><span id="more-2654"></span>Un petit avertissement au passage : cet article suppose un minimum de familiarité avec Git.</p>
<h1>Git-SVN c’est quoi ?</h1>
<p>Git-SVN est un ensemble de scripts qui permettent d’utiliser un repository Git local, et de le synchroniser avec un repository central Subversion. Malgré quelques limitations inhérentes à Subversion, on peut alors profiter de l’essentiel des fonctionnalités de Git, tout en gardant cette utilisation transparente pour les autres personnes de l’équipe, qui peuvent continuer à utiliser SVN comme avant.<br />
Le but de cet article, plus que de présenter les 3 ou 4 commandes à connaître pour utiliser Git-SVN, sera de faire découvrir l&#8217;outil,  de comprendre son fonctionnement, et de bien saisir quelles sont les limites de son utilisation (et il y en a).<br />
Pour plus de détails sur son utilisation, il y a la doc !</p>
<h1>Avertissement</h1>
<p>Si vous avez le choix, utilisez plutôt Git. Vraiment. Git-SVN a beau être un gros pas en avant par rapport à Subversion, ça reste une façade qui ne parviendra pas à masquer toutes les imperfections de Subversion, et qui ajoutera d&#8217;autres problématiques à prendre en compte.</p>
<h1>Bon on y va maintenant ?</h1>
<p>Les avertissements d’usage étant exprimés, on va maintenant passer à la pratique.<br />
On va partir du principe que le point de départ est un projet Subversion existant, avec lequel on va s&#8217;interfacer avec Git. Si vous partez sur un nouveau projet, utilisez Git directement.</p>
<h2>Installation</h2>
<p>Git-SVN s&#8217;installe de la même façon que vous avez installé Git&#8230; Pour la plupart des distributions Linux, ou pour Cywin, installez le package <em>git-svn</em>.</p>
<h2>Initialisation</h2>
<p>On part d’un dossier vide, et on va créer un nouveau repository local Git, synchronisé avec le serveur SVN :</p>
<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git</span> <span style="color: #c20cb9; font-weight: bold;">svn</span> init <span style="color: #660033;">-s</span> http:<span style="color: #000000; font-weight: bold;">//</span>subversion-server<span style="color: #000000; font-weight: bold;">/</span>myproject<span style="color: #000000; font-weight: bold;">/</span></div></div>
<p>Cette commande initialise un repository git vide, et ajoute les méta-données qui permettront de retrouver le serveur SVN pour se synchroniser.<br />
Le paramètre <em>-s</em> indique que le repository SVN a une structure standard : trunk dans le sous-dossier <em>trunk</em>, branches dans <em>branches</em> et tags dans <em>tags</em>. Si ce n’est pas le cas, on peut remplacer le <em>-s</em> par <em>-T</em>, <em>-b</em> et <em>-t</em> pour indiquer respectivement l’emplacement du trunk, des branches et des tags. Les paramètres de ces commandes sont des dossiers, soit relatifs au repository SVN, soit absolus (une URI complète).</p>
<h2>Intermède</h2>
<p>Faisons une petite pause pour observer ce qu’a réalisé la commande ci-dessus, et peaufiner un peu les choses.<br />
La commande a donc créé un nouveau dossier <em>myproject</em>. Celui-ci est vide à l’exception d’un sous-dossier <em>.git</em>, puisque c’est effectivement un repository Git ordinaire.<br />
Mais alors quid des métadonnées SVN que j’ai mentionnées ? Celles qui nous intéressent sont présentes dans le fichier <em>.git/config</em> :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[svn-remote &quot;svn&quot;]<br />
url = http://subversion-server/myproject<br />
fetch = trunk:refs/remotes/trunk<br />
branches = branches/*:refs/remotes/*<br />
tags = tags/*:refs/remotes/tags/*</div></div>
<p>On y voit l’adresse du serveur, et la configuration des différents types de branches (pour rappel le trunk et les tags SVN sont des branches ordinaires qui suivent juste une certaine convention de nommage).</p>
<p>Le trunk SVN sera donc récupéré dans la branche git <em>ref/remotes/trunk</em>.<br />
Les branches, elles, iront directement en vrac dans <em>ref/remotes/*</em>. Aïe. C’est mal. Pour rappel la convention pour les branches remote d’un repository distant appelé <em>origin</em> sont de la forme<em> refs/remotes/origin/*</em>. Ici, si on ajoute par la suite un autre repository remote, ça va être le gros mélange dans les branches des différents remotes. On va donc arranger un peu ça, et mettre toutes les branches remote du serveur Subversion dans un sous-dossier :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[svn-remote &quot;svn&quot;]<br />
url = http://subversion-server/myproject<br />
fetch = trunk:refs/remotes/svn/trunk<br />
branches = branches/*:refs/remotes/svn/branches/*<br />
tags = tags/*:refs/remotes/svn/tags/*</div></div>
<h2>Récupération de l’historique</h2>
<p>Maintenant qu’on a bien configuré notre repository local, on va récupérer l&#8217;historique de notre serveur Subversion.</p>
<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git</span> <span style="color: #c20cb9; font-weight: bold;">svn</span> fetch</div></div>
<p>Le premier appel à cette commande va récupérer l’ensemble des révisions et des branches du projet. Un serveur Subversion n’étant pas conçu pour ça, il n&#8217;y a pas d&#8217;autre solution que de récupérer toutes les révisions une par une, en commençant par la toute première, puis en appliquant les révisions les unes à la suite des autres. Pour un gros repository, ça peut être long. Très long. Ça peut aller jusqu&#8217;à plusieurs jours pour un repository avec des dizaines de milliers de révisions.</p>
<p>Si on est pressé par le temps, et qu’on n’a pas besoin de tout l’historique des révisions, on peut limiter le nombre de commits récupérés. Pour cela, ajouter le paramètre<em> -r</em> qui précise la première révision à récupérer. Dans ce cas, faire bien attention à préciser une révision antérieure à la création des branches et tags que l’on va effectivement utiliser.<br />
En effet Subversion ne sauvegarde pas le &#8220;point de départ&#8221; des branches, donc il faut que Git-SVN ait suffisamment de données pour reconstituer ce point dans l&#8217;arborescence Git.</p>
<p>On pourrait aussi préciser un filtre sur les branches à récupérer, mais je ne le conseille pas : ce n’est pas tellement plus rapide à initialiser, mais par contre ça sera pénible par la suite pour créer ou suivre de nouvelles branches.</p>
<p>Une chose importante à noter est que, comme les tags Subversion sont en réalité des branches, ils sont récupérés sous la forme de branches Git dans <em>refs/remotes/svn/tags/*</em>.</p>
<h2>Manipulations locales</h2>
<p>À ce stade, on a un vrai repository Git local, qu’on va pouvoir manipuler localement comme n’importe quel repository Git, avec les commandes habituelles : <em>git status</em>, <em>git log</em>, <em>git add</em>, <em>git rm</em>, <em>git commit</em>, etc. Créez des branches locales autant que vous voulez. Par contre pour rapatrier vos branches locales dans les branches partagées, privilégiez le <em>rebase</em> plutôt que le <em>merge</em> : on verra plus bas que le détail de la branche de feature seront sinon perdues.</p>
<h2>Synchronisation avec le repository Subversion distant</h2>
<p>Pour récupérer les modifications distantes (équivalent d’un <em>git fetch</em>), on utilise la commande :</p>
<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git</span> <span style="color: #c20cb9; font-weight: bold;">svn</span> fetch</div></div>
<p>Cette commande met à jour les branches <em>remote</em>, mais ne touche pas les branches locales, exactement comme le fait <em>git fetch</em>.</p>
<p>Pour mettre à jour la branche locale courante, on peut lancer la commande suivante :</p>
<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git</span> <span style="color: #c20cb9; font-weight: bold;">svn</span> rebase</div></div>
<p>Cette commande est à peu près équivalente à la commande <em>git pull &#8211;rebase</em>. Elle effectue un <em>git svn fetch</em> (pour la branche courante uniquement), suivi d’un <em>git rebase</em> de la branche locale sur la branche remote.<br />
Il n’existe pas d’équivalent du <em>git pull</em> standard, puisque les commits de merge (commits avec plusieurs parents) ne sont de toutes façons pas gérés par Subversion.</p>
<p>Pour envoyer les modifications locales vers le serveur Subversion (équivalent d’un <em>git push</em><em></em>), on utilise :</p>
<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git</span> <span style="color: #c20cb9; font-weight: bold;">svn</span> dcommit</div></div>
<p>Attention, cette commande est assez particulière. Contrairement à un <em>git push</em>, les commits envoyés sont détruits, puis recréés à partir des données récupérées de Subversion, comme si on avait fait un rebase dessus. Ceci permet de s’assurer que le repository local représente bien l’état du repository Subversion, en ajoutant les données de la révision subversion dans le commentaire, mais aussi et surtout, ça permet de se débarrasser des fonctionnalités de Git non supportées par Subversion, comme par exemple les commits de merge.</p>
<h2>Manipulations de branches et de tags</h2>
<p>Pour créer une branche Subversion, utiliser :</p>
<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git</span> <span style="color: #c20cb9; font-weight: bold;">svn</span> branch ma-branche</div></div>
<p>Pour un tag :</p>
<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git</span> <span style="color: #c20cb9; font-weight: bold;">svn</span> tag mon-tag</div></div>
<p>Ces commandes manipulent directement le repository distant. Pas besoin donc d’effectuer un <em>dcommit</em> par la suite.</p>
<p>Pour modifier une branche distante, le fonctionnement est identique à la manipulation d’une branche remote Git : on crée une branche locale à partir de la branche distante, on modifie, on commite et on push/dcommit.</p>
<h1>Limitations</h1>
<p>On a vu les manipulations qui marchent. Maintenant attardons-nous un peu sur les points auxquels il faudra faire plus attention.</p>
<h2>Merges</h2>
<p>Comme on a pu le voir, la limitation la plus impactante est due à l’absence de gestion des merges par Subversion, qui occasionne des rebase à tout va.</p>
<p>En l’absence de suivi des merges, le suivi des reports de modifications d’une branche à l’autre est compliqué (un peu comme dans subversion seul, en fait) : difficile après un merge de savoir quelles modifications ont été reportées ou non.<br />
La solution, non optimale, que je préconise pour garder un suivi correct des reports de corrections d’une branche de recette à une branche de développement est l’utilisation de cherry-picks : les commits sont copiés un par un d’une branche à l’autre. Ainsi on n’a certes pas les infos du merge, comme la branche d&#8217;origine, mais on peut voir les modifications mieux qu’avec un gros commit qui regroupe toutes les modifications.</p>
<h2>Rebases</h2>
<p>En règle générale, il faut être bien conscient de quels commits ont déjà été synchronisés avec SVN, et surtout ne jamais les modifier. Avec Git c’est déjà mal. Avec Git-SVN ça tue des chatons. Il y a vraiment moyen de pourrir un repository Subversion avec des rebases mal maîtrisés.</p>
<h2>Commits non atomiques</h2>
<p>Un autre point d’attention est le fait que la commande <em>git svn dcommit</em> n’est pas atomique. Il faut bien être conscient qu’elle peut en fait effectuer plusieurs envois (<em>svn commit</em>) successifs, intercalés par des <em>fetch</em> et des <em>rebase</em>. Il faut donc éviter d’envoyer ses modifications en même temps qu’un autre utilisateur. Il m’est déjà arrivé de voir deux commits fusionnés localement en un (un local et un distant) dans un cas comme ça.</p>
<h2>Branches de tracking</h2>
<p>L’information de la branche remote associée à une branche locale n’est pas persistée. La branche SVN à prendre en compte lors d’un <em>git svn rebase</em> ou <em>git svn dcommit</em> est trouvée à l’exécution : c’est la branche remote la plus “proche” du <em>HEAD</em>, la première trouvée en redescendant l’arbre.<br />
On peut ainsi avoir quelques surprises. Par exemple en appelant par erreur un <em>git svn dcommit</em> depuis une branche locale créée depuis le master, on va mettre à jour la branche <em>trunk</em> avec le contenu de la branche locale.<br />
La meilleure façon de ne pas se tromper est d’utiliser les commandes remote uniquement depuis les branches locales créées à partir d’une branche remote. Ainsi pour créer une nouvelle branche subversion, utiliser d’abord <em>git svn branch</em>, puis créer la branche locale à partir de la branche distante nouvellement créée.</p>
<h2>Partage de repository</h2>
<p>Un gros problème, c’est que le hash d’un commit issu de Git-SVN n’est pas unique. Ainsi si deux développeurs utilisent Git-SVN à partir du même repository Subversion, les commits n’auront pas les mêmes identifiants. On ne pourra donc pas partager simplement une branche de travail entre deux développeurs.</p>
<h1>Conclusion</h1>
<p>J’espère que vous avez pu voir que cet outil est puissant, mais potentiellement dangereux si mal utilisé. Gardez donc à l’esprit que c’est un outil de transition, pas une solution à long terme. Et migrez vers Git !</p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2012/04/30/git-svn/" data-text="Git-SVN &#8211; Soignez votre addiction à Git"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2012/04/30/git-svn/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2012%2F04%2F30%2Fgit-svn%2F&amp;title=Git-SVN%20%E2%80%93%20Soignez%20votre%20addiction%20%C3%A0%20Git" id="wpa2a_4"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/1nsm0zpWpzU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2012/04/30/git-svn/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2012/04/30/git-svn/</feedburner:origLink></item>
		<item>
		<title>Le javascript, ça sert à tout et n’importe quoi. Surtout n’importe quoi.</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/iZ1AI1l_cA0/</link>
		<comments>http://labs.excilys.com/2012/03/16/le-javascript-ca-sert-a-tout-et-nimporte-quoi-surtout-nimporte-quoi/#comments</comments>
		<pubDate>Fri, 16 Mar 2012 12:32:19 +0000</pubDate>
		<dc:creator>David Duponchel</dc:creator>
				<category><![CDATA[WTF]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jszip]]></category>
		<category><![CDATA[wtf]]></category>
		<category><![CDATA[zip]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=2611</guid>
		<description><![CDATA[Disclaimer : cet article va parler d&#8217;une librairie js à laquelle j&#8217;ai contribué, et plus particulièrement d&#8217;une fonctionnalité que j&#8217;ai développé. J&#8217;ai tenté au maximum d&#8217;éviter les dérives nombrilistes pénibles à base de &#8220;moi je&#8221;, mais cassons le suspens tout &#8230; <a href="http://labs.excilys.com/2012/03/16/le-javascript-ca-sert-a-tout-et-nimporte-quoi-surtout-nimporte-quoi/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div style="text-align:justify">
Disclaimer : cet article va parler d&#8217;une librairie js à laquelle j&#8217;ai contribué, et plus particulièrement d&#8217;une fonctionnalité que j&#8217;ai développé. J&#8217;ai tenté au maximum d&#8217;éviter les dérives nombrilistes pénibles à base de &#8220;moi je&#8221;, mais cassons le suspens tout de suite : j&#8217;ai échoué.</p>
<h2>D&#8217;un projet WTF</h2>
<p>Le javascript est à la mode, il est donc utilisé à toutes les sauces. On le trouve côté serveur, on compile du <a href="https://github.com/kripken/emscripten">bytecode llvm vers du js</a>, on fait <a href="https://hacks.mozilla.org/2012/03/mozilla-at-game-developer-conference-2012-gdc/">des jeux</a> avec, on peut même l&#8217;utiliser pour <a href="https://www.fsf.org/news/announcing-js-labels">attaquer les libertés fondamentales</a> des utilisateurs. Dans toutes ces librairies, on trouve par exemple <a href="https://github.com/Stuk/jszip/">JSZip</a>, une librairie js qui permet de générer des fichiers zip côté client. Sans intervention du serveur (parce que utiliser java.util.zip, c&#8217;est tricher), on pourra donc créer le binaire représentant un HelloWorld.txt zippé :</p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #003366; font-weight: bold;">var</span> zip <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> JSZip<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
zip.<span style="color: #660066;">file</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;HelloWorld.txt&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;Hello, world !&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #003366; font-weight: bold;">var</span> content <span style="color: #339933;">=</span> zip.<span style="color: #660066;">generate</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
location.<span style="color: #660066;">href</span><span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;data:application/zip;base64,&quot;</span><span style="color: #339933;">+</span>content<span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>Récupérer ce binaire <a href="http://stuartk.com/jszip/">n&#8217;est pas facile</a>, mais on a vu pire.</p>
<h2>À une contribution OMGWTFBBQ</h2>
<p>L&#8217;élément déclencheur est ici <a href="https://github.com/Stuk/jszip/issues/7">une feature request</a> dans le bug tracker sur github : </p>
<blockquote><p>It would be great to have a two-way zipfile object that can be created from a file, modified and downloaded as a file.</p></blockquote>
<p>Avec des motivations telles que :</p>
<ul>
<li>for the lulz</li>
<li>because I can</li>
</ul>
<p>Il m&#8217;était impossible de ne pas tenter l&#8217;écriture d&#8217;un parser zip en js.</p>
<p><img src="http://labs.excilys.com/wp-content/uploads/2012/03/challenge-accepted.png" alt="CHALLENGE ACCEPTED" width="325" height="265" class="aligncenter size-full wp-image-2617" /></p>
<p>Les principales difficultés furent :</p>
<ul>
<li>un fichier zip se lit à l&#8217;envers. Commencer par le début pose trop de problèmes (mais je l&#8217;ai su après).</li>
<li>IE. Charger un fichier binaire en ajax demande du code vbscript. DU CODE VBSCRIPT.</li>
<li>mes idées du type &#8220;tiens, et si j&#8217;ajoutais la gestion du ZIP64, prévu pour les fichiers de plus de 4Go ?&#8221;</li>
</ul>
<p>Au final, ça donne une <a href="https://github.com/Stuk/jszip/pull/11">pull request conséquente</a> (+7472, -1092) qui ajoute la possibilité de lire des zip dans un navigateur. Même des jar (avec leurs data descriptor), même des stream zippés (avec leur extension ZIP64), même des zip avec des symboles UTF8 bizarres dans le nom des fichiers, même dans IE6.</p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #003366; font-weight: bold;">var</span> zip <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> JSZip<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
zip.<span style="color: #660066;">load</span><span style="color: #009900;">&#40;</span>zipBinaryFromAjaxCall<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
zip.<span style="color: #660066;">file</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;HelloWorld.txt&quot;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">data</span><span style="color: #339933;">;</span> <span style="color: #006600; font-style: italic;">// &quot;Hello, world !&quot;</span><br />
zip.<span style="color: #660066;">file</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;new_file.txt&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;yeeeeah !&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
zip.<span style="color: #660066;">generate</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #006600; font-style: italic;">// zip with 2 files</span></div></td></tr></tbody></table></div>
<p>Ça donne également un sacré sentiment d&#8217;accomplissement quand la pull request est acceptée <img src='http://labs.excilys.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><img src="http://labs.excilys.com/wp-content/uploads/2012/03/done.png" alt="DONE" width="351" height="438" class="aligncenter size-full wp-image-2618" /></p>
<p><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Contrat Creative Commons" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a> Article écrit sous <a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons 3.0 BY-SA</a>.
</div>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2012/03/16/le-javascript-ca-sert-a-tout-et-nimporte-quoi-surtout-nimporte-quoi/" data-text="Le javascript, ça sert à tout et n&#8217;importe quoi. Surtout n&#8217;importe quoi."></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2012/03/16/le-javascript-ca-sert-a-tout-et-nimporte-quoi-surtout-nimporte-quoi/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2012%2F03%2F16%2Fle-javascript-ca-sert-a-tout-et-nimporte-quoi-surtout-nimporte-quoi%2F&amp;title=Le%20javascript%2C%20%C3%A7a%20sert%20%C3%A0%20tout%20et%20n%E2%80%99importe%20quoi.%20Surtout%20n%E2%80%99importe%20quoi." id="wpa2a_6"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/iZ1AI1l_cA0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2012/03/16/le-javascript-ca-sert-a-tout-et-nimporte-quoi-surtout-nimporte-quoi/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2012/03/16/le-javascript-ca-sert-a-tout-et-nimporte-quoi-surtout-nimporte-quoi/</feedburner:origLink></item>
		<item>
		<title>Préparez-vous à réécrire l’histoire avec Git rebase</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/jOSCvW3TaDc/</link>
		<comments>http://labs.excilys.com/2012/02/28/preparez-vous-a-reecrire-lhistoire-avec-git-rebase/#comments</comments>
		<pubDate>Tue, 28 Feb 2012 16:23:06 +0000</pubDate>
		<dc:creator>Alexandre Dergham</dc:creator>
				<category><![CDATA[Outils]]></category>
		<category><![CDATA[Trucs & astuces]]></category>
		<category><![CDATA[Bonnes pratiques]]></category>
		<category><![CDATA[Git]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=2383</guid>
		<description><![CDATA[L&#8217;un des intérêts de Git est qu&#8217;il vous permet d&#8217;organiser vos développements locaux comme vous le souhaitez et de pratiquer la politique du &#8220;commit early, commit often&#8221;. Cependant, Si vous commitez souvent vos sources, vous vous rendrez compte que vos &#8230; <a href="http://labs.excilys.com/2012/02/28/preparez-vous-a-reecrire-lhistoire-avec-git-rebase/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>L&#8217;un des intérêts de Git est qu&#8217;il vous permet d&#8217;organiser vos développements locaux comme vous le souhaitez et de pratiquer la politique du &#8220;commit early, commit often&#8221;.<br />
Cependant, Si vous commitez souvent vos sources, vous vous rendrez compte que vos révisions peuvent parfois perdre de leur sens. Vous verrez ainsi certaines tâches s&#8217;étaler sur plusieurs révisions, plusieurs tâches dans la même révision ou des révisions de merge (pas toujours significatives). Pour vous faciliter la lecture de l&#8217;historique des sources, vous aurez besoin de le retravailler avant de le pousser sur le dépôt distant de référence. <span id="more-2383"></span><br />
C&#8217;est là que la commande <a href="http://schacon.github.com/git/git-rebase.html"><i>git rebase</i></a> entre en jeu.</p>
<p><H2>Précautions à prendre au cours de l&#8217;utilisation de <i>rebase</i></H2><br />
La commande <i>git rebase</i> est très puissante et nécessite certaines précautions :</p>
<ul>
<li>Evitez d&#8217;utiliser cette commande sur des révisions qui ont déjà été poussées sur le dépôt distant.</li>
<li>En cas d&#8217;erreur, n&#8217;oubliez pas que vous avez toujours la possibilité de revenir sur vos pas grâce aux commandes <a href="http://labs.excilys.com/2011/04/12/git-reflog-ou-comment-toujours-retomber-sur-ses-pieds/"><i>git reflog</i> et <i>git reset</i></a>.</li>
</ul>
<p><H2>Comment masquer les révisions de <i>merge</i></H2><br />
Imaginons que mon dépôt local se trouve dans la situation suivante :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; A---B---C mes_devs [HEAD]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/<br />
&nbsp; &nbsp; D---E---F---G master</div></div>
<p>A la révision <b>C</b>, je me dis que mon travail (mes_devs) peut être poussé sur la branche <i>[origin/master]</i> (la branche de référence du dépôt distant).<br />
A ce moment là, si je choisis de merger ma branche avant de pousser mes modifications, Git génère une révision de merge <b>H</b> :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; A---B---C <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/ &nbsp; &nbsp; &nbsp; &nbsp; \<br />
&nbsp; &nbsp; D---E---F---G---H master mes_devs [HEAD]</div></div>
<p>La révision <b>H</b> ne sert pas à grand chose et au final mes collaborateurs voient une boucle dans l&#8217;historique du projet.<br />
Personnellement, je pense que créer une branche locale pour chaque nouveau développement est une bonne pratique mais pas au point de la voir apparaître dans l&#8217;historique distant sous forme de boucle.<br />
Multipliez cette boucle par le nombre de collaborateurs et votre <i>gitk</i> finit rapidement par vous afficher un historique qui ressemble à <a href="http://labs.excilys.com/wp-content/uploads/2012/02/ratp.jpeg">ceci</a>.</p>
<p>Revenons un peu en arrière :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; A---B---C mes_devs [HEAD]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/<br />
&nbsp; &nbsp; D---E---F---G master</div></div>
<p>En utilisant la commande <i>git rebase</i>, il est possible d&#8217;appliquer les modifications des révisions <b>A</b>, <b>B</b> et <b>C</b> sur la révision <b>G</b>:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">git rebase master</div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;A---B---C mes_devs [HEAD]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/<br />
&nbsp; &nbsp; D---E---F---G master</div></div>
<p>On peut alors merger notre branche master pour maintenir notre historique linéaire.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">git checkout master<br />
git merge mes_devs</div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;A---B---C mes_devs master [HEAD]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/<br />
&nbsp; &nbsp; D---E---F---G</div></div>
<p>Mon historique s&#8217;en trouve nettement simplifié et la santé mentale de mes collaborateurs est préservée.</p>
<p><H2>Utilisation de <i>rebase</i> dans la commande <i>pull</i></H2><br />
Pour rappel, la commande <i>git pull</i> équivaut à <i>git fetch</i> + <i>git merge</i>.<br />
Imaginons que je me trouve dans cette situation :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;C master [HEAD]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/<br />
&nbsp; &nbsp; A---B---D [origin/master]</div></div>
<p>Si je lance la commande <i>git pull</i> sans la moindre option, j&#8217;obtiens ceci :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-C-<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/ &nbsp; &nbsp; \<br />
&nbsp; &nbsp; A---B---D---E master [origin/master] [HEAD]</div></div>
<p>Si je lance la commande <i>git pull &#8211;rebase</i>, je change le comportement par défaut de la commande <i>git pull</i> pour qu&#8217;elle devienne l&#8217;équivalent de <i>git fetch</i> + <i>git rebase</i> pour m&#8217;épargner la création du commit de merge:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;C master [HEAD]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/<br />
&nbsp; &nbsp; A---B---D [origin/master]</div></div>
<p><H2>Comment modifier ses révisions avant de les pousser</H2><br />
Revenons sur la situation suivante</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; A---B---C mes_devs [HEAD]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/<br />
&nbsp; &nbsp; D---E---F---G master</div></div>
<p>Avant d&#8217;appliquer la procédure décrite dans la section précédente, je me rends compte que mes révisions ont été mal définies. Les révisions <b>A</b> et <b>B</b> correspondent à la correction d&#8217;un même bug si bien que la révision <b>A</b> n&#8217;a aucune raison d&#8217;exister seule.<br />
De plus, la révision <b>C</b> contient une évolution et une correction de bug complètement décorrelées.<br />
Si vous tenez à pousser des révisions qui ont du sens, alors l&#8217;option <i>-i</i> (ou <i>&#8211;interactive</i>)de <i>git rebase</i> est ce que vous cherchez.<br />
La commande suivante va nous permettre de travailler sur l&#8217;historique de la branche courante en se limitant aux 3 dernières révisions:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">git rebase -i HEAD~3</div></div>
<p>Le mode interactif de git s&#8217;active et vous verrez alors les lignes suivantes dans un éditeur de texte:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">pick 15ed2a6 Début de correction du bug 1. (A)<br />
pick 87be587 Fin de correction du bug 1. (B)<br />
pick bc4875d Ajout de la fonctionnalité X. (C)<br />
<br />
# Rebase b9be212..259a7e6 onto bc4875d<br />
#<br />
# Commands:<br />
# &nbsp;p, pick = use commit<br />
# &nbsp;r, reword = use commit, but edit the commit message<br />
# &nbsp;e, edit = use commit, but stop for amending<br />
# &nbsp;s, squash = use commit, but meld into previous commit<br />
# &nbsp;f, fixup = like &quot;squash&quot;, but discard this commit's log message<br />
# &nbsp;x, exec = run command (the rest of the line) using shell<br />
#<br />
# If you remove a line here THAT COMMIT WILL BE LOST.<br />
# However, if you remove everything, the rebase will be aborted.<br />
#</div></div>
<p>C&#8217;est là que vous pouvez agir sur votre historique en remplaçant le mot clé &#8220;<i>pick</i>&#8221; par une des commandes suggérées par le mode interactif de git.<br />
Vous pouvez supprimer ou changer l&#8217;ordre des commits en supprimant/échangeant les lignes correspondantes. Il ne faut surtout pas chercher à modifier les clés (hash) identifiant les révisions.<br />
Dans notre exemple nous voulons appliquer les opérations suivantes : </p>
<ul>
<li>Changer le message de <b>A</b>.</li>
<li>Combiner <b>A</b> et <b>B</b> sans conserver le message de <b>B</b>.</li>
<li>Séparer la révision <b>C</b> en <b>C1</b> et <b>C2</b>.</li>
</ul>
<p>Ce qui nous donne :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">reword 15ed2a6 Début de correction du bug 1.<br />
fixup 87be587 Fin de correction du bug 1.<br />
edit bc4875d Ajout de la fonctionnalité X.</div></div>
<p>Git va vous inviter à saisir le message de commit de la révision <b>A</b> dans un premier temps.<br />
Ensuite, vous devrez restaurer le système de fichiers à l&#8217;état qui précédait le commit de la révision <b>C</b> avant de reconstruire les révisions <b>C1</b> et <b>C2</b>.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">git reset HEAD~1<br />
git add /correction/de/bug/2/.<br />
git commit -m &quot;correction du bug 2&quot;<br />
git add /ajout/de/fonctionnalité/X/.<br />
git commit -m &quot;Ajout de la fonctionnalité X.&quot;<br />
git rebase --continue</div></div>
<p>Vous finissez avec l&#8217;arborescence suivante :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; A---C1---C2 mes_devs [HEAD]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/<br />
&nbsp; &nbsp; D---E---F---G master</div></div>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2012/02/28/preparez-vous-a-reecrire-lhistoire-avec-git-rebase/" data-text="Préparez-vous à réécrire l&#8217;histoire avec Git rebase"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2012/02/28/preparez-vous-a-reecrire-lhistoire-avec-git-rebase/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2012%2F02%2F28%2Fpreparez-vous-a-reecrire-lhistoire-avec-git-rebase%2F&amp;title=Pr%C3%A9parez-vous%20%C3%A0%20r%C3%A9%C3%A9crire%20l%E2%80%99histoire%20avec%20Git%20rebase" id="wpa2a_8"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/jOSCvW3TaDc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2012/02/28/preparez-vous-a-reecrire-lhistoire-avec-git-rebase/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2012/02/28/preparez-vous-a-reecrire-lhistoire-avec-git-rebase/</feedburner:origLink></item>
		<item>
		<title>Hibernate n’aime pas la discrimination</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/0cQxuflon8w/</link>
		<comments>http://labs.excilys.com/2011/12/30/hibernate-naime-pas-la-discrimination/#comments</comments>
		<pubDate>Fri, 30 Dec 2011 09:16:39 +0000</pubDate>
		<dc:creator>Benoit Lateltin</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[discriminator]]></category>
		<category><![CDATA[Hibernate]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=2178</guid>
		<description><![CDATA[Travailler sur des applications legacy nous amène parfois à faire face à des difficultés qui nous gênent dans la mise en place d&#8217;outils modernes tout en conservant le modèle existant. Cela m’est arrivé récemment lorsque j’ai du mapper une table &#8230; <a href="http://labs.excilys.com/2011/12/30/hibernate-naime-pas-la-discrimination/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Travailler sur des applications legacy nous amène parfois à faire face à des difficultés qui nous gênent dans la mise en place d&#8217;outils <em>modernes</em> tout en conservant le modèle existant.<br />
Cela m’est arrivé récemment lorsque j’ai du mapper une table de jointure un peu spéciale sur un projet de portage.<span id="more-2178"></span></p>
<p>Cette table, qu’on appellera <em>USERLINK</em> permet d’associer un object User <em>soit</em> avec une Company <em>soit</em> avec un BankAccount. Dans la table, on a une colonne <em>LINKTYPE</em> qui permet justement de savoir de quel type est l’association.</p>
<p>Jusque là tout va bien, on choisit la stratégie Hibernate “Table per class hierarchy” et on utilise la colonne <em>LINKTYPE</em> comme discriminant. On obtient alors un fichier de mapping de ce genre :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;class</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;com.excilys.labs.model.UserGenericLink&quot;</span> <span style="color: #000066;">table</span>=<span style="color: #ff0000;">&quot;USERLINK&quot;</span> <span style="color: #000066;">abstract</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="color: #000066;">discriminator-value</span>=<span style="color: #ff0000;">&quot;-1&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;id</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;id&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;column</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;IDUSERLINK&quot;</span> <span style="color: #000066;">not-null</span>=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; [...]<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/id<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;discriminator</span> <span style="color: #000066;">column</span>=<span style="color: #ff0000;">&quot;LINKTYPE&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;integer&quot;</span> <span style="color: #000066;">not-null</span>=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;many-to-one</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;user&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;column</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;IDUSER&quot;</span> <span style="color: #000066;">not-null</span>=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/many-to-one<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;subclass</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;com.excilys.labs.model.UserToCompanyLink&quot;</span> <span style="color: #000066;">discriminator-value</span>=<span style="color: #ff0000;">&quot;0&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;many-to-one</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;company&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;com.excilys.labs.model.Company&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;column</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;IDOBJECT&quot;</span> <span style="color: #000066;">not-null</span>=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/many-to-one<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/subclass<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;subclass</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;com.excilys.labs.model.UserToBankAccountLink&quot;</span> <span style="color: #000066;">discriminator-value</span>=<span style="color: #ff0000;">&quot;1&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;many-to-one</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;bankAccount&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;com.excilys.labs.model.BankAccount&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;column</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;IDOBJECT&quot;</span> <span style="color: #000066;">not-null</span>=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/many-to-one<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/subclass<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/class<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p>De l’autre coté, dans le mapping de la classe User on rajoute les sets qui vont bien :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;set</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;companyLinks&quot;</span> <span style="color: #000066;">inverse</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="color: #000066;">embed-xml</span>=<span style="color: #ff0000;">&quot;false&quot;</span> <span style="color: #000066;">cascade</span>=<span style="color: #ff0000;">&quot;save-update, delete, delete-orphan&quot;</span> <span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;key<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;column</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;IDUSER&quot;</span> <span style="color: #000066;">not-null</span>=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/key<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;one-to-many</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;com.excilys.labs.model.UserToCompanyLink&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/set<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;set</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;bankAccountsLinks&quot;</span> <span style="color: #000066;">inverse</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="color: #000066;">embed-xml</span>=<span style="color: #ff0000;">&quot;false&quot;</span> <span style="color: #000066;">cascade</span>=<span style="color: #ff0000;">&quot;save-update, delete, delete-orphan&quot;</span> <span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;key<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;column</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;IDUSER&quot;</span> <span style="color: #000066;">not-null</span>=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/key<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;one-to-many</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;com.excilys.labs.model.UserToBankAccountLink&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/set<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p>Tout ça ne semble pas trop mal, Hibernate a maintenant toutes les informations dont il a besoin, et pourtant&#8230;<br />
Lorsqu’on essaye de récupérer tous les liens d’un utilisateur vers ses sociétés :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Set<span style="color: #339933;">&lt;</span>UserToCompanyLink<span style="color: #339933;">&gt;</span> companyLinks <span style="color: #339933;">=</span> user.<span style="color: #006633;">getCompanyLinks</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>on récupère un Set qui contient plus d’objets que prévu.</p>
<p>Pour comprendre ce qui se passe on active les logs Hibernate et on se rend compte que ce dernier omet tout simplement d&#8217;utiliser le discriminant (pas de &#8220;WHERE LINKTYPE=?&#8221; dans les requêtes).<br />
Un petit tour dans la <a href="http://www.hibernate.org/docs">doc</a> Hibernate et on trouve notre <a href="http://docs.jboss.org/hibernate/core/4.0/manual/en-US/html/mapping.html#mapping-declaration-discriminator">parade</a> :</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;discriminator</span> <span style="color: #000066;">column</span>=<span style="color: #ff0000;">&quot;LINKTYPE&quot;</span> <span style="color: #000066;">force</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;integer&quot;</span> <span style="color: #000066;">not-null</span>=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span></div></td></tr></tbody></table></div>
<p>En effet dans ce cas particulier, <em>&#8220;mapping d&#8217;une association dont le type est mappé avec un discriminant&#8221;</em>, il est nécessaire de forcer l&#8217;utilisation de ce dernier avec le paramètre force=&#8221;true&#8221;.</p>
<p>Bug Hibernate ou mauvaise modélisation, dans tous les cas il n&#8217;est pas naturel de devoir forcer l&#8217;utilisation du discriminant. Certains se sont déjà posés <a href="http://www.gmarwaha.com/blog/2009/08/26/hibernate-why-should-i-force-discriminator/">la question</a> mais je n&#8217;ai pas un avis tranché à ce jour. Et vous, qu&#8217;en pensez vous ?</p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2011/12/30/hibernate-naime-pas-la-discrimination/" data-text="Hibernate n&#8217;aime pas la discrimination"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2011/12/30/hibernate-naime-pas-la-discrimination/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2011%2F12%2F30%2Fhibernate-naime-pas-la-discrimination%2F&amp;title=Hibernate%20n%E2%80%99aime%20pas%20la%20discrimination" id="wpa2a_10"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/0cQxuflon8w" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2011/12/30/hibernate-naime-pas-la-discrimination/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2011/12/30/hibernate-naime-pas-la-discrimination/</feedburner:origLink></item>
		<item>
		<title>Les Apache Commons</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/fUj1gbLtbhA/</link>
		<comments>http://labs.excilys.com/2011/12/28/les-apache-commons/#comments</comments>
		<pubDate>Wed, 28 Dec 2011 14:06:15 +0000</pubDate>
		<dc:creator>Baptiste Pagnier</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Non classé]]></category>
		<category><![CDATA[Trucs & astuces]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=2131</guid>
		<description><![CDATA[Apache Commons est l&#8217;un des principaux projets de la fondation Apache. Il est composé d&#8217;un ensemble de bibliothèques open-sources et réutilisables mettant une multitude d&#8217;utilitaires à la disposition des développeurs Java. Le projet est composé de 3 parties : The Commons &#8230; <a href="http://labs.excilys.com/2011/12/28/les-apache-commons/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://commons.apache.org/">Apache Commons</a> est l&#8217;un des principaux projets de la fondation Apache.<br />
Il est composé d&#8217;un ensemble de bibliothèques open-sources et réutilisables mettant une multitude d&#8217;utilitaires à la disposition des développeurs Java.<span id="more-2131"></span></p>
<p>Le projet est composé de 3 parties :</p>
<ul>
<li><strong>The Commons Proper</strong> : Principale partie, elle contient l&#8217;ensemble des utilitaires Java.</li>
<li><strong>The Commons Sandbox</strong> : Repository SVN qui fonctionne comme un workspace pour la collaboration sur les projets en cours, destinés à faire partie des Commons Proper.</li>
<li><strong>The Commons Dormant</strong> : Projets de Sandbox qui sont inactifs.</li>
</ul>
<p>Commons Proper est composé de nombreux modules qui regroupent des utilitaires par sujets. On trouve par exemple : <a href="http://commons.apache.org/collections/">Commons Collections</a>, <a href="http://commons.apache.org/lang/">Commons Lang</a>, <a href="http://commons.apache.org/logging/">Commons Logging</a>,  <a href="http://commons.apache.org/pool/">Commons Pool</a>, &#8230;</p>
<h1>Pour quoi faire ?</h1>
<p>Le travail de développeur consiste non seulement à écrire du code, mais également et surtout à <a href="http://en.wikipedia.org/wiki/Code_reuse">réutiliser</a> ce qui existe déjà.<br />
En plus d&#8217;être généralement moins couteux en temps et en énergie, la réutilisation réduit le travail redondant et permet d&#8217;utiliser du code qui a été pensé et écrit de manière optimale. C&#8217;est donc un gain de temps et de performance (à condition de ne pas prendre n&#8217;importe quel code &#8230;).<br />
Les Apache Commons sont créés avec cette optique. Ils proposent de très nombreux utilitaires pour répondre aux petits problèmes auxquels sont régulièrement confrontés les développeurs. Ils sont simples et rapides à mettre en place et à comprendre et existent pour des sujets divers et variés.</p>
<p>Que ce soit pour travailler sur des Strings ou des Collections, en ligne de commande, pour communiquer avec une base de données, lire des fichiers de configuration, logger, travailler avec des fichiers XML, créer des emails, uploader du contenu, faire des statistiques, &#8230; (la liste est longue !) il y a les Apache Commons pour nous simplifier la vie !</p>
<h1>Exemples :</h1>
<p>Etablir une liste exhaustive de ce que proposent les Apache Commons prendrait beaucoup de temps.<br />
Je vais donc présenter par des exemples quelques une des librairies existantes.</p>
<h2>Commons Lang :</h2>
<p>Additions au package java.lang : chaines de caractères, calculs, réflexion, sérialization, etc.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">StringUtils.<span style="color: #006633;">isBlank</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot; &quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// true</span><br />
StringUtils.<span style="color: #006633;">leftPad</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;12345&quot;</span>, <span style="color: #cc66cc;">10</span>, <span style="color: #0000ff;">'0'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// 0000012345</span><br />
StringUtils.<span style="color: #006633;">isNumeric</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;45a4&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// false</span><br />
StringUtils.<span style="color: #006633;">swapCase</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;The dog has a BONE&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// &quot;tHE DOG HAS A bone&quot;</span><br />
StringUtils.<span style="color: #006633;">equalsIgnoreCase</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;bonjour&quot;</span>, <span style="color: #0000ff;">&quot;BonJouR&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// true</span><br />
BooleanUtils.<span style="color: #006633;">toBoolean</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;truez&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// false</span><br />
BooleanUtils.<span style="color: #006633;">isTrue</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// false</span><br />
ClassUtils.<span style="color: #006633;">getPackageName</span><span style="color: #009900;">&#40;</span>Main.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// com.test</span></div></td></tr></tbody></table></div>
<p>ToStringBuilder :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">org.apache.commons.lang.builder.ToStringBuilder</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">org.apache.commons.lang.builder.ToStringStyle</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> ExampleToStringBuilder <span style="color: #009900;">&#123;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> name<span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000066; font-weight: bold;">int</span> counter<span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000066; font-weight: bold;">boolean</span> isOk<span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">public</span> ExampleToStringBuilder<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> name, <span style="color: #000066; font-weight: bold;">int</span> counter, <span style="color: #000066; font-weight: bold;">boolean</span> isOk<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
<span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">name</span> <span style="color: #339933;">=</span> name<span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">counter</span> <span style="color: #339933;">=</span> counter<span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">isOk</span> <span style="color: #339933;">=</span> isOk<span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">String</span> toString<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
<span style="color: #000000; font-weight: bold;">return</span> ToStringBuilder.<span style="color: #006633;">reflectionToString</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">this</span>, ToStringStyle.<span style="color: #006633;">SHORT_PREFIX_STYLE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">void</span> main<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> args<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
ExampleToStringBuilder ex <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> ExampleToStringBuilder<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;bonjour&quot;</span>, <span style="color: #cc66cc;">123</span>, <span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span>ex<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>Ce qui affiche : &#8220;<em>ExampleToStringBuilder[name=bonjour,counter=123,isOk=true]</em>&#8221;</p>
<p>Il existe le même genre d&#8217;outils utilisant la réflexion avec EqualsBuilder ou encore HashCodeBuilder :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">boolean</span> equals<span style="color: #009900;">&#40;</span><span style="color: #003399;">Object</span> obj<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>obj <span style="color: #000000; font-weight: bold;">instanceof</span> Person<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
Person other <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>Person<span style="color: #009900;">&#41;</span> obj<span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000000; font-weight: bold;">new</span> EqualsBuilder<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">//</span><br />
.<span style="color: #006633;">append</span><span style="color: #009900;">&#40;</span>getId<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>, other.<span style="color: #006633;">getId</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">//</span><br />
.<span style="color: #006633;">append</span><span style="color: #009900;">&#40;</span>getName<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>, other.<span style="color: #006633;">getName</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">//</span><br />
.<span style="color: #006633;">append</span><span style="color: #009900;">&#40;</span>isFemale<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>, other.<span style="color: #006633;">isFemale</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">//</span><br />
.<span style="color: #006633;">isEquals</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">false</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<h2>Commons Configuration :</h2>
<p>Cette librairie propose une manière générique de charger sa configuration en provenance de sources très diverses. Elle permet également un accès typé aux paramètres :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #003399;">Double</span> <span style="color: #000066; font-weight: bold;">double</span> <span style="color: #339933;">=</span> config.<span style="color: #006633;">getDouble</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;number&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #003399;">Integer</span> integer <span style="color: #339933;">=</span> config.<span style="color: #006633;">getInteger</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;number&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>La configuration peut être chargée en provenance de multiples sources :</p>
<ul>
<li>Properties files</li>
<li>XML documents</li>
<li>Windows INI files</li>
<li>Property list files (plist)</li>
<li>JNDI</li>
<li>JDBC Datasource</li>
<li>System properties</li>
<li>Applet parameters</li>
<li>Servlet parameters</li>
</ul>
<h2>Commons Collections :</h2>
<p>Cette librairie est construite comme une couche supplémentaire sur les Collections du JDK. Elle propose des utilitaires pour travailler avec les collections, mais également de nouvelles interfaces et implémentations.</p>
<p>Parmi les utilitaires, on trouve une classe par interface de Collection du SDK (<a href="http://commons.apache.org/collections/apidocs/org/apache/commons/collections/MapUtils.html">MapUtils</a>, <a href="http://commons.apache.org/collections/api-3.1/org/apache/commons/collections/ListUtils.html">ListUtils</a>, &#8230;).</p>
<p>Pour ce qui est des interfaces, on a notamment <a href="http://commons.apache.org/collections/apidocs/org/apache/commons/collections/Predicate.html">Predicate </a>qui est utilisée pour évaluer un objet, c&#8217;est-à-dire vérifier qu&#8217;il remplit bien certaines conditions, ou encore <a href="http://commons.apache.org/collections/apidocs/org/apache/commons/collections/Transformer.html">Transformer</a>, qui permet de transformer un objet en un autre selon des critères définis dans les implémentations.</p>
<p>Il y a aussi de nouvelles Collections comme le <a href="http://commons.apache.org/collections/apidocs/org/apache/commons/collections/Bag.html">Bag </a>qui représente une collection d&#8217;objet avec un certain nombre de copies pour chaque élément :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Bag bag <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> HashBag<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
bag.<span style="color: #006633;">add</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;ONE&quot;</span>, <span style="color: #cc66cc;">6</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// Ajoute 6 copies de &quot;ONE&quot;</span><br />
bag.<span style="color: #006633;">remove</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;ONE&quot;</span>, <span style="color: #cc66cc;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// Enlève 2 copies de &quot;ONE&quot;</span><br />
bag.<span style="color: #006633;">getCount</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;ONE&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// retourne 4</span></div></td></tr></tbody></table></div>
<p>Une nouvelle hiérarchie totale de Collection est proposée pour les map bidirectionnelles : <a href="http://commons.apache.org/collections/api-3.1/org/apache/commons/collections/BidiMap.html">BidiMap</a>.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">BidiMap bidi <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> TreeBidiMap<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
bidi.<span style="color: #006633;">put</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;SIX&quot;</span>, <span style="color: #0000ff;">&quot;6&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
bidi.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;SIX&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// returns &quot;6&quot;</span><br />
bidi.<span style="color: #006633;">getKey</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;6&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// returns &quot;SIX&quot;</span><br />
bidi.<span style="color: #006633;">removeValue</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;6&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// removes the mapping</span><br />
BidiMap inverse <span style="color: #339933;">=</span> bidi.<span style="color: #006633;">inverseBidiMap</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// retourne une map avec les clefs et les valeurs inversées</span></div></td></tr></tbody></table></div>
<p>Et ce n&#8217;est encore une fois que de simples exemples parmi tant d&#8217;autres possibles !</p>
<h1>Conclusion</h1>
<p>Les Apaches Commons peuvent être utiles quotidiennement. Il est bon de savoir que ces librairies existent, et d&#8217;aller voir régulièrement s&#8217;il n&#8217;y a pas une méthode correspondant à ce que l&#8217;on cherche avant de l&#8217;écrire soit même !<br />
Les sources sont également très intéressantes pour regarder comment de simples choses peuvent être faites de manière performante.</p>
<p>Je n&#8217;ai donné ici qu&#8217;un très bref aperçu de l&#8217;étendue de ces librairies, à vous d&#8217;aller en voir plus !</p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2011/12/28/les-apache-commons/" data-text="Les Apache Commons"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2011/12/28/les-apache-commons/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2011%2F12%2F28%2Fles-apache-commons%2F&amp;title=Les%20Apache%20Commons" id="wpa2a_12"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/fUj1gbLtbhA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2011/12/28/les-apache-commons/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2011/12/28/les-apache-commons/</feedburner:origLink></item>
		<item>
		<title>Args4j – CLI Parser (Command Line Interface Parser)</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/u1AGl7yLwuw/</link>
		<comments>http://labs.excilys.com/2011/12/24/args4j-cli-parser-command-line-interface-parser/#comments</comments>
		<pubDate>Fri, 23 Dec 2011 23:30:02 +0000</pubDate>
		<dc:creator>Yassine BEN-KHALED</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[Outils]]></category>
		<category><![CDATA[CLI Parser]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=2008</guid>
		<description><![CDATA[Args4j est un projet open-source, sous licence MIT, permettant de parser nos arguments/options de la ligne de commande. Le responsable du développement est Kohsuke Kawaguchi. Args4j est un sous-projet de Java Tools, qui est un ensemble de petits outils assez pratique pour &#8230; <a href="http://labs.excilys.com/2011/12/24/args4j-cli-parser-command-line-interface-parser/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Args4j est un projet open-source, sous licence MIT, permettant de parser nos arguments/options de la ligne de commande. Le responsable du développement est <a title="Kohsuke Kawaguchi" href="https://github.com/kohsuke" target="_blank">Kohsuke Kawaguchi</a>. Args4j est un sous-projet de <a title="Java Tools" href="http://java.net/projects/javatools" target="_blank">Java Tools</a>, qui est un ensemble de petits outils assez pratique pour tout développeur Java. On y trouve notamment <a title="Visual VM" href="http://visualvm.java.net/" target="_blank">Visual VM</a>, pour monitorer notre VM, ou encore <a title="Hudson" href="http://hudson-ci.org/" target="_blank">Hudson</a> dont Kohsuke a forké le projet pour devenir <a title="Jenkins" href="http://jenkins-ci.org/" target="_blank">Jenkins</a>.</p>
<p><span id="more-2008"></span>La manipulation d&#8217;arguments d&#8217;une application peut devenir vite contraignante quand il s&#8217;agit de les traiter comme des options.</p>
<p>En raison de sa simplicité et de sa taille (das le but de créer un uberJar), j&#8217;ai opté pour l&#8217;utilisation d&#8217;Args4j et ce, malgré la présence d&#8217;autres projets, tel que <a title="Commons CLI" href="http://commons.apache.org/cli/" target="_blank">Commons CLI</a>.</p>
<p>Je vais vous faire découvrir cette librairie à travers une explication détaillée. Vous trouverez les sources citées dans cet article sur <a title="Github Yassine BEN-KHALED" href="https://github.com/ybenkhaled/article-args4j" target="_blank">mon github</a>.</p>
<p>Pour commencer, on va créer une classe qui va nous permettre de déclarer toutes les options de notre application :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> CommandLineOptions <span style="color: #009900;">&#123;</span><br />
...<br />
<span style="color: #009900;">&#125;</span></div></div>
<p>On va procéder à la déclaration de la première option dans notre application qui aura pour intérêt de définir <strong>obligatoirement</strong> un nom en tant qu&#8217;option.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@<span style="color: #003399;">Option</span><span style="color: #009900;">&#40;</span>name <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;-n&quot;</span>, usage <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;Name of the application&quot;</span>, metaVar <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;String&quot;</span>, aliases <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;--name&quot;</span>, required <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> name<span style="color: #339933;">;</span></div></div>
<p>La première chose que l&#8217;on puisse remarquer est l&#8217;utilisation d&#8217;annotation. Cela participe grandement à la lisibilité et la clarté de notre code mais cependant nécessite d&#8217;avoir au minimum un JRE en version 1.5.<br />
Ainsi, nous avons déclaré notre attribut de type <strong>String</strong> de manière classique, qu&#8217;on a ensuite précéder de l&#8217;annotation <strong>@Option</strong> avec différents paramètres.<br />
Voici une description de chacun de ces paramètres :</p>
<ul>
<li><em><strong>name</strong></em> : représente le nom de l&#8217;option</li>
<li><strong><em>usage</em></strong> : permet de définir une description de l&#8217;option</li>
<li><strong><em>metaVar</em></strong> : modifie le type affiché lors de l&#8217;affichage de l&#8217;utilisation de l&#8217;option</li>
<li><strong><em>aliases</em></strong> : crée un alias au paramètre <strong><em>name</em></strong>, souvent utilisé pour définir des options avec un nom plus complet</li>
<li><strong><em>req</em><em>uired</em></strong> : comme son nom l&#8217;indique, permet d&#8217;indiquer si cette option est obligatoire (<em><strong>false</strong></em> par défaut)</li>
</ul>
<p>Si l&#8217;option n&#8217;est pas obligatoire, il est possible de lui définir une valeur par défaut. Ceci en initialisant tout simplement notre attribut :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@<span style="color: #003399;">Option</span><span style="color: #009900;">&#40;</span>name <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;-s&quot;</span>, usage <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;Override the default separator&quot;</span>, aliases <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;--separator&quot;</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> separator <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span></div></div>
<p>Si nous devons également définir une liste ou autre collection, il suffit d&#8217;utiliser le paramètre <strong>multiValued</strong> sur notre annotation.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@<span style="color: #003399;">Option</span><span style="color: #009900;">&#40;</span>name <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;-i&quot;</span>, usage <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;Number of integers&quot;</span>, aliases <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;--integer&quot;</span>, multiValued <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">List</span>‹<span style="color: #003399;">Integer</span>› integers<span style="color: #339933;">;</span></div></div>
<p>Voilà, nous avons vu quasiment tous les types de paramètre possible sur nos options. Un dernier point qui peut s&#8217;avérer très utile, Args4j va nous permettre de récupérer tous les arguments de notre programme dans une liste, grâce à l&#8217;annotation <strong>@Argument</strong> :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@Argument<br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">List</span>‹<span style="color: #003399;">String</span>› arguments<span style="color: #339933;">;</span></div></div>
<p>Si nous voulions définir un nombre précis d&#8217;argument avec un ordre spécifique, il suffirait d&#8217;annoter chacun de nos attributs avec <strong>@Argument</strong> et de mettre le paramètre <strong>index</strong> :</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@Argument<span style="color: #009900;">&#40;</span>index <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> argument1<span style="color: #339933;">;</span><br />
<br />
@Argument<span style="color: #009900;">&#40;</span>index <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">String</span> argument2<span style="color: #339933;">;</span></div></div>
<p>( L&#8217;<strong>index</strong> commence bien-sur à 0 <img src='http://labs.excilys.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  )</p>
<p>Jusqu&#8217;ici, nous avons utilisé que des types de base, mais comment faire pour définir une option (ou argument) d&#8217;un type complexe ?</p>
<p>On crée tout d&#8217;abord une classe <strong>Complex.java</strong>, qui va représenter un nombre complexe dans notre cas.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> Complex <span style="color: #009900;">&#123;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000066; font-weight: bold;">int</span> r, i<span style="color: #339933;">;</span><br />
...<br />
<span style="color: #009900;">&#125;</span></div></div>
<p>Ensuite on déclare un attribut de type <strong>Complex</strong> et ajoutons parmi les paramètres de l&#8217;annotation un <strong>handler</strong>.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@<span style="color: #003399;">Option</span><span style="color: #009900;">&#40;</span>name <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;-c&quot;</span>, usage <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;Definition of a complex type&quot;</span>, aliases <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;--complex&quot;</span>, handler <span style="color: #339933;">=</span> ComplexHandler.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #000000; font-weight: bold;">private</span> Complex complex<span style="color: #339933;">;</span></div></div>
<p>Ce handler va permettre de gérer la création de notre attribut à l&#8217;aide de la valeur passée à l&#8217;option. Pour cela, le handler doit hériter de la classe <strong>OptionHandler</strong> et ainsi implémenter les différentes méthodes nécessaires. La première est <strong>getDefaultMetaVariable()</strong> qui va permettre de définir, comme vu auparavant, le paramètre <strong>metaVar</strong> de notre option. Si le paramètre <strong>metaVar</strong> est défini sur l&#8217;annotation, alors sa valeur écrase la valeur du handler. La seconde méthode est <strong>parseArguments(String[])</strong>. Cette méthode va permettre de récupérer la valeur de l&#8217;option fournie en ligne de commande pour que l&#8217;on puisse effectuer le traitement dans cette méthode et créer notre attribut complexe.</p>
<p>Pour finir, on n&#8217;oublie pas les getters/setters. Apres quoi, nous pouvons passer au point d&#8217;entrée de l&#8217;application.</p>
<div class="codecolorer-container java default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">CommandLineOptions cliOpts <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> CommandLineOptions<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
CmdLineParser parser <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> CmdLineParser<span style="color: #009900;">&#40;</span>cliOpts<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">try</span> <span style="color: #009900;">&#123;</span><br />
parser.<span style="color: #006633;">parseArgument</span><span style="color: #009900;">&#40;</span>args<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span>CmdLineException e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
<span style="color: #003399;">System</span>.<span style="color: #006633;">err</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span>e.<span style="color: #006633;">getMessage</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
parser.<span style="color: #006633;">printUsage</span><span style="color: #009900;">&#40;</span><span style="color: #003399;">System</span>.<span style="color: #006633;">err</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #003399;">System</span>.<span style="color: #006633;">err</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\t</span>Example: Main &quot;</span> <span style="color: #339933;">+</span> parser.<span style="color: #006633;">printExample</span><span style="color: #009900;">&#40;</span>ALL<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #003399;">System</span>.<span style="color: #006633;">exit</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<p>Nous déclarons et instancions notre bean contenant toutes nos définitions d&#8217;options et arguments que l&#8217;on passe à un <strong>CmdLineParser</strong>. Ce parser va s&#8217;occuper de faire tout notre travail, il suffit d&#8217;appeler <strong>parseArgument(args)</strong>. Si une option n&#8217;existe pas ou est mal formée, alors une exception sera levée et l&#8217;on affiche à l&#8217;utilisateur le manuel d&#8217;utilisation de l&#8217;application. Voici un exemple de ligne de commande correcte :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Main -d -i 1 -i 2 -c &quot;1,2&quot; --name MyApplication myArg1 myArg2</div></div>
<p>On remarque que pour utiliser l&#8217;option du type <strong>boolean</strong> il suffit juste de spécifier l&#8217;option suivi d&#8217;aucune valeur. Si on regarde dans le <strong>pom</strong>, on voit qu&#8217;on utilise actuellement la version 2.0.16 de Args4j, qui est la dernière version disponible sur le <a href="http://search.maven.org/#search%7Cga%7C1%7Cargs4j" target="_blank">repository de Maven</a>.</p>
<p>Depuis la version 2.0.19, Args4j permet de spécifier la valeur de notre option pour le type <strong>boolean</strong>. Avant la version 2.0.19, si une valeur était spécifiée pour une option de type <strong>boolean</strong>, alors cette valeur était prise comme argument de l&#8217;application. Pour ceux qui voudraient utiliser la version 2.0.19, vous devez rajouter le repository de Glassfish à votre <strong>pom</strong>.</p>
<div class="codecolorer-container xml default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">‹repository›<br />
‹id›glasshfish‹/id›<br />
‹name›glassfish‹/name›<br />
‹url›http://maven.glassfish.org/content/repositories/public‹/url›<br />
‹/repository›</div></div>
<p>Enfin, lorsqu&#8217;on essaye de lancer cette application sans aucun argument (ou que nous faisons une erreur de saisie), on se retrouve avec un joli message d&#8217;explication ainsi que les différentes options possibles et un exemple d&#8217;utilisation :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Option &quot;-n (--name)&quot; is required<br />
<br />
-c (--complex) &quot;int r, int i&quot;: Definition of a complex type<br />
-d (--display) : Display or not the configuration<br />
-i (--integer) int : Number of integers<br />
-n (--name) String : Name of the program<br />
-s (--separator) String : Override the default character separator<br />
<br />
Example: Main -c (--complex) &quot;int r, int i&quot;-d (--display) -i (--integer) int -n (--name) String-s (--separator) String</div></td></tr></tbody></table></div>
<p>Voilà à peu près tout pour cette librairie assez sympa !</p>
<blockquote><p>Vous trouverez les sources de cet article sur mon <a title="Github Yassine BEN-KHALED" href="https://github.com/ybenkhaled/article-args4j" target="_blank">repository github</a>.</p></blockquote>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2011/12/24/args4j-cli-parser-command-line-interface-parser/" data-text="Args4j &#8211; CLI Parser (Command Line Interface Parser)"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2011/12/24/args4j-cli-parser-command-line-interface-parser/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2011%2F12%2F24%2Fargs4j-cli-parser-command-line-interface-parser%2F&amp;title=Args4j%20%E2%80%93%20CLI%20Parser%20%28Command%20Line%20Interface%20Parser%29" id="wpa2a_14"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/u1AGl7yLwuw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2011/12/24/args4j-cli-parser-command-line-interface-parser/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2011/12/24/args4j-cli-parser-command-line-interface-parser/</feedburner:origLink></item>
		<item>
		<title>SpringFuse, un générateur intéressant ?</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/wsBO2QaD0Tc/</link>
		<comments>http://labs.excilys.com/2011/12/21/springfuse-un-generateur-interessant/#comments</comments>
		<pubDate>Wed, 21 Dec 2011 20:04:47 +0000</pubDate>
		<dc:creator>Julien Pladeau</dc:creator>
				<category><![CDATA[Outils]]></category>
		<category><![CDATA[Début JEE]]></category>
		<category><![CDATA[Générateur]]></category>
		<category><![CDATA[Génération de code]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Spring Fuse]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=2073</guid>
		<description><![CDATA[Pour mon premier article, j’ai décidé de vous présenter SpringFuse. Pourquoi ? En fait, je suis récemment rendu à une conférence sur le générateur de code SpringFuse et je me suis dit : « Pourquoi ne pas faire découvrir à &#8230; <a href="http://labs.excilys.com/2011/12/21/springfuse-un-generateur-interessant/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Pour mon premier article, j’ai décidé de vous présenter <strong>SpringFuse</strong>. Pourquoi ? En fait, je suis récemment rendu à une conférence sur le générateur de code <strong>SpringFuse </strong>et je me suis dit :</p>
<p style="text-align: left">« <em>Pourquoi ne pas faire découvrir à ceux n&#8217;ayant pas eu la chance d&#8217;assister à cette conférence ce qu&#8217;est cet outil</em> ?».</p>
<p>Je tiens à souligner que cet article est avant tout un article « découverte », je ne rentrerai donc pas aujourd&#8217;hui dans les détails. Cependant, je pense creuser un peu plus durant les mois à venir. Je posterai donc très probablement d&#8217;autres articles sur cet outil.</p>
<h2><span id="more-2073"></span>Mais alors, qu&#8217;est-ce que c’est ?</h2>
<p><strong>SpringFuse</strong> est un générateur de code Java 1.6 (1.7 dans les prochaines releases). Il permet, entre autre, de générer du code Java représentant votre modèle de données ainsi que vos DAO (<strong>D</strong>ata <strong>A</strong>ccess <strong>O</strong>bject) et vos services. Il peut aussi générer des tests unitaires.</p>
<h2>Mais sur quoi se base-t-il ?</h2>
<p><strong>SpringFuse</strong> s&#8217;appuie sur la base de données de votre application. Pour générer au mieux le code de votre application, il va utiliser plusieurs technologies du marché actuel ainsi que sur Celerio développé par l&#8217;éditeur Jaxio. Parmi les différentes technologies utilisées, voici une liste non exhaustive des plus connues.</p>
<ul>
<li><a title="Spring" href="http://www.springsource.org/">Spring</a>, framework pour la construction d&#8217;applications Java,</li>
<li><a title="JPA2" href="http://en.wikipedia.org/wiki/Java_Persistence_API">JPA 2</a>, utilisé pour tout ce qui est persistance,</li>
<li><a title="Spring Security" href="http://static.springsource.org/spring-security/site/">Spring Security</a>, qui est un module de Spring pour la gestion de la sécurité,</li>
<li><a title="Maven" href="http://maven.apache.org/">Maven 2</a>, pour la gestion de votre projet Java.</li>
</ul>
<p>Il est pour le moment possible de générer 3 types de projets :</p>
<ul>
<li>On peut générer un projet utilisant <a title="Spring Web Flow" href="http://www.springsource.org/webflow">Spring Web Flow</a> (utilisé pour la gestion de flux, notamment pour tout ce qui est formulaire) et <a title="JSF" href="http://fr.wikipedia.org/wiki/JavaServer_Faces">JSF</a> pour la vue,</li>
<li>On peut également générer un projet utilisant Spring MVC et JSP,</li>
<li>Enfin, si vous voulez utiliser une autre technologie pour gérer la vue, il est possible de générer seulement le back-end de votre projet.</li>
</ul>
<h2>Pourquoi l’utiliser ?</h2>
<p>Pour le moment, je n&#8217;ai utilisé aucun autre générateur de code. Par conséquent, il est pour moi encore très difficile de conseiller celui-ci plutôt qu&#8217;un autre. Cependant, voici quelques points me paraissant intéressant :</p>
<ul>
<li>Le code produit est relativement propre et compréhensible,</li>
<li>Les développeurs de <strong>SpringFuse</strong> s’efforcent d’utiliser les meilleures technologies du moment,</li>
<li>Il est grandement configurable,</li>
<li>Il est facile à utiliser,</li>
<li>Il a un taux de mise à jour important, apportant sans cesse de nouvelles améliorations (<a title="Change log" href="http://www.springfuse.com/change-log.html">changel-log.html</a>).</li>
</ul>
<p>On peut utiliser cet outil pour une application bancaire par exemple. Via l&#8217;intégration de Spring Security, le développeur a la base de la sécurité de l&#8217;application, il faut bien entendu ensuite renforcer celle-ci mais les fondations sont là. De plus, les tests unitaires de l&#8217;application sont déjà fournis. Par conséquent, le développeur peut plus facilement gérer la non régression de celle ci.</p>
<p>Il peut être aussi utilisé pour faire des prototypes. En effet, ici, le développement de la partie « basique », c&#8217;est à dire ce que génère <strong>SpringFuse</strong>, n&#8217;est pas la première préoccupation des développeurs. On peut donc partiellement exclure cette partie que l&#8217;on peut qualifier de répétitive grâce à ce générateur.</p>
<p>Enfin, un dernier petit exemple. Si vous êtes tous simplement débutant dans les technologies JEE, vous pouvez utiliser ce générateur afin d&#8217;avoir des exemples concrets par rapport à votre projet personnel plutôt qu&#8217;un exemple sur le net qui peut être insuffisant.</p>
<p>Ajoutons à cela la problématique de l&#8217;entreprise : Le temps. On vit dans un monde où le temps est important, et ici l&#8217;expression « le temps, c&#8217;est de l&#8217;argent » prend tout son sens.<br />
Cela peut par exemple être une aide au développement pour la migration d&#8217;anciennes applications. Ce genre de projet est souvent coûteux pour l&#8217;entreprise et cela explique (dans certains cas) que certains projets n&#8217;ont pas été migrés depuis 10, 20 ou 30 ans car trop complexe et/ou trop coûteux. Avec ce genre d&#8217;outil, le coût de développement peut être beaucoup moins important même si cet outil ne suffit certainement pas pour certaines applications très complexes. Cependant, vous aurez au final la &#8220;tuyauterie&#8221; de votre application, une base solide. A vous ensuite de personnaliser le code pour peaufiner les règles métiers, en ajouter d&#8217;autres, etc. et tester votre code via les tests unitaires fournis. <strong>SpringFuse</strong> intègrera bientôt Sélénium 2 permettant d&#8217;effectuer des tests fonctionnels pour votre application.</p>
<p>Seul petit bémol, le serveur ne génère qu&#8217;une seule application n&#8217;utilisant pas le mode multi-projet de Maven. C’est à vous de découper.</p>
<h2>Je suis intéressé, de quoi ai-je besoin ?</h2>
<p>Pour pouvoir utiliser au mieux <strong>SpringFuse</strong>, il est impératif d’avoir trois choses :</p>
<ul>
<li>Un ordinateur <img src='http://labs.excilys.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ,</li>
<li><a title="Java Oracle" href="http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u29-download-513648.html">Java 1.6</a>,</li>
<li><a title="Maven" href="http://maven.apache.org/download.html">Maven 2</a>.</li>
</ul>
<p>Et c’est tout ! (enfin je vous conseille Eclipse pour visualiser le code généré)</p>
<h2>Le fonctionnement</h2>
<p>Pour pouvoir générer un projet, il faut passer avant tout par une phase de configuration. Il existe une phase de configuration simple et une phase de configuration avancée. Je ne vous parlerai que de la phase simple et reviendrai dans un autre article sur la configuration avancée passant par différent fichiers XML.</p>
<p>Via un formulaire où l&#8217;on va renseigner diverses informations dont :</p>
<ul>
<li>Le nom de notre projet,</li>
<li>Le moyen d&#8217;accès à notre base de données,</li>
<li>Le type de projet que l&#8217;on va générer
<p><div id="attachment_2087" class="wp-caption aligncenter" style="width: 634px"><a href="http://labs.excilys.com/wp-content/uploads/2011/12/screen.png"><img class=" wp-image-2087" src="http://labs.excilys.com/wp-content/uploads/2011/12/screen.png" alt="Screenshot du formulaire SpringFuse" width="624" height="292" /></a><p class="wp-caption-text">Formulaire pour la génération du code disponible sur http://www.springfuse.com/</p></div></li>
</ul>
<p>Après renseignement de tout ceci, une commande Maven est proposée afin de générer et télécharger le nouveau projet.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">mvn -U archetype:generate -DarchetypeGroupId=com.springfuse.archetypes -DarchetypeArtifactId=quickstart-embedded-db-with-configuration -DarchetypeVersion=3.0.68 -DgroupId=com.excilys.lab -Dpackage=com.excilys.lab -DartifactId=excilysLab -Dversion=1.0.0 -DfrontEnd=springMvc -Demail=toto@tata.com -DinteractiveMode=false -DarchetypeRepository=http://maven2.springfuse.com/</div></td></tr></tbody></table></div>
<p>On peut remarquer que les informations insérées dans le formulaire sont présentes dans la commande et seront envoyées au serveur.<br />
Lorsque celle-ci est exécutée dans votre terminal préféré, le serveur de <strong>SpringFuse</strong> va recevoir les informations (les métadonnées) de votre base de données et va générer le code correspondant au mieux à votre schéma de données par introspection (il existe également un mode démo pour ceux qui seraient seulement curieux et qui ne voudraient pas envoyer les métadonnées de leurs bases). Le code est généré côté serveur, cela peut prendre plusieurs minutes car le serveur :</p>
<ul>
<li>Analyse les métadonnées de la base que vous lui avez envoyée. Plus la base est complexe et plus cela prend du temps,</li>
<li>Génère le code de votre application,</li>
<li>Génère également les tests unitaires et les exécute ensuite afin de vous envoyer un code correct.</li>
</ul>
<p>Une fois cette phase finie, le code généré est téléchargé et sauvegardé sur votre machine.</p>
<div id="attachment_2088" class="wp-caption aligncenter" style="width: 632px"><a href="http://labs.excilys.com/wp-content/uploads/2011/12/FUSE.png"><img class=" wp-image-2088" src="http://labs.excilys.com/wp-content/uploads/2011/12/FUSE.png" alt="Schéma de génération du code par SpringFuse" width="622" height="282" /></a><p class="wp-caption-text">Processus de génération du code d’un projet</p></div>
<p>Il est alors possible de travailler à partir du code généré. Il est très facile de l&#8217;importer dans Eclipse en utilisant l&#8217;option « <em>importer un projet Maven déjà existant</em> » avec le plugin M2eclipse de sonatype. Je vous laisse le soin de faire un test via la démo pour vous faire votre propre idée.</p>
<h2>Le mot de la fin</h2>
<p>Je pense que <strong>SpringFuse</strong> n&#8217;est certainement pas la réponse à tous les problèmes d&#8217;intégration et de traitement de données mais la génération de code peut apporter une grande aide. Le code étant généré de façon à ce que votre application puisse directement se lancer.<br />
Les outils en la matière sont nombreux, il en existe certains transformant du code d’un langage en un autre, d’autres comme celui que je viens de présenter. Leurs applications sont nombreuses et permettent souvent de gagner du temps.<br />
Il existe des générateurs de code dans beaucoup d’autres langages (Java, .Net, PHP, &#8230;), le but étant toujours de faciliter le développement de nouvelles applications.<br />
Voilà pour cette petite présentation de <strong>SpringFuse</strong>. Je vais tenter d&#8217;approfondir l&#8217;utilisation de cet outil et essayerai de faire d’autres articles dans les prochains mois.</p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2011/12/21/springfuse-un-generateur-interessant/" data-text="SpringFuse, un générateur intéressant ?"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2011/12/21/springfuse-un-generateur-interessant/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2011%2F12%2F21%2Fspringfuse-un-generateur-interessant%2F&amp;title=SpringFuse%2C%20un%20g%C3%A9n%C3%A9rateur%20int%C3%A9ressant%20%3F" id="wpa2a_16"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/wsBO2QaD0Tc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2011/12/21/springfuse-un-generateur-interessant/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2011/12/21/springfuse-un-generateur-interessant/</feedburner:origLink></item>
		<item>
		<title>Binder une date en Grails</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/atCkJNPQFpM/</link>
		<comments>http://labs.excilys.com/2011/12/06/binder-une-date-en-grails/#comments</comments>
		<pubDate>Tue, 06 Dec 2011 17:56:11 +0000</pubDate>
		<dc:creator>Audrey Romanet</dc:creator>
				<category><![CDATA[Grails]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[binding]]></category>
		<category><![CDATA[date]]></category>
		<category><![CDATA[grails]]></category>
		<category><![CDATA[groovy]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=1970</guid>
		<description><![CDATA[Grails est un framework web pour Groovy, qui facilite beaucoup l&#8217;écriture d&#8217;une appli web. Le binding est une des facilités de Grails : disons qu&#8217;on ait un objet User, contenant les propriétés lastName, firstName et birthdayDate. Notre formulaire de création/édition &#8230; <a href="http://labs.excilys.com/2011/12/06/binder-une-date-en-grails/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Grails est un framework web pour Groovy, qui facilite beaucoup l&#8217;écriture d&#8217;une appli web.</p>
<p>Le binding est une des facilités de Grails : disons qu&#8217;on ait un objet User, contenant les propriétés lastName, firstName et birthdayDate. Notre formulaire de création/édition d&#8217;utilisateurs aura les champs correspondants. Dans le controlleur, on peut remplir notre objet User en écrivant simplement :</p>
<div class="codecolorer-container groovy default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="groovy codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">bindData<span style="color: #66cc66;">&#40;</span>user, params<span style="color: #66cc66;">&#41;</span></div></td></tr></tbody></table></div>
<p>Mais pour binder la date birthdayDate, la map params doit avoir les champs birthdayDate, birthdayDate_year, birthdayDate_month, birthdayDate_day, birthdayDate_hour et enfin birthdayDate_minute. Ces champs sont automatiquement générés par la balise &lt;g:datePicker /&gt; fournie par Grails, mais dès qu&#8217;on n&#8217;utilise plus cette balise (date picker personnalisé, utilisation d&#8217;un input text classique&#8230;), il faut générer soi-même tous les champs. C&#8217;est beaucoup trop long, lourd et complétement inadapté.<br />
<span id="more-1970"></span></p>
<p>Heureusement, on peut également indiquer à Grails d&#8217;autres façons de binder des objets en créant un PropertyEditorRegistrar. Cet objet &#8220;enregistre&#8221; différents types de conversion textobjet. Pour chaque type de conversion, on crée une classe étendant PropertyEditorSupport.<br />
Concernant les dates, la classe CustomDateEditor est déjà présente dans le package de Spring, mais n&#8217;est pas &#8220;enregistrée&#8221; par défaut. Dans notre cas, il nous suffit donc d&#8217;enregistrer un CustomDateEditor.</p>
<div class="codecolorer-container groovy default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="groovy codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> CustomPropertyEditorRegistrar <span style="color: #000000; font-weight: bold;">implements</span> PropertyEditorRegistrar<span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">def</span> messageSource<br />
<br />
&nbsp; &nbsp; @Override<br />
&nbsp; &nbsp; <span style="color: #993333;">void</span> registerCustomEditors<span style="color: #66cc66;">&#40;</span>PropertyEditorRegistry propertyEditorRegistry<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; propertyEditorRegistry.<span style="color: #006600;">registerCustomEditor</span><span style="color: #66cc66;">&#40;</span><span style="color: #aaaadd; font-weight: bold;">Date</span>, <span style="color: #000000; font-weight: bold;">new</span> CustomDateEditor<span style="color: #66cc66;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> <span style="color: #aaaadd; font-weight: bold;">SimpleDateFormat</span><span style="color: #66cc66;">&#40;</span>messageSource.<span style="color: #006600;">getMessage</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;dateFormat.day&quot;</span>, <span style="color: #000000; font-weight: bold;">null</span>, <span style="color: #ff0000;">&quot;MM/dd/yyyy&quot;</span>, LocaleContextHolder.<span style="color: #006600;">locale</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>, <span style="color: #000000; font-weight: bold;">true</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #66cc66;">&#125;</span><br />
<br />
<span style="color: #66cc66;">&#125;</span></div></td></tr></tbody></table></div>
<p>Rien de bien compliqué ! Ce &#8220;convertisseur&#8221; gère même l&#8217;i18n (il faut donc ajouter une propriété dateFormat.day dans les fichiers messages.properties pour que le code ci-dessus fonctionne)</p>
<p>Il ne reste plus qu&#8217;à déclarer ce &#8220;registrar&#8221; en tant que bean, dans le fichier Configuration/spring/resources.groovy :</p>
<div class="codecolorer-container groovy default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="groovy codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">beans <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; customPropertyEditorRegistrar<span style="color: #66cc66;">&#40;</span>CustomPropertyEditorRegistrar<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; messageSource <span style="color: #66cc66;">=</span> ref<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'messageSource'</span><span style="color: #66cc66;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #66cc66;">&#125;</span><br />
<span style="color: #66cc66;">&#125;</span></div></td></tr></tbody></table></div>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2011/12/06/binder-une-date-en-grails/" data-text="Binder une date en Grails"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2011/12/06/binder-une-date-en-grails/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2011%2F12%2F06%2Fbinder-une-date-en-grails%2F&amp;title=Binder%20une%20date%20en%20Grails" id="wpa2a_18"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/atCkJNPQFpM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2011/12/06/binder-une-date-en-grails/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2011/12/06/binder-une-date-en-grails/</feedburner:origLink></item>
		<item>
		<title>DevOps : c’est à la mode !</title>
		<link>http://feedproxy.google.com/~r/ExcilysLabs/~3/JEidP-n7Vzc/</link>
		<comments>http://labs.excilys.com/2011/11/22/devops-cest-a-la-mode/#comments</comments>
		<pubDate>Tue, 22 Nov 2011 11:05:23 +0000</pubDate>
		<dc:creator>Khoa Nghiem</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[devops]]></category>

		<guid isPermaLink="false">http://labs.excilys.com/?p=1922</guid>
		<description><![CDATA[Depuis très récemment (2009), une nouvelle tendance, ou plutôt mouvement, est apparue au sein des développements des projets informatiques et commence à être de plus en plus adoptée : je parle du DevOps. Encore peu répandu en France, certains d&#8217;entre &#8230; <a href="http://labs.excilys.com/2011/11/22/devops-cest-a-la-mode/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify">Depuis très récemment (2009), une nouvelle tendance, ou plutôt mouvement, est apparue au sein des développements des projets informatiques et commence à être de plus en plus adoptée : je parle du <strong>DevOps</strong>.</p>
<p style="text-align: justify">Encore peu répandu en France, certains d&#8217;entre vous en ont peut-être déjà entendu parlé sur des blogs informatiques ou par le biais d&#8217;une offre d&#8217;emploi. Pour les autres, je vais essayer de répondre de la manière la plus concise possible à cette question qui vous brûle les lèvres :</p>
<p style="text-align: justify"><strong>Mais c&#8217;est quoi le DevOps ?<br />
</strong><strong></strong>Le <strong>DevOps</strong><span style="text-align: -webkit-auto"> est la contraction des deux mots Anglais « </span><em>Development » et « Operations</em><span style="text-align: -webkit-auto"> »</span>, ou traduit respectivement en Fraçais « Développement » et « Exploitation ».</p>
<p style="text-align: justify">Il s&#8217;agit d&#8217;un mouvement dont le but est de définir un ensemble de règles et de techniques permettant aux différentes équipes (développement, tests et exploitation) de communiquer efficacement, ainsi que travailler à la fois en coopération et de manière optimale, le tout afin d&#8217;améliorer la qualité du produit.</p>
<p style="text-align: justify">Ce petit schéma situe la place du <strong>DevOps </strong>parmi les autres entités d&#8217;un projet :</p>
<div class="wp-caption aligncenter" style="width: 488px"><img src="http://upload.wikimedia.org/wikipedia/commons/4/4e/Devops.png" alt="DevOps" width="478" height="384" /><p class="wp-caption-text">Le DevOps situé à l&#039;intersection entre de développoment, l&#039;exploitation et la qualité (issu de Wikipédia)</p></div>
<p> </p>
<p><strong>Qu&#8217;est-ce que cela apporte, concrétement ?<br />
</strong>Dans les grands projets informatiques, il existe généralement différentes équipes dont les rôles sont bien définis.</p>
<p>Principalement, il s&#8217;agit de l&#8217;équipe de développement, d&#8217;un côté, et celle chargée de l&#8217;exploitation de l&#8217;autre.<br />
En temps normal, les équipes de développement tentent de répondre à des spécifications fonctionnelles en provenance du métier ou du client. Elles travaillent dans leur propre environnement avec leurs outils optimisés pour les développements et ne se soucient pas de l&#8217;impact que peut avoir leurs codes lors de la phase d&#8217;exploitation qui, généralement, exécute l&#8217;application sur un environnement différent.<br />
Les équipes placées du côté de l&#8217;exploitation tentent de répondre à des requis non fonctionnels tels que les performances ou la stabilité du système. De ce fait, elles sont plus enclin à minimiser l&#8217;impact des modifications afin de conserver ces performances, et cela peut constituer un goulot d&#8217;étranglement pour l&#8217;ajout de nouvelles fonctionnalités aux applications.<br />
Mais qu&#8217;on soit dans une équipe ou dans l&#8217;autre, il est malheureusement très fréquent que chaque équipe ne soit pas consciente de comment l&#8217;autre équipe gère sa tâche (c&#8217;est-à-dire comment les développeurs codent et comment est configuré l&#8217;environnement d&#8217;exploitation), rendant difficile l&#8217;optimisation des deux côtés.</p>
<p>C&#8217;est là qu&#8217;entre en jeu le DevOps qui se chargent en quelque sorte de faire le pont entre les différentes équipes afin de faciliter la communication et de propager l&#8217;information utile. Ses autres fonctions incluent aussi l&#8217;amélioration de la coordination lors des phases de livraisons et de déploiements (cela est surtout important pour des projets demandant des déploiements réguliers, comme plusieurs fois par jour), ainsi que la mise en place d&#8217;automatisation des déploiements afin de limiter les erreurs.</p>
<p><strong>Le DevOps, c&#8217;est un métier ou pas finalement ?<br />
</strong>Pas exactement, il n&#8217;existe, à ce jour, pas de métier dédié entièrement au DevOps. Il faut plutôt voir cela comme une voie de spécialisation dans laquelle on peut vouloir s&#8217;engager. Néanmoins, un ingénieur DevOps doit être polyvalent et être capable de comprendre facilement les contraintes associées à la fois au développement et à l&#8217;exploitation. Son domaine de connaissance doit être assez vaste, même si sa principale fonction est le développement JEE, par exemple&#8230;<br />
Si vous êtes intéressés par cette fonction, il existe de nombreuse offre d&#8217;emploi en ce moment (plutôt aux Etats-Unis) qui recherchent des DevOps Software Engineer. Leur lecture vous apportera sûrement des réponses à vos interrogations.</p>
<p>Sinon, pour encore plus de détails, je vous invite tous à assister au <strong>Paris JUG</strong> du mois de <strong>Janvier 2012 </strong>qui sera une soirée DevOps.</p>
<p><a class="a2a_button_twitter_tweet addtoany_special_service" data-count="horizontal" data-url="http://labs.excilys.com/2011/11/22/devops-cest-a-la-mode/" data-text="DevOps : c&#8217;est à la mode !"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://labs.excilys.com/2011/11/22/devops-cest-a-la-mode/"></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Flabs.excilys.com%2F2011%2F11%2F22%2Fdevops-cest-a-la-mode%2F&amp;title=DevOps%20%3A%20c%E2%80%99est%20%C3%A0%20la%20mode%20%21" id="wpa2a_20"><img src="http://labs.excilys.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p><img src="http://feeds.feedburner.com/~r/ExcilysLabs/~4/JEidP-n7Vzc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://labs.excilys.com/2011/11/22/devops-cest-a-la-mode/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://labs.excilys.com/2011/11/22/devops-cest-a-la-mode/</feedburner:origLink></item>
	</channel>
</rss>

