<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
><channel><title>The Coffee Workshop</title> <atom:link href="http://blog.arkey.fr/feed/" rel="self" type="application/rss+xml" /><link>http://blog.arkey.fr</link> <description>Don&#039;t waste your time!</description> <lastBuildDate>Mon, 05 Mar 2012 00:29:56 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>Hackergarten Paris &#8211; ce qu&#8217;il faut savoir sur les projets</title><link>http://blog.arkey.fr/2012/03/05/hackergarten-paris-ce-quil-faut-savoir-sur-les-projets/</link> <comments>http://blog.arkey.fr/2012/03/05/hackergarten-paris-ce-quil-faut-savoir-sur-les-projets/#comments</comments> <pubDate>Sun, 04 Mar 2012 23:54:28 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[code]]></category> <category><![CDATA[hibernate]]></category> <category><![CDATA[infinitest]]></category> <category><![CDATA[jenkins]]></category> <category><![CDATA[maven]]></category> <category><![CDATA[mockito]]></category> <category><![CDATA[open source]]></category><guid
isPermaLink="false">http://blog.arkey.fr/?p=337</guid> <description><![CDATA[Hello à tous, Mercredi 7 mars aura lieu la 2ème session Hackergarten. Après Soat, on a le plaisir d&#8217;être hébergé par Valtech (103 Rue de Grenelle, 75007 Paris) et il y aura des pizzas, gros merci à eux. Hackergarten c&#8217;est le rendez-vous des gens qui veulent participer aux projets opensource. L&#8217;idée c&#8217;est, dans un format de 3h,...]]></description> <content:encoded><![CDATA[<p>Hello à tous,</p><p><img
class="alignnone" style="vertical-align: baseline; border-image: initial; border-width: 1px; border-color: black; border-style: solid; background-color: slategray;" title="Hackergarten Logo BW" src="http://hackergarten.net/hackergarten_b_and_w_small.png" alt="Hackergarten Logo" width="392" height="150" /></p><p><strong>Mercredi 7 mars</strong> aura lieu la 2ème session Hackergarten. Après <strong>Soat</strong>, on a le plaisir d&#8217;être hébergé par <strong>Valtech</strong> (103 Rue de Grenelle, 75007 Paris) et il y aura des <strong>pizzas</strong>, gros merci à eux.</p><blockquote
title="Hackergarten Paris"><p>Hackergarten c&#8217;est le rendez-vous des gens qui veulent participer aux projets opensource. L&#8217;idée c&#8217;est, dans un format de 3h, de <strong>contribuer</strong> un <strong>logiciel</strong>, un <strong>fix</strong>, un <strong>feature</strong>, une <strong>documentation</strong> dont d&#8217;autres pourraient avoir l&#8217;usage. Il s&#8217;articule autour de commiters actifs pour mentorer les hackers qui participent à l&#8217;évènement.</p></blockquote><p>Bref que du bon. Pour la planification de l&#8217;évènement c&#8217;est par là ⇒ <a
href="http://hackergarten-paris.eventbrite.com/">http://hackergarten-paris.eventbrite.com/</a></p><p>Alors pour éviter les soucis de setup le jour J, ce post donne quelques informations sur ce qu&#8217;il y aurait à récupérer ou faire en avance sur votre machine. Si vous avez des questions ou si vous voulez participez aux discussions : inscrivez vous sur la mailing-list à cette adresse ⇒ <a
href="http://groups.google.com/group/hackergarten-paris/">http://groups.google.com/group/hackergarten-paris/</a></p><p><span
id="more-337"></span></p><h2>Hibernate OGM / Hibernate Search ⇐ mentoré par Emmanuel Bernard</h2><p>IDE : <strong>IntelliJ</strong> mais <strong>Eclipse</strong> fait l&#8217;affaire</p><p>Pour builder : <strong>Maven 3.0.3</strong><br
/> JDK 1.6 recommandé<br
/> changer les <strong>~/.me/settings.xml</strong> selon <a
href="https://community.jboss.org/wiki/MavenGettingStarted-Users" target="_blank">https://community.jboss.org/<wbr>wiki/MavenGettingStarted-Users</wbr></a></p><p><strong>Installer Cassandra 1.0.8 (download sur Apache)</strong><br
/> Et pour les motivés NoSQL, installer votre moteur préféré aussi histoire de contribuer un dialect pour Hibernate OGM</p><p>Forker + cloner localement<br
/> - git clone <a
href="https://github.com/hibernate/hibernate-search" target="_blank">https://github.com/hibernate/<wbr>hibernate-search</wbr></a><br
/> - git clone <a
href="https://github.com/hibernate/hibernate-ogm" target="_blank">https://github.com/hibernate/<wbr>hibernate-ogm</wbr></a></p><p>Cloner<br
/> - git clone <a
href="https://code.google.com/a/apache-extras.org/p/cassandra-jdbc/" target="_blank">https://code.google.com/a/<wbr>apache-extras.org/p/cassandra-</wbr><wbr>jdbc/</wbr></a></p><p>Lancer mvn clean install sous chacun des clones pour être sûr que Maven télécharge bien la terre.</p><h2>Maven / Jenkins ⇐ mentoré par Arnaud Héritier</h2><p>IDE : Pour Maven &amp; Jenkins : IntelliJ ou eclipse + m2e versions recentes si possibles</p><p><strong>Git</strong> pour <strong>Jenkins</strong>, <strong>SVN</strong> pour <strong>Maven</strong></p><p>Pour builder :  <strong>Maven</strong> 2.2.1 min, 3.0.x serait un mieux<br
/> (De memoire pas de settings additionnels pour seulement builder)</p><div>Jenkins :</div><div>Checkout du plugin qui vous interesse puis <strong>mvn clean install hpi:run</strong></div><div></div><div>Maven :</div><div>Checkout du plugin qui vous interesse puis <strong>mvn clean install -Prun-its</strong></div><h2>Infinitest ⇐ mentoré par David Gageot</h2><div>IDE : <strong>Eclipse</strong> mais <strong>IntelliJ</strong> fait l&#8217;affaire</div><div>Utiliser <strong>mvn eclipse:eclipse</strong> plutot que <strong>m2eclipse</strong></div><div><div></div><div>Pour builder : Maven 3.0.3</div><div>JDK 1.6 recommandé</div><div></div></div><div>Forker + cloner localement <a
href="https://github.com/infinitest/infinitest" target="_blank">https://github.com/<wbr>infinitest/infinitest</wbr></a></div><p>Lancer <strong>mvn dependency:go-offline</strong> pour récupérer toutes les dépendances</p><h2>Mockito ⇐ mentoré par Brice Dutheil</h2><p>IDE : <strong>IntelliJ</strong> en particulier, la <strong>version Community</strong> suffira. Eclipse peut faire l&#8217;affaire. Attention on a eu des soucis avec Netbeans la dernière fois.</p><p>Pour Builder : <strong>Ant</strong> (IntelliJ contient un Ant)<br
/> JDK 5 préféré, mais pas obligatoire.</p><p>Forker et cloner localement depuis Google code : <a
href="http://code.google.com/p/mockito/source/checkout">http://code.google.com/p/mockito/source/checkout</a><br
/> Attention ici c&#8217;est du <strong>mercurial</strong>.</p><p>Il n&#8217;y a plus qu&#8217;à ouvrir les fichiers du projet.</p><h2>FluentLenium ⇐ mentoré par Mathilde Lemée</h2><p>IDE : Un IDE qui gère maven, genre IntelliJ.</p><p>Pour Builder : Maven</p><p>Checkout depuis : <a
href="https://github.com/FluentLenium/FluentLenium">https://github.com/FluentLenium/FluentLenium</a></p><h2>Votre projet</h2><p>Et oui si vous avez une idée à coder en Open Source et que vous cherchez des intéressés c&#8217;est peut-être l&#8217;endroit pour en parler et dresser un plan <img
src='http://blog.arkey.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p><h2>1000 mercis</h2><p>Gros merci à tous ceux qui participent aux Hackergarten Paris</p><ul><li>Arnaud Héritier</li><li>Guillaume Laforge</li><li>Emmanuel Bernard</li><li>David Gageot</li><li>Eric Lefevre</li><li>Mathilde Lemée</li><li>Et vous tous qui venez</li></ul><p>Gros merci aussi à ceux qui hébergent l&#8217;évènement, c&#8217;est bien sympa :</p><ul><li>Soat</li><li>Valtech</li></ul><p>Évidement merci à <strong>Hamlet D&#8217;Harcy</strong> pour avoir initier l&#8217;idée là bas en Suisse <img
src='http://blog.arkey.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2012/03/05/hackergarten-paris-ce-quil-faut-savoir-sur-les-projets/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Mockito 1.9.0 is out &#8211; Bientôt sur les repos maven</title><link>http://blog.arkey.fr/2011/12/19/quoi-de-neuf-docteur-dans-mockito-1-9/</link> <comments>http://blog.arkey.fr/2011/12/19/quoi-de-neuf-docteur-dans-mockito-1-9/#comments</comments> <pubDate>Mon, 19 Dec 2011 08:37:06 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[code]]></category> <category><![CDATA[TDD]]></category> <category><![CDATA[constructor injection]]></category> <category><![CDATA[mockito]]></category><guid
isPermaLink="false">http://dutheil.brice.online.fr/blog/?p=243</guid> <description><![CDATA[EDIT: Hop. Enfin la release 1.9.0 est dispo en téléchargement. Après pas mal de travail avec des périodes plus ou moins intenses &#8211; bref les vicissitudes du développement Open Source &#8211; le projet sort une nouvelle version 1.9.0 en Release Candidate, avec des bugfixes et bien sûr des nouvelles features. Il y a un changelog...]]></description> <content:encoded><![CDATA[<p><span
style="font-weight: normal;"><strong>EDIT:</strong> Hop. Enfin la release 1.9.0 est dispo en téléchargement.</span></p><p><span
style="font-weight: normal;">Après pas mal de travail avec des périodes plus ou moins intenses &#8211; <em>bref les vicissitudes du développement Open Source</em> &#8211; le projet sort une nouvelle version <strong>1.9.0</strong> en <del>Release Candidate</del>, avec des bugfixes et bien sûr des nouvelles features. Il y a un <a
title="Release notes de Mockito 1.9.0" href="http://code.google.com/p/mockito/wiki/ReleaseNotes">changelog</a> mais dans les faits le billet suivant liste brièvement ce qui est nouveau. Ah oui la version est téléchargeable <a
title="Télécharger la version 1.9.0 de Mockito" href="http://code.google.com/p/mockito/downloads/detail?name=mockito-1.9.0.zip">ici</a> et bientôt disponible sur le central maven.</span></p><ul><li>Pour être plus fluent et expressif, l&#8217;API introduit les alias <strong>then</strong> et <strong>will</strong> pour les réponses personnalisées (Answer). Ainsi que d&#8217;autres petits tweak de l&#8217;API:<pre class="brush: java; title: ; notranslate">@Test
public void engine_should_only_work_with_diesel() {
  given(engine.start()).will(throwExceptionIfEssenceInsteadOfDiesel());
  // ...
}

private Answer throwExceptionIfEssenceInsteadOfDiesel() {
  return new Answer&lt;EngineStatus&gt;() {
    public EngineStatus answer(InvocationOnMock invocation) {
      // answer code
    }
  };
}</pre></li><li>Les mocks peuvent maintenant être déclaré dans la configuration du stub, sur une ligne.<pre class="brush: java; title: ; notranslate">DieselEngine de = given(mock(DieselEngine.class).start()).willThrow(TankIsEmpty.class).getMock();</pre></li><li>On peut maintenant renvoyer la classe d&#8217;une exception plutôt que son instance.<pre class="brush: java; title: ; notranslate">given(someMock).willThrow(IllegalArgumentException.class, SomethingIsWrongException.class);</pre></li><li>Si jamais vous avez besoin de debugguer un bout de code ou les interactions sont non prédictibles, il est maintenant possible de loguer les invocations du mock ou de l&#8217;espion. Attention, bien qu&#8217;utile à l&#8217;occasion avec du code legacy, quand même si jamais ce besoin s&#8217;en fait sentir sur un nouveau développement c&#8217;est que ce code devient trop complexe.<pre class="brush: java; title: ; notranslate">List mockedList = mock(List.class, withSettings().verboseLogging());
mockedList.get(0);</pre><p>On pourra également ajouter des callbacks sur chaque interaction du mock.</p><pre class="brush: java; title: ; notranslate">Observer observer = mock(Observer.class, withSettings().invocationListeners(listener1, listener2));
willThrow(IllegalArgumentException.class).given(observer.update(observable, &quot;what has changed&quot;));</pre></li><li>Pas mal de travail a été fait sur les annotations. Maintenant il n&#8217;est plus nécéssaire d&#8217;initialiser un champ annoté par @Spy s&#8217;il existe dans la classe un constructeur sans argument.<pre class="brush: java; title: ; notranslate">@RunWith(MockitoJUnitRunner.class)
public class SomeTest {
  // pas besoin d'initialiser le champs
  @Spy private ArrayList spiedArrayList;

  @Test public void verify_some_interactions() {
    spiedArrayList.iterator();
    verify(spiedArrayList, once()).iterator();
  }
}</pre></li><li>Et pour la fin mais pas des moindres, le mécanisme d&#8217;injection de mockito supporte maintenant l&#8217;injection par constructeur. A l&#8217;heure actuelle, seul les mocks et spies déclaré dans le test en tant que champs pourront être injecté dans le constructeur du champs annoté par @InjectMocks.<pre class="brush: java; highlight: [4]; title: ; notranslate">@RunWith(MockitoJUnitRunner.class)
public class EngineTest {
  @Mock Diesel diesel;
  @InjectMocks Engine engine;

  @Test public void engine_should_consume_Diesel() {
    engine.start();
  }
}</pre><p>Ou Engine a un constructeur avec le paramètre Diesel.</p><pre class="brush: java; highlight: [4]; title: ; notranslate">public class Engine {
  Diesel diesel;
  public Engine(Diesel diesel) {
    this.diesel = diesel;
  }

  public boolean start() {
    checkNotEmpty(diesel);
    // ...
  }
  // ...
}</pre></li></ul><p>Pour l&#8217;instant en RC, cette release permettra d&#8217;adoucir les angles si nous en avons loupé certains éléments. N&#8217;hésitez pas à nous poser des questions sur la <a
title="Mockito mailing list" href="http://groups.google.com/group/mockito" target="_blank">mailing list</a> ou <a
title="StackOverflow" href="http://stackoverflow.com/" target="_blank">stackoverflow</a>.</p> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2011/12/19/quoi-de-neuf-docteur-dans-mockito-1-9/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Quelques Live Template IntelliJ pour Mockito</title><link>http://blog.arkey.fr/2011/10/21/quelques-live-template-intellij-pour-mockito/</link> <comments>http://blog.arkey.fr/2011/10/21/quelques-live-template-intellij-pour-mockito/#comments</comments> <pubDate>Fri, 21 Oct 2011 18:06:46 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[code]]></category> <category><![CDATA[TDD]]></category> <category><![CDATA[intellij]]></category> <category><![CDATA[mockito]]></category> <category><![CDATA[template]]></category> <category><![CDATA[test]]></category> <category><![CDATA[test unitaire]]></category><guid
isPermaLink="false">http://blog.arkey.fr/?p=329</guid> <description><![CDATA[Hello, j&#8217;en avais un peu marre d&#8217;écrire régulièrement voire répétitivement dans mes tests les constructions mockito. Pour ça je me suis créé dans mon IDE favori, IntelliJ, ce qu&#8217;on appelle des Live Template. Ces templates permettent à partir d&#8217;une abréviation d&#8217;insérer des fragments de code. Ainsi par exemple : Taper iter dans votre éditeur puis...]]></description> <content:encoded><![CDATA[<p>Hello, j&#8217;en avais un peu marre d&#8217;écrire régulièrement voire répétitivement dans mes tests les constructions mockito.</p><p>Pour ça je me suis créé dans mon IDE favori, <a
title="IntelliJ IDEA" href="http://www.jetbrains.com/idea/">IntelliJ</a>, ce qu&#8217;on appelle des Live Template. Ces templates permettent à partir d&#8217;une abréviation d&#8217;insérer des fragments de code. Ainsi par exemple :</p><p>Taper <code>iter</code> dans votre éditeur puis de faire <kbd>Ctrl+J</kbd> (sous OSX) va développer cette abréviation dans le bout de code ci-dessous (suivant le contexte bien entendu)</p><pre class="brush: java; title: ; notranslate">for (TypeInIterable type : someIterable) {

}</pre><p>Taper sur <kbd>Ctrl+J</kbd> (sous OSX) vous permet de lister les abréviations disponible dans le contexte courant.</p><h3>Les Live Template pour Mockito</h3><p>Bien qu&#8217;imparafaite pour des raisons de limite technique d&#8217;IntelliJ, elles sauvent un minimum de temps, multiplié par le nombre de test. Malheureusement il n&#8217;y a pas non plus d&#8217;import export uniquement pour les live template, il faut donc se taper la configuration de intellij à la main. Cela dit il est possible de contourner partiellement ce problème avec la sauvegarde de la configuration personnelle sur les serveurs intellij, ou encore d&#8217;exporter la configuration pour les live templates, les file templates, et encore autre chose.</p><p>J&#8217;ai défini toutes ces annotations dans un nouveau groupe &#8216;test&#8217;, et j&#8217;ai activé pour toutes le contexte Java, avec reformatage et simplification du nom qualifié.</p><ol><li><span
style="text-decoration: underline;">Description :</span> Creates a field with the @Mock annotation<br
/> <span
style="text-decoration: underline;">Abbréviation :</span> &#8216;am&#8217;<br
/> <span
style="text-decoration: underline;">Template text :</span></p><pre class="brush: java; title: ; notranslate">
@org.mockito.Mock private $TYPE$ $MOCK_FIELD$
</pre><p><span
style="text-decoration: underline;">Les variables du templates sont :</span></p><table
style="border-color: #000000; border-width: 1px; background-color: #ffffff; width: 100%;" border="0"><tbody><tr
style="background-color: #ffefd5;"><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Name</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Expression</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Default value</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Skip if defined</td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">TYPE</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;Object&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">MOCK_FIELD</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">suggestVariableName()</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr></tbody></table></li><li><span
style="text-decoration: underline;">Description :</span> Creates a field with the @Spy annotation<br
/> <span
style="text-decoration: underline;">Abbréviation :</span> &#8216;as&#8217;<br
/> <span
style="text-decoration: underline;">Template text :</span></p><pre class="brush: java; title: ; notranslate">
@org.mockito.Spy private $TYPE$ $MOCK_FIELD$
</pre><p><span
style="text-decoration: underline;">Les variables du templates sont :</span></p><table
style="border-color: #000000; border-width: 1px; background-color: #ffffff; width: 100%;" border="0"><tbody><tr
style="background-color: #ffefd5;"><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Name</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Expression</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Default value</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Skip if defined</td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">TYPE</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;Object&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">MOCK_FIELD</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">suggestVariableName()</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr></tbody></table></li><li><span
style="text-decoration: underline;">Description :</span> Creates a field with the @InjectMocks annotation<br
/> <span
style="text-decoration: underline;">Abbréviation :</span> &#8216;aim&#8217;<br
/> <span
style="text-decoration: underline;">Template text :</span></p><pre class="brush: java; title: ; notranslate">
@org.mockito.InjectMocks private $TYPE$ $MOCK_FIELD$
</pre><p><span
style="text-decoration: underline;">Les variables du templates sont :</span></p><table
style="border-color: #000000; border-width: 1px; background-color: #ffffff; width: 100%;" border="0"><tbody><tr
style="background-color: #ffefd5;"><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Name</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Expression</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Default value</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Skip if defined</td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">TYPE</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;Object&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">MOCK_FIELD</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">suggestVariableName()</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr></tbody></table></li><li><span
style="text-decoration: underline;">Description :</span> Add @RunWith(MockitoJUnitRunner.class)<br
/> <span
style="text-decoration: underline;">Abbréviation :</span> &#8216;rwm&#8217;<br
/> <span
style="text-decoration: underline;">Template text :</span></p><pre class="brush: java; title: ; notranslate">
@org.junit.runner.RunWith(org.mockito.runners.MockitoJUnitRunner.class)
</pre></li><li><span
style="text-decoration: underline;">Description :</span> BDD Stub mock with given(&#8230;).willReturn(&#8230;) style<br
/> <span
style="text-decoration: underline;">Abbréviation :</span> &#8216;gw&#8217;<br
/> <span
style="text-decoration: underline;">Template text :</span></p><pre class="brush: java; title: ; notranslate">
given($MOCK$).willReturn($ARGS$)$END$
</pre><p><span
style="text-decoration: underline;">Les variables du templates sont :</span></p><table
style="border-color: #000000; border-width: 1px; background-color: #ffffff; width: 100%;" border="0"><tbody><tr
style="background-color: #ffefd5;"><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Name</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Expression</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Default value</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Skip if defined</td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">MOCK</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;Object&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">ARGS</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr></tbody></table></li><li><span
style="text-decoration: underline;">Description :</span> BDD Stub spy/mock with willReturn(&#8230;).given(&#8230;) style<br
/> <span
style="text-decoration: underline;">Abbréviation :</span> &#8216;wg&#8217;<br
/> <span
style="text-decoration: underline;">Template text :</span></p><pre class="brush: java; title: ; notranslate">
org.mockito.BDDMockito.willReturn($RETURNED$).given($MOCK$).$CALL$ $END$
</pre><p><span
style="text-decoration: underline;">Les variables du templates sont :</span></p><table
style="border-color: #000000; border-width: 1px; background-color: #ffffff; width: 100%;" border="0"><tbody><tr
style="background-color: #ffefd5;"><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Name</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Expression</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Default value</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Skip if defined</td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">RETURNED</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">complete()</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">MOCK</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;Object&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">CALL</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">complete()</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr></tbody></table></li><li><span
style="text-decoration: underline;">Description :</span> Inserts a verify(&#8230;) statement<span
style="text-decoration: underline;"><br
/> Abbréviation :</span>&#8216;verif&#8217;<span
style="text-decoration: underline;"><br
/> Template text :</span><pre class="brush: java; title: ; notranslate">
org.mockito.Mockito.verify($MOCK$).$CALL$
</pre><p><span
style="text-decoration: underline;">Les variables du templates sont :</span></p><table
style="border-color: #000000; border-width: 1px; background-color: #ffffff; width: 100%;" border="0"><tbody><tr
style="background-color: #ffefd5;"><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Name</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Expression</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Default value</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Skip if defined</td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">MOCK</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;Object&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">CALL</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">complete()</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr></tbody></table></li><li><span
style="text-decoration: underline;">Description :</span> Inserts Mockito.inOrder(mocks) followed by inOrder.verify(&#8230;) statement<br
/> <span
style="text-decoration: underline;">Abbréviation :</span> &#8216;ioverif&#8217;<span
style="text-decoration: underline;"><br
/> Template text :</span><pre class="brush: java; title: ; notranslate">
org.mockito.InOrder $inOrderVar$ = org.mockito.Mockito.inOrder($MOCKS$);
$IN_ORDER_VAR$.verify($MOCK$).$CALL$;
</pre><p><span
style="text-decoration: underline;">Les variables du templates sont :</span></p><table
style="border-color: #000000; border-width: 1px; background-color: #ffffff; width: 100%;" border="0"><tbody><tr
style="background-color: #ffefd5;"><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Name</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Expression</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Default value</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Skip if defined</td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">IN_ORDER_VAR</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">suggestVariableName()</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">MOCKS</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;Object&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"> MOCK</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;Object&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"> CALL</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">complete()</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr></tbody></table></li><li><span
style="text-decoration: underline;">Description :</span>Inserts a verify(&#8230;) statement<span
style="text-decoration: underline;"><br
/> Abbréviation :</span>&#8216;verif&#8217;<span
style="text-decoration: underline;"><br
/> Template text :</span><pre class="brush: java; title: ; notranslate">
$IN_ORDER_VAR$.verify($MOCK$).$CALL$;
</pre><p><span
style="text-decoration: underline;">Les variables du templates sont :</span></p><table
style="border-color: #000000; border-width: 1px; background-color: #ffffff; width: 100%;" border="0"><tbody><tr
style="background-color: #ffefd5;"><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Name</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Expression</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Default value</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">Skip if defined</td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">IN_ORDER_VAR</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;org.mockito.InOrder&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">MOCK</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">variableOfType(&#8220;Object&#8221;)</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr><tr><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">CALL</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;">complete()</td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td><td
style="border-color: #000000; width: 25%; border-style: solid; border-width: 1px;"></td></tr></tbody></table></li></ol><p>Voilà donc les templates que je me suis créé pour IntelliJ, il manque certainement des cas d&#8217;utilisation, mais je trouvais plus judicieux de mettre ces cas là au moins. Pour nos amis Eclipse oou Netbeans, il y a des fonctionnalités comparables plus ou moins évoluées (de mémoire le système d&#8217;Eclipse est plutôt pas mal).</p><h3>Références</h3><ul><li><a
title="Live Templates" href="http://www.jetbrains.com/idea/webhelp/live-templates-2.html">Live Templates</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2011/10/21/quelques-live-template-intellij-pour-mockito/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Afficher les properties d&#8217;un projet maven</title><link>http://blog.arkey.fr/2011/10/18/afficher-les-properties-dun-projet-maven/</link> <comments>http://blog.arkey.fr/2011/10/18/afficher-les-properties-dun-projet-maven/#comments</comments> <pubDate>Tue, 18 Oct 2011 10:15:28 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[Uncategorized]]></category><guid
isPermaLink="false">http://blog.arkey.fr/?p=318</guid> <description><![CDATA[Pas vraiment un article mais plutôt une astuce que j&#8217;ai utilisé pour afficher la valeur de certaines property. Il faut ajouter dans la section build/plugins du pom une tache ant qui efera simplement un echo. A noter que cette tache est disponible dans la phase validate. Cela dit n&#8217;étant pas un expert maven, il y...]]></description> <content:encoded><![CDATA[<p>Pas vraiment un article mais plutôt une astuce que j&#8217;ai utilisé pour afficher la valeur de certaines property.</p><p>Il faut ajouter dans la section build/plugins du pom une tache ant qui efera simplement un echo. A noter que cette tache est disponible dans la phase validate.</p><pre class="brush: xml; title: ; notranslate">&lt;build&gt;
&lt;plugins&gt;

    &lt;plugin&gt;
        &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
        &lt;artifactId&gt;maven-antrun-plugin&lt;/artifactId&gt;
        &lt;version&gt;1.1&lt;/version&gt;
        &lt;executions&gt;
            &lt;execution&gt;
                &lt;phase&gt;validate-property&lt;/phase&gt;
                &lt;goals&gt;
                    &lt;goal&gt;run&lt;/goal&gt;
                &lt;/goals&gt;
                &lt;configuration&gt;
                    &lt;tasks&gt;
                        &lt;echo&gt;Displaying properties resolution&lt;/echo&gt;
                        &lt;echo&gt;some.property]= ${some.property}&lt;/echo&gt;
                        &lt;echo&gt;project.build.directory = ${project.build.directory&lt;/echo&gt;

                        &lt;echo&gt;project.build.finalName= ${project.build.finalName}&lt;/echo&gt;
                    &lt;/tasks&gt;
                &lt;/configuration&gt;
            &lt;/execution&gt;
        &lt;/executions&gt;
    &lt;/plugin&gt;

&lt;/plugins&gt;
&lt;build&gt;</pre><p>Cela dit n&#8217;étant pas un expert maven, il y existe peut-être une solution plus élégante, un commentaire est le bienvenu dans ce cas.</p> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2011/10/18/afficher-les-properties-dun-projet-maven/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Tester votre code JMX dans des conditions pseudo réelle.</title><link>http://blog.arkey.fr/2011/10/14/tester-votre-code-jmx-dans-des-conditions-pseudo-reelle/</link> <comments>http://blog.arkey.fr/2011/10/14/tester-votre-code-jmx-dans-des-conditions-pseudo-reelle/#comments</comments> <pubDate>Fri, 14 Oct 2011 11:01:31 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[code]]></category> <category><![CDATA[TDD]]></category> <category><![CDATA[jmx]]></category> <category><![CDATA[test unitaire]]></category><guid
isPermaLink="false">http://blog.arkey.fr/?p=303</guid> <description><![CDATA[Vous devez écrire du code qui fait appel à JMX, en bon citoyen et bon développeur vous voulez tester ce code. Première approche; vous enregistrez vos MBean sur un MBeanServer, disons celui de la plateforme (avec Java 6 : ManagementFactory.getPlatformMBeanServer()). Étant donné que MBeanServer étends MBeanServerConnection il est possible d’exécuter des querys, de faire des...]]></description> <content:encoded><![CDATA[<p>Vous devez écrire du code qui fait appel à JMX, en bon citoyen et bon développeur vous voulez tester ce code.</p><p>Première approche; vous enregistrez vos MBean sur un <code>MBeanServer</code>, disons celui de la plateforme (avec Java 6 : <code>ManagementFactory.getPlatformMBeanServer()</code>).</p><pre class="brush: java; title: ; notranslate">mBeanServer.registerMBean(theMBean, theMBean.getObjectName());</pre><p>Étant donné que <code>MBeanServer</code> étends <code>MBeanServerConnection</code> il est possible d’exécuter des querys, de faire des invocations sur les MBean etc. Si le code est suffisamment isolé des aspects techniques de connexion à JMX, vous passerez le <code>MBeanServer</code> en lieu et place de la <code>MBeanServerConnection</code>.</p><p>Supposons le code suivant.</p><pre class="brush: java; title: ; notranslate">public class OperateOnJMXConnection implements JMXOperation {

    public void perform(MBeanServerConnection connection) {
        // doing some stuff there
    }

    public Result getResult() { return result; }
}</pre><p>Pour tester ce code il faudrait alors écrire :</p><pre class="brush: java; title: ; notranslate">@Test
public void do_not_fail() {
    operateOnJMXConnection.perform(mbeanServer);

    assertThat(result).satisfies(someCondition);
}</pre><p>Mais voilà, vous restez en local, et par exemple si vous avez merdé sur la sérialisation de vos beans, vous ne verrez pas d&#8217;échec dans vos test et vous aurez une surprise en prod, ou avant si votre projet a un processus qualité décent.</p><p>Évidement il y a une solution, l&#8217;idée c&#8217;est de pouvoir se connecter au <code>mBeanServer</code> local à votre processus (typiquement dans maven 3, l’exécution de vos tests peuvent être forkée).</p><p>Alors j&#8217;ai essayé de récupérer les informations pour récupérer les informations de la VM qui tourne, mais bon on tombe dans des classes <strong>sun</strong>, j&#8217;ai préféré ne pas continuer sur ce chemin semé d&#8217;embûches, sans compter sur la faiblesse de cette approche.</p><p>Bref en relisant les articles de Khanh sur JMX, j&#8217;ai vu quelque chose d&#8217;intéressant <code>JMXConnectorServerFactory</code>. Cette classe permet donc de créer un <code>JMXConnectorServer</code> avec l&#8217;URL qu&#8217;on lui spécifie et d&#8217;un <code>MBeanServer</code>. A noter que cette URL doit respecter un certain formalisme tel que la javadoc l&#8217;indique : <code>service:jmx:<em>protocol</em>:<em>remainder</em></code>.</p><p>Le protocole ne peut pas être n&#8217;importe quoi, il faut qu&#8217;il y ait le bon service enregistré pour qu&#8217;il soit géré. Dans notre cas RMI est standard, c&#8217;est donc le protocole que je prendrai. Pour le remainder, il s&#8217;agit plus d&#8217;une partie d&#8217;une URL, je vous laisse voir la Javadoc de <code>JMXServiceUrl</code> à ce sujet, mais dans les grandes lignes la forme doit être la suivante :  <code>//[host[:port]][url-path]</code></p><pre class="brush: java; title: ; notranslate">JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(
new JMXServiceURL(&quot;service:jmx:rmi://&quot;),
null,
mBeanServer
);

connectorServer.start();</pre><p>Hop dans le code précédent, on a créé puis démarrer notre <code>JMXConnectorServer</code>. Il n&#8217;y a plus qu&#8217;à se connecter dessus de manière standard :<br
/> Je vais utiliser <code>connectorServer.getJMXServer()</code> pour récupérer l&#8217;URL du service, il y a une raison à cela, c&#8217;est que comme l&#8217;indique la javadoc, l&#8217;URL passée pour la création du <code>JMXConnectorServer</code> peut être légèrement modifiée par celui-ci, il faut donc récupérer la nouvelle URL.</p><pre class="brush: java; title: ; notranslate">JMXConnector jmxConnetor = JMXConnectorFactory.connect(connectorServer.getJMXServiceUrl());
MBeanServerConnection connection = jmx.getgetMBeanServerConnection();</pre><p>Et voilà vous avez accès à une <code>MBeanServerConnection</code>, qui vit dans la JVM locale, mais qui utilise RMI pour communiquer avec le <code>MBeanServer</code>, du coup vous êtes nettement plus proches des conditions du code de production et c&#8217;est ce qui nous intéresse dans cet article.</p><p>Pour référence les articles de Khanh, et en français s&#8217;il vous plait <img
src='http://blog.arkey.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> :</p><ul><li>Partie 1 : <a
href="http://jetoile.blogspot.com/2010/10/jmx-pour-les-nuls-les-concepts-partie-1.html">http://jetoile.blogspot.com/2010/10/jmx-pour-les-nuls-les-concepts-partie-1.html</a></li><li>Partie 2 : <a
href="http://jetoile.blogspot.com/2010/11/jmx-pour-les-nuls-les-differents-mbeans.html">http://jetoile.blogspot.com/2010/11/jmx-pour-les-nuls-les-differents-mbeans.html</a></li><li>Partie 3 : <a
href="http://jetoile.blogspot.com/2010/11/jmx-pour-les-nuls-les-agents-jmx-partie.html">http://jetoile.blogspot.com/2010/11/jmx-pour-les-nuls-les-agents-jmx-partie.html</a></li><li>Partie 4 : <a
href="http://jetoile.blogspot.com/2010/11/jmx-pour-les-nuls-les-classes-de-base.html">http://jetoile.blogspot.com/2010/11/jmx-pour-les-nuls-les-classes-de-base.html</a></li><li>Partie 5 : <a
href="http://jetoile.blogspot.com/2010/11/jmx-pour-les-nuls-le-mbean-server.html">http://jetoile.blogspot.com/2010/11/jmx-pour-les-nuls-le-mbean-server.html</a></li><li>Partie 6 : <a
href="http://jetoile.blogspot.com/2010/12/jmx-pour-les-nuls-chargement-dynamique.html">http://jetoile.blogspot.com/2010/12/jmx-pour-les-nuls-chargement-dynamique.html</a></li><li>Partie 7 : <a
href="http://jetoile.blogspot.com/2010/12/jmx-pour-les-nuls-les-services-jmx.html">http://jetoile.blogspot.com/2010/12/jmx-pour-les-nuls-les-services-jmx.html</a></li><li>Partie 8 : <a
href="http://jetoile.blogspot.com/2010/12/jmx-pour-les-nuls-les-connecteurs.html">http://jetoile.blogspot.com/2010/12/jmx-pour-les-nuls-les-connecteurs.html</a></li></ul><p>Quelques liens javadoc :</p><ul><li><a
href="http://download.oracle.com/javase/6/docs/api/javax/management/remote/JMXConnectorFactory.html">JMXConnectorFactory</a></li><li> <a
href="http://download.oracle.com/javase/6/docs/api/javax/management/remote/JMXConnectorServerFactory.html">JMXConnectorServerFactory</a></li><li><a
href="http://download.oracle.com/javase/6/docs/api/javax/management/remote/JMXServiceURL.html">JMXServiceURL</a></li><li><a
href="http://download.oracle.com/javase/6/docs/api/javax/management/remote/JMXConnectorServer.html">JMXConnectorServer</a></li></ul><p>Enfin je me suis créé une petite classe de commodité qui permet de créé facilement un loopback pour les TU :</p><p><script src="https://gist.github.com/1286811.js"></script></p> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2011/10/14/tester-votre-code-jmx-dans-des-conditions-pseudo-reelle/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Script pour installer le JDK 5 sur MacOSX Lion</title><link>http://blog.arkey.fr/2011/08/22/script-pour-installer-le-jdk-5-sur-macosx-lion/</link> <comments>http://blog.arkey.fr/2011/08/22/script-pour-installer-le-jdk-5-sur-macosx-lion/#comments</comments> <pubDate>Mon, 22 Aug 2011 17:51:40 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[code]]></category> <category><![CDATA[jdk5]]></category> <category><![CDATA[lion]]></category> <category><![CDATA[macosx]]></category><guid
isPermaLink="false">http://blog.arkey.fr/?p=274</guid> <description><![CDATA[Avec l&#8217;arrivée de Lion, Apple change les choses avec Java. Heureusement s&#8217;il s&#8217;agit d&#8217;une mise à jour depuis Snow Leopard, vous ne perdrez pas votre runtime JDK 6, en revanche si vous faites une installation clean, et bien il faudra télécharger le runtime ici : http://support.apple.com/kb/DL1421 Bon ça fait une chose de plus pour nous...]]></description> <content:encoded><![CDATA[<p>Avec l&#8217;arrivée de Lion, Apple change les choses avec Java. Heureusement s&#8217;il s&#8217;agit d&#8217;une mise à jour depuis Snow Leopard, vous ne perdrez pas votre runtime JDK 6, en revanche si vous faites une installation clean, et bien il faudra télécharger le runtime ici :</p><blockquote><p><a
title="Java 6 Runtime pour Lion" href="http://support.apple.com/kb/DL1421">http://support.apple.com/kb/DL1421</a></p></blockquote><p>Bon ça fait une chose de plus pour nous ennuyer, mais bon comme toujours pour ceux qui veulent bosser sur un JDK 1.5, il vous faudra tricher un peu plus, il n&#8217;y a pas de mise à jour standard ou facile pour installer le JDK 5 sur 10.7.</p><p>Certains ont trouvé l&#8217;astuce en téléchargeant la mise à jour Java pour Mac OS X 10.5, et avec quelques outils et commandes dans le terminal. Cela dit le processus est un poil long. Du coup je me suis codé un petit script pour automatiser ces étapes. Pour l&#8217;instant le script repose sur un téléchargement manuel de cette mise à jour.</p><ol><li>En premier on télécharge la mise à jour du JDK5 ici :<br
/><blockquote><p><a
title="Mise à jour Java pour Leopard" href="http://support.apple.com/kb/DL1359">http://support.apple.com/kb/DL1359</a></p></blockquote></li><li>Ensuite dans le même répertoire on y téléchargera le <strong><a
title="Script d'install du JDK 5 sur Lion" href="/wp-content/uploads/2011/08/install_jdk5_lion.sh">script</a></strong></li><li>Dans un terminal dans le dossier du téléchargement<pre class="brush: plain; title: ; notranslate">chmod +x install_jdk5_lion.sh</pre></li><li>Il faut être <strong>root</strong>, attention quand même, le script fonctionne sur les environnements Lion que j&#8217;ai pu testé, mais il peut très bien casser votre système, déclencher un tempête ou je ne sais quoi encore&#8230; je ne garantis rien.<pre class="brush: plain; title: ; notranslate">sudo -s</pre></li><li>Bref il se lance comme ça :<pre class="brush: plain; title: ; notranslate">./install_jdk5_lion.sh</pre></li><li>Si tout se passe bien alors, les préférences Java de Mac s&#8217;ouvriront en listant le JDK 5.</li><li>exit</li></ol><p>En images, ça donne :</p><p><a
href="http://blog.arkey.fr/wp-content/uploads/2011/08/jdk5_lion_install_terminal.png"><img
class="alignnone size-full wp-image-279" title="jdk5_lion_install_terminal" src="http://blog.arkey.fr/wp-content/uploads/2011/08/jdk5_lion_install_terminal.png" alt="" width="593" height="399" /></a></p><p><a
href="http://blog.arkey.fr/wp-content/uploads/2011/08/lion_java_preferences.png"><img
class="alignnone size-full wp-image-280" title="lion_java_preferences" src="http://blog.arkey.fr/wp-content/uploads/2011/08/lion_java_preferences.png" alt="" width="577" height="402" /></a></p><p>Evidement si vous repérez une coquille, je suis à l&#8217;écoute. Bonne soirée <img
src='http://blog.arkey.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p><p><strong>EDIT 29/08/2011</strong>: Tant qu&#8217;à faire autant montrer comment avoir plusieurs JDK dans IntelliJ sous macosx.</p><ol><li>Donc une fois le projet ouvert, il faut aller dans les préférences du projet (Project Settings).<br
/> <a
href="http://blog.arkey.fr/wp-content/uploads/2011/08/project_setting-e1314642274278.png"><img
class="alignnone size-full wp-image-289" title="project_setting" src="http://blog.arkey.fr/wp-content/uploads/2011/08/project_setting-e1314642274278.png" alt="IntelliJ Project Setting" width="496" height="327" /></a></li><li>Ensuite ajouter le JSDK.<br
/> <a
href="http://blog.arkey.fr/wp-content/uploads/2011/08/add_new_jsdk.png"><img
class="alignnone size-full wp-image-290" title="add_new_jsdk" src="http://blog.arkey.fr/wp-content/uploads/2011/08/add_new_jsdk.png" alt="Add New JSDK" width="190" height="211" /></a></li><li>Puis sélectionner dans l&#8217;explorateur le dossier /System/Library/Java/JavaVirtualMachines/1.5.0/Contents/Home<br
/> <a
href="http://blog.arkey.fr/wp-content/uploads/2011/08/choose_jdk5_home.png"><img
class="alignnone size-full wp-image-291" style="border-style: initial; border-color: initial;" title="choose_jdk5_home" src="http://blog.arkey.fr/wp-content/uploads/2011/08/choose_jdk5_home-e1314642723756.png" alt="Choose JDK 5 Home" width="496" height="236" /></a></li><li>Hop, c&#8217;est fini, dans IntelliJ vous avez le JDK 5<br
/> <a
href="http://blog.arkey.fr/wp-content/uploads/2011/08/intellij_found_it.png"><img
class="alignnone size-full wp-image-292" title="intellij_found_it" src="http://blog.arkey.fr/wp-content/uploads/2011/08/intellij_found_it-e1314642860664.png" alt="" width="496" height="325" /></a></li></ol><p>Idée originale : <a
href="http://www.s-seven.net/zend_5x_lion">Zend Studio <em>5</em>.x for OS X <em>Lion</em> (<em>Java</em> SE 6)</a></p><p>Sources : <br
/><script src="https://gist.github.com/1163008.js"></script></p> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2011/08/22/script-pour-installer-le-jdk-5-sur-macosx-lion/feed/</wfw:commentRss> <slash:comments>7</slash:comments> </item> <item><title>S&#8217;exprimer régulièrement (Partie 3)</title><link>http://blog.arkey.fr/2010/11/04/sexprimer-regulierement-partie-3/</link> <comments>http://blog.arkey.fr/2010/11/04/sexprimer-regulierement-partie-3/#comments</comments> <pubDate>Thu, 04 Nov 2010 18:55:50 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[code]]></category> <category><![CDATA[design]]></category> <category><![CDATA[pattern]]></category> <category><![CDATA[expression régulière]]></category> <category><![CDATA[regex]]></category> <category><![CDATA[regexp]]></category> <category><![CDATA[regular expression]]></category> <category><![CDATA[unicode]]></category><guid
isPermaLink="false">http://dutheil.brice.online.fr/blog/?p=228</guid> <description><![CDATA[Dans cette troisième et dernière partie sur les expressions régulières en Java. Je vais aborder deux thèmes assez peu utilisés et pourtant très utiles. Le premier, dans la continuité des groupes ce sont les constructions de look behind et look ahead. Le deuxième point abordera le support de Unicode dans nos expressions régulières. Avoir le coup d&#8217;œil...]]></description> <content:encoded><![CDATA[<p>Dans cette troisième et dernière partie sur les expressions régulières en Java. Je vais aborder deux thèmes assez peu utilisés et pourtant très utiles.</p><ul><li>Le premier, dans la continuité des groupes ce sont les constructions de <strong>look behind</strong> et <strong>look ahead</strong>.</li><li>Le deuxième point abordera le support de Unicode dans nos expressions régulières.</li></ul><h1>Avoir le coup d&#8217;œil</h1><p>C&#8217;est bien de ça dont il s&#8217;agit; ce feature, introduit grâce aux groupes non-capturant, permet de vérifier si une autre expression matche avant ou après une expression capturante  <strong>sans consommer</strong> de caractères. Il y a 4 constructions de types :</p><ul><li>Les constructions look ahead</li></ul><ol><li><ol><li><strong>(?=X)</strong> X, via zero-width <strong>positive lookahead</strong> : L&#8217;expression cherche à matcher X <strong>après</strong> la position courante et sans consommer.</li><li><strong>(?!X)</strong> X, via zero-width <strong>negative lookahead</strong> : L&#8217;expression cherche à <strong>ne pas</strong> matcher X <strong>après</strong> la position courante et sans consommer.</li></ol></li></ol><ul><li>Les expressions look behind</li></ul><ol><li><ol><li><strong>(?&lt;=X)</strong> X, via zero-width <strong>positive lookbehind</strong> : L&#8217;expression cherche à matcher X <strong>avant</strong> la position courante et sans consommer, ou X est une expression régulière de <strong>longueur connue</strong>.</li><li><strong>(?&lt;!X)</strong> X, via zero-width <strong>negative lookbehind</strong> : L&#8217;expression cherche à <strong>ne pas</strong> matcher X <strong>avant</strong> la position courante et sans consommer, ou X est une expression régulière de <strong>longueur connue</strong>.</li></ol></li></ol><p>Ces assertions ressemblent aux bornes <strong>b</strong> elles ont un fonctionnement similaire mais plus complexes. Passons aux tests pour voir leur fonctionnement.</p><h2>Les groupes de look ahead</h2><p>Par exemple avec le look ahead positif :</p><pre class="brush: java; highlight: [10,15,21]; title: ; notranslate">public class LookAheadLookBehind {

    private String text = &quot;static private String aStaticVarLabel;&quot; +
            &quot;static private Long anotherStaticVarLabel;&quot; +
            &quot;private String anInstanceVar;&quot; +
            &quot;protected String anInteger;&quot;;

    @Test
    public void classicRegex() {
        assertEquals(&quot;aStaticVarLabel&quot;, regexMatch(text, &quot;\w+Label&quot;));
    }

    @Test
    public void positiveLookAhead() {
        assertEquals(&quot;aStaticVar&quot;, regexMatch(text, &quot;\w+(?=Label)&quot;));
    }

    private String regexMatch(String text, String regex) {
        Matcher matcher = Pattern.compile(regex).matcher(text);

        return matcher.find() ? matcher.group(0) : &quot;&quot;;
    }
}</pre><p>Ligne 10, on veut chopper les lignes qui se terminent par <em>&#8220;Label&#8221;</em> avec une expression usuelle. Si on ne voulais pas la partie <em>&#8220;Label&#8221;</em>, alors il aurait fallu créer un autre groupe autour de <strong>w+</strong>, cependant le curseur aura consommé les caractères. L&#8217;alternative est d&#8217;utiliser un look ahead positif, c&#8217;est ce qu&#8217;on a à la ligne 15, ici le curseur s&#8217;arrête après le <em>&#8220;r&#8221;</em> juste avant <em>&#8220;Label&#8221;</em>.</p><p>Notez que dans l&#8217;exemple ce qui est retourné est le <strong>groupe 0</strong> (ligne 21), c&#8217;est à dire l’ensemble de ce qui est capturé par toute la regex. Ceci illustre à nouveau que les groupes de look ahead/begind ne capturent pas (méthode positiveLookAhead, ligne 15). C&#8217;est assez pratique pour faire des sélections ou des remplacements, dans Eclipse par exemple.</p><p>Si typiquement on cherche des termes qui ne se terminent pas par <em>&#8220;Label&#8221;</em>. On écrira simplement :</p><pre class="brush: java; title: ; notranslate">@Test
public void negativeLookAhead() {
    assertEquals(&quot;private&quot;, regexMatch(text, &quot;\w+(?!Label)&quot;));
}</pre><p>L&#8217;expression chope en premier <em>&#8220;private&#8221;</em>, tout simplement parce que cette partie du texte matche le fait qu&#8217;il n&#8217;y a pas Label qui suit, si on veut chopper le nom d&#8217;une variable alors on peut ajouter des constructions de <strong>look behind</strong>. C&#8217;est ce qu&#8217;on regarde juste après.</p><h2>Les groupes de look behind</h2><pre class="brush: java; title: ; notranslate">@Test
public void positiveLookBehind() {
    assertEquals(&quot;anotherStaticVarLabel&quot;, regexMatch(text, &quot;(?&lt;=private Long )\w+&quot;));
}</pre><p>Donc là j&#8217;ai préfixé la regex par ce que je voulais voir juste avant. De la même manière si on ne veut pas d&#8217;un terme, on utilisera un <strong>look behind</strong> négatif <strong>(?&lt;!)</strong>, par exmple si on ne veut pas de <em>&#8220;String&#8221;</em>.</p><pre class="brush: java; title: ; notranslate">@Test
public void negativeLookBehind() {
    assertEquals(&quot;anotherStaticVarLabel&quot;, regexMatch(text, &quot;(?&lt;=private \w{4,8} )(?&lt;!String )\w+&quot;));
}</pre><p>Observez ici qu&#8217;il y a deux constructions adjacentes look behind, l&#8217;une positive l&#8217;autre négative, ce qui illustre encore mieux que ces constructions ne consomment pas la séquence de caractères.</p><p>Observez également que l&#8217;expression ici est de longueur connue : le <strong>w{4,8}</strong> ne prend que de 4 à 8 caractères. Il n&#8217;est pas possible d&#8217;écrire un look behind avec un quantificateur où la longueur n&#8217;est pas connue, la <span
style="text-decoration: underline;">construction suivante est fausse</span> et provoquera une erreur de syntaxe : <strong>(?&lt;!private w+ )</strong>. C&#8217;est une limite technique qui impose aux groupes de look behind d&#8217;avoir une longueur fixe ou calculable; le quantificateurs borné <strong>{n,m}</strong>, l&#8217;option <strong>?</strong> ou l&#8217;alternative <strong>|</strong> tombent dans cette catégorie. Ainsi on pourrait écrire :</p><pre class="brush: java; title: ; notranslate">@Test
public void negativeLookBehind() {
    assertEquals(&quot;anotherStaticVarLabel&quot;, regexMatch(text, &quot;(?&lt;=(?:static )?private long|Long )\w+&quot;));
}</pre><p>Et donc par opposition les quantificateurs <strong>*</strong> et <strong>+</strong> ne sont pas autorisés.</p><h2>Attention aux quantificateurs sur une même classe de caractère</h2><p>Bon, il existe certains cas un peu délicats ou les caractères adjacents d&#8217;une séquence font partie de la même classe. Dans le bout de texte utilisé dasn le premier exemple, les nom variables correspondent typiquement à ça:</p><pre><span style="color: #800000;">anotherStaticVar</span><span style="color: #333399;">Label</span></pre><p>Le nom de la variable appartient à la classe de caractère <strong>[a-zA-Z0-9_]</strong> ou encore à <strong>w</strong>.</p><p>Lorsqu&#8217;on faisait un <strong>positive look ahead</strong>, le quantificateur <strong>w+</strong> va chercher à matcher l’ensemble des caractères de cette classe, ce qui veut dire que <strong>w+</strong> va <strong>matcher et consommer</strong> les caractères <em>&#8220;anotherStaticVarLabel&#8221;</em>. Du coup lorsque la construction <strong>(?=Label)</strong> cherche à matcher <em>&#8220;Label&#8221;</em>, elle n&#8217;y arrive pas. Ce n&#8217;est pas grave, avec le backtracking l&#8217;expression <strong>w+</strong> reviens en arrière jusqu&#8217;à ce que <strong>(?=Label)</strong> matche.</p><p>L&#8217;histoire est différente avec un <strong>negative look ahead</strong>; une fois que la partie <strong>w+</strong> a matché <em>&#8220;anotherStaticVarLabel&#8221;</em>, le curseur est positionné après le <em>&#8220;l&#8221;</em>. Maintenant le moteur teste <strong>(?!Label)</strong>, qui cherche donc à ne pas matcher <em>&#8220;Label&#8221;</em>, normal c&#8217;est une négation. Et là ça marche, cette partie de l&#8217;expression ne peut plus trouver <em>&#8220;Label&#8221;</em>, donc la construction est validée.</p><p>Bref ce n&#8217;est pas ce qu&#8217;on veut, nous voulons par exemple identifier les variables qui ne sont pas suffixées par <em>&#8220;Label&#8221;</em> !</p><p>Pour ne éviter ce problème, il faut placer le groupe look ahead négatif avant <strong>w+</strong>. Cela ne posera pas de problème étant donné que les look ahead ne consomment pas la séquence de caractères. Ainsi en écrivant :</p><pre class="brush: java; title: ; notranslate">@Test
public void controlYourQuantifiers() throws Exception {
    assertEquals(&quot;anInstanceVar&quot;, regexMatch(text, &quot;(?&lt;=String )(?!\w+Label)\w+&quot;));
}</pre><p>La première partie est un look behind pour avoir ce qui est après <em>&#8220;String &#8220;</em>, le deuxième groupe est le look ahead dont je parlais, ce groupe cherche à ne matcher <strong>w+Label</strong>, si les derniers caractères <strong>Label</strong> de la regex ne sont pas trouvés alors c&#8217;est bon. Finalement l&#8217;expression se termine par <strong>w+</strong>. L&#8217;astuce donc se fait en deux étapes:</p><ol><li>Déplacer le look ahead avant l&#8217;expression qui consomme les caractères et qu&#8217;on veut capturer, ici <strong>w+</strong></li><li>Faire précéder dans le look ahead négatif l&#8217;expression qu&#8217;on veut capturer, ici le groupe est devenu <strong>(?!w+Label)</strong>, grâce au backtracking dans ce groupe une valeur <em>&#8220;aStaticVarLabel&#8221;</em> ne sera pas matchée (negative look ahead).</li></ol><p>Voilà pour les possibilités de look ahead et de look behind dans les expressions rationnelles.</p><p>Passons maintenant au support <a
href="http://unicode.org/">Unicode</a> par la classe <a
href="http://download.oracle.com/javase/1.5.0/docs/api/java/util/regex/Pattern.html">Pattern</a>.</p><h1>Unicode</h1><p>En quoi Unicode est intéressant dans nos regex en Java?</p><ol><li>Unicode est supporté nativement par Java, le format interne des String est Unicode.</li><li>Unicode nous apporte des classes, des catégories ou des propriétés de caractères bien plus étendues que les classes ASCII couramment utilisées.</li></ol><h2>Juste pour une lettre</h2><p>Par exemple, j&#8217;ai une application US qui vérifie que le texte entré est uniquement composé de lettres. Facile avec la regex suivante:</p><pre class="brush: plain; title: ; notranslate">[a-zA-Z]</pre><p>Maintenant je me dit que je souhaiterais avoir des clients français! Aille! L&#8217;approche facile mais peu élégante est d&#8217;écrire une regex dans ce genre :</p><pre class="brush: plain; title: ; notranslate">[a-zA-Zéèêïôàù]</pre><p>Et encore j&#8217;oublie les accents sur les majuscules et encore d&#8217;autre caractères spéciaux, alors qu&#8217;ils ont pourtant <a
href="http://www.academie-francaise.fr/langue/questions.html#accentuation">pleine valeur orthographique sur les majuscules également</a>. S&#8217;il fallait en plus gérer le grec, l’allemand, l’espagnol, nous aurions du mal avec une telle expression régulière. Et le raccourci <strong>w</strong> n&#8217;aide pas vraiment non plus! C&#8217;est là que viennent les classes de caractère Unicode, pour identifier un caractère qui est une lettre, on écrira très simplement :</p><pre class="brush: plain; title: ; notranslate">p{L}</pre><p>Ainsi en Java on aura par exemple</p><pre class="brush: java; title: ; notranslate">@Test
public void matchLettersInDifferentLanguage() throws Exception {
    Assert.assertTrue(Pattern.matches(&quot;(\p{L}| )+&quot;, &quot;une manœuvre sur un chêne&quot;));
    Assert.assertFalse(Pattern.matches(&quot;[\p{Lower} ]+&quot;, &quot;une manœuvre sur un chêne&quot;)); // Attention p{Lower} est une classe POSIX ou ASCII !
    Assert.assertTrue(Pattern.matches(&quot;[\p{Ll} ]+&quot;, &quot;une manœuvre sur un chêne&quot;)); // Classe des petites lettres en Unicode p{Ll} !

    Assert.assertTrue(Pattern.matches(&quot;[\p{L} ]+&quot;, &quot;eine kleine Straße in München&quot;));

    Assert.assertTrue(Pattern.matches(&quot;[\p{L} ]+&quot;, &quot;Это настоящая красота&quot;));
}</pre><p>IntelliJ est très bien, il fourni l&#8217;auto-complétion dans les regex c&#8217;est assez pratique à l&#8217;intérieur du code, mais pas d&#8217;explication sur la signification de ces blocs de caractères Unicode. Eclipse n&#8217;en parlons pas, et NetBeans je ne sais pas. En tous cas on trouve une réponse <a
href="http://unicode.org/reports/tr18/">ici</a> ou encore <a
href="http://www.unicode.org/Public/5.1.0/ucd/UCD.html">là</a> à propos des blocs Unicode:</p><table
style="border-spacing: 0px; border: 1px solid black; width: 574px; height: 704px;"><thead><tr
style="background-color: #d2e1f8;"><th
style="border-collapse: collapse; border: 1px solid #000000;">Abréviation reconnue par Pattern</th><th
style="border-collapse: collapse; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; border: 1px solid #000000;">Signification</th></tr></thead><tbody><tr><td
style="border: 1px solid #000000;">L</td><td
style="border: 1px solid #000000;">Letter</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Lu</td><td
style="border: 1px solid #000000;">Uppercase Letter</td></tr><tr><td
style="border: 1px solid #000000;">Ll</td><td
style="border: 1px solid #000000;">Lowercase Letter</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Lt</td><td
style="border: 1px solid #000000;">Titlecase Letter</td></tr><tr><td
style="border: 1px solid #000000;">Lm</td><td
style="border: 1px solid #000000;">Modifier Letter</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Lo</td><td
style="border: 1px solid #000000;">Other Letter</td></tr><tr><td
style="border: 1px solid #000000;">M</td><td
style="border: 1px solid #000000;">Mark</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Mn</td><td
style="border: 1px solid #000000;">Non-Spacing Mark</td></tr><tr><td
style="border: 1px solid #000000;">Mc</td><td
style="border: 1px solid #000000;">Spacing Combining Mark</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Me</td><td
style="border: 1px solid #000000;">Enclosing Mark</td></tr><tr><td
style="border: 1px solid #000000;">N</td><td
style="border: 1px solid #000000;">Number</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Nd</td><td
style="border: 1px solid #000000;">Decimal Digit Number</td></tr><tr><td
style="border: 1px solid #000000;">Nl</td><td
style="border: 1px solid #000000;">Letter Number</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">No</td><td
style="border: 1px solid #000000;">Other Number</td></tr><tr><td
style="border: 1px solid #000000;">S</td><td
style="border: 1px solid #000000;">Symbol</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Sm</td><td
style="border: 1px solid #000000;">Math Symbol</td></tr><tr><td
style="border: 1px solid #000000;">Sc</td><td
style="border: 1px solid #000000;">Currency Symbol</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Sk</td><td
style="border: 1px solid #000000;">Modifier Symbol</td></tr><tr><td
style="border: 1px solid #000000;">So</td><td
style="border: 1px solid #000000;">Other Symbol</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">P</td><td
style="border: 1px solid #000000;">Punctuation</td></tr><tr><td
style="border: 1px solid #000000;">Pc</td><td
style="border: 1px solid #000000;">Connector Punctuation</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Pd</td><td
style="border: 1px solid #000000;">Dash Punctuation</td></tr><tr><td
style="border: 1px solid #000000;">Ps</td><td
style="border: 1px solid #000000;">Open Punctuation</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Pe</td><td
style="border: 1px solid #000000;">Close Punctuation</td></tr><tr><td
style="border: 1px solid #000000;">Pi</td><td
style="border: 1px solid #000000;">Initial Punctuation</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Pf</td><td
style="border: 1px solid #000000;">Final Punctuation</td></tr><tr><td
style="border: 1px solid #000000;">Po</td><td
style="border: 1px solid #000000;">Other Punctuation</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Z</td><td
style="border: 1px solid #000000;">Separator</td></tr><tr><td
style="border: 1px solid #000000;">Zs</td><td
style="border: 1px solid #000000;">Space Separator</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Zl</td><td
style="border: 1px solid #000000;">Line Separator</td></tr><tr><td
style="border: 1px solid #000000;">Zp</td><td
style="border: 1px solid #000000;">Paragraph Separator</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">C</td><td
style="border: 1px solid #000000;">Other</td></tr><tr><td
style="border: 1px solid #000000;">Cc</td><td
style="border: 1px solid #000000;">Control</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Cf</td><td
style="border: 1px solid #000000;">Format</td></tr><tr><td
style="border: 1px solid #000000;">Cs</td><td
style="border: 1px solid #000000;">Surrogate</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">Co</td><td
style="border: 1px solid #000000;">Private Use</td></tr><tr><td
style="border: 1px solid #000000;">Cn</td><td
style="border: 1px solid #000000;">Not Assigned</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">-</td><td
style="border: 1px solid #000000;">Any*</td></tr><tr><td
style="border: 1px solid #000000;">-</td><td
style="border: 1px solid #000000;">Assigned*</td></tr><tr
style="background-color: #d2e1f8;"><td
style="border: 1px solid #000000;">-</td><td
style="border: 1px solid #000000;">ASCII*</td></tr></tbody></table><h2>Matcher les caractère d&#8217;un alphabet seulement</h2><p>Si je veux vérifier que mon texte appartient à de l&#8217;hébreu ou du chinois c&#8217;est faisable. Dans Unicode il faut remarquer qu&#8217;il y a plusieurs notion pour les &#8220;alphabets&#8221;; il y a les <strong>Blocs</strong> et les <strong>Scripts</strong>, cependant le moteur de Java qui se base essentiellement sur le moteur de perl, ne gère pas les scripts, donc on se contentera des blocs.</p><p>Ci-dessous je teste l&#8217;appartenance à un bloc :</p><pre class="brush: java; title: ; notranslate">@Test
public void matchABlock() throws Exception {
    Assert.assertFalse(Pattern.matches(&quot;(\p{InBASIC_LATIN}| )+&quot;, &quot;une manœuvre sur un chêne&quot;));
    Assert.assertTrue(Pattern.matches(&quot;(\p{InLATIN_EXTENDED_A}|\p{InLATIN_1_SUPPLEMENT}|\p{InBASIC_LATIN}| )+&quot;, &quot;une manœuvre sur un chêne&quot;));

    Assert.assertTrue(Pattern.matches(&quot;[\p{InLATIN_1_SUPPLEMENT}\p{InBASIC_LATIN} ]+&quot;, &quot;eine kleine Straße in München&quot;));

    Assert.assertTrue(Pattern.matches(&quot;[\p{InCYRILLIC} ]+&quot;, &quot;Это настоящая красота&quot;));
    Assert.assertFalse(Pattern.matches(&quot;[\p{InHEBREW} ]+&quot;, &quot;Это настоящая красота&quot;));

    Assert.assertTrue(Pattern.matches(&quot;[\p{InCJK_UNIFIED_IDEOGRAPHS} ]+&quot;, new String(Character.toChars(0x6C23)))); // chi écriture traditionnel
    Assert.assertTrue(Pattern.matches(&quot;[\p{InHIRAGANA} ]+&quot;, new String(Character.toChars(0x304D)))); // ki écriture Hiragana
}</pre><p>Plusieures choses sont à remarquer :</p><ul><li>Le nom de l&#8217;alphabet est précédé par &#8220;<strong>In</strong>&#8220;</li><li>Pour avoir une phrase en français on a très vite plusieurs blocs &#8220;LATIN EXTENDED A&#8221; pour le graphème <em>&#8216;œ&#8217;</em>, &#8220;LATIN 1 SUPPLEMENT&#8221; pour le <em>&#8216;ê&#8217;</em> E accent circonflexe.</li><li>D&#8217;autres alphabet sont plus pratique à utiliser comme l&#8217;hébreu, le cyrillique, le grecque, etc.</li><li>L&#8217;utilisation des alphabet Chinois, Japonais, Coréen peut aussi soulever des question surtout quand on ne le parle pas <img
src='http://blog.arkey.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /></li></ul><p>A noter également :</p><blockquote><p>Sur les deux dernière lignes noter que j&#8217;ai utiliser le code hexadécimal <strong>UTF-16</strong> (j&#8217;y reviendrais après) pour obtenir les caractères <span
style="font-size: large;">氣</span> et <span
style="font-size: large;">き</span> (Chi en chinois traditionnel, Ki avec l&#8217;alphabet Hiragana). Pourquoi? Parce que Unicode c&#8217;est bien joli mais dans le monde réel il y a des limitations, pour moi il s&#8217;agit de la police de caractère de mon éditeur qui ne possède pas ces blocs de caractères défini. Peut-être aurez vous des limitations sur la police de votre navigateur. A noter également que l&#8217;encodage de vos fichier peut faire mal quand on joue avec les caractères en dehors du latin basique.</p><table
style="width: 50px;" border="0"><tbody><tr><td><a
href="http://www.fileformat.info/info/unicode/char/6c23/index.htm"><img
class="alignright size-full wp-image-234" title="0x6C23" src="http://blog.arkey.fr/wp-content/uploads/2010/11/0x6C23-chi.png" alt="Chi (Chinois traditionnel)" width="100" height="100" /></a></td><td><a
href="http://www.fileformat.info/info/unicode/char/304d/index.htm"><img
class="alignright size-full wp-image-235" title="0x304D" src="http://blog.arkey.fr/wp-content/uploads/2010/11/0x304D-ki-hiragana.png" alt="Ki (Alphabet Hiragana)" width="100" height="100" /></a></td></tr><tr><td
style="text-align: center;">Chi (0x6C23)</td><td
style="text-align: center;">Ki (0x304D)</td></tr></tbody></table></blockquote><h2>On peut encore s&#8217;amuser</h2><p>Pour revenir dans les choses qui nous intéresse, imaginons que nous voulions compter tous les caractères accentués dans un texte. Le bloc Unicode <strong>p{L}</strong> n&#8217;est pas approprié, mais comme je l&#8217;ai dit avec Unicode on peut accéder aux propriété d&#8217;un caractère.</p><p>Déjà pour commencer il faut savoir qu&#8217;en Unicode, un graphème comme <em>&#8216;é&#8217;</em> peut correspondre à un seul caractère <em>&#8216;é&#8217;</em> ou à deux caractères <em>&#8216;e&#8217;</em> suivi du modificateur accent grave. Cela dépend de la source, mais <strong>ces cas sont probables</strong>.</p><pre class="brush: java; highlight: [5]; title: ; notranslate">@Test
public void graphemes() {
    Assert.assertTrue(Pattern.matches(&quot;\p{InLATIN_1_SUPPLEMENT}+&quot;, &quot;éèêu00E9&quot;));
    Assert.assertFalse(Pattern.matches(&quot;\p{InLATIN_1_SUPPLEMENT}+&quot;, &quot;eu0301&quot;));
    Assert.assertTrue(Pattern.matches(&quot;(\p{L}\p{M})+&quot;, &quot;eu0301&quot;));
    Assert.assertTrue(Pattern.matches(&quot;(\p{InLATIN_1_SUPPLEMENT}|\p{L}\p{Mn})+&quot;, &quot;éêeu0301u0065u0302&quot;));
    System.out.println(&quot;éêeu0301u0065u0302&quot;);
}</pre><p>Ainsi dans les lignes précédentes pour rechercher un graphème représenté par un seul codepoint, il faudra aller le chercher dans le bloc idoine, ici &#8220;LATIN 1 COMPLEMENT&#8221;, 0x00E9 est le codepoint du caractère <em>&#8216;é&#8217;</em>. La forme décomposée de <em>&#8216;é&#8217;</em> est <em>&#8216;e&#8217;</em> (0&#215;0065) suivi du modificateur accent grave (0&#215;0301).</p><p>Pour matcher cette forme décomposée du graphème, il faut simplement écrire <strong>p{L}p{M}</strong>. Il est toujours possible d&#8217;affiner l&#8217;expression en choisissant des propriétés plus précises (cf. Tableau plus haut, voire la référence Unicode). Du coup pour matcher n&#8217;importe quelle forme d&#8217;un graphème on pourra écrire l&#8217;expression de la ligne 6.</p><p>Enfin rapidement on peut exprimer les compléments à la manière standard avec <strong>[^p{Lu}]</strong> ou plus simple avec un grand P <strong>P{Lu}</strong>. Les intersections entres les classes / propriétés Unicode se font sans problèmes également :</p><pre class="brush: java; highlight: [3,5]; title: ; notranslate">@Test
public void complements() throws Exception {
    Assert.assertTrue(Pattern.matches(&quot;[^\p{Lu}]+&quot;, &quot;une manœuvre sur un chêne&quot;)); // exclusion
    Assert.assertFalse(Pattern.matches(&quot;[^\p{Lu}]+&quot;, &quot;Une Manœuvre sur un chêne&quot;));
    Assert.assertFalse(Pattern.matches(&quot;\P{Lu}+&quot;, &quot;Une Manœuvre sur un chêne&quot;)); // complément

    Assert.assertFalse(Pattern.matches(&quot;[[^\p{Lu}]&amp;&amp;\p{IsL} ]+&quot;, &quot;une manœuvre sur un chêne 123164&quot;)); // exclusion et intersection
    Assert.assertTrue(Pattern.matches(&quot;[[^\p{Lu}]&amp;&amp;\p{IsL} ]+&quot;, &quot;une manœuvre sur un chêne&quot;)); // exclusion et intersection
}</pre><h2>Petit retour sur les base de Java</h2><p>Java gère nativement Unicode, les <strong>String sont encodées en UTF-16</strong>. Ce qui explique par conséquent que lorsque je veux exprimer un caractère sous forme hexadécimale, il faut <strong>l&#8217;écrire dans sa forme UTF-16</strong>.</p><pre class="brush: java; title: ; notranslate">@Test
public void utf16() throws Exception {
    Assert.assertTrue(Pattern.matches(&quot;u00E9&quot;, &quot;é&quot;));
    Assert.assertTrue(Pattern.matches(&quot;\u00E9&quot;, &quot;é&quot;));
    Assert.assertTrue(Pattern.matches(&quot;u00E9&quot;, new String(Character.toChars(0x00E9))));
    Assert.assertTrue(Pattern.matches(&quot;\u00E9&quot;, new String(Character.toChars(0x00E9))));
}</pre><p>Ces assertions marches toutes mais il faut noter que <strong>u00E9</strong> est compris par le compilateur et remplacera <strong>u00E9</strong> par <em>&#8216;é&#8217;</em>, alors que dans la forme ou le backslash est échappé <strong>\u00E9</strong> le compilateur ne fera rien. Ce sera au moteur Pattern de traiter la chaîne.</p><pre class="brush: java; title: ; notranslate">@Test
public void charLengthForCodePoint() throws Exception {
    Assert.assertEquals(1, Character.toChars(0x00E9).length); // é
    Assert.assertEquals(1, Character.toChars(0x304D).length); // ki
    Assert.assertEquals(2, Character.toChars(0x0001D50A).length); // MATHEMATICAL FRAKTUR CAPITAL G
}</pre><p>La plupart des caractères tiendront dans le type primitif <strong>char</strong> qui fait donc <strong>16 bits</strong> (voilà pourquoi Java gère nativement l&#8217;UTF-16) , cependant il peut arriver que certains caractères demandent davantage. <strong>Character.toChars(int)</strong> prend donc un <strong>codepoint</strong> représenté par en <strong>entier</strong>, qui fait en Java <strong>32 bits</strong> pour exprimer Unicode en UTF-32 donc. Dans le code ci-dessus la 3ème assertion montre d&#8217;ailleurs que Java doit splitter le caractère en question sur deux <strong>char</strong>.</p><p>De la même manière l&#8217;encodage change naturellement la taille d&#8217;un tableau de <strong>byte</strong> (<strong>8 bits</strong>).</p><pre class="brush: java; title: ; notranslate">@Test
public void encodingDifferenceForAsciiChars() throws Exception {
    String string = &quot;une chaine ascii&quot;;
    assertEquals(string.length(), string.getBytes(&quot;ASCII&quot;).length);
    assertEquals(string.length(), string.getBytes(&quot;UTF-8&quot;).length);
}

@Test
public void encodingDifferenceForAccentedChars() throws Exception {
    String string = &quot;un chêne, un frêne, une orchidée&quot;;
    assertTrue(string.length() == string.getBytes(&quot;ASCII&quot;).length);
    assertEquals(string.length() + 3, string.getBytes(&quot;UTF-8&quot;).length);
}</pre><h1>Bilan</h1><p>Voilà cet article clos la série que je voulais écrire sur les expressions régulière. Il y a probablement d&#8217;autres arcanes à connaître. Mais sur cette série le but était de couvrir ce que le moteur Java nous permet de faire. Je pense que comprendre le fonctionnement du moteur en particulier sur le backtracking, la manière du moteur de tester une expression, la manière dont le moteur parcoure / consomme les caractères en entrée, sont des facteurs clé pour réussir une bonne expression. Cette compréhension est d&#8217;autant plus importante quand celles-ci sont liée à des éléments de performance.</p><p>Les constructions apportées avec Unicode, même limitées, ouvrent certaines possibilités intéressantes, mais clairement il y a du travail à faire : Unicode n&#8217;est manifestement pas simple.</p><h1>Références</h1><ul><li>Le tutorial perl : <a
href="http://perldoc.perl.org/perlretut.html">http://perldoc.perl.org/perlretut.html</a></li><li>La classe Pattern : <a
href="http://download.oracle.com/javase/1.5.0/docs/api/java/util/regex/Pattern.html">http://download.oracle.com/javase/1.5.0/docs/api/java/util/regex/Pattern.html</a></li><li>Unicode Regular Expressions: <a
href="http://unicode.org/reports/tr18/">http://unicode.org/reports/tr18/</a></li><li>Unicode Character Datablase : <a
href="http://www.unicode.org/Public/5.1.0/ucd/UCD.html">http://www.unicode.org/Public/5.1.0/ucd/UCD.html</a></li><li>FileFormat, pour en savoir plus sur un certain caractère :  <a
href="http://www.fileformat.info/info/unicode/char/search.htm">http://www.fileformat.info/info/unicode/char/search.htm</a></li><li>Types primitifs en Java : <a
href="http://download.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">http://download.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2010/11/04/sexprimer-regulierement-partie-3/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>S’exprimer régulièrement (Partie 2)</title><link>http://blog.arkey.fr/2010/10/15/sexprimer-regulierement-partie-2/</link> <comments>http://blog.arkey.fr/2010/10/15/sexprimer-regulierement-partie-2/#comments</comments> <pubDate>Fri, 15 Oct 2010 13:40:15 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[code]]></category> <category><![CDATA[design]]></category> <category><![CDATA[pattern]]></category> <category><![CDATA[performance]]></category><guid
isPermaLink="false">http://dutheil.brice.online.fr/blog/?p=211</guid> <description><![CDATA[La première partie de cette mini-série s&#8217;est focalisée sur une petite intro, je n&#8217;ai pas vraiment insisté sur les bases des expressions régulières, j&#8217;ai juste abordé les ancres et les options, et j&#8217;ai parlé de certaines astuces à connaître. La suite de cette série continue comme prévu sur les constructions suivantes : Les groupes qui...]]></description> <content:encoded><![CDATA[<p>La première partie de cette mini-série s&#8217;est focalisée sur une petite intro, je n&#8217;ai pas vraiment insisté sur les bases des expressions régulières, j&#8217;ai juste abordé les ancres et les options, et j&#8217;ai parlé de certaines astuces à connaître. La suite de cette série continue comme prévu sur les constructions suivantes :</p><ul><li>Les groupes qui ne capturent pas (<em>non-capturing group</em>)</li><li>Les backreferences</li><li>Les autres quantificateurs<ul><li>Les quantificateurs gourmands (dits <em>greedy quantifiers</em>)</li><li>Les quantificateurs paresseux (dits <em>lazy quantifiers</em> ou comme le dit la javadoc de Pattern <em>reluctant quantifiers</em>)</li><li>Les quantificateurs possessifs (dits <em>possessive quantifiers</em>)</li></ul></li><li>Le backtracking</li></ul><p>Certaines des constructions présentées ici démontrent que le moteur de regex de java fait partie des toutes dernières générations. Afin de mieux expliquer la manière de fonctionner des quantificateurs, je vais faire un tour sur la technique de backtracling du moteur de regex, feature essentiel pour faire fonctionner ces constructions.</p><p>Et aussi pourquoi certaines expressions régulières sont risquées en ce qui concerne les performances.</p><h1>Les groupes qui ne capturent pas</h1><h2>Pour comparer voici les groupes capturant</h2><p>Vous connaissez certainement déjà les groupes, par exemple :</p><pre class="brush: java; title: ; notranslate">public class Groups {
  private static final String mail = &quot;brice [dot] dutheil [at] gmail [dot] com&quot;;

  @Test
  public void grouping() {
    Matcher matcher = Pattern.compile(&quot;([a-z]+) ( ?\[[a-z]+\] ([a-z]+))+&quot;).matcher(mail);
    matcher.find();

    assertEquals(&quot;brice&quot;, matcher.group(1));
  }
}</pre><p>Dans l&#8217;expression ci-dessus, il y a trois groupes définis dans l&#8217;expression rationnelle.</p><ul><li><strong>([a-z]+)</strong> qui est donc le groupe 1</li><li><strong><span
style="color: #993300;">(</span> ?[[a-z]+] ([a-z]+)<span
style="color: #993300;">)</span></strong> qui est le <strong>groupe 2</strong></li><li><strong>([a-z]+)</strong> enfin qui est le <strong>groupe 3</strong> il est défini à l&#8217;intérieur du groupe 2</li></ul><p>Le moteur de l&#8217;expression régulière enregistre juste la référence du groupe, et lorsque qu&#8217;il y a récursion sur les groupes, le groupe prend la valeur du dernier contenu matché. Ainsi dans cet exemple après un premier appel à <strong>find</strong>, l&#8217;ensemble de la chaîne de caractère a été consommée, et les groupes 2 et 3 son valorisés par :</p><pre class="brush: java; title: ; notranslate">assertEquals(&quot;[dot] com&quot;, matcher.group(2));
assertEquals(&quot;com&quot;, matcher.group(3));</pre><p>En plus de ça, les références aux groupes sont limitées à 10. Honnêtement c&#8217;est rare d&#8217;avoir besoin de plus de 10 groupes. Dans ce cas il faut splitter la chaîne ou la regex.</p><p>Cependant on peut en partie s&#8217;arranger pour que les groupes qui ne nous intéressent pas ne soit pas référencés, il faut utiliser un groupe on-capturant.</p><h2>Les non-capturing groups</h2><p>C&#8217;est presque à la fin de la javadoc de la classe Pattern. Ils se construisent de la manière suivante :</p><pre class="brush: java; title: ; notranslate">(?:regex)</pre><p>Pour reprendre l&#8217;exemple plus haut, le groupe 2 n&#8217;est pas vraiment utile à notre expression régulière. Du coup on pourrait écrire :</p><pre class="brush: java; highlight: [7]; title: ; notranslate">@Test
public void groupingNonCapturing() {
  Matcher matcher = Pattern.compile(&quot;([a-z]+) (?:[ ]?\[[a-z]+\] ([a-z]+))+&quot;).matcher(mail);
  matcher.find();

  assertEquals(&quot;brice&quot;, matcher.group(1));
  assertEquals(&quot;com&quot;, matcher.group(2));
}</pre><p>Il y a alors 2 groupes uniquement qui sont référencés.</p><p>Les constructions avancées des groupes sont la preuve que le moteur fait partie des dernière générations.</p><h1>Les backreferences</h1><p>Les références des groupes qui capturent, elles sont utilisées dans les moteurs de recherche et de remplacement. Typiquement avec Eclipse, le format utilisé pour faire référence à un groupe est :</p><pre class="brush: java; title: ; notranslate">X</pre><p>Ou X est le numéro du groupe, sa référence.</p><p>En fait cette notation peut également s&#8217;utiliser à l&#8217;intérieur d&#8217;une expression régulière, c&#8217;est ce qu&#8217;on appelle donc une <strong>backrefrence</strong>. Pour back; parceque le groupe doit déjà être défini et matché pour être référencé dans une regex. Par exemple le cas le plus simple :</p><pre class="brush: java; title: ; notranslate">assertTrue(Pattern.compile(&quot;([0-9]+) \1&quot;).matcher(&quot;123 123&quot;).matches());
assertFalse(Pattern.compile(&quot;([0-9]+) \1&quot;).matcher(&quot;123 1234&quot;).matches());</pre><p>Première assertion; le groupe 1 matche 123, la backreference va chercher à matcher le contenu exacte qui a été matché par le groupe 1, donc 123. La deuxième assertion montre bien que la backreference ne matchera pas 1234, car elle s&#8217;attend donc au même contenu que 123 (notez quand même l&#8217;utilisation de l&#8217;appel &lt;strong&gt;matches()&lt;/strong&gt; plutôt que &lt;strong&gt;find()&lt;/strong&gt;).</p><p>Bien entendu il faut que ce soit un groupe capturant, sinon la backreference ne sait pas ou chercher sa valeur. L&#8217;exemple qui suit montre un Pattern qui compile, mais qui ne fonctionnera pas:</p><pre class="brush: java; title: ; notranslate">assertFalse(Pattern.compile(&quot;(?:[0-9]+) \1&quot;).matcher(&quot;123 123&quot;).matches());</pre><p>Le simple fait que ce pattern compile m&#8217;étonne, j&#8217;aurais plutôt choisi une approche fail-fast dans ce cas, c&#8217;est peut-être un oubli.</p><p>Ce genre de construction est assez pratique si on veut valider un langage comme le XML.</p><pre class="brush: java; title: ; notranslate">assertTrue(Pattern.compile(&quot;&lt;([a-z]+)&gt;.*&lt;/\1&gt;&quot;).matcher(&quot;&lt;strong&gt;dude!&lt;/strong&gt;&quot;).matches());
assertTrue(Pattern.compile(&quot;&lt;([a-z]+)[^&gt;]*&gt;.*&lt;/\1&gt;&quot;).matcher(&quot;&lt;strong style=&quot;&quot;&gt;dude!&lt;/strong&gt;&quot;).matches());
assertTrue(Pattern.compile(&quot;&lt;([a-z]+)[^&gt;]*&gt;.*&lt;/\1&gt;&quot;).matcher(&quot;&lt;strong&gt;dude!&lt;/strong&gt;&quot;).matches());</pre><p>Attention il peut y avoir des astuces, en particulier sur le groupe qui fait le premier match. Par exemple dans le suivant on va voir le moteur regex valider l&#8217;expression, alors que la chaîne à valider n&#8217;est pas correcte :</p><pre class="brush: java; title: ; notranslate">assertTrue(Pattern.compile(&quot;&lt;([a-z]+)[^&gt;]*&gt;.*&lt;/\1&gt;&quot;).matcher(&quot;&lt;strong&gt;dude!&lt;/s&gt;&quot;).matches());</pre><p>Effectivement <em>&#8220;&lt;strong&gt;&lt;/s&gt;&#8221;</em> n&#8217;est pas correct pourtant, le moteur valide la séquence de caractère. En fait c&#8217;est un des features du moteur de regex, le backtracking, que je vais expliquer dans la section suivante. L&#8217;idée c&#8217;est que le moteur matche bien <em>&#8220;strong&#8221;</em> pour le groupe 1, mais lorsqu&#8217;il essaye de matcher la backreference avec <em>&#8220;strong&#8221;</em>, il n&#8217;y arrive pas donc il reviens en arrière jusqu&#8217;à ce que le groupe 1 est pour valeur <em>&#8220;s&#8221;</em>, ce qui permet à la backreference de matcher. Le reste de la balise <em>&#8220;trong&#8221;</em> est matchée par cette partie de l&#8217;expression &#8220;<strong>[^&gt;]*</strong>&#8220;.</p><p>La solution, est d&#8217;utiliser une borne de mot vu dans la partie 1 de cette petite série d&#8217;article.</p><pre class="brush: java; title: ; notranslate">assertFalse(Pattern.compile(&quot;&lt;([a-z]+\b)[^&gt;]*&gt;.*&lt;/\1&gt;&quot;).matcher(&quot;&lt;strong&gt;dude!&lt;/s&gt;&quot;).matches());</pre><p>De cette façon le groupe 1 est littéralement obligé d&#8217;être suivi par autre chose qu&#8217;un caractère de mot. Avec cette expression la mauvaise séquence de caractère n&#8217;est donc plus validée.</p><p>Utilisation sympa des backreference est de chercher dans un texte les mots répétés :</p><pre class="brush: java; title: ; notranslate">assertTrue(Pattern.compile(&quot;\b(\w+)\s+\1\b&quot;).matcher(&quot;the the is repeated&quot;).find());</pre><h1>Les quantificateurs</h1><p>Les quantificateurs permettent comme leur nom l&#8217;indique de quantifier (une expression). À l&#8217;exception de l&#8217;opérateur de Kleene, géré par les moteurs de regex depuis très longtemps, tous les autres quantificateurs sont des représentations simplifiées de ce qui est exprimable par des constructions basiques.</p><ul><li
style="text-align: justify;"><strong>dady?</strong> Le quantificateur optionnel peut s&#8217;exprimer par une alternative (attention à l&#8217;ordre) : <strong>dady|dad</strong></li><li
style="text-align: justify;"><strong>(?:pa){1,3}</strong> Le quantificateur borné peut s&#8217;exprimer en répétant les termes et/ou avec une alternative : <strong>pa|papa|papapa</strong></li><li
style="text-align: justify;"><strong>vrou+m</strong> Le quantificateur 1 ou plus peut être remplacé par l’occurrence 1 puis par une construction avec l&#8217;opérateur de Kleene : <strong>vrouu*m</strong></li></ul><p>Bref ces notations simplifiées sont bien pratiques.</p><h2>Les quantificateurs gourmand (greedy quantifiers)</h2><p>Pas de surprise ces quantificateurs font partie de la catégorie des quantificateurs dit gourmands. Vous savez certainement déjà les utiliser, cependant il peut y avoir des cas qui peuvent poser problèmes.</p><p>Dans l&#8217;exemple suivant je voudrais chopper la balise ouvrante.</p><pre class="brush: java; title: ; notranslate">public class Quantifiers {
  @Test
  public void greedy() {
    assertEquals(&quot;&lt;h1&gt;wont match&lt;/h1&gt;&quot;, regexFirstMatch(&quot;&lt;h1&gt;wont match&lt;/h1&gt;&quot;, &quot;&lt;.+&gt;&quot;)); // greediness busted
    assertEquals(&quot;&lt;h1&gt;&quot;, regexFirstMatch(&quot;&lt;h1&gt;wont match&lt;/h1&gt;&quot;, &quot;&lt;.+?&gt;&quot;));
    assertEquals(&quot;&lt;h1&gt;&quot;, regexFirstMatch(&quot;&lt;h1&gt;wont match&lt;/h1&gt;&quot;, &quot;&lt;[^&gt;]+&gt;&quot;));
  }

  private String regexFirstMatch(String text, String regex) {
    Matcher matcher = Pattern.compile(regex).matcher(text);

    return matcher.find() ? matcher.group(0) : &quot;didnt found match&quot;;
  }
}</pre><p>Dans la première approche on utilise un quantificateur gourmand &#8220;<strong>&lt;.+&gt;</strong>&#8221; ce qui veut dire que le moteur va essayer de consommer au maximum la séquence de caractères.</p><ol><li>Pour la section .+ de la regex, le quantificateur va essayer de valider au maximum le &#8220;<strong>.</strong>&#8220;</li><li>Du coup le premier caractère <em>&#8220;&gt;&#8221;</em> est validée par la construction &#8220;<strong>.</strong>&#8220;,</li><li>Puis il valide le deuxième <em>&#8220;&gt;&#8221;</em>.</li><li>Après ce dernier <em>&#8220;&gt;&#8221;</em> dans la séquence de caractère la chaîne complète est consommée, mais il reste le dernier &#8220;<strong>&gt;</strong>&#8221; dans l&#8217;expression <span
style="text-decoration: line-through;">régulière</span> rationnelle.</li><li>Du coup le moteur utilise le backtracking pour revenir en arrière, il tombe sur le &#8220;<strong>1</strong>&#8220;.</li><li>Finalement le &#8220;<strong>&gt;</strong>&#8221; de l&#8217;expression matche le <em>&#8220;&gt;&#8221;</em> de la séquence de caractère.</li></ol><p>Comme ce n&#8217;est pas ce qu&#8217;on veut  récupérer, la balise ouvrante, une solution serait donc de prendre un <strong>quantificateur paresseux</strong> identifiable par le point d’interrogation qui suit le quantificateur.</p><p>Question performance dans le cas présent, il est plus intéressant de ne pas utiliser le point &#8220;<strong>.</strong>&#8221; mais plutôt d&#8217;utiliser un complément de l&#8217;ensemble qu&#8217;on ne veut pas matcher, c&#8217;est à dire une classe de caractère avec exclusion du caractère non voulu <em>&#8220;&gt;&#8221;</em>. C&#8217;est la troisième solution du bout de code.</p><h2>Les quantificateurs paresseux (lazy quantifiers)</h2><p>Ces quantificateurs sont bien nommés parce dans le genre, ils vont en faire vraiment le moins possible. Pour les comparer donc avec un quantificateur gourmand ou la séquence maximum est consommée (notez que la méthode <strong>regexFirstMatch</strong> est la même que dans le bout de code ci-dessus) :</p><pre class="brush: java; title: ; notranslate">assertEquals(&quot;abc1abc2&quot;, regexFirstMatch(&quot;abc1abc2&quot;, &quot;abc1(?:abc\d)?&quot;));</pre><p>Le quantificateur &#8220;<strong>?</strong>&#8221; essaye de matcher la regex du groupe, et il y arrive, donc la séquence complète est consommée. Par contre ci la regex utilise une construction avec un quantificateur paresseux &#8220;<strong>??</strong>&#8221; :</p><pre class="brush: java; title: ; notranslate">assertEquals(&quot;abc1&quot;, regexFirstMatch(&quot;abc1abc2&quot;, &quot;abc1(?:abc\d)??&quot;));</pre><p>Alors le quantificateur ne va pas s&#8217;emmerder à matcher, si la regex matche déjà ce qui est fait par la première partie de la regex &#8220;<strong>abc1</strong>&#8220;. Ce qu&#8217;il faut retenir c&#8217;est qu&#8217;un lazy quantifier, ne matchera jamais si le moteur valide déjà l&#8217;expression, et le corollaire est que le lazy quantifer cherchera toujours à matcher si et uniquement si la regex n&#8217;a pas déjà été validée.</p><p>Autre exemple avec un quantificateur borné :</p><pre class="brush: java; highlight: [2]; title: ; notranslate">assertEquals(&quot;abc1abc2abc3&quot;, regexFirstMatch(&quot;abc1abc2abc3&quot;, &quot;(?:abc\d){2,3}&quot;)); // greediness busted
assertEquals(&quot;abc1abc2&quot;, regexFirstMatch(&quot;abc1abc2abc3&quot;, &quot;(?:abc\d){2,3}?&quot;)); // lazyness
assertEquals(&quot;didnt found match&quot;, regexFirstMatch(&quot;abc1&quot;, &quot;(?:abc\d){2,3}?&quot;));</pre><p>À la ligne 2, le quantificateur paresseux est obligé d&#8217;être exécuté une fois au moins pour matcher, mais il en fait le moins possible.</p><h2>Les quantificateurs possessifs (possessive quantifiers)</h2><p>Les quantificateurs gourmands et paresseux, utilisent intelligement la capacité de backtracking afin d&#8217;évaluer les permutations possible qui permettent de valider l&#8217;expression régulière suivant leur stratégies respectives (en faire le plus ou en faire le moins). Cette propriété permet d&#8217;avoir des expressions assez souples pour matcher un grand nombre de séquence de caractère.</p><p>Cependant <strong>cette souplesse a un coût, le backtracking a un coût en mémoire et en temps CPU</strong>. Ce coût monte suivant la complexité de l&#8217;expression rationnelle et en fonction de la séquence de caractère. Pour des raisons de performance les créateurs des moteurs de regex ont introduit une nouvelle construction qui améliore les performances de votre regex : les quantificateurs possessifs.</p><p>Cette catégorie de quantificateur est un peu différente des deux autres, dans la mesure ou le <strong>backtracking est désactivé</strong>. Ce qui veut dire, si vous avez suivi, que l&#8217;expression régulière ne peut pas revenir en arrière chercher une précédente position ou la regex validait. Cependant il faut noter qu&#8217;un possessive quantifier cherche également à matcher le plus possible.</p><p>Typiquement dans le code suivant :</p><pre class="brush: java; title: ; notranslate">assertEquals(&quot;&lt;h1&gt;will match&lt;/h1&gt;&quot;, regexFirstMatch(&quot;&lt;h1&gt;will match&lt;/h1&gt;&amp;nbsp;&quot;, &quot;&lt;.+&gt;&quot;));</pre><p>La partie de l&#8217;expression régulière &#8220;<strong>.+</strong>&#8221; va tout matcher jusqu&#8217;au point virgule <em>&#8220;;&#8221;</em>. Seulement comme expliqué plus haut, une fois que la String est consommée, le caractère <em>&#8220;<strong>&gt;</strong>&#8220;</em> dans la regex ne peut pas matcher, donc le moteur reviens plusieurs fois sur ses pas, puis ressaye de matcher le &#8220;<strong>&gt;</strong>&#8221; de la regex. Ce comportement peut être désiré dans certains cas, mais parfois si on souhaite juste rechercher quelque chose de spécifique ou valider très vite un texte sans chercher d&#8217;autres combinaisons alors ce n&#8217;est pas l&#8217;idéal.</p><pre class="brush: java; title: ; notranslate">assertEquals(&quot;didnt found match&quot;, regexFirstMatch(&quot;&lt;h1&gt;will match&lt;/h1&gt;&amp;nbsp;&quot;, &quot;&lt;.++&gt;&quot;));</pre><p>Ici l&#8217;expression est constituée d&#8217;un possessive quantifier, et en effet l&#8217;expression ne matche pas parce qu’une fois que la regex a consommée l&#8217;ensemble de la chaîne, et qu&#8217;elle ne peut plus matcher le dernier <em>&#8220;<strong>&gt;</strong>&#8220;</em>, elle se déclare en erreur. On peut voir ça comme une construction du genre <em>fail-fast</em>.</p><p>L&#8217;intérêt véritable des constructions de cette catégorie est intéressante uniquement si les <strong>sections adjacentes de la regex sont mutuellement exclusives</strong>. L&#8217;exemple le plus prégnant est lorsqu&#8217;on utilise un complément avec un quantificateur possessif :</p><pre class="brush: java; title: ; notranslate">assertFalse(Pattern.compile(&quot;&lt;[^&gt;]++&gt;&quot;).matcher(&quot;&lt;property attr1=&quot;blah&quot; ....&quot;).matches());</pre><p>Ici le complément &#8220;<strong>[^&gt;]</strong>&#8221; est naturellement mutuellement exclusif avec le caractère &#8220;<strong>&gt;</strong>&#8220;, ce qui permet à la regex d&#8217;invalider très vite la séquence de caractère. Si on avait utilisé un greedy quantifier, alors le moteur serait revenu en arrière autant de fois que possible pour tenter de valider l&#8217;expression, ce qui est impossible avec la séquence passée en paramètre.</p><p>Exemple à ne pas faire, car les tokens ne sont pas mutuellement exclusifs ; &#8220;<strong>a*+</strong>&#8221; immédiatement suivi d&#8217;un &#8220;<strong>a</strong>&#8220;, du coup la regex ne peut pas matcher car &#8220;<strong>a*+</strong>&#8221; consomme tous les <em>a</em> :</p><pre class="brush: java; title: ; notranslate">assertFalse(Pattern.matches(&quot;\ba*+ab\b&quot;, &quot;aaaaaaab&quot;));</pre><p>Les quantificateurs possessifs sont des constructions qui sont supportées par les dernières générations de moteur de regex, parce qu&#8217;ils sont en réalité des groupes spéciaux. En effet dans la Javadoc de la classe <a
href="http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html">Pattern</a>, on trouve à la fin une partie sur les constructions spéciales, et celle qui nous intéresse dans ce cas, c&#8217;est celle là :</p><ul><li><strong>(?&gt;X)</strong> X, as an <strong>independent, non-capturing group</strong></li></ul><ol><li><em>&#8220;non capturing&#8221;</em> : Simplement parce que le groupe ne fait pas de capture lorsque X matche.</li><li><em>&#8220;independant&#8221;</em> : Ici ce n&#8217;est pas très clair dans la javadoc de Pattern, pour trouver la signification il faut se rendre sur la <a
href="http://perldoc.perl.org/perlretut.html">documentation des regex en Perl</a>, on y apprend qu&#8217;il s&#8217;agit d&#8217;un groupe indépendant du reste de l&#8217;expression régulière, que ce groupe ne sait pas revenir en arrière (pas de backtracking), en gros le moteur de regex permet à ce groupe de consommer tout ce qu&#8217;il peut sans considérer les autres parties de la regex.</li></ol><p>Une petite vérification :</p><pre class="brush: java; title: ; notranslate">assertTrue(Pattern.matches(&quot;\ba*+b\b&quot;, &quot;aaaaaaab&quot;));
assertFalse(Pattern.matches(&quot;\ba*+b\b&quot;, &quot;aaaaaaa&quot;));

assertTrue(Pattern.matches(&quot;\b(?&gt;a*)b\b&quot;, &quot;aaaaaaab&quot;));
assertFalse(Pattern.matches(&quot;\b(?&gt;a*)b\b&quot;, &quot;aaaaaaa&quot;));</pre><p>Donc un quantificateur possessif est une notation simplifiée d&#8217;un groupe indépendant et non capturant!</p><h1>Le backtracking</h1><p>Comme vous le savez, je l&#8217;ai bien répété, le backtracking c&#8217;est ce qui permet au moteur de regex de traquer les constructions qui ont validé. Le backtracking n&#8217;a de sens que pour les quantificateurs, en effet ce sont les quantificateurs qui vont essayer de tester une construction un certain nombre de fois. Cela dit cette construction peut-être suvi par une autre et le moteur doit s&#8217;assurer que les constructions qui suivent le quantificateur valident également le reste de la séquence.</p><p><span
style="text-decoration: underline;">Prenons un exemple :</span></p><p>Dans le cas suivant on le pattern, observez le fait que le point &#8220;<strong>.</strong>&#8221; n&#8217;est pas mutuellement exclusif avec &#8220;<strong>bob</strong>&#8220;.</p><pre class="brush: plain; title: ; notranslate">ab.*bob</pre><p>Et on essaye de valider la chaine de caractères, les chiffres sont là pour illustrer la partie sur la quelle la construction &#8220;<strong>.*</strong>&#8221; devrait matcher, mais des lettres auraient pu faire l&#8217;affaire.</p><pre class="brush: plain; title: ; notranslate">ab1234bob</pre><p>A la première étape <strong>Pattern.compile</strong>, l&#8217;expression va être transformée dans un arbre. Techniquement le code ressemble à la fois au pattern <em>Chain of Responsability</em> et au pattern <em>Composite</em> (pour les groupes ou pour les quantificateurs notamment). Le moteur ajoute ses propres nœud au début et à la fin de l&#8217;arbre pour travailler avec cette représentation.</p><p>Dans le diagramme suivant chaque cadre correspond à l&#8217;état de la consommation de la séquence de caractère et à celui de l&#8217;expression régulière ainsi découpée en nœuds.</p><p
style="text-align: center;"><a
href="http://blog.arkey.fr/wp-content/uploads/2010/10/backtracking.png"><img
class="size-full wp-image-225 aligncenter" title="backtracking" src="http://blog.arkey.fr/wp-content/uploads/2010/10/backtracking.png" alt="" width="397" height="1607" /></a></p><p>On comprend immédiatement le problèmes potentiels sur des expressions qui utilisent énormément les quantificateurs non-possessifs :</p><ul><li>Plus la partie à matchée est longue pour le quantificateur, plus la mémoire sera consommée.</li><li>Si les constructions qui suivent ne matchent pas, celles-ci devront être annulée et réessayée, ce qui veut dire un temps d’exécution plus long!</li></ul><p>La solution c&#8217;est de faire attention quand on construit une expression rationnelle. En particulier si elle est critique, l&#8217;idée serait de la benchmarquer, mais bon il faut pas tomber non plus dans ce qu&#8217;on appelle <strong>Premature Optimisation</strong>.</p><h1>Bilan</h1><p>Le backtracking c&#8217;est bien ; c&#8217;est ce qui permet à la regex d&#8217;être souple, mais clairement il faut faire attention à ce mécanisme. Il sera intéressant du coup d&#8217;utiliser des groupes non-capturants et indépendants si l&#8217;opportunité le permet.</p><p>Cette série s&#8217;achèvera par une troisième et dernière partie ou j’aborderais les possibilité de travailler avec Unicode, et surtout comment indiquer dans une regex qu&#8217;on ne veut pas d&#8217;une construction complète.</p><p><em><br
/> </em></p><h1>Références</h1><ul><li><a
href="http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html">http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html</a></li><li><a
href="http://perldoc.perl.org/perlretut.html">http://perldoc.perl.org/perlretut.html</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2010/10/15/sexprimer-regulierement-partie-2/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>S&#8217;exprimer régulièrement (Partie 1)</title><link>http://blog.arkey.fr/2010/09/27/sexprimer-regulierement-partie-1/</link> <comments>http://blog.arkey.fr/2010/09/27/sexprimer-regulierement-partie-1/#comments</comments> <pubDate>Mon, 27 Sep 2010 14:52:58 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[code]]></category> <category><![CDATA[design]]></category> <category><![CDATA[pattern]]></category> <category><![CDATA[expression régulière]]></category> <category><![CDATA[java]]></category> <category><![CDATA[performance]]></category> <category><![CDATA[regex]]></category> <category><![CDATA[regexp]]></category> <category><![CDATA[regular expression]]></category><guid
isPermaLink="false">http://dutheil.brice.online.fr/blog/?p=202</guid> <description><![CDATA[Il était une fois les expressions régulières Depuis bien longtemps je connais et pratique les expressions régulières, à la fois au moment de coder, mais également dans mes éditeurs de texte, parfois aussi dans le shell, lors d&#8217;un grep par exemple. Bref les expressions régulières sont pratiques dans la vie de tous les jours pour...]]></description> <content:encoded><![CDATA[<h1>Il était une fois les expressions régulières</h1><p>Depuis bien longtemps je connais et pratique les expressions régulières, à la fois au moment de coder, mais également dans mes éditeurs de texte, parfois aussi dans le shell, lors d&#8217;un <a
href="http://www.panix.com/~elflord/unix/grep.html">grep</a> par exemple. Bref les expressions régulières sont pratiques dans la vie de tous les jours pour un ingénieur logiciel.</p><p>Seulement voilà je me suis aussi rendu compte que certains d&#8217;entre nous n&#8217;ont pas une connaissance approfondie des expressions régulières et de leurs arcanes. Effectivement il y a parfois certaines expressions qui sont assez absconses. Et aujourd&#8217;hui les moteurs des expressions régulières dépassent ce le cadre dans lequel ces expressions ont été conçue. Elle permettent certaines constructions qui sont peu connues.</p><h2>Les expressions régulières hier et aujourd&#8217;hui</h2><p>Sans remonter aux origines des expressions régulières -cette partie là est couverte par wikipedia- il est intéressant de noter que les expressions régulières et leur moteur ont bien évoluées en 60ans. En effet le mot <em>régulier</em> vient de l&#8217;état de fait que ces expressions permettaient de rechercher dans des <strong>langages formels</strong> et <strong>non-contextuel</strong>; aujourd&#8217;hui les recherches ont avancées et les moteurs permettent maintenant de dépasser le cadre du langage formel pour permettre de travailler dans l&#8217;espace du <strong>langage contextuel</strong>. Les racines des expressions régulières remontent bien avant l&#8217;avènement de l&#8217;informatique pour aller jusqu&#8217;aux raisonnements complexes de la logique mathématique.</p><p>Il y a une une grosse différence entre un langage non-contextuel et un lange contextuel, dans les faits cette évolution explique pourquoi il y a aujourd&#8217;hui des différences dans les moteurs qui sont intégrés dans les différents programmes (en fonction de la plateforme, des outils, du langages etc.) Aujourd&#8217;hui en Perl, en C#, et en Java nous avons la chance d&#8217;avoir des moteurs qui font partie des dernières générations. C&#8217;est sur cet héritage que je vais disserter, cela dit uniquement dans le cadre de Java et de sa fameuse classe <strong>Pattern</strong>. (<em>Vous remarquerez d&#8217;ailleurs que le moteur est nommé </em>Pattern<em> plutôt que Regex ou quelque chose du genre, l&#8217;explication est simple : cette génération de moteur n&#8217;est plus simplement à propos d&#8217;expression régulière mais donc de <strong>pattern</strong>.</em>) Je tiens aussi à préciser que cet article se concentre sur la création d&#8217;expressions régulières et non sur l&#8217;usage de la classe Pattern.</p><h1>Les différentes constructions</h1><h2>Petit rappel</h2><p>Je passe rapidement sur les bases, j&#8217;imagine que tout le monde connaît les constructions basiques d&#8217;une expression régulière :</p><ul><li>Les classes de caractère <strong>[ ]</strong> et les compléments <strong>[^ ]</strong></li><li>L&#8217;opérateur de Kleene <strong>*</strong></li><li>L&#8217;alternative <strong>|</strong> (le pipe)</li><li>Les autres quantificateurs : <strong>+</strong>, <strong>?</strong>, <strong>{}</strong>, ces quantificateurs ne sont vraiment que des raccourcis de ce qui est déjà exprimable avec les autres constructions, mais ils nous simplifient la vie.</li><li>Les groupes <strong>()</strong></li></ul><p>Globalement pas de surprises ici, avec ses constructions il assez facile d&#8217;écrire l&#8217;expression la plus simple jusqu&#8217;à l&#8217;expression un poil plus élaborée.</p><p>Par exemple pour valider un mail (sans rentrer dans les arcanes de la RFC) on peut avoir ça:</p><pre class="brush: java; title: ; notranslate">@Test
 public void simpleEmailMatch() {
 String regex = &quot;[a-z]+(\.[a-z]+)*@[a-z]+\.[a-z]{2,6}&quot;;

 assertTrue(Pattern.compile(regex).matcher(&quot;brice.dutheil@gmail.com&quot;).matches());
 }</pre><p>Ok, c&#8217;est déjà pas mal, mais si on veut extraire une section d&#8217;un texte ou valider précisément certaines sections d&#8217;un texte, il faut connaitre les constructions un peu plus pointues.</p><h2>Les ancres</h2><p>Les ancres sont rangées dans la javadoc de la classe <a
href="http://download.oracle.com/javase/1.5.0/docs/api/java/util/regex/Pattern.html">Pattern</a> sous la catégorie <strong>Boundary matchers<span
style="font-weight: normal;">. Une ancre identifie juste une position à laquelle elle matche, elle ne consomme pas de caractères dans la séquence traitée.</span></strong></p><h3>Le début et la fin d&#8217;une ligne</h3><p>Généralement les personnes qui ont beaucoup travaillé avec le shell connaissent les deux principales ancres, à savoir le début d&#8217;une ligne <strong>^</strong> et la fin d&#8217;une ligne <strong>$</strong>. Mais il y a une astuce en Java, c&#8217;est que par défaut <strong>^</strong> et <strong>$</strong> repèrent le début et la fin du CharSequence uniquement, pas de notion de saut de ligne!</p><p>Pour s&#8217;en convaincre on écrit un petit test simple qu&#8217;on enrichira d&#8217;assertions, la méthode regexFirstMatch extrait la première section du texte qui matche la regex :</p><pre class="brush: java; highlight: [8,9]; title: ; notranslate">@Test
public void startEndOfLineVsPermanentStartEndOfString() {
  String text = &quot;The account number is :n&quot; +
  &quot;t123456789n&quot; +
  &quot;the client phone number is :n&quot; +
  &quot;t0-987-654-321n&quot;;

  assertEquals(&quot;T&quot;, regexFirstMatch(text, &quot;^.&quot;)); // Tout le temps le début de la séquence
  assertEquals(&quot;1&quot;, regexFirstMatch(text, &quot;.$&quot;)); // Tout le temps la fin de la séquence
}

private String regexFirstMatch(String text, String regex) {
  Matcher matcher = Pattern.compile(regex).matcher(text);
  return matcher.find() ? matcher.group(0) : &quot;&quot;;
}</pre><p>Et ouai, on ne s’attend pas à ça (matche T et 1) surtout quand la description de ces ancres utilise le mot <strong>ligne</strong>. En fait il faut activer l&#8217;option multiligne <strong>Pattern.MULTILINE</strong> dans le moteur, pour que celui-ci identifie les sauts de ligne.</p><p>Ainsi dans le contexte du bout de code du dessus, les lignes suivantes permettent de voire qu&#8217;il s&#8217;agit bien du caractère &#8216;:&#8217; de la première ligne qui est trouvé.</p><pre class="brush: java; highlight: [1,3]; title: ; notranslate">Matcher matcher = Pattern.compile(&quot;.$&quot;, Pattern.MULTILINE).matcher(text);
matcher.find();
assertEquals(&quot;:&quot;, matcher.group(0));</pre><p>Nice, mais il y a encore mieux, le moteur de regex de Java (comme certains autres) permet de donner les options à l&#8217;intérieur de la regex, la javadoc de Pattern donne cette info dans la catégorie <strong>Special constructs (non-capturing)</strong>, celle qui nous intéresse est la construction sur les options pour toute l&#8217;expression.</p><ul><li><strong>(?idmsux-idmsux)</strong> Nothing, but turns match flags on &#8211; off</li></ul><p>Il faut le placer au début de l&#8217;expression régulière, ici <strong>(?m)</strong> :</p><pre class="brush: java; title: ; notranslate">assertEquals(&quot;:&quot;, regexFirstMatch(text, &quot;(?m).$&quot;));</pre><p>On choppe alors bien le caractère à la fin de la première ligne.</p><h3>Le début et la fin d&#8217;une séquence de caractères</h3><p>Dans notre expression si on veut se caler dans tous les cas sur le début et la fin d&#8217;une séquence de caractères, il y a des ancres dédiées <strong>A</strong> et <strong>Z</strong>. Celles-ci ne sont bien entendu pas affectées par l&#8217;option multiligne.</p><pre class="brush: java; title: ; notranslate">assertEquals(&quot;T&quot;, regexFirstMatch(text, &quot;\A.&quot;)); // Tout le temps le début de la séquence
assertEquals(&quot;1&quot;, regexFirstMatch(text, &quot;.\Z&quot;)); // Tout le temps la fin de la séquence
assertEquals(&quot;1&quot;, regexFirstMatch(text, &quot;(?m).\Z&quot;)); // Tout le temps la fin de la séquence</pre><p>Notez quand même qu&#8217;en ce qui concerne le <strong>Z</strong> le dernier caractère de la séquence qui est un séparateur de ligne n&#8217;est pas retourné! Comme indiqué dans la javadoc, cette ancre repère la position avant le dernier caractère séparateur (écrit comme <a
href="http://download.oracle.com/javase/1.5.0/docs/api/java/util/regex/Pattern.html#lt">terminators</a> dans la javadoc).</p><p>Il existe d&#8217;autres ancres, mais elles sont moins utiles, je vous laisse voir par vous même.</p><h2>Les options</h2><p>On a vu qu&#8217;on pouvait activer des options pour une expression régulière, effectivement c&#8217;est assez pratique.</p><p>Les options possibles utilisables à la construction ou dans le pattern sont dans la javadoc, mais les plus intéressantes sont :</p><table
style="border-width: 0px; background-color: #000000;" border="0" cellpadding="1"><tbody><tr><td
style="background-color: #ffffff;"><strong>Option</strong></td><td
style="background-color: #ffffff;"><strong>Flag</strong></td><td
style="background-color: #ffffff;"><strong>Flag à la construction</strong></td></tr><tr><td
style="background-color: #ffffff;">Multi-ligne</td><td
style="background-color: #ffffff;">m</td><td
style="background-color: #ffffff;">Pattern.MULTILINE</td></tr><tr><td
style="background-color: #ffffff;">Insensibilité à la casse</td><td
style="background-color: #ffffff;">i</td><td
style="background-color: #ffffff;">Pattern.CASE_INSENSITIVE</td></tr><tr><td
style="background-color: #ffffff;">Matching de la casse relatif aux règles Unicode</td><td
style="background-color: #ffffff;">u</td><td
style="background-color: #ffffff;">Pattern.UNICODE_CASE</td></tr><tr><td
style="background-color: #ffffff;">Matching des caractère en fonction de leur forme canonique</td><td
style="background-color: #ffffff;"></td><td
style="background-color: #ffffff;">Pattern.CANON_EQ</td></tr></tbody></table><p>Certaines options comme vu dans le tableau n&#8217;ont pas d&#8217;équivalence dans la regex.</p><p>Bon c&#8217;est bien pratique ça, mais parfois on aimerait bien s&#8217;assurer que la casse est ou n&#8217;est pas vérifiée sur une portion de la regex. Il existe une construction qui permet d&#8217;activer/désactiver une option dans une section de l&#8217;expression régulière :</p><ul><li><strong>(?idmsux-idmsux:X)</strong> X, as a non-capturing group with the given flags on &#8211; off</li></ul><p>A peu près la même chose que pour les options avec une portée sur toute la regex, sauf que cette fois, la portion soumise à l&#8217;option changée est à l&#8217;intérieur d&#8217;un <strong>groupe</strong>. Et là vous remarquerez que la javadoc dit bien &#8220;<em>non-capturing</em>&#8221; ça veut dire que la regex ne gardera pas en mémoire le contenu de ce groupe, contrairement aux groupes qui, donc, <em>capturent</em> et sont identifiables par l&#8217;encadrement du groupe par des parenthèses <strong>(X)</strong>.</p><p>Ainsi par exemple si on ne veut pas tenir compte de la casse dans une portion de la regex on écrirait:</p><pre class="brush: java; title: ; notranslate">assertFalse(Pattern.compile(&quot;(?-i)[a-z]+ [a-z]+ [a-z]+&quot;).matcher(&quot;jqsdfkjkd fdfhJGJKGFQSDKjb ckbvg&quot;).matches());
assertTrue(Pattern.compile(&quot;(?-i)[a-z]+ (?i:[a-z]+) [a-z]+&quot;).matcher(&quot;jqsdfkjkd fdfhJGJKGFQSDKjb ckbvg&quot;).matches());</pre><p>Dans la première expression, qui ne marche pas, l&#8217;ensemble de l&#8217;expression est sensible à la casse c&#8217;est l&#8217;option <strong>(?-i)</strong> en début d&#8217;expression. Mais au milieu on voudrait quand même autoriser les majuscules. Pour ce faire on active l&#8217;insensibilité uniquement pour le groupe du milieu <strong>(?i:[a-z]+)</strong>.</p><h2>Les bornes de mot</h2><p>Les bornes de mots sont des ancres de type particulier. Comme n&#8217;importe quelle ancre, ces bornes ne consomment aucun caractère. La borne <strong>b</strong> s&#8217;utilise avant ou après un mot pour marquer le début ou la fin d&#8217;un mot.</p><p>Par exemple en utilisant la classe de caractère <strong>w</strong>.</p><pre class="brush: java; title: ; notranslate">assertTrue(&quot;word&quot;.matches(&quot;\bword&quot;));
assertTrue(&quot;word&quot;.matches(&quot;word\b&quot;));
assertTrue(&quot;word&quot;.matches(&quot;\bword\b&quot;));
assertTrue(&quot;word&quot;.matches(&quot;\b\w+\b&quot;));

assertTrue(&quot;12dsk_&quot;.matches(&quot;\b\w+\b&quot;));

assertTrue(&quot;12dsk;  fdg987&quot;.matches(&quot;\w+\b.*\b\w+&quot;));

assertFalse(&quot;12dsk;   ;:!,:&quot;.matches(&quot;\w+\b.*\b\w+&quot;));

assertTrue(Pattern.compile(&quot;\bes\b&quot;).matcher(&quot;Tu es encore dans ces histoires &quot;).find());
assertFalse(Pattern.compile(&quot;\bes\b&quot;).matcher(&quot;Tu as encore des histoires &quot;).find());</pre><div>Effectivement <strong>b</strong> marque la différence entre une classe de caractère de type lettre par rapport aux classes adjacentes. On remarque néanmoins que s&#8217;il n&#8217;y a donc pas de classes de type caractère avant ou après, la borne fait sauter l&#8217;expression. De la même manière la borne ne fonctionne pas avec une classe de caractère composée de caractères qui sont considérés comme ne faisant pas partie des mots (exemple en ajoutant le tiret à la classe suivante : [0-9a-z-]).</div><div><pre class="brush: java; title: ; notranslate">assertFalse(&quot;12dsk-&quot;.matches(&quot;\w+&quot;));

assertFalse(&quot;12dsk-&quot;.matches(&quot;\w+-\b&quot;));
assertTrue(&quot;12dsk-&quot;.matches(&quot;[0-9a-z-]+&quot;));
assertFalse(&quot;12dsk-&quot;.matches(&quot;[0-9a-z-]+\b&quot;));

assertFalse(&quot;12dsk. &quot;.matches(&quot;\w+.\b.&quot;));</pre></div><div>Évidemment aussi, mettre une borne dans une regex au milieu de caractères ne marchera pas.</div><div><pre class="brush: java; title: ; notranslate">assertFalse(&quot;bobEtLéa&quot;.matches(&quot;bob\b\w+\bLéa&quot;));</pre></div><p>Bon c&#8217;est bien cool, mais si je veux matcher un texte en allemand, du grec ou simplement des lettres accentuées de notre bon français ? Là ça pèche un peu si on utilise le <strong>w</strong>.</p><pre class="brush: java; title: ; notranslate">assertFalse(&quot;Éole&quot;.matches(&quot;\b\w+&quot;));
assertTrue(&quot;Éole&quot;.matches(&quot;\bÉole&quot;));

assertTrue(&quot;Éole&quot;.matches(&quot;\b[Éa-z]+&quot;));
assertTrue(&quot;Éole&quot;.matches(&quot;\b\p{L}+&quot;));</pre><p>En effet la classe <strong>w</strong> ne connait que les caractères ASCII et plus précisément; uniquement ceux de cette classe <strong>[a-zA-Z0-9_]</strong> tel que c&#8217;est mentionné dans la javadoc. Pour palier à cette limitation soit il faut ajouter le caractère accentué à une classe de caractère, soit on utilise une <strong>classe de caractère Unicode</strong>, c&#8217;est ce qui est fait dans la dernière assertion j&#8217;utilise <strong>p{L}</strong> ! Je reviendrais plus tard sur Unicode avec les expressions régulières.</p><p
style="color: #a42713;">Attention à l&#8217;encodage de vos codes source ! J&#8217;ai eu des erreurs d&#8217;encodage du fichier sur Eclipse, IntelliJ et NetBeans qui provenaient de plateformes différentes (MacOSX et Windows), du coup le caractère <strong>É</strong> n&#8217;était pas bien encodé (comprendre que l&#8217;IDE encodait ce caractère dans autre chose qu&#8217;une lettre), ce qui faisait évidement échouer l&#8217;expression.</p><p>Enfin le complément d&#8217;une borne <strong>b</strong> est représenté par la borne <strong>B</strong>, celle-ci matche tout ce que <strong>b</strong> ne matche pas. Dans les faits <strong>B</strong> marque la borne entre deux classes de caractères à l&#8217;exception d&#8217;une classe composée des caractères qu&#8217;on peut trouvé dans <strong>w</strong>.</p><pre class="brush: java; title: ; notranslate">assertTrue(&quot;12dsk-&quot;.matches(&quot;\w+-\B&quot;)); // B capture l'inverse b
assertTrue(&quot;12dsk.&quot;.matches(&quot;\w+\.\B&quot;));
assertTrue(&quot;.!? nt&quot;.matches(&quot;[.!?]+\B\s+&quot;));
assertTrue(&quot;.!?,,,;:&quot;.matches(&quot;[.!?]+\B[,;:]+&quot;));</pre><h1>Fin de la partie 1</h1><p>Voilà pour la première partie, la plus simple, sur les expressions régulières en Java. Pour la suite qui arrive très bientôt j&#8217;exposerai la manière de fonctionner de certaines constructions un peu particulières :  les backreferences, les quantificateurs possessifs, les possibilités de lookahead / lookbehind.</p><h2>Références</h2><ul><li><a
href="http://en.wikipedia.org/wiki/Regular_expression">http://en.wikipedia.org/wiki/Regular_expression</a></li><li><a
href="http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html">http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html</a></li><li><a
href="http://perldoc.perl.org/perlretut.html">http://perldoc.perl.org/perlretut.html</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2010/09/27/sexprimer-regulierement-partie-1/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Lenteur d&#8217;une appli Web sur une JVM IBM</title><link>http://blog.arkey.fr/2010/08/09/lenteur-dune-appli-web-sur-une-jvm-ibm/</link> <comments>http://blog.arkey.fr/2010/08/09/lenteur-dune-appli-web-sur-une-jvm-ibm/#comments</comments> <pubDate>Mon, 09 Aug 2010 15:16:43 +0000</pubDate> <dc:creator>Brice Dutheil</dc:creator> <category><![CDATA[code]]></category> <category><![CDATA[mco]]></category> <category><![CDATA[performance]]></category> <category><![CDATA[prod]]></category> <category><![CDATA[fuite]]></category> <category><![CDATA[memoryleak]]></category><guid
isPermaLink="false">http://dutheil.brice.online.fr/blog/?p=147</guid> <description><![CDATA[Sujet intéressant, les problèmes en production sont quand même pour beaucoup des casse-têtes. Effectivement c&#8217;est bien vrai, les problèmes en production sont difficiles à résoudre. Il y a bien une partie technique souvent absconse, mais ce n&#8217;est pas un travail en solitaire, c&#8217;est un travail d&#8217;équipe. L&#8217;organisation dans la boite favorise ou freine cette opportunité...]]></description> <content:encoded><![CDATA[<p>Sujet intéressant, les problèmes en production sont quand même pour beaucoup des casse-têtes. Effectivement c&#8217;est bien vrai, les problèmes en production sont difficiles à résoudre. Il y a bien une partie technique souvent absconse, mais ce n&#8217;est pas un travail en solitaire, c&#8217;est un travail d&#8217;équipe. L&#8217;organisation dans la boite favorise ou freine cette opportunité de résoudre un problème à travers les différentes équipe impliquées. Dans ce billet je vous fait un petit retour d&#8217;expérience sur un problème de lenteur sur une application web genre &#8220;<em>CRUD</em>&#8221; sur une JVM IBM.</p><h1>Le contexte</h1><p><em>&#8220;The context is King&#8221;</em> disait Andy Hunt dans son livre Pragmatic Learning and Thinking. Alors brève introduction au contexte du problème.</p><ul><li>Application assez simple qui permet de rechercher, consulter, créer, modifier des données différentes issues du domaine métier.</li><li>L&#8217;application tourne sur un WebSphere.</li><li>WebSphere tourne sur une JVM IBM. <em>Ah là ça sent la pêche aux informations, n&#8217;ayant jamais travaillé sur cette JVM, et pas de bol non plus la JVM IBM sur Windows n&#8217;est pas accessible gratuitement depuis le site de IBM.</em></li><li>L&#8217;ensemble tourne sur une machine AIX.</li><li>L&#8217;entreprise utilise l&#8217;outils Introscope qui permet d&#8217;avoir plein de métriques.</li></ul><h1>Le problème</h1><p>Les utilisateurs ressentent des lenteurs, voire des freezes. L&#8217;outil INSC identifie ces threads en <em>Stalled</em>, et permet de donner des métriques sur les temps d&#8217;attentes et de réponses de certains éléments du systèmes, les ingénieurs systèmes utilisent abondamment cet outils. Bref, les temps de réponses vont de quelques dizaines de secondes à plusieurs minutes.</p><p>Il y a définitivement un problème. Forcément quand on a un super outils comme INSC qui a plein de métriques, on se balade dedans pour essayer de trouver le problème. Malheureusement <strong>un seul</strong> thread dump pour voir ce qu&#8217;il se passe dans les threads ne suffit pas. Le thread dump révèle que la plupart des threads sont à l&#8217;état &#8220;<strong>Waiting On Condition</strong>&#8220;. Le super outils INSC indique un usage de la heap, il indique aussi que certaines requêtes SQL sont très très longues, il indique les sessions web ouvertes une petite trentaine, pas de quoi fouetter un chat. Et pourtant il y aurait du CPU assez fortement utilisé, vu depuis Introscope.</p><p>Mais voilà l&#8217;analyse tourne un peu autour du pot. Pourquoi ces threads sont-elles bloquées? Pourquoi les requêtes SQL sont-elles aussi longues?!</p><h1>Le problème dans l&#8217;analyse</h1><p>Récapitulons, dans cette situation les métriques de Introscope ont été regardées, et on a un seul thread dump.</p><p>Introscope malgré ces métriques ne dit pas ou est le problème, il ne dit même pas quel est le type de problème. Les métriques affichées sont pour certaines intéressantes, je pense à l&#8217;identification des threads figées, les temps de réponses de certains composants, la consommation mémoire, et l&#8217;utilisation du CPU.</p><p>Mais pourquoi cet outils n&#8217;a pas aidé à trouver le problème, parce qu&#8217;il ne mesure pas les bonnes choses. Et il faut en particulier comprendre que quand un système fonctionne mal il y a un effet de corrélation qui s&#8217;applique sur un ensemble de variable. Et ce n&#8217;est pas avec Introscope qu&#8217;on va pouvoir identifier la cause du ralentissement généralisé, ni trouver<strong> une relation de cause-à-effet</strong>.</p><blockquote><p>Pour résoudre un problème, il faut s&#8217;équiper avec les bons outils! Il faut aussi regarder les bonnes données, au bon endroit, et au bon moment!</p></blockquote><h1>A la poursuite du vrai problème (partie 1)</h1><p>Bon hop, déjà pour commencer j&#8217;écarte pour le moment Introscope. Et j&#8217;ai un thread dump &#8230; de la JVM de IBM, il va falloir essayer les outils IBM qui permettent de traiter ces informations. Je vais me satisfaire ça pour l&#8217;instant.</p><h2>L&#8217;analyse du thread dump</h2><h3>Avec : IBM Thread and Monitor Dump Analyzer for Java</h3><p>Évidement le format ne correspond pas à celui de Sun, heureusement IBM nous fournit des outils pour analyser ces informations. Direction <a
href="http://www.alphaworks.ibm.com/tech/jca">http://www.alphaworks.ibm.com/tech/jca</a>.</p><p>A l&#8217;ouverture un rapport apparait, il commence par ça en rouge :</p><blockquote><p><span
style="color: #993300;">﻿﻿***WARNING*** Java heap is almost exhausted : 0% free Java heap Please enable verbosegc trace and use IBM Pattern Modeling and Analysis Tool(http://www.alphaworks.ibm.com/tech/pmat) to analyze garbage collection activities. If heapdumps are generated at the same time, please use IBM HeapAnalyzer(http://www.alphaworks.ibm.com/tech/heapanalyzer) to analyze Java heap.</span></p></blockquote><p>Ok, là c&#8217;est assez facile de savoir ou ça va! Mais allons plus loin!</p><p>Il y a également :</p><blockquote><p><strong>Number of Processors : </strong><strong>4</strong><br
/> Java version : J2RE 5.0 <strong>IBM J9</strong> 2.3 AIX ppc64-64 build j9vmap6423-20090707</p><p>Java Heap Information<br
/> <strong>Maximum Java heap size : 384m<br
/> Initial Java heap size : 384m</strong></p></blockquote><p>OK, j&#8217;en apprends un peu plus sur la JVM et la machine.</p><blockquote><p>﻿Free Java heap size: 0 bytes</p><p>Allocated Java heap size: 402 653 184 bytes</p></blockquote><p>Ok évidement tout s&#8217;explique il ne reste plus rien pour allouer dans la Heap.</p><p>Tiens dans la ligne de commande je voit que Introscope est un agent de le JVM:</p><blockquote><pre class="brush: plain; title: ; notranslate">-Xshareclasses:name=webspherev61_%g,groupAccess,nonFatal
-Dibm.websphere.internalClassAccessMode=allow
-Dcom.wily.introscope.agentProfile=/opt/wily/wilyAgent/AvtAgent.profile
-javaagent:/opt/wily/wilyAgent/Agent.jar</pre></blockquote><p><strong>C&#8217;est intéressant, si la JVM est lente, ca peut vouloir dire que les mesures Introscope sont aussi soumises aux lenteurs de la JVM.</strong></p><p>Le rapport est sympa, il donne la répartition de la mémoire de la JVM :</p><blockquote><p>Memory Segment Analysis</p><table
style="background-color: #000000; width: 557px; height: 193px; border: 1px solid #000000;" border="1" cellspacing="1" cellpadding="2"><thead><tr><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Memory Type</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right"># of Segments</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Used Memory(bytes)</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Used Memory(%)</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Free Memory(bytes)</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Free Memory(%)</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Total Memory(bytes)</td></tr></thead><tbody><tr><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Internal</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">13</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">﻿﻿1 191 740</td><td
style="border: 0px solid #000000;" align="right"><span
style="background-color: #ffff00;">88,4</span></td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">156 452</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">11,6</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">1 348 192</td></tr><tr><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Object (reversed)</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">1</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">402 653 184</td><td
style="border: 0px solid #000000;" align="right"><span
style="background-color: #ffff00;">100</span></td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">0</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">0</td><td
style="border: 0px solid #000000;" align="right"><span
style="background-color: #ffff00;">402 653 184</span></td></tr><tr><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Class</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">9 735</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">228 637 344</td><td
style="border: 0px solid #000000;" align="right"><span
style="background-color: #ffff00;">90,96</span></td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">22 724 116</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">9,04</td><td
style="border: 0px solid #000000;" align="right"><span
style="background-color: #ffff00;">251 361 460</span></td></tr><tr><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">JIT Code Cache</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">5</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">41 943 040</td><td
style="border: 0px solid #000000;" align="right"><span
style="background-color: #ffff00;">100</span></td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">0</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">0</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">41 943 040</td></tr><tr><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">JIT Data Cache</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">3</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">17 018 496</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">67,63</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">8 147 328</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">32,37</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">25 165 824</td></tr><tr><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">Overall</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">9 757</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">691 443 804</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">95,71</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">31 027 896</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">4,29</td><td
style="border: 0px solid #000000; background-color: #ffffff;" align="right">722 471 700</td></tr></tbody></table></blockquote><p>Ok, c&#8217;était un tableau intéressant, on voit clairement que dans une JVM il n&#8217;y a pas que de la Heap (pour ceux qui ne le savait pas), en effet on voit donc les sections suivantes (les passages surlignés viennent de moi, malheureusement l&#8217;outil d&#8217;IBM ne nous aide pas là dessus) :</p><ul><li>la mémoire interne de la JVM (les objets internes, les structure de thread, et autres objets natifs) : bonne utilisation</li><li>les objets, en une seule section de mémoire, c&#8217;est la Heap, et là ben effectivement elle utilisée à 100%.</li><li>les sections des classes, c&#8217;est la ou le byte code de vos classes est stocké par la JVM, <strong>mais ce n&#8217;est pas dans la Heap</strong> (chez la JVM de Sun ça correspondrait à la PermGen area), bref là aussi 90% d&#8217;utilisation c&#8217;est plutôt pas mal.</li><li>JIT Code Cache et JIT Data Cache, c&#8217;est là ou la JVM va stocker le code natif qu&#8217;elle aura compilée depuis le bytecode, là aussi c&#8217;est rempli à100% mais c&#8217;est peut-être normal, après tout la taille totale est plus petite.</li></ul><p>On voit aussi que la mémoire accessible dans la Heap est quand même supérieure a ce qui est indiqué dans la ligne de commande, à savoir les 384 MB. Ne connaissant pas la JVM IBM, je ne suis pas certains des raisons induisant ce phénomène.</p><p>Mais à 100% d&#8217;utilisation, ça sent le GC qui s&#8217;excite pour garder ses petits. Mais le rapport est long et n&#8217;est pas terminé, il reste des choses à lire.</p><blockquote><p>Thread Status Analysis</p><table
style="background-color: #000000; border: 1px solid #000000;" border="1" cellspacing="1" cellpadding="2"><tbody><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Status</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Number of Threads : 170</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Percentage</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Deadlock</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Runnable</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">12</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">7 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Waiting on Condition</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">158</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">93 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Waiting On Monitor</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Suspended</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Object.wait()</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Blocked</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Parked</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">0 %</td></tr></tbody></table></blockquote><p>Vous vous souvenez des threads vues en <strong>Wait on Condition</strong> au tout début, on les retrouve donc ici dans les stats du thread dump. Il y a environ 158 thread qui ne font rien et 12 threads qui travaillent. Alors petite parenthèse, qu&#8217;est ce que ça veut dire ce Waiting on Condition. Les raisons peuvent être les suivantes :</p><ul><li>Thread.sleep(), en gros on indique simplement à la thread de ne rien faire, mais c&#8217;est quand même la JVM qui gère ce sleep()</li><li>Object.wait(), en gros quelque part dans le code un thread est en attente pour qu&#8217;une condition se réalise, voire le code en question pour en savoir plus sur la condition. Cette condition peut aussi être une condition interne à la JVM.</li><li>La thread est en train de se synchroniser avec une autre, elle doit donc attendre que l&#8217;autre thread finisse son job, on verra probablement dans la stack un appel à un Thread.join().</li><li>Unsafe.park, et autres support pour les lock</li><li>La thread est blockée par des opérations d&#8217;I/O.</li></ul><p>Déjà ce n&#8217;est pas forcément un problème pour toutes les threads,  typiquement on peut s&#8217;attendre à voir des threads relatives  aux systèmes de cache (ehcache et consorts) qui sont dans ces états. Ensuite il faut comprendre que ce mécanisme implique des conditions internes à la JVM. L&#8217;ordonnanceur (scheduler) de la JVM, qui en réalité fait appel au scheduler de l&#8217;OS, donne la main à d&#8217;autres traitements (java ou pas).</p><p>Le prochain tableau du rapport nous indique ou sont (toutes) nos threads, mais pas d&#8217;analyse par type de statut. On observe bien certaines des raisons citées plus haut.</p><blockquote><p>Thread Method Analysis</p><table
style="background-color: #000000; border: 1px solid #000000;" border="1" cellspacing="1" cellpadding="2"><tbody><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Method Name</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Number of Threads : 170</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">Percentage</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">java/lang/Object.wait(Native Method)</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">88</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">52 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">java/lang/Thread.sleep(Native Method)</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">63</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">37%</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">NO JAVA STACK</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">6</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">4 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">java/net/PlainSocketImpl.socketAccept(<strong>Native Method</strong>)</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">5</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">3 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">java/net/SocketInputStream.socketRead0(<strong>Native Method</strong>)</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">3</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">2 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">﻿com/ibm/misc/SignalDispatcher.waitForSignal(<strong>Native Method</strong>)</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">1</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">1 %</td></tr><tr><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right">d&#8217;autres ligne à 1%&#8230;</td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right"></td><td
style="border-width: 0px; border-style: solid; background-color: #ffffff;" align="right"></td></tr></tbody></table></blockquote><p>Pas mal de thread sont en attente, et beaucoup d&#8217;autres dorment. Quelques threads sans stack Java, ce sont des threads qui appartiennent à la JVM.</p><p>J&#8217;arrête là pour le moment  sur le rapport de cet outils IBM, mais ce qu&#8217;il faut retenir c&#8217;est que le thread dump est utile pour étudier des threads, mais c&#8217;est utile sur un laps de temps, avec un seul cliché on ne se rends pas compte réellement du comportement. Et en plus ces données ne sont pas intéressantes pour savoir ce qu&#8217;il se passe coté gestion mémoire. Heureusement que J9 (la JVM de Mr IBM) fournit quelques infos, sinon je ne vois pas comment diagnostiquer le problème sans jeter un œil sur les données adéquates, c&#8217;est à dire le log du Garbage Collector.</p><h2>L&#8217;analyse du log GC, enfin!</h2><p>Après l&#8217;obtention du fameux log, il faut encore trouver un outil IBM pour analyser le fichier. Les choses deviennent intéressantes. Comme je l&#8217;ai déjà dit, les logs  sont au format IBM, ça ressemble à du XML. Et pour analyser ces logs  rien de mieux que les outils de IBM, non? Google me dit rapidement qu&#8217;il  me faut donc ce truc <strong>Pattern Modeling and Analysis Tool for Java Garbage Collector</strong>, direction <a
href="http://www.alphaworks.ibm.com/tech/pmat">http://www.alphaworks.ibm.com/tech/pmat</a>.</p><h3>Avec : Pattern Modeling and Analysis Tool for Java Garbage Collector</h3><p>Un petit graphique pour regarder ce qu&#8217;il se passe.</p><p><a
href="http://blog.arkey.fr/wp-content/uploads/2010/07/gclog.png"><img
class="alignnone size-full wp-image-148" title="gclog" src="http://blog.arkey.fr/wp-content/uploads/2010/07/gclog.png" alt="" width="869" height="621" /></a></p><p>En rouge l&#8217;usage de la Heap, en bleu le marquage des objets à virer, et en vert les temps de compression de la mémoire. Effectivement le GC à l&#8217;air de bien s&#8217;amuser dans la mémoire, et d&#8217;être appelé assez souvent.</p><p>Bon celà dit je ne suis pas convaincu par l&#8217;outil IBM, il manque des informations que j&#8217;avais vu en texte dans le log GC ; un évènement GC à  cette tête là :</p><pre class="brush: xml; highlight: [1,4,8,17,21]; title: ; notranslate">&lt;af type=&quot;tenured&quot; id=&quot;388571&quot; timestamp=&quot;Jun 21 03:01:11 2010&quot; intervalms=&quot;228.858&quot;&gt;&lt;/pre&gt;
  &lt;minimum requested_bytes=&quot;168&quot; /&gt;
  &lt;time exclusiveaccessms=&quot;0.489&quot; /&gt;
  &lt;tenured freebytes=&quot;0&quot; totalbytes=&quot;402653184&quot; percent=&quot;0&quot; &gt;
    &lt;soa freebytes=&quot;0&quot; totalbytes=&quot;402653184&quot; percent=&quot;0&quot; /&gt;
    &lt;loa freebytes=&quot;0&quot; totalbytes=&quot;0&quot; percent=&quot;0&quot; /&gt;
  &lt;/tenured&gt;
  &lt;gc type=&quot;global&quot; id=&quot;388576&quot; totalid=&quot;388576&quot; intervalms=&quot;230.720&quot;&gt;
    &lt;refs_cleared soft=&quot;0&quot; threshold=&quot;32&quot; weak=&quot;76&quot; phantom=&quot;0&quot; /&gt;
    &lt;finalization objectsqueued=&quot;0&quot; /&gt;
    &lt;timesms mark=&quot;408.282&quot; sweep=&quot;5.061&quot; compact=&quot;0.000&quot; total=&quot;415.341&quot; /&gt;
    &lt;tenured freebytes=&quot;209018360&quot; totalbytes=&quot;402653184&quot; percent=&quot;51&quot; &gt;
      &lt;soa freebytes=&quot;209018360&quot; totalbytes=&quot;402653184&quot; percent=&quot;51&quot; /&gt;
      &lt;loa freebytes=&quot;0&quot; totalbytes=&quot;0&quot; percent=&quot;0&quot; /&gt;
    &lt;/tenured&gt;
  &lt;/gc&gt;
  &lt;tenured freebytes=&quot;209013328&quot; totalbytes=&quot;402653184&quot; percent=&quot;51&quot; &gt;
    &lt;soa freebytes=&quot;209013328&quot; totalbytes=&quot;402653184&quot; percent=&quot;51&quot; /&gt;
    &lt;loa freebytes=&quot;0&quot; totalbytes=&quot;0&quot; percent=&quot;0&quot; /&gt;
  &lt;/tenured&gt;
  &lt;time totalms=&quot;417.431&quot; /&gt;
&lt;/af&gt;</pre><p>Vu l&#8217;allure du log, on a pas l&#8217;impression d&#8217;être sur un GC de type generationnel, mais je ne suis pas encore sûr, c&#8217;est une JVM IBM. Bon revenons à nos moutons:</p><ul><li>il y a eu 230 ms d&#8217;écoulées avant le dernier GC.</li><li> la tenured indique directement qu&#8217;il n&#8217;y a plus de place dans la mémoire,</li><li>on voit que le GC est de type global, ce qui veut dire que c&#8217;est toute la zone mémoire qui est affectée par le GC, c&#8217;est long</li><li>la tenured libère environ 200 MB, soit 51%!</li><li>le temps total mis par ce GC est de 420 ms, c&#8217;est long.</li></ul><p>Et il y a plein d&#8217;entrées comme ça, ça fait beaucoup de GC globaux de 1 demi-secondes, tous les 5ème de secondes. Le GC prends du temps CPU pour nettoyer la mémoire un peu trop souvent. Et ce ci pourrait bien être la cause des ralentissements observés. En gros soit il n&#8217;y a simplement pas assez de mémoire, soit il y a une fuite mémoire.</p><h3>Avec : IBM Support Assistant et Garbage Collection and Memory  Visualizer</h3><p>En me renseignant, je voulais jeter un œil aux outils IBM plus récents, tel que celui mentionné par <a
href="http://www.parisjug.org/xwiki/bin/view/Meeting/20100608">Holly Cummins au ParisJug</a> (le 8 Juin 2010). Pour commencer il faut un compte IBM et télécharger <strong>IBM Support Assistant</strong>, direction <a
href="http://www-01.ibm.com/software/support/isa/">http://www-01.ibm.com/software/support/isa/</a>.</p><p>Après une fois qu&#8217;on a l&#8217;outil, il faut à nouveau télécharger des plugins (nommé <em>additif</em> sur l&#8217;interface en français). Bon en fait le Health Center en me sert à rien puisqu&#8217;il il faut se connecter à une <strong>JVM IBM</strong>, ayant une JVM Sun sur mon poste je ne vais quand même pas aller taper sur la prod si tenté que ce soit possible. Finalement j&#8217;opte pour le plugin : <strong>Garbage Collection and Memory  Visualizer</strong>.</p><p>Donc finalement j&#8217;essaye cet outils d&#8217;analyse, et j&#8217;ai un rapport bien plus sympa et complet avec plein de graphiques qui m&#8217;intéressent.</p><p>Déjà le rapport débute par :</p><blockquote><p>Your application appears to be leaking memory. This is indicated by the used  heap increasing at a greater rate than the application workload (measured by the  amount of data freed). To investigate further see <a
href="http://publib.boulder.ibm.com/infocenter/javasdk/tools/index.jsp?topic=/com.ibm.java.doc.igaa/_1vg00011e17d8ea-1163a087e6c-7ffe_1001.html">Guided  debugging for Java</a></p></blockquote><p>Ok, je m&#8217;en doutais déjà mais c&#8217;est quand même mieux que de dire que la mémoire est quasiment entièrement utilisée. Et on retrouve les alertes suivantes dans le rapport :</p><blockquote><p>The application seems to be using some quite large objects. The largest request  which triggered an allocation failure (and was recorded in the verbose gc log)  was for 5242904 bytes.</p></blockquote><p>5MB quand même! Cela dit ça n&#8217;arrive pas souvent, c&#8217;est peut-être un cache qui charge des données depuis le disque. Le graphe suivant (choisir Object Size dans les templates de graphique sur la droite) montre la taille des allocations demandées.</p><p><a
href="http://blog.arkey.fr/wp-content/uploads/2010/07/object_sizes.jpg"><img
class="alignnone size-full wp-image-156" title="object_sizes" src="http://blog.arkey.fr/wp-content/uploads/2010/07/object_sizes.jpg" alt="" width="823" height="682" /></a></p><p>Mais la concomitance de ses demandes d&#8217;allocation avec l&#8217;utilisation de la heap fait sourciller.</p><p>On continue</p><blockquote><p>Garbage collection is causing some large pauses. The largest pause was 7362 ms.  This may affect application responsiveness. If responsiveness is a concern then  a switch of policy or reduction in heap size may be helpful.</p></blockquote><p>Effectivement le temps passé dans l&#8217;application et le temps passé dans le GC indique manifestement qu&#8217;il y a une suractivité anormale du GC.</p><p><a
href="http://blog.arkey.fr/wp-content/uploads/2010/07/compaction_pauses.jpg"><img
class="alignnone size-full wp-image-157" title="compaction_pauses" src="http://blog.arkey.fr/wp-content/uploads/2010/07/compaction_pauses.jpg" alt="" width="823" height="682" /></a></p><p>En fait on voit même que le GC est en train de compacter la mémoire au moment  de l&#8217;incident, c&#8217;est la courbe rouge clair (entre ~0.5s et 1s), ajouté à cela le temps de marquage des objets à virer (<em>bon en fait dans le graphique que j&#8217;ai fait, le temps de pause est principalement du au temps de marquage</em>), l&#8217;ensemble donnant un temps de pause pour laisser le GC travailler allant de 1 à 7s (par GC bien évidement).</p><p>Et là effectivement expliquer les ralentissements de l&#8217;application devient plus facile, merci à ces beaux graphiques explicites.</p><p>On peut regarder vraiment beaucoup de chose avec cet outils, même s&#8217;il y a des défauts manifestes dans l&#8217;interface utilisateur. C&#8217;est quand même plutôt pas mal.</p><p>Je termine sur un petit résumé des valeurs intéressantes que nous donne cet outils.</p><blockquote><table
style="width: 400px; height: 200px; background-color: #000000; border: 1px solid #000000;" border="1" cellspacing="1" cellpadding="2"><tbody><tr><td
style="background-color: white; border-width: medium; border-style: none;">Allocation failure count</td><td
style="background-color: white; border-width: medium; border-style: none;">59971</td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">Forced collection count</td><td
style="background-color: white; border-width: medium; border-style: none;">3</td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">GC Mode</td><td
style="background-color: white; border-width: medium; border-style: none;"><span
style="background-color: #ffff99;">optthruput</span></td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">Largest memory request (bytes)</td><td
style="background-color: white; border-width: medium; border-style: none;">5242904</td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">Mean garbage collection pause (ms)</td><td
style="background-color: white; border-width: medium; border-style: none;">491</td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">Mean heap unusable due to fragmentation (MB)</td><td
style="background-color: white; border-width: medium; border-style: none;">0.2</td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">Mean interval between collections (minutes)</td><td
style="background-color: white; border-width: medium; border-style: none;"><span
style="background-color: #ffff99;">0.01</span></td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">Number of collections</td><td
style="background-color: white; border-width: medium; border-style: none;">59974</td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">Proportion of time spent in garbage collection pauses  (%)</td><td
style="background-color: white; border-width: medium; border-style: none;"><span
style="background-color: #ffff99;">58.24</span></td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">Proportion of time spent unpaused (%)</td><td
style="background-color: white; border-width: medium; border-style: none;"><span
style="background-color: #ffff99;">41.76</span></td></tr><tr><td
style="background-color: white; border-width: medium; border-style: none;">Rate of garbage collection (MB/minutes)</td><td
style="background-color: white; border-width: medium; border-style: none;"><span
style="background-color: #ffff99;">13250</span></td></tr></tbody></table></blockquote><p>Tiens le mode GC est optthruput, en fait c&#8217;est une des polices du comportement du GC, et probablement de la manière de segmenter la mémoire (Nursery (Young), Old (Tenured)).</p><p>En effet dans les logs GC, je n&#8217;ai pratiquement vu que des GC globaux et uniquement sur la section de la tenured, à priori pas de zone nursery, c&#8217;est probablement du à ce comportement du GC.</p><p>En me renseignant donc, il y a 4 polices de GC dans la JVM J9 de IBM :</p><ul><li><strong>optthruput</strong> : Optimisé pour throughput (le débit), flat heap <strong>&lt;= Bingo</strong></li><li><strong>optavgpause</strong> : Optimisé pour les temps de pause (Stop-The-World), le CMS est configuré pour prendre le moins de temps, flat heap</li><li><strong>subpool</strong> : Un police optimisé pour les machine multi-processeur, flat heap</li><li><strong>gencon</strong> : C&#8217;est le GC générationnel, qui est divisé en zone<ul><li>nursery : qui permet la collection rapide et efficace des objets de vie courte, pas de pause</li><li>tenured : zone des vieux objets, mais un GC dans cette zone est global et demande à pauser l&#8217;application</li></ul></li></ul><h1>A la poursuite du vrai problème (partie 2)</h1><h2>La pèche aux informations</h2><p>Après avoir passé le GC au mode générationnel, il y a toujours ces problèmes de lenteurs mais ce n&#8217;est plus généralisé à toute l&#8217;appli, pas de log GC pour vérifier mais Introscope indique une utilisation relativement correcte de la mémoire, bizarre. Back to basics!</p><p>Le thread dump de la JVM IBM me dit toujours que la Heap est utilisée à 100%, mais je vois quand même</p><blockquote><p>﻿Free Java heap size: 72 041 864 bytes</p><p>Allocated Java heap size: 402 653 184 bytes</p></blockquote><p>Et plus loin :</p><blockquote><p>Last Garbage Collection Detail</p><p>Nursery Area Free : 59 307 392 bytes Total : 60 397 568 bytes 98 % free<br
/> Tenured Area Free : 17 058 368 bytes Total : 335 544 320 bytes 5% free<br
/> Global Garbage Collector Counter : 148</p></blockquote><p>La tenured est bien remplie et utilise un très grosse partie de la heap ; memory leak ou beaucoup d&#8217;objet à mettre en cache. Ou encore autre chose, sans mesures claires pour écarter les hypothèses ces dur.</p><p>Pour quoi ne pas <strong>activer dans tous les cas le log GC</strong>, la JVM IBM offre des option pour gérer la rotation des logs GC, comme ça l&#8217;argument de saturation du disque tombe à l&#8217;eau. Mais bon il faut lire la documentation; donc petit passage chez IBM grâce à Google, et hop :</p><blockquote><pre class="brush: plain; title: ; notranslate">-Xverbosegclog[:&lt;file&gt;[,&lt;X&gt;,&lt;Y&gt;]]</pre></blockquote><p>Et voilà : <strong>file</strong> étant le couple chemin + fichier, <strong>X</strong> le nombre de fichier maximum (ça tourne et écrase les fichiers), <strong>Y</strong> le nombre de cycle GC. Ce que ne dis pas par contre la doc IBM c&#8217;est la taille approximative d&#8217;un GC, donc 700 cycles de GC ≃ 1 MB. Il est même possible d&#8217;utiliser des tokens utilisés pour les dumps dans WAS 7 (voir <a
href="http://www-01.ibm.com/support/docview.wss?rs=180&amp;context=SSEQTP&amp;dc=DB560&amp;dc=DB520&amp;uid=swg21384096&amp;loc=en_US&amp;cs=UTF-8&amp;lang=en&amp;rss=ct180websphere">ici</a>).</p><h2>Recoupement des informations</h2><p>Le log du Garbage Collector ne venant pas, il faut chercher autrement. Je demande au moment ou le problème se reproduit , de faire plusieurs thread dump d&#8217;affilé séparé de quelques secondes (~20s) et de faire également un listing des sous-processus java.</p><p>En effet le thread dump est bien sympa, mais il ne donne pas la consommation CPU des threads.</p><p>Dans un environnement il faut entre dans le terminal (Dans Linux l&#8217;identifiant des thread est dans la colonne LWD.) :</p><blockquote><pre class="brush: plain; title: ; notranslate">ps -fLp &lt;processid&gt; -L</pre></blockquote><p>Evidement il s&#8217;agit d&#8217;un AIX et les commendes sont différents, pas de soucis un petit tour dans la doc IBM et il faut entrer la commande suivante, et là l&#8217;identifiant de la thread est dans la colonne TID :</p><blockquote><pre class="brush: plain; title: ; notranslate">ps -mp &lt;processid&gt; -o THREAD</pre></blockquote><p>On a alors un listing énorme, que j&#8217;ai tronqué ici.</p><blockquote><pre class="brush: plain; highlight: [2,8]; title: ; notranslate">USER    PID   PPID       TID S  CP PRI SC    WCHAN        F     TT BND COMMAND
wasadmin 393262 401580         - A 188  60 203        *   202001      -   - /opt/was61/java/bin/java ...
 -      -      -    700535 S   0  82  1 f100070f1000ab40  8410400      -   - -
 -      -      -    741581 S   0  82  1 f100070f1000b540  8410400      -   - -
 -      -      -    802997 S   0  82  1 f100070f1000c440  8410400      -   - -
 -      -      -    884895 S   0  82  1 f100070f1000d840  8410400      -   - -
 -      -      -   1183791 Z   0  82  1         -       c00001         -   - -
 -      -      -   1667157 R  60 122  0            -    400000       -   - -
 -      -      -   1708269 S   0  82  1 f100070f1001a140  8410400      -   - -
 -      -      -   1736831 S   0  82  1 f100070f1001a840  8410400      -   - -</pre></blockquote><p>La colonne CP me dit que manifestement la thread <strong>1667157</strong> utilise plutôt pas mal le CPU, qu&#8217;est-ce que donne cette thread du coté du thread dump ?! Au fait on repère 3 threads dans le même cas.</p><p>Il faut savoir que dans le thread dump il y a l&#8217;identifiant de la thread en Java, mais qu&#8217;il y a aussi et surtout de mentionné l&#8217;identifiant natif de la thread, par exemple <strong>NID</strong>.</p><p>Je google &#8220;<strong>1667157 in hex</strong>&#8221; ce qui me renvoie <strong>0&#215;197055</strong>. En utilisant l&#8217;outils IBM mentionné plus haut, on voit clairement que la thread en cause correspond à du code métier, développé ici.</p><p><a
href="http://blog.arkey.fr/wp-content/uploads/2010/08/tdump-cause2.png"><img
class="alignnone size-full wp-image-200" title="tdump-cause" src="http://blog.arkey.fr/wp-content/uploads/2010/08/tdump-cause2.png" alt="" width="1103" height="700" /></a></p><p>Chacune des 3 threads passent dans le même bout de code. Autant au début j&#8217;ai des doutes, après toute la présomption d&#8217;innocence compte aussi pour le code, d&#8217;autant plus qu&#8217;il s&#8217;agit d&#8217;un code lent qui utilise beaucoup de reflection. Mais faut prendre en compte aussi le fait que la pile descend à chaque fois dans la couche Hibernate, ça vaut le coup d&#8217;aller voir. Les développeurs qui connaissent un peu mieux le code poussent dans cette direction.</p><p>Entre temps les DBA confirme que la base de données réponds très bien, mais qu&#8217;elle enregistre un très fort nombre d&#8217;un certain type de requête SQL.</p><p>Bingo, il y a une race condition dans une des boucles, et celle-ci part en boucle infinie. Ceci explique la très forte utilisation de la mémoire et les lenteurs remarquées.</p><h1>Bilan</h1><p>Un problème peut en cacher un autre, ou plus exactement un problème peut en provoquer d&#8217;autres. Il faut juste avoir des moyens de mesurer les changements qu&#8217;on apporte si on veut isoler / écarter des catégorie de problèmes.</p><p>L&#8217;outillage on l&#8217;a vu est essentiel, Introscope apporte des choses, mais il ne permet pas tout. Qui plus est, on ne sait pas précisément ce qu&#8217;il mesure et ou! Les temps de réponses SQL, n&#8217;étaient par exemple pas crédible, car Introscope mesurait également les GC.</p><p>Dans notre cas ici, j&#8217;aurais aussi bien aimé avoir une JVM IBM sur mon poste histoire de jouer plus facilement avec. C&#8217;est dommage que IBM ne fournisse pas gratuitement sa JVM au moins pour le développement.</p><p>Accessoirement ce serait bien un jour d&#8217;avoir des format de log normalisé entre les JVM, ainsi que certaines des options afférentes.</p><p>Finalement ce qui a pris le plus de temps était d&#8217;obtenir les bonnes données, pour prendre les meilleurs choix. L&#8217;impression de travailler les mains dans le noir n&#8217;était pas l&#8217;idéal pour résoudre le problème, mais c&#8217;est au moins formateur. Je peux dire que j&#8217;ai bien apprécié certains retours et recommandations des  développeurs. L&#8217;équipe système étant surchargée n&#8217;a pas pu nous donné un support optimal, et cette carence s&#8217;est ressentie notamment pour avoir les données à temps. Mais leur vu du problème a permis d&#8217;orienter la recherche sur les parties qui pouvait poser problème.</p><h1>Références &amp; Documentation</h1><ul><li><a
href="http://websphere.sys-con.com/node/921279?page=0,1">http://websphere.sys-con.com/node/921279?page=0,1</a></li><li><a
href="http://sites.google.com/site/threaddumps/java-thread-dumps">http://sites.google.com/site/threaddumps/java-thread-dumps</a></li><li><a
href="http://java.sun.com/developer/technicalArticles/Programming/Stacktrace/">http://java.sun.com/developer/technicalArticles/Programming/Stacktrace/</a></li><li><a
href="http://geekexplains.blogspot.com/2008/07/threadstate-in-java-blocked-vs-waiting.html">http://geekexplains.blogspot.com/2008/07/threadstate-in-java-blocked-vs-waiting.html</a></li><li><a
href="http://www.ibm.com/developerworks/java/library/j-nativememory-aix/">http://www.ibm.com/developerworks/java/library/j-nativememory-aix/</a></li><li><a
href="http://www.ibm.com/developerworks/ibm/library/i-garbage1/">http://www.ibm.com/developerworks/ibm/library/i-garbage1/</a></li><li><a
href="http://www-01.ibm.com/support/docview.wss?rs=180&amp;context=SSEQTP&amp;dc=DB560&amp;dc=DB520&amp;uid=swg21384096&amp;loc=en_US&amp;cs=UTF-8&amp;lang=en&amp;rss=ct180websphere">http://www-01.ibm.com/support/docview.wss?rs=180&amp;context=SSEQTP&amp;dc=DB560&amp;dc=DB520&amp;uid=swg21384096&amp;loc=en_US&amp;cs=UTF-8&amp;lang=en&amp;rss=ct180websphere</a></li><li><a
href="http://publib.boulder.ibm.com/infocenter/javasdk/v6r0/index.jsp?topic=/com.ibm.java.doc.diagnostics.60/diag/appendixes/cmdline/cmdline_gc.htm">http://publib.boulder.ibm.com/infocenter/javasdk/v6r0/index.jsp?topic=/com.ibm.java.doc.diagnostics.60/diag/appendixes/cmdline/cmdline_gc.htm</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://blog.arkey.fr/2010/08/09/lenteur-dune-appli-web-sur-une-jvm-ibm/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>
<!-- Served from: blog.arkey.fr @ 2012-03-05 02:30:21 by W3 Total Cache -->
