<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="http://davidmasclet.gisgraphy.com/feed/rss2/xslt" ?><rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>Blog de David MASCLET</title>
  <link>http://davidmasclet.gisgraphy.com/</link>
  <atom:link href="http://davidmasclet.gisgraphy.com/feed/rss2" rel="self" type="application/rss+xml"/>
  <description>Solutions aux problèmes fréquents de développeur de projets open source (et autres)</description>
  <language>fr</language>
  <pubDate>Tue, 09 Mar 2010 13:52:13 +0100</pubDate>
  <copyright></copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>Détecter et corriger les fuites mémoire java</title>
    <link>http://davidmasclet.gisgraphy.com/post/2010/02/05/D%C3%A9tecter-et-corriger-les-fuites-m%C3%A9moire-java</link>
    <guid isPermaLink="false">urn:md5:65a9815b7b4f6e02789a20fd82c50d9b</guid>
    <pubDate>Mon, 01 Mar 2010 09:16:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Java / J2EE</category>
        <category>Fuites mémoire</category><category>java</category>    
    <description>&lt;p&gt;Ceux qui ont déjà développé en C, puis en Java auront probablement apprécié
la gestion de la mémoire par la JVM. En effet, Java masque la complexité de la
gestion de la mémoire aux développeurs. Cette tache est déléguée au
&lt;code&gt;garbage Collector&lt;/code&gt;. Cependant même si cette gestion est dite
'automatique', cela ne permet pas de faire n'importe quoi. Que ce soit en C,
Java, ou un autre langage, la mémoire est une ressource limitée ! En java
lorsque la JVM n'a plus assez de mémoire pour s'exécuter, elle génère une
Erreur &lt;code&gt;java.lang.OutOfMemoryError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Plusieurs cas sont alors possible :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Soit vous n'avez pas alloué assez de mémoire à la JVM&lt;/li&gt;
&lt;li&gt;Soit votre code a ce qu'on appelle un fuite mémoire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si vous êtes dans le premier cas, les choses se résolvent assez
facilement : il vous suffit d'allouer plus de mémoire grâce aux paramètres
&lt;code&gt;-Xmx&lt;/code&gt;, &lt;code&gt;-Xms&lt;/code&gt; et &lt;code&gt;-XX:PermSize&lt;/code&gt; (ou de
modifier le code pour qu'il gaspille moins de mémoire). Dans le deuxième cas
vous allez devoir trouver où se situe la fuite mémoire. Si vous avez utilisé
des logiciels pour vous y aider, vous avez peut être été perdu par toutes les
possibilités qu'ils offrent et par la profusion d'informations qu'il
fournissent.&lt;/p&gt;
&lt;p&gt;Le but de ce billet est d'expliquer comment détecter et corriger les fuites
mémoires avec Eclipse Memory Analyser Tool un outil gratuit et très utile qui
aide vraiment à trouver les fuites mémoires. Nous allons voir comment extraire
les informations pertinentes, les comprendre, et trouver les responsables des
fuites mémoire en ayant une approche pragmatique. Je vous propose de nous
appuyer sur du code simple (disponible en fichier attaché à ce billet) générant
une fuite mémoire.&lt;/p&gt;    &lt;h3&gt;La gestion de la mémoire en Java&lt;/h3&gt;
&lt;p&gt;La JVM dispose de plusieurs zones mémoire distinctes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La pile (ou heap), est l'espace mémoire où tous les objets et tableaux sont
stockés. Lorsque la JVM démarre, la pile est initialisée à la valeur spécifiée
par &lt;code&gt;-Xms&lt;/code&gt; et elle grandira dynamiquement quand les objets seront
crées, jusqu'à ce qu'elle atteigne sa taille maximale spécifiée par le
paramètre &lt;code&gt;-Xmx&lt;/code&gt;. Si la taille maximale est atteinte, et que plus de
mémoire est nécessaire, la JVM générera une &lt;code&gt;java.lang.OutOfMemoryError:
Java heap space&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Permgen space : il s'agit de l'espace mémoire qui est alloué pour le
chargement des classes (pas les objets). A la différence de la pile, la taille
de cette région est fixe et ne varie plus après le démarrage de la JVM. Pour la
JVM de Sun, la taille par défaut du permGen space est de 64M. Vous pouvez
changer cette valeur grâce au paramètre &lt;code&gt;-XX:PermSize&lt;/code&gt;. Rappelons
tout de même que les options commençant par &lt;code&gt;XX&lt;/code&gt; ne sont pas des
paramètres HotSpot stables et que leurs compatibilités dépendent du vendeur et
de la version de la JVM. Si la mémoire du permgen est insuffisante une
&lt;code&gt;java.lang.OutOfMemoryError: PermGen space&lt;/code&gt; sera renvoyée&lt;/li&gt;
&lt;li&gt;il existe un troisième type d'erreurs : les erreurs natives. Elles
apparaissent lorsque le système hôte ne peut plus fournir de mémoire (plus
assez de place en mémoire vive, ou plus assez de swap). Dans ce cas la JVM
génère une Erreur &lt;code&gt;java.lang.OutOfMemoryError: request &amp;lt;size&amp;gt; bytes
for &amp;lt;reason&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cas des champs statiques : Les champs statiques ne peuvent être garbage
collectés tant que la classe qui les référence est chargée. il ne peuvent être
garbage collectés que lorsque le class loader qui est responsable du chargement
de la classe est lui même garbage collecté.&lt;/p&gt;
&lt;p&gt;Chaque instance de classe a une taille en mémoire, cette taille appelée
&amp;quot;Shallow Heap&amp;quot; est constituée de types primitifs (int, char, ...). Une instance
peut avoir également des références sur d'autres objets qui ont eux même une
taille. Les objets ainsi liés entre eux constituent un graphe.&lt;/p&gt;
&lt;p&gt;Tous les graphes disposent d'une racine d'attache, ces racines peuvent être,
par exemple, des threads ou des class loaders qui tiennent des références sur
les attributs statiques. Ces racines se nomment en anglais des &amp;quot;GC root&amp;quot;.
Toutes les instances en mémoire finissent par être attachées à une racine, si
ce n'est pas le cas, le Garbage Collector peut supprimer ces instances du
graphe, et libérer de la mémoire.&lt;/p&gt;
&lt;p&gt;Les références peuvent être de plusieurs types. Il y avait les références
fortes &amp;quot;StrongReference&amp;quot; et depuis java 5 il existe des références dites douces
&amp;quot;SoftReference&amp;quot; : L'objet est référencé, mais si la JVM a besoin de
mémoire, elle peut libérer cet objet. On trouve également les références
légères &amp;quot;WeakReference&amp;quot;: L'objet restera en mémoire, tant qu'il existe une
référence forte ou douce sur lui. enfin il existe des
&lt;code&gt;PhantomReference&lt;/code&gt; : les objets peuvent être supprimés de la
mémoire si aucune référence forte, douce, ou légère existe sur cette objet.&lt;/p&gt;
&lt;h3&gt;Empreinte de la mémoire (Heap dump)&lt;/h3&gt;
&lt;p&gt;Afin de permettre des diagnostiques et de voir ce que contient la mémoire de
la JVM, il est possible de générer une &amp;quot;empreinte de la mémoire&amp;quot; qui correspond
à tous les objets chargés à un instant précis. Ces empreintes sont plus connus
sous le nom de &amp;quot;Heap dump&amp;quot;.&lt;/p&gt;
&lt;p&gt;Ces dumps contiennent :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tous les objets : champs, références, et objet primitifs.&lt;/li&gt;
&lt;li&gt;Toutes les classes : Class loader, super Class, champs statiques&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En revanche il ne contient pas les informations sur qui et où sont crées les
objets, ni quels sont les objets qui ont déjà été garbage collectés.&lt;/p&gt;
&lt;p&gt;Il existe différentes méthodes pour générer ces dumps. Soit en spécifiant
les options &lt;code&gt;-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/dump.hprof&lt;/code&gt; au lancement de la JVM afin qu'un dump
soit généré lorsqu'une &lt;code&gt;java.lang.OutOfMemoryError&lt;/code&gt; survient. Notez
toutefois que comme toutes les options commençant par &lt;code&gt;XX&lt;/code&gt;, elles
sont dépendantes du vendeur de JVM. La deuxième solution est de faire un dump à
la demande, à l'aide du &lt;code&gt;JDK 6&lt;/code&gt; et de &lt;code&gt;JConsole&lt;/code&gt;. les
dumps à la demande sont utiles, par exemples, après des benchs.&lt;/p&gt;
&lt;p&gt;Voici la procédure :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lancez &lt;code&gt;JConsole&lt;/code&gt; situé dans le répertoire &amp;quot;bin&amp;quot; de la JVM&lt;/li&gt;
&lt;li&gt;Sélectionnez le processus Java pour lequel vous voulez faire le dump.
Vérifiez sur l'onglet &amp;quot;VM summary&amp;quot; que c'est bien le bon processus.&lt;/li&gt;
&lt;li&gt;Rendez vous sur l'onglet Mbeans&lt;/li&gt;
&lt;li&gt;Sélectionnez l'item com.sun.management / hotspot diagnostic / operation /
dumpheap&lt;/li&gt;
&lt;li&gt;Spécifiez le chemin complet du fichier où vous voulez stocker le dump dans
le premier champs de saisie (laissez le deuxième champs à true). Les dumps ont
souvent l'extension hprof, et MAT ne verra pas le fichier s'il n'a pas la bonne
extension.&lt;/li&gt;
&lt;li&gt;Validez en cliquant sur le bouton &amp;quot;dumpHeap&amp;quot;. Cela figera l'exécution de la
JVM (donc à éviter en production), exécutera un garbage collector, et générera
un fichier binaire avec le contenu de la mémoire.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.jconsole_m.jpg&quot; alt=&quot;jconsole&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;jconsole, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Qu'est ce qu'une fuite mémoire&lt;/h3&gt;
&lt;p&gt;Une fuite mémoire est une dérive non contrôlée de l'utilisation de la
mémoire. elle est souvent due à un objet qui garde des références à une
multitude d'autres, ce qui empêche le garbage collector de les enlever de la
mémoire. Cette dernière grandit alors de plus en plus et finit par saturer.&lt;/p&gt;
&lt;p&gt;Pour comprendre une fuite mémoire il faut connaitre quelques termes (en
anglais):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dominator tree : Liste des plus gros objets et de qui les
référencent.&lt;/li&gt;
&lt;li&gt;Leak suspect : Objet ayant une grosse taille dans le dominator
tree.&lt;/li&gt;
&lt;li&gt;Accumulation point : Gros objet référençant beaucoup d'autres petits
objets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voici un représentation graphique des termes évoqués ci dessus :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.dominator_tree_m.jpg&quot; alt=&quot;IMAGE&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;dominator tree, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Shallow Heap et retained Heap : il s'agit de la taille que prend un
objet sur la pile. &amp;quot;shallow heap&amp;quot; représente la taille des éléments de l'objet
sans inclure les objets qu'il référence, tandis que &amp;quot;retained Heap&amp;quot; les prend
en compte : elle représente la taille qui sera libéré si cet objet est
garbage collecté. Voici un exemple pour mieux comprendre :&lt;/p&gt;
&lt;p&gt;Ci dessous ce trouve la représentation interne d'une string que j'ai pu
obtenir avec le mode debug d'Eclipse :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/retained_size.jpg&quot; alt=&quot;retained size&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;retained size, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;On voit qu'une string est représentée par trois entiers : count, hash,
et offset, qui représente la shallow heap, ainsi qu'une référence &amp;quot;value&amp;quot; à un
tableau de char&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/02/05/&quot;&gt;&lt;/a&gt; . count, hash, offset et value représente la
retained heap car le char&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/02/05/&quot;&gt;&lt;/a&gt; sera également garbage collecté si la
string l'est aussi.&lt;/p&gt;
&lt;h3&gt;Eclipse Memory Analyser Tool&lt;/h3&gt;
&lt;p&gt;Il existe déjà des outils permettant d'analyser les dumps, mais ils sont
soit difficiles d'approche comme &lt;a href=&quot;http://java.sun.com/javase/6/docs/technotes/tools/share/jhat.html&quot; hreflang=&quot;en&quot;&gt;jhat&lt;/a&gt; (Java Heap Analysis Tool) et jmap soit il sont payants comme
Jprofiler.&lt;/p&gt;
&lt;p&gt;Eclipse a développé un outil permettant d'analyser les heap dumps. Cela
permet d'aider à trouver où se situent les fuites mémoires et de détecter les
gaspillages et améliorations possibles de l'utilisation de la mémoire. L'outil
ne se contente pas de vous analyser la mémoire, il cherche également où peuvent
se situer les fuites ainsi que les optimisations. c'est là le point fort de cet
outil.&lt;/p&gt;
&lt;p&gt;MAT peut être installé en tant que plugin dans Eclipse, ce qui a l'avantage
de pouvoir regarder le code en même temps que de regarder le dump, ou en tant
que client lourd (RCP) à part entière, des scripts sont alors disponibles en
plus du plugins. Peu importe votre choix, vous pouvez vous rendre sur &lt;a href=&quot;http://www.eclipse.org/mat/downloads.php&quot; hreflang=&quot;en&quot;&gt;cette page&lt;/a&gt; pour le
télécharger.&lt;/p&gt;
&lt;h3&gt;Utilisation de MAT avec un code d'exemple&lt;/h3&gt;
&lt;p&gt;Je vous propose de tester MAT avec du code simple (disponible en fichier
attaché à ce billet) générant une fuite mémoire. Le code représentera plusieurs
objets métiers : une entreprise, des salariés, et des adresses, ainsi
qu'une classe permettant de générer quelques instances de ces objets et une
&lt;code&gt;HashMap&lt;/code&gt; statique servant de cache pour les objets
&lt;code&gt;Entreprise&lt;/code&gt;. Enfin une fois tous ces objets chargés, je provoquerai
une fuite mémoire en remplissant un tableau avec des &lt;code&gt;Double&lt;/code&gt;. Je
dis bien &amp;quot;provoquerai&amp;quot;, car à la différence des Exceptions, les Errors sont
générées par la JVM et non par le code : si je fais&lt;/p&gt;
&lt;pre&gt;
throw new OutOfMemoryError();
&lt;/pre&gt;
&lt;p&gt;et que je spécifie les options &lt;code&gt;HeapDumpOnOutOfMemoryError&lt;/code&gt; le
dump ne sera pas généré.&lt;/p&gt;
&lt;p&gt;je lance donc la classe Application avec les options
&lt;code&gt;-XX:+HeapDumpOnOutOfMemoryError -Xms5m -Xmx10m
-XX:MaxPermSize=256m&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;En l'absence de &lt;code&gt;HeapDumpPath&lt;/code&gt;, c'est un fichier avec le PID qui
sera généré, l'avantage étant qu'un nouveau fichier de dump sera généré à
chaque &lt;code&gt;java.lang.OutOfMemoryError&lt;/code&gt; (il ne sera pas écrasé puisque
le nom sera différent à chaque fois)&lt;/p&gt;
&lt;p&gt;Le lancement du programme me donne la sortie suivante :&lt;/p&gt;
&lt;pre&gt;
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid2331.hprof ...
Heap dump file created [13486084 bytes in 0.401 secs]
Exception in thread &amp;quot;main&amp;quot; java.lang.OutOfMemoryError: Java heap space
        at java.util.ArrayList.ensureCapacity(ArrayList.java:169)
        at java.util.ArrayList.add(ArrayList.java:351)
        at com.demo.mat.OutOfMemoryHelper.addDouble(OutOfMemoryHelper.java:18)
        at com.demo.mat.OutOfMemoryHelper.throwOutOfMemoryError(OutOfMemoryHelper.java:13)
        at com.demo.mat.Application.main(Application.java:74)

&lt;/pre&gt;
&lt;p&gt;Il ne reste plus qu'a analyser le dump. vous pouvez l'ouvrir directement en
lançant MAT, puis en allant dans &lt;code&gt;File-&amp;gt;Open Heap Dump&lt;/code&gt; et en
sélectionnant le fichier java_pid2331.hprof. (seuls les fichiers .hprof
apparaitrons). MAT générera alors plusieurs indexes et un rapport HTML. Les
indexes permettent de naviguer plus rapidement car le format hprof n'est pas
très exploitable en l'état. Cette génération n'aura lieu qu'une seul fois et
lorsque vous rouvrirez ce dump, Mat utilisera les fichier déjà générés.&lt;/p&gt;
&lt;p&gt;Dans notre cas le fichier sera de 10 méga octets (environ taille du Xmx),
mais si vous voulez analyser de gros fichiers je vous conseille d'utiliser la
ligne de commande:&lt;/p&gt;
&lt;pre&gt;
./MemoryAnalyzer -consolelog -application org.eclipse.mat.api.parse java_pid2331.hprof
&lt;/pre&gt;
&lt;p&gt;car MAT a des fois du mal à les digérer. L'ouverture par MAT sera alors plus
rapide et facile.&lt;/p&gt;
&lt;h3&gt;Prise en main de l'outil&lt;/h3&gt;
&lt;p&gt;Le rapport donne une première vue avec&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les informations sur la taille du dump (8.7M) et le nombre de classe
chargées (360)&lt;/li&gt;
&lt;li&gt;un camembert représentant les plus gros objets en mémoire. En passant votre
souris sur chaque partie du camembert, vous pouvez avoir des informations sur
les attributs (références et champs natifs) de l'objet dans la colonne de
gauche 'attributes'&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.mat_report_m.jpg&quot; alt=&quot;mat report&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;mat report, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Différentes actions menant vers différentes vues du dump :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Un histogramme : qui affiche une représentation de l'utilisation
mémoire par classes (nombre d'objets instanciés par classe, ainsi que leurs
tailles 'shallow' et 'retained'). Vous pouvez d'ailleurs voir que le nombre de
&lt;code&gt;char&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/02/05/&quot;&gt;&lt;/a&gt;&lt;/code&gt; est sensiblement le même que le nombre de
&lt;code&gt;String&lt;/code&gt; ;)&lt;/li&gt;
&lt;li&gt;Un dominator tree (voir définition ci dessus) : présente non plus une
vue par classes mais par objets&lt;/li&gt;
&lt;li&gt;Une vue graphique des objets du dominator tree groupés selon différents
critères : class loaders, packages, classes. Le dominator tree travaille,
si on ne spécifie pas de groupage, au niveau objet&lt;/li&gt;
&lt;li&gt;Une liste des classes chargées plusieurs fois par différents class
loader.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les parties les plus intéressantes du rapport et les plus exploitables
directement sont sans aucun doute les deux rapports suivants:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Liste des 'leak suspects', c'est à dire que l'outil vous suggère où peuvent
se situer les responsables de fuites mémoires.&lt;/li&gt;
&lt;li&gt;Une liste des objets gaspillant la mémoire (e.g : ArrayList
surdimensionnées ayant beaucoup d'éléments vides) avec des suggestions
d'optimisations de l'utilisation mémoire (à ne pas confondre avec le rapports
des fuites mémoires.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voyons vue par vue, les choses que nous pouvons faire : Dans toutes les
vues vous avez la possibilité de voir ce que contient l'objet sélectionné dans
l'onglet &amp;quot;attributes&amp;quot;.&lt;/p&gt;
&lt;p&gt;Il est possible de calculer la taille de la 'retained heap' via l'icône
représentant un calculatrice.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.calculate_retained_heap_size_m.jpg&quot; alt=&quot;calculate retained size&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;calculate retained size, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Vous pouvez ainsi trier les objets par 'retained heap' pour identifier les
'leak supect'. Vous pouvez aussi les filtrés en rentrant des expressions
régulières juste en dessous des libellés de colonnes (e.g : &amp;quot;regex&amp;quot; en
dessous de 'Class Name'). Attention pour les regexp, elles sont sensibles à la
casse.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.histograme_filtres_m.jpg&quot; alt=&quot;histograme filtres&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;histograme filtres, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Une action qui révèle souvent des informations intéressantes, consiste à
grouper les objets. Si vous les groupez par classe, vous pouvez par exemple
obtenir le nombre d'objets et le pourcentage de la mémoire totale qu'occupe les
objets de cette classe. Cela permet d'identifier les points d'accumulation plus
facilement lorsque la fuite mémoire ne concerne pas un seul gros objet (ce qui
n'est pas notre cas) mais une multitude d'objets de la même classe. Par défaut,
la distribution de l'occupation de la mémoire ce fait par objet, en les
groupant, les shallow / retained heap sont alors additionnés, laissant souvent
voir une répartition inégale de l'utilisation de la mémoire selon les classes
(&lt;a href=&quot;http://dev.eclipse.org/blogs/memoryanalyzer/2008/05/08/the-power-of-aggregation-making-sense-of-the-objects-in-a-heap-dump/&quot; hreflang=&quot;en&quot;&gt;en savoir plus&lt;/a&gt;). Vous pouvez aussi les grouper par class
loader, ce qui peut se révéler intéressant pour des webapps, Tomcat, par
exemple, utilise des class loader differents : webappClassLoader et un
SystemClassLoader (&lt;a href=&quot;http://opensource.atlassian.com/confluence/spring/pages/viewpage.action?pageId=2669&quot; hreflang=&quot;en&quot;&gt;en savoir plus&lt;/a&gt;). OSGI (&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2009/11/26/10-minutes-pour...comprendre-OSGI&quot; hreflang=&quot;en&quot;&gt;voir
l'article publié précédemment&lt;/a&gt;) utilise aussi beaucoup de class loader
différents&lt;/p&gt;
&lt;h3&gt;Explication des menus&lt;/h3&gt;
&lt;p&gt;Lorsque vous cliquez droit sur les objets du dominator tree ou sur les
classes de l'histogramme, vous obtenez un menu vous permettant d'avoir des
informations sur l'élément sélectionné :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List objects=&amp;gt;with outgoing references: objets référencés par l'élément
sélectionné. Si on additionne la taille de ces objets, on obtient la retained
size.&lt;/li&gt;
&lt;li&gt;List objects=&amp;gt;with incomming references : objets qui référencent
l'élément sélectionné.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;br /&gt;
La liste des objets est contextuelle à l'élément sélectionné : si les
objets sont groupés par classe, ou que l'élément est une classe, une ligne par
objet de cette classe sera affichée, si l'élément est un objet, alors seuls les
objets concernés par cet objet (référencé par / qui référence) seront
affichés&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List class =&amp;gt; by outgoing references : les classes des objets
référencés par l'élément.&lt;/li&gt;
&lt;li&gt;List class =&amp;gt; by Incoming references : Les classes des objets qui
ont une référence sur l'élément&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;br /&gt;
La liste des classes affichée est également contextuelle. Exemple pour outgoing
référence : si l'élément sélectionné est une classe, alors la liste de
toutes les classes référencées par tous les objets de cette classe sera listée,
si l'élément est un objet, seule la liste des classes référencées par cet objet
sera affichée.&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Path to GC root =&amp;gt; Affiche les chemins des objets qui référence cet
objet jusqu'à leur élément racine (un peu comme les incomming objects mais
moins en profondeur car s'arrête lorsque l'on arrive au sommet de la pile du
thread auquel appartient l'élément). Il est possible via les sous menus de
filtrer selon les weak / soft / hard références. ce qui peut être utile lorsque
vous utilisez des caches d'objets avec des références soft, car vous pouvez
uniquement inspecter l'élément en tenant compte ou pas du cache. Ce menu n'est
applicable que pour un objet pas pour classe.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Merge Path to GC root : affiche le chemin &lt;strong&gt;le plus
court&lt;/strong&gt; parmi les références racines pour chaque instance de la classe
ou de l'objet sélectionné&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;br /&gt;
Java basics permet d'avoir des renseignements divers sur l'objet (e.g :
chercher une &lt;code&gt;String&lt;/code&gt;). Depuis la version 0.8, il est possible
d'analyser les stacktraces. Ces informations sont disponibles via le menu java
basics=&amp;gt;threads stacks&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Java collections sert plus à détecter les gaspillages de mémoire. il permet
par exemple d'avoir le ratio de remplissage d'un tableau.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Leak identification=&amp;gt; Component Report : permet d'avoir des
informations sur la mémoire occupée par cette objet : taille que l'objet
ou que les objets d'une classe occupent dans la pile, leurs &lt;code&gt;soft&lt;/code&gt;
et leurs &lt;code&gt;hard&lt;/code&gt; références,&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Leak identification=&amp;gt; Top consumer : les plus gros éléments
regroupés et triés selon différents critères : Objet, Classe, Class
loader, Package. il permet d'avoir une répartition des objets par classe
(e.g : trouver quels sont les plus gros objets de la classe Salariés). ce
menu est utile lorsque vous l'appliquez sur une classe, car il montre quels
sont les plus gros objets de cette classe et leurs répartitions&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Immediate dominator : Donne les classes des objets qui font que
l'élément est gardé en mémoire (e.g : les immediate dominators de
char&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/02/05/&quot;&gt;&lt;/a&gt; sont bien souvent des &lt;code&gt;String&lt;/code&gt;). Il est souvent
utile de filtrer les classes des package &lt;code&gt;com.sun.*&lt;/code&gt; et
&lt;code&gt;java.*&lt;/code&gt; car on peut supposer que la fuite ne vienne pas de là. Dans
l'exemple ci dessous, pour les immediate dominator de char&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/02/05/&quot;&gt;&lt;/a&gt;, si
on considère la ligne concernant &lt;code&gt;Entreprise&lt;/code&gt; : La première
colonne donne le nombre d'objets de la classe listée (1), la deuxième donne le
nombre d'objets référencés par cette classe (3) (un objet Entreprise référence
3 char&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/02/05/&quot;&gt;&lt;/a&gt; correspondant aux trois commentaires).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.imediate_dominator_m.jpg&quot; alt=&quot;immediate dominator&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;immediate dominator, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Show retained set : donne le nombre d'objets que référence l'élément,
groupés par classe. le retained set représente les objets qui seront libérés si
l'élément est garbage collecté. Pour un objet Entreprise, il contient 3
char&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/02/05/&quot;&gt;&lt;/a&gt;, 3 Strings et une Entreprise (les trois commentaires+ la
class ). vous pouvez filtrer le retained set par le ou les noms des champs. On
peut alors se demander &amp;quot;Pourquoi les objets &amp;quot;raison social&amp;quot;, &amp;quot;salariés&amp;quot; ne sont
pas dans la retained set alors qu'on les voit si on liste les outgoing objects
de Entreprise. La réponse est que ces objets ne seront pas libérés si
Entreprise disparait. Pour le prouver faites un incomming référence sur les
objet de Entreprise : les commentaires ne sont uniquement référencés par
Entreprise, alors que raison social par exemple est également référencé comme
clé pour la &lt;code&gt;Hashmap&lt;/code&gt; de &lt;code&gt;cacheEntreprise&lt;/code&gt;. De même pour
&amp;quot;raison social&amp;quot; qui est une constante car déclarée dans le code, alors que les
3 commentaires sont générés au runtime.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Savoir lire les graphes&lt;/h3&gt;
&lt;p&gt;Lorsque l'on est devant un graphe, il est important de savoir le lire.
prenons l'exemple pour des 'outgoing références' :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.outgoing_m.jpg&quot; alt=&quot;outgoing&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;outgoing, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;La classe Entreprise à des référence vers 4 objets de type String qui se
nomme 'commentaire', 'commentaire2', commentaire3, et 'raison social'. et une
ArrayList qui se nome salarié. si l'un des objet avait une valeur null, il
n'aurait pas été listé.&lt;/p&gt;
&lt;p&gt;Voyons maintenant comment lire des 'incoming références' :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.incoming_m.jpg&quot; alt=&quot;incoming&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;incoming, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;L'objet entreprise (je dis bien objet et pas classe) est référencé
par :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le champs 'entreprise' de 3 objets de type Salarié,&lt;/li&gt;
&lt;li&gt;Le thread lui même , correspondant à la déclaration de la variable
(Entreprise entreprise = new Entreprise() )&lt;/li&gt;
&lt;li&gt;Le champs 'value' d'une HashMap, correspondant à la &lt;code&gt;HashMap&lt;/code&gt; de
cache nommée entreprise et appartenant à la classe CacheEntreprise .&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lorsque l'on analyse des dumps mémoire, il est fortement conseillé de
connaitre la représentation interne des objets &lt;code&gt;Map&lt;/code&gt;,
&lt;code&gt;Collections&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, etc. Cela permet de mieux
naviguer. Une &lt;code&gt;HashMap&lt;/code&gt; est par exemple constitué d'un tableau de
hashMapEntry, lui même constitué de champs key, value, et hash. Si vous voulez
explorer un objet, allez sur l'onglet 'attributes', cliquez droit, puis
sélectionnez 'Go into' :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.attribut_m.jpg&quot; alt=&quot;attribut&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;attribut, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Les rapports de fuites mémoires&lt;/h3&gt;
&lt;p&gt;Entrons maintenant dans le vif du sujet. Le rapport proposant des
responsables des fuites mémoires. Comment cela fonctionne ? En fait
l'outil analyse le dump et recherche les 'Leak suspects', Il commence par
chercher les points d'accumulation en analysant les écarts significatifs dans
la retained size entre deux objets du graphe, puis remonte les 'incomming
references', prend le plus court chemin, et en déduit le suspect.&lt;/p&gt;
&lt;p&gt;Un rapport HTML comprenant plusieurs sections est alors généré :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les points d'accumulation : donnent les informations sur la taille et
le type d'objets, le thread incriminé, et le nombre d'objets contenus.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le rapport génère aussi une liste de mots clés permettant de le googliser ou
de les taper dans un bug tracker. L'idée est à mon sens excellente :
Lorsque vous créez le bug, vous y ajoutez ces mots clés, et une personne qui
désire retrouver ce bug ou voir si une résolution a déjà été trouvée, tape ces
mots clés, retrouve le bug et une potentielle résolution éprouvée.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.leak_report_m.jpg&quot; alt=&quot;leak report&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;leak report, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Deux questions se posent alors :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Qui garde une référence à cet objet ?&lt;/li&gt;
&lt;li&gt;Pourquoi l'objet est si gros et que contient il ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour la première question on va chercher quel est le plus court chemin du GC
root jusqu'à l'objet du point d'accumulation :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.leak_report_shortest_path_m.jpg&quot; alt=&quot;leak report shortest path&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;leak report shortest path, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Le graphe ce lit 'ligne du haut' est contenu par 'ligne du dessous'. Dans
l'exemple ci dessus : le tableau d'objet est référencé par le thread
(Chemin le plus court) et par une &lt;code&gt;ArrayList&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Pour la deuxième question MAT nous donne un aperçu des objets référencés par
le point d'accumulation. Cela permet en général d'avoir une idée plus précise
de la cause de la fuite.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.leak_report_accumulated_object_m.jpg&quot; alt=&quot;leak report accumulated object&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;leak report accumulated object, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Le rapport fournit également une répartition des objets référencé par le
point d'accumulation par classes. Dans notre cas, le tableau d'objet ne
contient que des &lt;code&gt;Double&lt;/code&gt;, ce qui laisse à penser qu'une méthode
remplit le tableau.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.leak_suspect_byclass_m.jpg&quot; alt=&quot;leak suspect by class&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;leak suspect by class, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Une autre information intéressante est l'environnement d'exécution. Dans
quel contexte a eu lieu le dump. Cela à une importance quand vous utilisez
&lt;code&gt;-XX:HeapDumpOnOutOfMemoryError&lt;/code&gt; . Les informations importantes sont
entre autres :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les propriétés de la JVM (Xms, Xmx). Peut être un mauvais dimensionnement
de la mémoire ?&lt;/li&gt;
&lt;li&gt;Le nombre de threads et leurs tailles permettent de voir si un seul thread
est incriminé, et dans ce cas, selon la nature de traitement qu'à le thread,
vous pouvez plus cibler la fuite (exemple : un thread de purge d'un
cache)&lt;/li&gt;
&lt;li&gt;L'histogramme des classes peut également donner des informations sur la
cause. S'il existe un grand nombre d'objets appartenant à un classe d'un de vos
package, il est probable (pas à 100%, mais vous disposez déjà d'une piste) que
la fuite est causée par du code vous appartenant.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Informations sur les threads&lt;/h3&gt;
&lt;p&gt;MAT fournit des informations sur le context d'exécution des threads. il
inspecte les variables &lt;code&gt;threadLocal&lt;/code&gt; et selon ce qu'il y trouve,
nous donne des informations supplémentaires. exemple :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;s'il trouve des objets sur des requêtes HTTP il inspectera l'objet et en
extraira les headers, URL, status code, etc.&lt;/li&gt;
&lt;li&gt;s'il trouve des connections JDBC, il extraira les resultsets, query,...on
peut par exemple voir qu'il manque un clause &lt;code&gt;where&lt;/code&gt;, ce qui fait
que beaucoup trop d'objets sont rapatriés, et cause une trop grande
consommation mémoire&lt;/li&gt;
&lt;li&gt;s'il trouve des contexts OSGI, il explorera leurs contenus.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voici deux exemples : le premier pour un thread exécutant une requête
SQL :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.thread_sql_m.jpg&quot; alt=&quot;thread sql&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;thread sql, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;
le deuxième pour un thread exécutant une requête soap :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/memoryleak/.thread_soap_m.jpg&quot; alt=&quot;thread soap&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;thread soap, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;On peut légitimement se poser la question &amp;quot;comment sont déterminer ces
informations ? &amp;quot; en fait si le suspect est un thread, il explore son nom
(exemple &amp;quot;HTTP-processor&amp;quot; pour un thread Tomcat, &amp;quot;RMI TCP
Connection(8)-127.0.0.1 pour un connecteur jmx, &amp;quot;Timer-3&amp;quot; pour un
&lt;code&gt;timerThread&lt;/code&gt;), il inspecte les variables &lt;code&gt;threadLocal&lt;/code&gt;
et selon ce qu'il y trouve nous donne des informations supplémentaires. Ce
système d'exploration est basé sur un système de plugins, vous pouvez donc en
créer vous même selon vos besoins&lt;/p&gt;
&lt;h3&gt;OQL ou Object Query Language&lt;/h3&gt;
&lt;p&gt;Pour permetre de rechercher des objets facilement, vous pouvez les
rechercher avec une syntaxe SQL like. Exemple :&lt;/p&gt;
&lt;pre&gt;
select e.nom from com.demo.mat.Entreprise e
&lt;/pre&gt;
&lt;p&gt;Vous pouvez utiliser des clauses WHERE, des UNION, des projections (avg,
min, max,...). vraiment très pratique pour trouver, compter, avoir des
statistiques sur des objets en particulier. (&lt;a href=&quot;http://en.wikipedia.org/wiki/Object_Query_Language&quot; hreflang=&quot;en&quot;&gt;en savoir
plus&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Voilà pour la théorie :) j'espère que vous maitrisez les différents concepts
de l'outil et que vous savez désormais aller chercher les informations
pertinentes.&lt;/p&gt;
&lt;p&gt;Le prochain post sera basé sur un cas concret et expliquera comment j'ai
résolu une fuite mémoire sur Gisgraphy, en 15 minutes à l'aide de MAT.&lt;/p&gt;</description>
    
          <enclosure url="http://davidmasclet.gisgraphy.com/public/memoryleak/memory_leak_exemple.zip"
      length="2977" type="application/zip" />
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2010/02/05/D%C3%A9tecter-et-corriger-les-fuites-m%C3%A9moire-java#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2010/02/05/D%C3%A9tecter-et-corriger-les-fuites-m%C3%A9moire-java#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/482002</wfw:commentRss>
      </item>
    
  <item>
    <title>Nouveau look aux idées larges !</title>
    <link>http://davidmasclet.gisgraphy.com/post/2010/02/27/nouveau-look-aux-id%C3%A9es-larges</link>
    <guid isPermaLink="false">urn:md5:ade6e2b91458fa0adcd0dbfee3449a86</guid>
    <pubDate>Sat, 27 Feb 2010 15:56:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Divers</category>
            
    <description>    &lt;p&gt;Le blog à désormais un nouveau look. oh... pas très très différent. Je suis
juste partie du thème précédent et j'ai changé le style pour l'adapter aux
styles des billets : c'est-à-dire des articles techniques.&lt;br /&gt;
&lt;br /&gt;
En effet en regardant les statistiques des résolutions d'écran des
utilisateurs, J'ai remarqué que personne n'était en 800*600, alors que le thème
était optimisé pour une résolution 800*600. J'ai donc augmenté la largeur de la
colonne centrale en diminuant les marges de chaque coté, attribué plus de place
à la colonne de contenu, et diminué la colonne avec les rubriques, qui me
semblait moins importantes que le contenu. A mon sens, le texte est beaucoup
plus lisible. Pour les zone de code, j'ai également opté pour une couleur
jaune, plus sympathique que le gris. Faites moi part de vos impressions, et des
éventuels problèmes rencontrés.&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2010/02/27/nouveau-look-aux-id%C3%A9es-larges#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2010/02/27/nouveau-look-aux-id%C3%A9es-larges#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/490478</wfw:commentRss>
      </item>
    
  <item>
    <title>Tester du code en ligne</title>
    <link>http://davidmasclet.gisgraphy.com/post/2010/02/04/Tester-du-code-en-ligne</link>
    <guid isPermaLink="false">urn:md5:af837b1c49a518630a71db5e6c4150dc</guid>
    <pubDate>Thu, 04 Feb 2010 15:35:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Divers</category>
        <category>javascript</category>    
    <description>    &lt;p&gt;Certains connaissent peut être &lt;a href=&quot;http://pastebin.com/&quot; hreflang=&quot;en&quot;&gt;pastebin&lt;/a&gt; qui permet d'envoyer du code à quelqu'un (ou d'un PC à
l'autre ;) ) en générant une shortURL. Il existe des sites qui vont plus loin
en permettant de tester du code directement en ligne : &lt;a href=&quot;http://codepad.org/&quot; hreflang=&quot;en&quot;&gt;http://codepad.org/&lt;/a&gt;. il suffit de
copier / coller votre code, et le site vous le compile et vous l'interprète.
c'est le cas de &lt;a href=&quot;http://codepad.org/&quot; hreflang=&quot;en&quot;&gt;codepad&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/.codepad_m.jpg&quot; alt=&quot;codepad&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;codepad, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Il supporte les langages suivants :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C&lt;/li&gt;
&lt;li&gt;C++&lt;/li&gt;
&lt;li&gt;D&lt;/li&gt;
&lt;li&gt;Haskell&lt;/li&gt;
&lt;li&gt;Lua&lt;/li&gt;
&lt;li&gt;OCaml&lt;/li&gt;
&lt;li&gt;PHP&lt;/li&gt;
&lt;li&gt;Perl&lt;/li&gt;
&lt;li&gt;Plain Text&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Ruby&lt;/li&gt;
&lt;li&gt;Scheme&lt;/li&gt;
&lt;li&gt;Tcl&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vous pouvez mettre votre code &amp;quot;privé&amp;quot;, ce qui est pratique, car j'ai déjà
retrouvé du code, que je ne voulais pas partager publiquement, indexé par
Google, après l'avoir envoyé via pastebin)&lt;/p&gt;
&lt;p&gt;Dans la même lignée, il existe des sites plus spécialisés pour tester les
interactions HTML&amp;lt;=&amp;gt;Javascript : &lt;a href=&quot;http://jsbin.com/&quot; hreflang=&quot;en&quot;&gt;http://jsbin.com/&lt;/a&gt;. Son utilisation est triviale : vous
avez le code javascript à gauche et le code HTML à droite.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/.jsbin_m.jpg&quot; alt=&quot;jsbin&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;jsbin, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Le site va beaucoup plus loin que pastebin, d'une part, car il est
spécialisé dans un langage, mais aussi parce qu' il offre la
possibilité :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;De sauvegarder votre code.&lt;/li&gt;
&lt;li&gt;D'inclure des librairies connues, comme Prototype, Jquery, Mootools, Dojo
et de choisir parmi différentes versions&lt;/li&gt;
&lt;li&gt;De générer une short URL de votre code (exemple :
http://jsbin.com/ilomu3)&lt;/li&gt;
&lt;li&gt;De modifier un code existant en cliquant sur ''Edit using JS Bin&amp;quot; en haut à
droite (j'avoue c'est pas flagrant :) ).&lt;/li&gt;
&lt;li&gt;De gérer plusieurs versions d'un même code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L'avantage est qu'il permet de tester du code avant de mettre à jour un
librairie (exemple : passer de Prototype 1.6.03 à 1.6.1.0), de vous faire
la main sur des librairies, ou de partager du code entre collègues.&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2010/02/04/Tester-du-code-en-ligne#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2010/02/04/Tester-du-code-en-ligne#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/481779</wfw:commentRss>
      </item>
    
  <item>
    <title>iPad nano</title>
    <link>http://davidmasclet.gisgraphy.com/post/2010/02/01/iPad-nano</link>
    <guid isPermaLink="false">urn:md5:a21b270de4a84d0e36befc39217543cd</guid>
    <pubDate>Mon, 01 Feb 2010 11:25:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>humour</category>
        <category>Apple</category><category>Humour</category><category>Ipad</category>    
    <description>    &lt;p&gt;Pour faire suite à mon post &lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/01/04/Strat%C3%A9gie-d-Apple-vs-strat%C3%A9gie-Google&quot;&gt;Stratégie
d'Apple vs stratégie Google&lt;/a&gt;, je vous propose une image qui m'a fait bien
rire&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/.ipadnano_m.jpg&quot; alt=&quot;ipadnano&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;ipadnano, fév. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sinon, je vous conseille la lecture de ce &lt;a href=&quot;http://www.presse-citron.net/ipad-si-jetais-apple-je-minquieterais&quot;&gt;billet&lt;/a&gt;
sur l'ipad et son effet d'annonce manqué&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2010/02/01/iPad-nano#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2010/02/01/iPad-nano#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/480921</wfw:commentRss>
      </item>
    
  <item>
    <title>Tester le javascript : JSUnit, JSmock, Jstester (partie 2)</title>
    <link>http://davidmasclet.gisgraphy.com/post/2010/01/26/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-2%29</link>
    <guid isPermaLink="false">urn:md5:cb80e7b4d3cc6bce000ad3004d406279</guid>
    <pubDate>Tue, 26 Jan 2010 13:16:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Tests / XUnit</category>
        <category>Javascript</category><category>JsMock</category><category>JsUnit</category><category>test</category>    
    <description>&lt;p&gt;Après le &lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/01/08/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-1%29&quot;&gt;
billet&lt;/a&gt; consacré à JsUnit et &lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2009/03/13/Mock-ajax-request&quot;&gt;celui&lt;/a&gt; qui expliquait comment mocker
une requête Ajax, nous allons maintenant voir comment mocker en javascript.
Pour cela je vous propose une courte présentation de JsMock. Les exemples de
code sont en annexe de ce billet.&lt;/p&gt;    &lt;p&gt;Pour montrer les capacités de JsMock, rien de tel que des exemples de code.
Nous allons donc reprendre le code du &lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2010/01/08/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-1%29&quot;&gt;
premier billet&lt;/a&gt; (vous pouvez télécharger les sources en annexe du premier
billet)&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;La première chose à faire est de télécharger &lt;a href=&quot;http://jsmock.sourceforge.net/&quot; hreflang=&quot;en&quot;&gt;JsMock&lt;/a&gt; et de l'ajouter dans
le répertoire JsUnit (ou ailleurs, mais le tout est d'être cohérent lors de la
déclaration dans le fichier HTML) , puis de l'importer à partir de notre
fichier de tests &lt;code&gt;SimpleCodeTest&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;
&amp;lt;script language=&amp;quot;JavaScript&amp;quot; type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../jsmock.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3&gt;Exemple de code à mocker&lt;/h3&gt;
&lt;p&gt;Pour comprendre les mocks il vaux mieux faire interagir deux objets entre
eux : cela permet de tester un objet et d'en mocker un autre. Ajoutons
donc un objet &lt;code&gt;ConfigParameter&lt;/code&gt; et définissons en une instance dans
MyObjectToTest :&lt;/p&gt;
&lt;pre&gt;
function ConfigParameter() {
        this.getAlertMessage = function(){
                return &amp;quot;alertMessage&amp;quot;;
        }

        this.setParam = function(Aname, Avalue) {
                //this[name]=value
        }
}

function MyObjectToTest(){
        this.configParameter = new ConfigParameter();

        var message = &amp;quot;Not defined yet!&amp;quot;;
        
        this.setMessage = function(messageParam){
                message = messageParam
        }

        this.getMessage = function(){
                return message;
        }

        this.getAlertMessage = function(){
               return this.configParameter.getAlertMessage();
        }

        this.setConfigValue = function(){
                this.configParameter.setParam(&amp;quot;MyName&amp;quot;, &amp;quot;MyValue&amp;quot;);
        }

}
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3&gt;Mocker des valeurs de retour&lt;/h3&gt;
&lt;p&gt;Voici un exemple où nous allons mocker le retour de
&lt;code&gt;getAlertMessage&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;
function testMockReturnedValue() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Mock
        mockControl = new MockControl();
        mockConfig = mockControl.createMock(ConfigParameter);
        mockConfig.expects().getAlertMessage().andReturn(&amp;quot;mock Alert Message&amp;quot;);

        MyObj.configParameter = mockConfig;

        // Assert
        assertEquals(&amp;quot;mock Alert Message&amp;quot;, MyObj.getAlertMessage());
        
        mockControl.verify();
}

&lt;/pre&gt;
&lt;p&gt;Vous remarquerez qu'à la différence de EasyMock en Java, il n'est pas
nécessaire d'appeler la méthode &lt;code&gt;replay()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;La méthode &lt;code&gt;vérify&lt;/code&gt; permet de vérifier que les méthodes mockées
sont bien appelées le bon nombre de fois : si une méthode non défini par
votre mock est appelée le test ne passera pas, en revanche vous pourriez avoir
défini le comportement d'une méthode mais que cette dernière ne soit jamais
appelée, dans ce cas, si vous n'appelez pas la méthode &lt;code&gt;verify&lt;/code&gt;, le
test passera mais vous ne saurez pas que la méthode n'a pas été appelée).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3&gt;mocker des fonctions&lt;/h3&gt;
&lt;p&gt;Il est possible de simuler les comportement (le corps de la méthode) d'une
fonction grâce à la méthode &lt;code&gt;andStub&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;
function testStubFunction() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Mock
        mockControl = new MockControl();
        mockConfig = mockControl.createMock(ConfigParameter);
        mockConfig.expects().getAlertMessage().andStub(
                 function() { 
                        return &amp;quot;test&amp;quot;;
                 } 
        );

        MyObj.configParameter = mockConfig;

        // Assert
        assertEquals(&amp;quot;test&amp;quot;, MyObj.getAlertMessage());
}
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3&gt;Simuler des exceptions&lt;/h3&gt;
&lt;p&gt;Il est également possible de simuler des exceptions avec la méthode
&lt;code&gt;andThrow&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;
function testThrow() {
        exception = false ;
        // Actor
        MyObj = new MyObjectToTest();
        
        // Mock
        mockControl = new MockControl();
        mockConfig = mockControl.createMock(ConfigParameter);
        mockConfig.expects().getAlertMessage().andThrow(&amp;quot;Error !&amp;quot;);

        MyObj.configParameter = mockConfig;

        try {   
        MyObj.getAlertMessage();
        } catch (e){
          exception = true;
        }

        assertEquals(true, exception);
}
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3&gt;Mocker des fonctions avec paramètres&lt;/h3&gt;
&lt;p&gt;Pour mocker une fonction avec des paramètres, vous avez deux
possibilités : soit vous spécifiez le type des paramètres lors de la
définition du mock, dans le cas où seul le type des paramètres vous importe.
Pour cela utiliser &lt;code&gt;TypeOf.isA()&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;
function testFunctionWithTypedParameters() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Mock
        mockControl = new MockControl();
        mockConfig = mockControl.createMock(ConfigParameter);
        mockConfig.expects().setParam(TypeOf.isA(String),TypeOf.isA(String));

        MyObj.configParameter = mockConfig;


        MyObj.setConfigValue();

        mockControl.verify();
}
&lt;/pre&gt;
&lt;p&gt;soit vous spécifiez la valeur des paramètres, si vous voulez vérifier que la
fonction est appelée avec les bonnes valeurs :&lt;/p&gt;
&lt;pre&gt;
function testFunctionWithParameters() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Mock
        mockControl = new MockControl();
        
        mockConfig = mockControl.createMock(ConfigParameter);
        mockConfig.expects().setParam(&amp;quot;MyName&amp;quot;, &amp;quot;MyValue&amp;quot;);

        MyObj.configParameter = mockConfig;
        MyObj.setConfigValue();

        mockControl.verify();
}

&lt;/pre&gt;
&lt;p&gt;J'espère que cela vous à donner envie de l'utiliser, car le Javascript est
de plus en plus présent dans les applications web et les tests permettent de
mieux découpler votre code et de le rendre plus robuste.&lt;/p&gt;
&lt;p&gt;La troisième et dernière partie traitera de &lt;code&gt;JsTester&lt;/code&gt; qui permet
de tester du javascript coté serveur (JsUnit dispose d'une partie serveur mais
elle n'est là que pour piloter les tests, il faut toujours avoir un
navigateur). nous verrons ses avantages et ses inconvénients et comment
l'intégrer avec Maven et JUnit.&lt;/p&gt;</description>
    
          <enclosure url="http://davidmasclet.gisgraphy.com/public/jsmock.zip"
      length="2959" type="application/zip" />
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2010/01/26/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-2%29#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2010/01/26/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-2%29#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/479006</wfw:commentRss>
      </item>
    
  <item>
    <title>Configurer et utiliser Google bot comme outil de tests</title>
    <link>http://davidmasclet.gisgraphy.com/post/2010/01/18/Configurer-et-utiliser-Google-bot-comme-outil-de-tests</link>
    <guid isPermaLink="false">urn:md5:0bc32c0e18e2cc11fd6a413c29e46859</guid>
    <pubDate>Mon, 18 Jan 2010 14:16:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Google</category>
        <category>Bots</category><category>mod_bandwith</category><category>mod_evasive</category><category>Tests</category>    
    <description>&lt;p&gt;Lorsque l'on parle de Google bot, on pense souvent à &amp;quot;référencement&amp;quot;, mais
il existe une autre utilisation : les tests. Je ne parle pas de tests
fonctionnels ou unitaires mais de tests pour améliorer la qualité de votre
site, comme les tests de liens morts, de pages indisponibles, d'erreurs HTTP
etc.&lt;/p&gt;
&lt;p&gt;Il y a quelques semaines, je postais plusieurs billets sur comment gérer un
serveur de download :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2009/11/12/Limiter-la-bande-passante-d-un-serveur-de-download&quot;&gt;Limiter
la bande passante d'un serveur de download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2009/11/05/Limiter-l-acces-%C3%A0-un-site-/-service-%C3%A0-un-certain-nombre-de-requ%C3%AAtes&quot;&gt;
Limiter l'accès à un site / service pour un certain nombre de requêtes par
IP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mais cette configuration peut poser des problèmes pour les bots des moteurs
de recherches. C'est pourquoi je voudrais expliquer comment mieux configurer
les serveurs pour faciliter le travail des bots.&lt;/p&gt;    &lt;h3&gt;Utiliser les bots comme outils de tests&lt;/h3&gt;
&lt;p&gt;Le rôle premier d'un bot est bien sur d'indexer votre site, mais il en
existe un autre qui consiste à l'utiliser pour tester et améliorer vos
pages :&lt;/p&gt;
&lt;p&gt;La procédure est la suivante :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vous faites une sitemap avec toutes les pages de votre site. Ayant
plusieurs millions de pages, j'ai préféré, pour ma part, faire un &lt;a href=&quot;http://code.google.com/p/gisgraphy/source/browse/trunk/gisgraphy/src/main/python/sitemap_generator.py&quot; hreflang=&quot;en&quot;&gt;générateur de sitemaps personnel&lt;/a&gt; en Python. Il permet de
générer plusieurs fichiers sitemaps (la taille et le nombre d'URLs par fichier
étant limités) et un fichier sitemap d'index contenant toutes les URLs des
fichiers sitemaps générés.&lt;/li&gt;
&lt;li&gt;Puis vous soumettez à Google le fichier d'index.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rien ne vous empêche de soumettre des pages dont vous voulez qu'elles
génèrent une erreur. exemple : http://monsite.com/private/ qui est censé
générer une erreur 403.&lt;/p&gt;
&lt;p&gt;Vous pouvez ainsi voir des informations sur le passage de Google bot, comme
le nombre de kilo-octets téléchargés, le temps de réponse moyen, et le nombre
de pages explorées par jour, sur cette &lt;a href=&quot;https://www.google.com/webmasters/tools/crawl-stats?hl=fr&quot; hreflang=&quot;fr&quot;&gt;page&lt;/a&gt; et consulter les pages en erreurs (timeout, erreur HTTP, pages
inaccessible, pages contenant des liens morts, etc), sur cette &lt;a href=&quot;https://www.google.com/webmasters/tools/crawl-errors?hl=fr&amp;amp;siteUrl=http%3A%2F%2Fservices.gisgraphy.com%2F&quot; hreflang=&quot;en&quot;&gt;page&lt;/a&gt;. Merci Google :)&lt;/p&gt;
&lt;h3&gt;Autoriser les bots à indexer votre site&lt;/h3&gt;
&lt;p&gt;Lors de mon billet sur &lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2009/11/05/Limiter-l-acces-%C3%A0-un-site-/-service-%C3%A0-un-certain-nombre-de-requ%C3%AAtes&quot;&gt;
mod_evasive&lt;/a&gt;, j'évoquais le moyen de limiter un site à un nombre de requêtes
défini. Il faut toutefois préciser que mod_evasive ne peut (à ma connaissance)
pas ce configurer par vhost : c'est donc tous les vhosts de votre Apache
qui seront limités. Si comme pour &lt;a href=&quot;http://www.gisgraphy.com&quot; hreflang=&quot;en&quot;&gt;Gisgraphy&lt;/a&gt;, vous disposez d'un &lt;a href=&quot;http://download.gisgraphy.com&quot; hreflang=&quot;en&quot;&gt;vhost&lt;/a&gt; pour le download et d'un &lt;a href=&quot;http://www.gisgraphy.com/&quot; hreflang=&quot;en&quot;&gt;autre&lt;/a&gt; pour le site web, et que
vous limitez le nombre de requêtes du serveur de download, vous limitez le
nombre de requêtes du site web également.&lt;/p&gt;
&lt;p&gt;Si vous avez créé un fichier &lt;a href=&quot;http://www.google.com/support/webmasters/bin/topic.py?topic=8476&quot; hreflang=&quot;fr&quot;&gt;sitemap&lt;/a&gt; afin de mieux référencer votre site, se pose alors le problème
des bots : si vous disposez de beaucoup d'URLs à indexer (9 millions pour
Gisgraphy), lors du passage d'un bot, votre site peux détecter une attaque et
renvoyer une erreur HTTP 403.&lt;/p&gt;
&lt;p&gt;Plusieurs solutions sont possibles :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.google.com/webmasters/tools/settings?hl=fr&quot; hreflang=&quot;fr&quot;&gt;Modifier la vitesse d'exploration&lt;/a&gt; dans la configuration des outils
pour webmasters, afin de limiter le nombre de pages indexées par jour.&lt;/li&gt;
&lt;li&gt;Installer deux serveurs Apache : Si vous ne disposez que d'une seule
machine, il vous faut mettre le serveur de download sur un port particulier, ou
dans le cas contraire, avoir deux serveurs séparés.&lt;/li&gt;
&lt;li&gt;Ajouter la liste des adresses IP des bots à la White liste de
mod_evasive :&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
DOSWhiteList 127.0.0.1
DOSWhiteList 66.249.64.*
DOSWhiteList 66.249.65.*
DOSWhiteList 66.249.66.*
DOSWhiteList 66.249.71.*
&lt;/pre&gt;
&lt;p&gt;La liste des IP ci dessus n'est donnée qu'a titre d'exemple, car la liste
des IP des bots change tout le temps. Vous pouvez utiliser * pour remplacer
jusqu'a 3 octets. (Sinon essayez avec un hostname comme 'googlebot.com', mais
je n'ai pas testé)&lt;/p&gt;
&lt;h3&gt;Ajouter un robots.txt&lt;/h3&gt;
&lt;p&gt;Si vous avez limité le nombre de requêtes simultanées avec mod_bandwith,
vous ne voulez peut être pas qu'un bot vous prenne un téléchargement au
détriment d'un utilisateur ou que votre bande passante soit diminuée. Il vous
faut alors ajouter un fichier robots.txt :&lt;/p&gt;
&lt;pre&gt;
User-agent: *
Disallow: /
&lt;/pre&gt;
&lt;p&gt;En faisant cela, vous ne permettez pas aux bots de télécharger les fichiers
et de les indexer. Dans mon cas il s'agit de fichiers ZIP ou TAR et leurs
indexations n'ont pas d'importance pour moi, mais il peut en être autrement
pour d'autres types de fichiers, car Google n'indexe pas que du HTML, il indexe
aussi, les PDF, document Word, Excel, texte, images, etc. Dans ce cas vous
devez mettre des règles de gestion plus fines et ne permettre l'indexation, que
de certains fichiers. Notez que dans l'exemple ci dessus, '*' est une valeur
particulière et que ni &lt;code&gt;Disallow&lt;/code&gt; ni &lt;code&gt;User-agent&lt;/code&gt; ne
supporte les expressions régulières. Pour vous aider, un outil est disponible
dans les &lt;a href=&quot;https://www.google.com/webmasters/tools&quot; hreflang=&quot;fr&quot;&gt;outils
pour webmasters&lt;/a&gt; afin de générer un fichier robots.txt et un &lt;a href=&quot;https://www.google.com/webmasters/tools/robots-analysis-ac?hl=fr&quot; hreflang=&quot;fr&quot;&gt;autre&lt;/a&gt; pour le tester&lt;/p&gt;
&lt;h3&gt;Voir ce que Google bot voit&lt;/h3&gt;
&lt;p&gt;Si vous avez des règles d'affichage de votre site un peu complexes, comme
une différenciation de l'affichage selon le User-Agent, ou que vous
géolocalisez d'après l'adresse IP, il peut être intéressant de savoir comment
Google bot voit votre site. Il existe un &lt;a href=&quot;http://googlewebmastercentral.blogspot.com/2009/10/fetch-as-googlebot-and-malware-details.html&quot; hreflang=&quot;en&quot;&gt;outil&lt;/a&gt; qui se situe dans la console des &lt;a href=&quot;https://www.google.com/webmasters/tools&quot; hreflang=&quot;fr&quot;&gt;outils pour
webmasters&lt;/a&gt;. Il est actuellement sous la rubrique labs, car en test.&lt;/p&gt;
&lt;p&gt;Je déconseille fortement d'utiliser cette outil pour faire un rendu
particulier afin de tromper Google, car c'est le meilleur moyen de se faire
exclure du moteur de recherche.&lt;/p&gt;
&lt;p&gt;je conclurai par dire qu'utiliser Google bot pour tester votre site n'est
pas illégal du tout, bien au contraire, les outils mis à votre disposition sont
justement là pour vous aider à améliorer la qualité de votre site.&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2010/01/18/Configurer-et-utiliser-Google-bot-comme-outil-de-tests#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2010/01/18/Configurer-et-utiliser-Google-bot-comme-outil-de-tests#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/476721</wfw:commentRss>
      </item>
    
  <item>
    <title>Tester le javascript : JSUnit, JSmock, Jstester (partie 1)</title>
    <link>http://davidmasclet.gisgraphy.com/post/2010/01/08/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-1%29</link>
    <guid isPermaLink="false">urn:md5:1961205bc2ba3a01494872238689aeaf</guid>
    <pubDate>Fri, 08 Jan 2010 10:59:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Tests / XUnit</category>
        <category>javascript</category><category>JsMock</category><category>JsTester</category><category>JsUnit</category><category>tests</category>    
    <description>&lt;p&gt;Beaucoup de développeurs Java sont familiers avec Junit, EasyMock,... ces
librairies qui permettent de tester du code Java afin de rendre les programmes
plus robustes. Javascript dispose également de frameworks équivalents. Avec
l'apparition du Web 2.0, le Javascript et l'Ajax sont de plus en plus présents
dans nos applications (j'avais d'ailleurs poster un billet sur &lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2009/03/13/Mock-ajax-request&quot;&gt;comment mocker une requête Ajax&lt;/a&gt;,il y a
quelques mois) et représente une part non négligeable de code.&lt;/p&gt;
&lt;p&gt;Si vous utilisez GWT, qui permet d'écrire à partir de code écrit en Java, de
générer une application en HTML / javascript, vous n'avez pas de problème pour
tester votre code, puisque Junit suffit. En revanche, si vous avez du code
javascript à tester, il existe des librairies qui permettent de simplifier les
tests. Je vous propose de voir comment tester du Javascript au moyen de ces 3
frameworks :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JsUnit : Qui fournit des mécanismes d'assertions et d'exécutions de
tests.&lt;/li&gt;
&lt;li&gt;JsMock : Permet de simuler des comportements pour des objets
Javascript,tout comme EasyMock le fait&lt;/li&gt;
&lt;li&gt;JsTester : Permet d'executer du code Javascript en java grâce à
&lt;a href=&quot;http://www.mozilla.org/rhino/&quot; hreflang=&quot;en&quot;&gt;Rhino&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;les exemples de code sont disponibles en annexe de ce billet&lt;/p&gt;    &lt;p&gt;Commençons par JsUnit.&lt;/p&gt;
&lt;h4&gt;Installation&lt;/h4&gt;
&lt;p&gt;Vous pouvez le télécharger sur le site de &lt;a href=&quot;http://www.jsunit.net/&quot; hreflang=&quot;en&quot;&gt;JsUnit&lt;/a&gt;. Une fois télécharger décompresser le. JsUnit dispose
d'une partie serveur que nous ne verrons pas ici (peut être dans un autre
billet) et d'une partie cliente. Pour lancer la partie cliente, il faut ouvrir
la page testRunner.html,qui se trouve à la racine du dossier que vous avez
décompressé, dans un navigateur :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://davidmasclet.gisgraphy.com/public/.jsunit_m.jpg&quot; alt=&quot;jsunit&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;jsunit, janv. 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Cette page vous permet de lancer un fichier contenant des tests écrits en
Javascript et de vous afficher le résultat de l'exécution.&lt;/p&gt;
&lt;p&gt;Si vous ne désirez pas installer JsUnit vous pouvez &lt;a href=&quot;http://www.jsunit.net/runner/testRunner.html?testpage=/runner/tests/jsUnitTestSuite.html&quot;&gt;
exécuter le testRunner sur le site de jsUnit&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Utilisation&lt;/h4&gt;
&lt;p&gt;Je vous propose de tester un code très simple avec un objet MyObjectToTest
disposant d'une variable privée, d'un getter et d'un setter:&lt;/p&gt;
&lt;pre&gt;
function MyObjectToTest(){
        var message = &amp;quot;Not defined yet!&amp;quot;;
        
        this.setMessage = function(messageParam){
                message = messageParam
        }

        this.getMessage = function(){
                return message;
        }

}       
/*example of use
MyObj = new MyObjectToTest();
alert(MyObj.getMessage());//Not defined yet!
MyObj.setMessage(&amp;quot;This is it&amp;quot;);
alert(MyObj.getMessage() );//This is it
*/

&lt;/pre&gt;
&lt;p&gt;Créons un répertoire &amp;quot;testMe&amp;quot; dans le dossier de JsUnit et sauvegardons le
contenu dans un fichier &amp;quot;simpleCode.js&amp;quot;&lt;/p&gt;
&lt;p&gt;Ecrivons ensuite un fichier &amp;quot;SimpleCodeTest.html&amp;quot;, contenant plusieurs cas
de tests&lt;/p&gt;
&lt;pre&gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot;&amp;gt;
&amp;lt;title&amp;gt;test de SimpleCode.html&amp;lt;/title&amp;gt;

&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
&amp;lt;script language=&amp;quot;JavaScript&amp;quot; type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../app/jsUnitCore.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script language=&amp;quot;JavaScript&amp;quot; type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;./simpleCode.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script language=&amp;quot;JavaScript&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;
function setUp(){
        //nothing to do
}
function tearDown(){
        //nothing special
}

function testMessageShouldBePrivate() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Action
        
        // Assert
        assertEquals(&amp;quot;undefined&amp;quot;, typeof MyObj.message);
        assertUndefined(MyObj.message);
}

function testMessageShouldHaveADefaultValue() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Action
        
        // Assert
        assertEquals(&amp;quot;Not defined yet!&amp;quot;, MyObj.getMessage());
}


function testGetAndSetMessageShouldGetAndSet() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Action
        MyObj.setMessage(&amp;quot;This is it&amp;quot;);                       

        // Assert
        assertEquals(&amp;quot;This is it&amp;quot;, MyObj.getMessage());
}

&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Comme vous pouvez le constater JsUnit dispose de plusieurs points communs
avec JUnit :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les tests doivent commencer par test, suivi d'une majuscule, afin que la
méthode soit détectée.&lt;/li&gt;
&lt;li&gt;Des mécanismes permettant de faire des assertions sont fournis.&lt;/li&gt;
&lt;li&gt;Il y a la possibilité de définir des méthodes qui seront appelés avant (
setUp() ) et après chaque test ( tearDown() ). Noter que le nom et la casse ont
une importance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Des assertion spécifiques au Javascript sont également disponibles :
assertUndefined, assertNotUndefined, assertNaN,...&lt;/p&gt;
&lt;p&gt;Déroulons maintenant notre test :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ouvrons la page testRunner.html dans un navigateur&lt;/li&gt;
&lt;li&gt;Sélectionnons le fichier SimpleCodeTest.html&lt;/li&gt;
&lt;li&gt;Cliquons sur &amp;quot;Run&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vous verrez alors : Runs: 3 Errors: 0 Failures: 0 et la fameuse &amp;quot;barre
verte&amp;quot; prouvant que votre code est conforme aux tests.&lt;/p&gt;
&lt;p&gt;Voyons maintenant avec un test qui échoue. Ajoutons ce cas de
test :&lt;/p&gt;
&lt;pre&gt;
function testThatFail() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Action
        MyObj.setMessage(&amp;quot;this is not it&amp;quot;);                   

        // Assert
        assertEquals(&amp;quot;This is it&amp;quot;, MyObj.getMessage());
}
&lt;/pre&gt;
&lt;p&gt;En re-exécutant les tests vous aurez : Runs: 4 Errors: 0 Failures: 1 ,
une &amp;quot;barre rouge&amp;quot;, et &amp;quot;testThatFail&amp;quot; dans la liste des tests échoués. en
cliquant dessus, vous aurez la stacktrace d'exécution.&lt;/p&gt;
&lt;h4&gt;Problèmes courants et solutions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Pour des raisons que j'ignore il peux arriver qu'en local, le fichier de
test ne soit pas trouvé, il faut alors appeler l'URL de testRunner.html avec le
paramètre testpage :&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
http://path/to/jsunit//testRunner.html?testpage=/path/to/jsunit/testMe/simpleCodeTest.html
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Sur Firefox, si la version est supérieure ou égale à 3.0 des soucis dus à
la politique de sécurité peuvent subvenir. il faut alors taper about:config
dans la barre d'adresse et mettre la valeur de
security.fileuri.strict_origin_policy a false en cliquant dessus. Redémarrer
FireFox, et recommencer le test.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Utilisation avancée&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Vous pouvez définir un suite de tests (aka : testSuite) grâce aux
méthodes &lt;code&gt;addTestPage(filename)&lt;/code&gt; et
&lt;code&gt;addTestSuite(aTestSuite)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Des fonction de logging sont fournies :&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;

function warn(message, [value])
function inform(message, [value]) (equivalent to function info(message, [value]))
function debug(message, [value])

&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Vous pouvez définir des timeouts d'exécution des tests sur la page du
testRunner.html&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Vous pouvez définir vos propres paramètres pour l'exécution de vos tests
(e.g : myTests.html?mode=0, ou myTests.html?mode=1.). &lt;a href=&quot;http://www.jsunit.net/documentation/customQueryStrings.html&quot; hreflang=&quot;en&quot;&gt;Plus d'infos&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Si vous voulez exécuter vos tests à partir d'un outil d'intégration
continue ou avec des outils comme maven, ant, ou buildr, une &lt;a href=&quot;http://www.jsunit.net/documentation/serverOverview.html&quot; hreflang=&quot;en&quot;&gt;version
serveur&lt;/a&gt; est fournies (personnellement, je la trouve lourde
d'utilisation)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lors du prochain post, nous verrons comment mocker les objets javascript et
simuler le DOM HTML&lt;/p&gt;</description>
    
          <enclosure url="http://davidmasclet.gisgraphy.com/public/testme.zip"
      length="1117" type="application/zip" />
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2010/01/08/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-1%29#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2010/01/08/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-1%29#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/473919</wfw:commentRss>
      </item>
    
  <item>
    <title>Google goggle</title>
    <link>http://davidmasclet.gisgraphy.com/post/2010/01/04/google-goggle</link>
    <guid isPermaLink="false">urn:md5:b2793ea81734a1ee843fbad574d47e24</guid>
    <pubDate>Mon, 04 Jan 2010 15:23:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Google</category>
        <category>goggle</category>    
    <description>&lt;p&gt;Il y a de cela quelques temps, Google lançait &lt;a href=&quot;http://images.google.com/imagelabeler/&quot; hreflang=&quot;en&quot;&gt;image labeler&lt;/a&gt; afin
d'améliorer la recherche d'images Google. Tout laisse à penser que ce service a
bien fonctionné, car Google expérimente &lt;a href=&quot;http://www.google.com/mobile/goggles&quot; hreflang=&quot;en&quot;&gt;Google goggle&lt;/a&gt;, un
service disponible à partir d'android 1.6+, et qui permet de prendre une photo
ou une vidéo et Google détectera de quoi il s'agit et lancer une recherche à
partir de ce qu'il a détecté. Le &lt;a href=&quot;http://fr.wikipedia.org/wiki/G%C3%A9otag&quot; hreflang=&quot;fr&quot;&gt;geotagging&lt;/a&gt; devient
quasi obsolète.&lt;/p&gt;
&lt;p&gt;Le nom n'est pas choisi au hasard, au départ Google aurait du s'appeler
&lt;em&gt;Goggle&lt;/em&gt;, en référence au nombre &lt;a href=&quot;http://fr.wikipedia.org/wiki/Gogol_%28nombre%29&quot; hreflang=&quot;en&quot;&gt;gogol&lt;/a&gt; de
Edward Kasner, mais une erreur de saisie a fait que &lt;em&gt;Google&lt;/em&gt; s'appelle
&lt;em&gt;Google&lt;/em&gt; et non &lt;em&gt;Goggle&lt;/em&gt;. c'est donc un moyen d'utiliser son nom
initial.&lt;/p&gt;    &lt;p&gt;Si vous ne voyez pas trop ce qu'est Goggle, voici un exemple
d'utilisation :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vous photographié la tour Eiffel avec votre Google phone et il détecte
automatiquement la tour Eiffel, et vous lance une recherche sur le mot 'tour
Eiffel', vous pouvez donc avoir l'année de construction, les horaires
d'ouvertures, et bien d'autres choses.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vous pouvez photographier des millions de choses en photos et en vidéos, et
Google vous détectera de quoi il s'agit et vous lancera une recherche sur ce
que vous photographiez&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La couverture d'un livre, pour en connaitre l'auteur&lt;/li&gt;
&lt;li&gt;Des cartes de visites.&lt;/li&gt;
&lt;li&gt;Des œuvres d'art, comme des tableaux ou des statues.&lt;/li&gt;
&lt;li&gt;Des cafés.&lt;/li&gt;
&lt;li&gt;Des couvertures de livres.&lt;/li&gt;
&lt;li&gt;Des étiquettes de bouteilles de vins.&lt;/li&gt;
&lt;li&gt;Des logos de marques.&lt;/li&gt;
&lt;li&gt;Les codes barres&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour certaines informations, Google se base sur les coordonnées GPS, mais
pas pour toutes (étiquettes de vins).&lt;/p&gt;
&lt;p&gt;Je ne sais pas si cela fonctionne avec les visages mais je ne pense pas que
Google s'aventurerait sur ce terrain, même s'il est actuellement possible de
rechercher des visages en spécifiant imgtype=face dans la recherche avancée
d'image (&lt;a href=&quot;http://images.google.fr/images?imgtype=face&amp;amp;as_st=y&amp;amp;hl=fr&amp;amp;sa=1&amp;amp;q=jobs&amp;amp;btnG=Recherche+d%27images&amp;amp;aq=f&amp;amp;oq=&amp;amp;start=0&amp;amp;imgtbs=t&quot; hreflang=&quot;en&quot;&gt;exemple&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Google se lance aussi dans la réalité augmentée. Pour ceux qui ne savent pas
de quoi il s'agit, la réalité augmentée permet de filmer quelque chose et de
voir ce que l'on film avec un modèle 3D en surimpression. Cette &lt;a href=&quot;http://www.youtube.com/watch?v=coeiHrZ0_DA&quot; hreflang=&quot;fr&quot;&gt;vidéo&lt;/a&gt; vous
donnera un aperçu avec le livre dokéo. Imaginez aussi les dérives : vous
pouvez déplacer votre téléphone dans la rue, filmer des maisons et avoir le nom
des personnes ou le numéros de téléphones en couplant votre recherche aux Pages
jaunes / Pages blanches. Dérives...? euh...&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2010/01/04/google-goggle#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2010/01/04/google-goggle#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/473075</wfw:commentRss>
      </item>
    
  <item>
    <title>Stratégie d'Apple vs stratégie Google</title>
    <link>http://davidmasclet.gisgraphy.com/post/2010/01/04/Strat%C3%A9gie-d-Apple-vs-strat%C3%A9gie-Google</link>
    <guid isPermaLink="false">urn:md5:bf23ce37db1ab7be78af93ae43876e48</guid>
    <pubDate>Mon, 04 Jan 2010 14:44:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Divers</category>
        <category>Apple</category><category>Google</category><category>Iphone</category>    
    <description>&lt;p&gt;Après la folie commerciale de fin d'année je voudrais bloguer sur les
différentes stratégies des sociétés dites &amp;quot;innovantes&amp;quot;. je sais dors et déjà
que je vais me faire des ennemis et que cette article ne fera pas l'unanimité,
mais peu importe, ce post trouvera aussi des personnes allant dans le même sens
que moi :)&lt;/p&gt;
&lt;p&gt;C'est demain que sort le téléphone de Google : le Nexus One. Ceux qui
me connaissent savent que je suis un fervent défenseur de Google. Ce n'est pas
que j'aime tout ce qu'ils font (je n'aime pas chrome, par exemple), mais je
trouve que leurs produits tapent souvent juste et fort. Et si certains sont
frileux d'utiliser des applications et services d'une firme, il est vrai, un
peu &amp;quot;big brother&amp;quot;, on saluera tout de même leur stratégie qui leur a permis
d'être le moteur de recherche utilisé par 90% des internautes et de sortir des
produits innovant comme, Google Maps et Google documents, GWT, etc.&lt;/p&gt;
&lt;p&gt;Je m'étonne encore de leur modèle économique basé sur la publicité. Pour
mémoire, ce même modèle a causé, entre autre, la chute de la bulle internet. De
très grosses sociétés (e.g : AOL) ont revu leur business modèle, en
passant la publicité de &amp;quot;source de revenus&amp;quot; à &amp;quot;complément d'activité&amp;quot; ;). La
stratégie de Google est de sortir des produits les plus complets possible, en
sachant très bien ce qu'il amélioreront à la prochaine version, mais en visant
un niveau de base déjà très élevé pour un effet &amp;quot;WHAOOOUU&amp;quot;. Je voudrais (entre
autre) comparer la différence de stratégie d'Apple et de Google pour leur
téléphone pour montrer comment Apple est vraiment une société reine dans le
marketing :&lt;/p&gt;    &lt;p&gt;Je trouve qu'Apple est une société innovante avec des dirigeants
visionnaires, et l'informatique n'aurait pas le visage qu'il a en 2010, sans
Steve Jobs, mais l'engouement pour l'iphone, montre que Apple (peut être encore
plus que Google) maitrise parfaitement ses produits et dispose d'une stratégie
parfaite : il sorte un produit révolutionnaire (l'iphone), et tout le
monde s'extasie, à juste titre, devant un téléphone portable permettant enfin
de surfer sur internet correctement.&lt;/p&gt;
&lt;p&gt;Apple sont vraiment très fort, car malgré certaines limitations, ils ont su
être premier sur le marché des smart phone, comme sur celui des lecteur MP3.
Souvent copié, jamais égalé, l'iphone reste le seul à pouvoir offrir un surf
sur téléphone mobile digne de ce nom. Personne n'y était arrivé ! Chapeau
bas ! on ne voit dans l'Iphone que ce qu'il apporte et pas ces
limitations. pareil pour l'Ipod (pour rappel, l'Ipod, à ses début, ne pouvez
même pas lire les MP3)&lt;/p&gt;
&lt;p&gt;il est fort a parier que pour &lt;a href=&quot;http://www.zone-numerique.com/news_6259_Apple_La_TabletMac_s_appellera_iSlate_.htm&quot; hreflang=&quot;fr&quot;&gt;iSlate&lt;/a&gt; (tablette tactile d'Apple), la guerre sera rude entre
Google et Apple. Sauf que cette fois ci Google ne se laissera pas distancer, et
à déjà &lt;a href=&quot;http://www.zone-numerique.com/news_6299_HTC_plancherait_sur_une_tablette_tactile_basee_sous_Chrome_OS.htm&quot; hreflang=&quot;fr&quot;&gt;pris les devant&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mais derrière l'engouement pour l'Iphone, on découvre aussi des limitations
surprenantes, et là s'en suivent tout un tas de versions successives, comblant
au goute à goute, les lacunes. A chaque fois l'iphone se vend de plus belle car
les limitations des anciennes versions nous paraissent alors tellement
limitantes, qu'il est necessaire de changer. Coluche disait : &amp;quot;Quand on
pense qu’il suffirait que les gens n’achètent plus pour que ça ne se vende pas
!&amp;quot;. D'autres sociétés ou milieux fonctionnent aussi comme cela :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le CD : Philips connaissait déjà le DVD, à la sortie du CD.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;La Wii : Nintendo sort au compte goutes les accessoires, redonnant à
chaque fois un regain d'enthousiasme pour la console (La balance board, le Wii
motion plus)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;La littérature : on sort le premier livre sur Harry Potter, puis le
deuxième, puis le double coffret, puis le troisième tome, puis le coffret avec
les 3 volumes, et ainsi de suite...jusqu'au septième volume. (à mon sens,
acheter livre par livre a du sens, mais un coffret de deux livres sur une série
de 7 a très peu d'intérêt, autant acheter les deux livres séparés : Un
coffret sous entend une boite et un packaging qui deviendra obsolète à la
sortie du prochain livre), ensuite viendront les coffrets complets, puis les
collectors, les coffret anniversaire, puis les versions de poches (le Da Vinci
Code n'est sorti que très tardivement en version de poche, et beaucoup l'ont
acheté en double pour pouvoir l'emporter plus facilement). et au final ce sont
toujours les même mots et la même histoire !&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Il en va de même pour la musique : on ne compte plus les versions
re-masterisés des chansons de Bob Marley, d'Elvis Presley, ou des Beatles qui
n'apporte souvent qu'un autre support ou une pochette différente. Est il normal
de repayer pour les droits d'auteurs lorsque nous achetons une œuvre artistique
sur un support différent ? pourquoi ne pas payer que le
support ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L'Iphone de déroge pas à la règle : peu de choses changent à chaque
nouvelles versions, choses qui nous paraissent alors indispensables
d'avoir ?&lt;/p&gt;
&lt;p&gt;L'Iphone est très fermé, et peut, par certain aspects faire penser à la
position dominante de Microsoft, tant décriée. L'Iphone souffre de lacunes non
négligeables :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;L'iPhone ne sait lire que du MPEG4 / H264, ce qui force à ré-encoder les
vidéos qui ne sont pas dans ce format.&lt;/li&gt;
&lt;li&gt;Pour mettre des fichiers sur l'iphone il faut passer par de la
synchronisation de fichiers, vous ne pouvez supprimer de l'iphone sans le
supprimer du PC, et resynchroniser.&lt;/li&gt;
&lt;li&gt;Le glisser déposer n'est pas possible&lt;/li&gt;
&lt;li&gt;Le SDK n'est disponible que sous Mac! certains diront &lt;q&gt;logiquel'iphone
est basé sur un Mac OS&lt;/q&gt;, certes mais je pense qu'il est tout à fait possible
techniquement de faire un SDK qui tourne sur linux, surtout que Mac OS est lui
même basé sur linux.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;La charte est très contraignante :&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;q&gt;you may not modify, publish, transmit, participate in the transfer or
sale of, reproduce, create derivative works based on, redistribute, perform,
display, or in any way exploit any of the Site, Content or Services in whole or
in part&lt;/q&gt;&lt;/p&gt;
&lt;p&gt;ou&lt;/p&gt;
&lt;p&gt;&lt;q&gt;you agree not to disclose, publish, or disseminate any Apple Confidential
Information to anyone other than to other Registered iPhone Developers&lt;/q&gt;&lt;/p&gt;
&lt;p&gt;Ceci explique pourquoi les tutoriauxet how to sont si peu nombreux.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Seul Safari est possible comme navigateur (certain s'offusque qu'Internet
Explorer soit installé par défaut sur Windows, mais on peut au moins en
installer d'autres)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;L'iphone 3G, n'avait même pas de base la fonction MMS (un comble pour un
téléphone),De base, pas de possibilité de transférer des SMS, d'avoir des
modèles de SMS, pas d'accusé de reception, il a fallu attendre le 3GS&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Pas de visio possible, le 3G est bridé en appareil photo, le 3GS dispose
d'une caméra, mais sur la face opposée à l'écran.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Pas de flash sur l'appareil photo.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Pas de batterie interchangeable.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Pas de possibilité d'ajouter une carte mémoire : ce qui oblige à
acheter un modèle disposant de beaucoup de mémoire.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Passage obligé par Itunes si on a pas &amp;quot;jailbreaké&amp;quot; son Iphone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela confirme que les fonctions liées à la téléphonie n'ont pas été
peaufinée mais que l'accent à été mis sur les fonctionnalités annexes&lt;/p&gt;
&lt;p&gt;Je comprends tout à fait qu'une première version n'est pas une version
finale et définitive (heureusement), mais les choix (cela n'engage que moi)
d'Apple révèlent plus un choix stratégique plutôt qu'une limitation technique.
Pour s'en convaincre, il suffit de comparer les fonctionnalités des téléphones
au moment de la sortie de l'Iphone pour voir que qu'Apple s'est gardé un bonne
marge de manœuvre. Beaucoup de choses auraient pu être mis dans l'Iphone 1ere
version. En fait l'iphone est un mini ordinateur qui permet de téléphoner.&lt;/p&gt;
&lt;p&gt;Je pense que les constructeurs ne s'y trompe pas et 90 % d'entre eux ont
déjà adopté le SDK (Android) du Google phone qui est en java et non en
Objective C. Steve Jobs a déclaré : &amp;quot;&lt;q&gt;Nobody uses Java anymore&lt;/q&gt;&amp;quot;.
Pour rappel, Java est installé sur plus de 90% des ordinateurs personnels, est
disponible sur quasi tous les téléphones portables un peu évolués, et fait
partie du standard Blu-Ray (supporté par Apple). j'ai hâte de voir la prochaine
version de l'Iphone et voir comment Apple va se positionner par rapport au
Nexus One de Google : vont-il faire un pas de géant dans les
fonctionnalités, ou alors sortir quelque chose d'innovant qui va encore une
fois scotcher la planète. Affaire à suivre.&lt;/p&gt;
&lt;p&gt;En tout cas ces stratégies stimulent la concurrence, et c'est souvent bon
pour le consommateur :)&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2010/01/04/Strat%C3%A9gie-d-Apple-vs-strat%C3%A9gie-Google#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2010/01/04/Strat%C3%A9gie-d-Apple-vs-strat%C3%A9gie-Google#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/473064</wfw:commentRss>
      </item>
    
  <item>
    <title>Différence entre un hard link et un lien symbolique</title>
    <link>http://davidmasclet.gisgraphy.com/post/2009/12/22/Diff%C3%A9rence-entre-un-hard-link-et-un-lien-symbolique</link>
    <guid isPermaLink="false">urn:md5:f7ee9ceef3bbfc503a0810750ec7b1fe</guid>
    <pubDate>Wed, 23 Dec 2009 11:39:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
            
    <description>&lt;p&gt;Lorsque j'ai voulu faire un système de sauvegarde pour les données de mon
serveur, j'ai voulu créer un dossier par date, mais je me suis vite rendu
compte que cela allait prendre beaucoup de place. alors j'ai utilisé un hard
link pour faire une sauvegarde incrémentale.&lt;/p&gt;    &lt;h3&gt;Hard link&lt;/h3&gt;
&lt;p&gt;Un hard link permet de faire un lien vers un autre répertoire mais dès que
le contenu du lien change, un nouvel inode est crée.&lt;/p&gt;
&lt;p&gt;Cela me permet d'avoir un dossier de sauvegarde où, lorsque je navigue à
l'intérieur, j'ai tous les fichiers, mais au niveau du disque dur chaque
dossier de sauvegarde d'une date &lt;em&gt;d&lt;/em&gt; pointe vers le dossier de
sauvegarde &lt;em&gt;d-1&lt;/em&gt;. Seules les modifications de la date &lt;em&gt;d&lt;/em&gt; sont
stockées et prennent de la place sur le disque. Exemple :&lt;/p&gt;
&lt;p&gt;Considérons un dossier le sauvegarde ci dessous :&lt;/p&gt;
&lt;pre&gt;
=&amp;gt;Dossier 01_01
     =&amp;gt;Dossier D1
          Fichier F1
          Fichier F2
&lt;/pre&gt;
&lt;p&gt;Lors de la sauvegarde du 02/01, je crée un hard link avec la commande
&lt;code&gt;cp&lt;/code&gt; et l'option -l plutôt que &lt;code&gt;ln&lt;/code&gt; car &lt;code&gt;ln&lt;/code&gt;
ne peux être fait que sur un fichier pas sur un répertoire : &lt;code&gt;cp -ral
01_01 02_01&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;
=&amp;gt;Dossier 02_01
     =&amp;gt;Dossier D1
          Fichier F1
          Fichier F2
&lt;/pre&gt;
&lt;p&gt;Les fichiers et 02_01 partagent les mêmes inodes que ceux de
01_01 :&lt;/p&gt;
&lt;pre&gt;
ls -i 01_01/D1
     232177 F1
     232178 F2
ls -i 01_02/D1
     232177 F1
     232178 F2
&lt;/pre&gt;
&lt;p&gt;A ce stade, la place sur mon disque dur reste inchangée. Maintenant
supposons que le fichier F2 soit différent entre le 02/01 et le 01/01, dans ce
cas le fait d'écraser le fichier F2 dans le dossier 02_01 va me créer
physiquement un nouveau fichier (un nouvel inode). Seul le fichier F2 de 02_01
sera modifié et aura son propre inode, les autres fichiers pointent vers ceux
du dossier 01_01 puisqu'ils sont inchangés. Par contre si D1 contenait des sous
dossiers, deux dossiers seraient physiquement crées un dans 01_01 et un autre
dans 02_01 car les hards links de répertoires ne sont pas possibles, en
revanche les fichiers contenus dans les sous dossiers partageraient par défaut
les mêmes inodes.&lt;/p&gt;
&lt;pre&gt;
ls -i 01_01/D1
     232177 F1
     232178 F2
ls -i 01_02/D1
     232177 F1
     232179 F2
&lt;/pre&gt;
&lt;p&gt;Il n'y aura pas de nouvel inode de crée si le fichier est renommé.&lt;/p&gt;
&lt;p&gt;Pour utiliser des hard links avec rsync, j'ai donc utiliser la commande
suivante :&lt;/p&gt;
&lt;pre&gt;
date=`date &amp;quot;+%Y-%m-%dT%H:%M:%S&amp;quot;`
rsync -aP --delete --link-dest=$HOME/Backups/current /path/to/important_files $HOME/Backups/back-$date
rm -f $HOME/Backups/current
ln -s back-$date $HOME/Backups/current
&lt;/pre&gt;
&lt;p&gt;En savoir plus sur comment réaliser des &lt;a href=&quot;http://www.mikerubel.org/computers/rsync_snapshots/&quot; hreflang=&quot;en&quot;&gt;sauvegardes
avec hard links et Rsync&lt;/a&gt; | &lt;a href=&quot;http://blog.interlinked.org/tutorials/rsync_time_machine.html&quot; hreflang=&quot;en&quot;&gt;An other link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Lien symbolique&lt;/h3&gt;
&lt;p&gt;Un lien symbolique ne prend pas plus de place qu'un hard link, mais la
différence et que toutes modifications apportées à la cible d'un lien se
répercute sur la source du lien, et inversement. Si j'utilise un lien
symbolique plutôt qu'un hard link, mes sauvegardes des dates &lt;em&gt;d&lt;/em&gt;
seraient égales aux sauvegardes des dates &lt;em&gt;d-1&lt;/em&gt; et je n'aurais donc,
qu'une sauvegarde, et non une sauvegarde par date.&lt;/p&gt;
&lt;p&gt;On crée un lien symbolique avec la commande &lt;code&gt;ln&lt;/code&gt; en passant le
paramètre &lt;code&gt;-s&lt;/code&gt; : &lt;code&gt;ln -s source dest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;La limitation des liens est qu'ils doivent se faire sur une même partition
pour partager les mêmes inodes.&lt;/p&gt;
&lt;p&gt;En savoir plus sur la commande &lt;a href=&quot;http://fr.wikipedia.org/wiki/Ln_%28Unix%29&quot; hreflang=&quot;fr&quot;&gt;ln&lt;/a&gt;.&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2009/12/22/Diff%C3%A9rence-entre-un-hard-link-et-un-lien-symbolique#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2009/12/22/Diff%C3%A9rence-entre-un-hard-link-et-un-lien-symbolique#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/470221</wfw:commentRss>
      </item>
    
  <item>
    <title>Mais qui a comité ça ?!</title>
    <link>http://davidmasclet.gisgraphy.com/post/2009/12/22/Mais-qui-a-comit%C3%A9-%C3%A7a</link>
    <guid isPermaLink="false">urn:md5:2aa2ee39be924497dfac42ebffb8c8c0</guid>
    <pubDate>Tue, 22 Dec 2009 10:12:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>git</category><category>svn</category>    
    <description>&lt;p&gt;Lorsque vous développez, Vous pouvez avoir envie de savoir qui a comité un
bout de code, pas forcément parce qu'il vous parait mauvais (soyons constructif
!), mais peut être pour comprendre certains choix, parce que vous trouvez le
code particulièrement beau, ou simplement par curiosité. La plupart des outils
de gestion de versions fournissent des moyens de le faire grâce à la commande
&lt;code&gt;blame&lt;/code&gt;. Même si la commande peut porter à confusion, ce post, n'est
pas à placer dans la rubrique &amp;quot;délation&amp;quot; mais plutôt &amp;quot;travail en équipe&amp;quot; :
)&lt;/p&gt;    &lt;p&gt;Avec SVN; il vous suffit de faire un &lt;code&gt;svn blame&lt;/code&gt; suivi du nom du
fichier. Peut être par déontologie, plusieurs alias sont disponibles :
&lt;code&gt;praise&lt;/code&gt;, &lt;code&gt;annotate&lt;/code&gt;, &lt;code&gt;ann&lt;/code&gt;. Une mauvaise
approche consiste à faire un &lt;code&gt;svn log&lt;/code&gt; parce qu'avec cette commande,
la granularité est le fichier, vous devez donc parcourir les logs et faire des
&lt;code&gt;diff&lt;/code&gt; afin de trouver qui a modifié le bout de code qui vous
intéresse.&lt;/p&gt;
&lt;p&gt;Vous pouvez consulter la doc de &lt;a href=&quot;http://www.linxit.de/svnbook/en/1.0/re02.html&quot; hreflang=&quot;en&quot;&gt;SVN blame&lt;/a&gt;
pour plus d'informations.&lt;/p&gt;
&lt;p&gt;Avec GIT, la même commande &lt;code&gt;git blame&lt;/code&gt; permet de faire la même
chose que &lt;code&gt;SVN blame&lt;/code&gt; (&lt;code&gt;git annotate&lt;/code&gt; aussi, bien que
présente pour assurer la compatibilité) mais GIT fournit beaucoup plus
d'options :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La possibilité de spécifier les lignes de début et de fin pour lesquelles
vous voulez voir les annotations.&lt;/li&gt;
&lt;li&gt;De voir les modifications de façon incrémentale.&lt;/li&gt;
&lt;li&gt;De savoir les lignes copiées à partir d'autres fichiers dans le même comit
(utile dans le cas de refactoring)&lt;/li&gt;
&lt;li&gt;De connaitre les lignes déplacées&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vous pouvez consulter la doc de &lt;a href=&quot;http://kernel.org/pub/software/scm/git/docs/git-blame.html&quot; hreflang=&quot;en&quot;&gt;GIT
blame&lt;/a&gt; pour plus d'informations&lt;/p&gt;
&lt;p&gt;La plupart des IDE permettent également de le faire. Dans le cas
d'Eclipse : Clic droit=&amp;gt;Team=&amp;gt;Show annotations.&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2009/12/22/Mais-qui-a-comit%C3%A9-%C3%A7a#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2009/12/22/Mais-qui-a-comit%C3%A9-%C3%A7a#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/470203</wfw:commentRss>
      </item>
    
  <item>
    <title>Améliorer les performances de like et recherche partielle de mots dans Postgres</title>
    <link>http://davidmasclet.gisgraphy.com/post/2009/12/21/Am%C3%A9liorer-les-performances-de-like-et-recherche-partielle-de-mots-avec-Postgres-pour-faire-de-l-autocompletion</link>
    <guid isPermaLink="false">urn:md5:d9ca4e310f17e4bbee892fac1289f76c</guid>
    <pubDate>Mon, 21 Dec 2009 10:23:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>PostGIS / PostgresQL</category>
        <category>Autocompletion</category><category>Fulltext</category><category>ilike</category><category>Index</category><category>like</category><category>Performances</category><category>Postgres</category>    
    <description>&lt;p&gt;Lorsque j'ai voulu faire un système d'auto-complétion pour un champs de
recherche sur une page web, j'ai pensé à utiliser un moteur de recherche
fulltext, malheureusement, les recherches fulltext ne se font que sur des mots
entiers. j'ai donc pensé à utiliser la fonction like de Postgres.
Malheureusement, like n'utilisent les indexes, seulement si '%' est placé en
fin de chaine, et cela ne correspondait pas à mon besoin. De plus il fallait
absolument que les requêtes soit rapides, pour que l'auto-complétion soit
utilisable. Après avoir écumé le web à la recherche d'une solution, et n'ayant
rien trouvé de concluant, j'ai décidé de faire un peu de R &amp;amp; D et
d'implémenter une solution maison qui utilise les indexes. L'astuce réside dans
l'utilisation d'un algorithme Edge n-gram et d'une recherche fulltext qui,
elle, utilise les indexes. Voici la solution pour utiliser les indexes dans les
différents cas suivants :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tous les mots commençant par&lt;/li&gt;
&lt;li&gt;tous les mots finissant par&lt;/li&gt;
&lt;li&gt;tous les mots contenant&lt;/li&gt;
&lt;/ul&gt;    &lt;h3&gt;tous les mots commençant par&lt;/h3&gt;
&lt;p&gt;Pour commencer, j'ai crée un table de données &lt;code&gt;city&lt;/code&gt; avec un
champs &lt;code&gt;name&lt;/code&gt; et je l'ai rempli avec 500 000 entrées. j'ai ensuite
fait une recherche :&lt;/p&gt;
&lt;pre&gt;
select name from city where name like 'par%';
&lt;/pre&gt;
&lt;p&gt;La requête prend plusieurs secondes, et un &lt;code&gt;explain&lt;/code&gt; m'indique un
parcours séquentiel. Logique! me direz vous il n'y a pas d'indexes. Je crée
donc un index et j'utilise la fonction &lt;code&gt;lower()&lt;/code&gt; pour rendre mes
requêtes insensibles à la casse:&lt;/p&gt;
&lt;pre&gt;
CREATE INDEX city_name_idx ON city(lower(&amp;quot;name&amp;quot;));
&lt;/pre&gt;
&lt;p&gt;L'explain sur la requête select, me montre que l'index est bien utilisé.&lt;/p&gt;
&lt;h3&gt;tous les mots finissant par&lt;/h3&gt;
&lt;p&gt;essayons de trouver tout les mot finissant par 'par' :&lt;/p&gt;
&lt;pre&gt;
select name from city where name like '%par';
&lt;/pre&gt;
&lt;p&gt;L'index n'est pas utilisé. Afin qu'il le soit, une astuce consiste à
inverser la chaine afin de pouvoir utiliser le '%' à la fin du mot recherché
plutôt qu'au début. Voici une fonction plpgsql qui le fait :&lt;/p&gt;
&lt;pre&gt;
CREATE OR REPLACE FUNCTION &amp;quot;public&amp;quot;.&amp;quot;inverserchaine&amp;quot; (chaine text) RETURNS text AS
$body$
DECLARE
 i integer;
 resultat text = '';
BEGIN
 FOR i IN REVERSE length(chaine)..1 LOOP
  resultat:=resultat||substring(chaine from i for 1);
 END LOOP;
 return resultat;
END;
$body$
LANGUAGE 'plpgsql'  RETURNS NULL ON NULL INPUT SECURITY INVOKER IMMUTABLE;
&lt;/pre&gt;
&lt;p&gt;créez ensuite un index :&lt;/p&gt;
&lt;pre&gt;
CREATE INDEX cityreversename_idx ON city(inverserchaine(&amp;quot;name&amp;quot;) text_pattern_ops);
&lt;/pre&gt;
&lt;p&gt;Il faudra que vos requêtes reflètent également cette inversion :&lt;/p&gt;
&lt;pre&gt;
select name from city where name like inverserchaine('par')||'%'
&lt;/pre&gt;
&lt;p&gt;L'opérateur || permet de concaténer le signe '%' à la chaine inversée. Le
signe '%' est bien placé à la fin et donc l'index est utilisé, dixit
&lt;code&gt;explain&lt;/code&gt;. j'ai trouvé cette astuce sur cette &lt;a href=&quot;http://www.davidgis.fr/documentation/win32/html/ape.html#id2521149&quot; hreflang=&quot;en&quot;&gt;page&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;tous les mots contenant&lt;/h3&gt;
&lt;p&gt;Maintenant attaquons nous aux choses sérieuses avec tous les mots contenant
'mot'. et la je n'ai rien trouvé de concluant sur le web.&lt;/p&gt;
&lt;pre&gt;
select name from city where name like '%mot%';
&lt;/pre&gt;
&lt;p&gt;Comme attendu, l'index n'est pas utilisé et la recherche prend plusieurs
secondes. Mon astuce pour améliorer les performances, consiste à découper le
texte en sous chaines représentant toutes les sous chaines le composant. Pour
cela j'utilise un algorithme Edge n-grams que j'applique plusieurs fois. Un
algorithme Edge n-­grams, décompose un mot à partir du début, exemple pour
'paris' : &amp;quot;p&amp;quot; &amp;quot;pa&amp;quot; &amp;quot;par&amp;quot; &amp;quot;pari&amp;quot; &amp;quot;paris&amp;quot;. J'applique donc cet algorithme
plusieurs fois sur le mot, en enlevant, à chaque fois la première lettre. Pour
'Paris' j'applique l'algorithme sur Paris, aris, ris, is. j'obtiens ainsi,
toutes les sous chaines de Paris&lt;/p&gt;
&lt;pre&gt;
P, Pa, Par, Pari, Paris, ar, ari, aris, ri, ris, is
&lt;/pre&gt;
&lt;p&gt;Ces sous chaînes correspondent à toutes les sous chaines que l'utilisateur
pourra taper et pour lesquelles 'Paris' sera un résultat valide.&lt;/p&gt;
&lt;p&gt;Il me suffit ensuite de mettre ces sous chaines dans un champs fulltext que
je nomme 'name_splited' et qui est de type 'tsvector' (le type fulltext de
Postgres) et de créer un index sur ce champs. Si je recherche 'ari', la
recherche fulltext me renverra le bon résultat car 'ari fait partie des sous
chaines découpées et indéxées'.&lt;/p&gt;
&lt;pre&gt;
ALTER TABLE city ADD COLUMN name_splited tsvector;
CREATE INDEX citytextsearchvectorindex ON city USING gin(name_splited);
&lt;/pre&gt;
&lt;p&gt;J'ai implémenté cet algorithme dans une fonction Java, mais elle pourrait
être écrite en plpgsql. Faire la fonction plpgsql me permettrait de mettre un
trigger. (si quelqu'un en a l'envie ou le besoin, il peut la poster, ou je le
ferai peut être lors d'un prochain post).&lt;/p&gt;
&lt;pre&gt;
public static final String getSubStrings(String originalString, char delimiter) {
        if (originalString == null) {
                return null;
        }
        //use hashset to remove duplicate
        String substring = null;
        StringBuffer sb = new StringBuffer();
        Set&amp;lt;String&amp;gt; set = new HashSet&amp;lt;String&amp;gt;();
        originalString = transformStringForFulltextIndexation(originalString);
        for (int i = 0; i &amp;lt; originalString.length(); i++) {
                for (int j = i + 1; j &amp;lt;= originalString.length(); j++) {
                        substring = originalString.substring(i, j);
                        if (!substring.endsWith(&amp;quot; &amp;quot;)) {//we have alredy add the entry the last loop
                                if (substring.startsWith(&amp;quot; &amp;quot;)) {//need to trim?
                                        substring = substring.substring(1);
                                }
                                if (substring.length() &amp;gt; 1) {//only index string that have length &amp;gt;=2
                                        set.add(substring.replace(&amp;quot; &amp;quot;, String.valueOf(delimiter)));
                                }
                        }
                }
        }
                for (String part : set) {
                sb.append(part).append(&amp;quot; &amp;quot;);
        }
        return sb.toString();
}
&lt;/pre&gt;
&lt;p&gt;Comme vous pouvez le voir, je remplace les espaces par un autre caractère
(delimiter), qui dans mon cas est '-'. Ceci s'explique par le fait qu'un champs
ts_vector, découpe les textes en mots et indique leurs positions. Exemle pour
&amp;quot;Saint jean&amp;quot;&lt;/p&gt;
&lt;pre&gt;
select to_tsvector('saint jean')
&amp;quot;'jean':2 'saint':1&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Donc, si mon champs &lt;code&gt;name&lt;/code&gt; contient des espaces, mes sous chaines
vont contenir des espaces aussi. Par exemple 'aint je', comme sous chaine de
'saint jean'. C'est pourquoi je remplace les espaces par un autre
caractère : la chaine 'aint je' deviendra 'aint-je' et sera indexée comme
telle. Dans le cas contraire 'aint' et 'je' seront découpées en deux mots,
alors que je veux indexer la chaine entière. Il me faut donc faire de même lors
de la requête : si quelqu'un cherche &amp;quot;aint je&amp;quot; , il faut chercher
&amp;quot;aint-je&amp;quot;, j'aurais ainsi un format pivot, et la recherche fulltext
matchera!&lt;/p&gt;
&lt;p&gt;Il faut donc que je sauvegarde dans &lt;code&gt;name_splited&lt;/code&gt;, le résultat
de la fonction &lt;code&gt;getSubStrings()&lt;/code&gt;, et mettre le résultat en lower
case si je veux être insensible à la casse. Une requêtes pour l'auto complétion
de &amp;quot;aint je&amp;quot; ressemblera à:&lt;/p&gt;
&lt;pre&gt;
select name from city where this_.name_splited @@ to_tsquery('simple',lower('aint-j'))
&lt;/pre&gt;
&lt;p&gt;@ @ correspond à l'opérateur fulltext de PostgreSQL, et la fonction
&lt;code&gt;lower()&lt;/code&gt; permet de rendre l'auto-complétion insensible à la casse.
La requête sur 500 000 tuples met désormais 13 ms, utilise les indexes,et le
résultat &amp;quot;saint Jean&amp;quot; fait partie des résultats. C'est gagné&lt;/p&gt;
&lt;p&gt;Si vous voulez que votre recherche soit insensible aux accents et caractères
spéciaux(comme c'est souvent le cas, pour que l'auto-complétion soit
pertinente), je vous propose une fonctions Postgres que vous utiliserez aux
mêmes endroits que vous utilisez la fonction &lt;code&gt;lower&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;
CREATE OR REPLACE FUNCTION to_flat_text(text) RETURNS text AS
$body$
DECLARE
 chaine text;
BEGIN
chaine:=trim(lower($1));
 chaine:=replace(chaine,'-',' ');
 chaine:=translate(chaine,'âãäåāăąàèéêëēĕėęěìíîïìĩīĭóôõöōŏőðøùúûüũūŭůçñÿž',  'aaaaaaaaeeeeeeeeeiiiiiiiiooooooooouuuuuuuucnyz');
 chaine:=replace(chaine,'.',' ');
 chaine:=replace(chaine,'&amp;quot;',' ');
 chaine:=replace(chaine,';',' ');
 RETURN chaine;
END;
$body$
LANGUAGE 'plpgsql' RETURNS NULL ON NULL INPUT ;
&lt;/pre&gt;
&lt;p&gt;A vous de l'adapter selon vos besoins.&lt;/p&gt;
&lt;p&gt;La solution peut vous paraître alambiquée mais marche vraiment très bien. Je
lai testée avec 28 millions de tuples, et l'auto-complétion est vraiment
fluide. Si quelqu'un a une autres solution (Postgres only), je suis preneur
:)&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2009/12/21/Am%C3%A9liorer-les-performances-de-like-et-recherche-partielle-de-mots-avec-Postgres-pour-faire-de-l-autocompletion#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2009/12/21/Am%C3%A9liorer-les-performances-de-like-et-recherche-partielle-de-mots-avec-Postgres-pour-faire-de-l-autocompletion#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/469985</wfw:commentRss>
      </item>
    
  <item>
    <title>Différence entre Bohr bug, Schrödinbug, Heisenbug, Mandelbug</title>
    <link>http://davidmasclet.gisgraphy.com/post/2009/12/18/Diff%C3%A9rence-entre-Bohr-bug%2C-Schr%C3%B6dinbug%2C-Heisenbug%2C-Mandelbug</link>
    <guid isPermaLink="false">urn:md5:8ed8b9b7f984484b6abd3686a722fe52</guid>
    <pubDate>Fri, 18 Dec 2009 12:33:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Tests / XUnit</category>
        <category>Bohr bug</category><category>Heisenbug</category><category>Mandelbug</category><category>Schrödinbugs</category>    
    <description>&lt;p&gt;Pendant plusieurs années, j'ai travaillé dans une équipe QA (Quality
Assurance), où mon rôle était de trouver les causes et de corriger les bugs que
d'autres équipes avaient faits: il y avait les développeurs qui implémentaient
les fonctionnalités, les testeurs / bêta-testeurs, et mon équipe qui prenait en
charge les bugs un peu retords. J'avoue avoir eu des fois beaucoup de mal à
reproduire certains bugs.&lt;/p&gt;
&lt;p&gt;J'ai appris par expérience que certains bugs pouvaient apparaitre seulement
dans des cas de tests et ne se seraient jamais reproduits dans une utilisation
normale, d'autres n'apparaissent plus lorsqu'on tente de les observer. C'est
là, la différence entre les Bohr bugs, Schrödinbugs, Heisenbugs, et Mandelbugs.
Toutes ses catégories de bugs tirent leurs noms de physiciens. Voici les
différences fondamentales :&lt;/p&gt;    &lt;ul&gt;
&lt;li&gt;Un Heisenbug, dont le nom est tiré de &lt;a href=&quot;http://fr.wikipedia.org/wiki/Werner_Heisenberg&quot; hreflang=&quot;fr&quot;&gt;Werner_Heisenberg&lt;/a&gt;, est un bug dont les caractéristiques sont altérées
lorsqu'on l'observe. L'affichage d'un log, l'ajout d'une sonde, le fait de
faire tourner un débuggeur ou une console jmx, peut modifier le comportement de
l'application qui fait que le bug ne se produit plus. La physique quantique
suit également cette règle comme la célèbre expérience des &lt;a href=&quot;http://fr.wikipedia.org/wiki/Fentes_de_Young&quot; hreflang=&quot;fr&quot;&gt;fentes doubles
d'Young&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Un bohr bug, tire son nom de &lt;a href=&quot;http://fr.wikipedia.org/wiki/Niels_Bohr&quot; hreflang=&quot;fr&quot;&gt;Niels_Bohr&lt;/a&gt;, et n'a
en rien ses caractéristiques modifiées lorsqu'on l'observe, à la différence
d'un heisenbug. C'est un bug dit 'classique'.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Un Schrödinbug, lui tire son nom de &lt;a href=&quot;http://fr.wikipedia.org/wiki/Erwin_Schr%C3%B6dinger&quot; hreflang=&quot;en&quot;&gt;Erwin
Schrödinger&lt;/a&gt; .C'est un bug qui n'apparait qu'après une utilisation
inhabituelle du logiciel et qui ne serait pas apparu dans une utilisation
classique par les utilisateurs. Ce bug est souvent déstabilisant car il
apparait sans qu'aucune modification de code ne soit faite. C'est le bug, bien
connu, du &amp;quot;oui mais ça marchait avant&amp;quot; :). C'est le cas, par exemple, lorsqu'un
test, ou l'utilisation d'un trick mode met le logiciel dans un état instable. A
l'inverse après un test, il peut arriver qu'un bug disparaisse, et là c'est le
bug, &amp;quot;si je te jure quand je l'ai fais, ça marchait pas&amp;quot;. Les Schrödinbugs,
vous poussent à croire un peu plus les gens, et à ne pas les considérer comme
des mythomanes :)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Un Mandelbug, en hommage à &lt;a href=&quot;http://fr.wikipedia.org/wiki/Beno%C3%AEt_Mandelbrot&quot; hreflang=&quot;fr&quot;&gt;Benoit
Mandelbrot&lt;/a&gt;, est par nature, le bug le plus difficile à reproduire car les
étapes pour le reproduire sont tellement complexes qu'il semble se reproduire
de façon aléatoire et chaotiques. Souvent le bug est reproductible, mais les
étapes imposent des conditions qui ne sont pas toujours identifiées ou sur
lesquelles nous n'avons pas la main. Les bugs concernant les threads, ou les
bugs apparaissant selon une certaine charge du système sont souvent des
mandelbugs. Ce bug engendre souvent des &amp;quot;can not reproduce&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Un bug peut être de plusieurs types à la fois, et si vous tombez sur un
mandelbug-heisenbug, je vous souhaite bon courage :)&lt;/p&gt;
&lt;p&gt;Il est amusant de constater que tous ces types de bugs tirent leur nom de
scientifiques. Comme quoi la correction de bugs est vraiment une
science !&lt;/p&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2009/12/18/Diff%C3%A9rence-entre-Bohr-bug%2C-Schr%C3%B6dinbug%2C-Heisenbug%2C-Mandelbug#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2009/12/18/Diff%C3%A9rence-entre-Bohr-bug%2C-Schr%C3%B6dinbug%2C-Heisenbug%2C-Mandelbug#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/469449</wfw:commentRss>
      </item>
    
  <item>
    <title>Encoding et alphabets internationaux : comprendre et debugger (2e partie)</title>
    <link>http://davidmasclet.gisgraphy.com/post/2009/12/11/Encoding-et-alphabets-internationaux-%3A-comprendre-et-debugger-%282e-partie%29</link>
    <guid isPermaLink="false">urn:md5:24710eefb3cd71af838bc90a6801d924</guid>
    <pubDate>Wed, 16 Dec 2009 17:42:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Encoding</category>
        <category>encoding</category><category>java</category>    
    <description>&lt;p&gt;Après le premier &lt;a href=&quot;http://davidmasclet.gisgraphy.com/post/2009/12/08/Travailler-avec-diff%C3%A9rents-alphabets&quot;&gt;post&lt;/a&gt; où
j'expliquais ce qu'était l'encoding, je vais, cette fois, aborder comment gérer
l'encoding en Java. mon but étant de faire un article qui se veut complet,
quitte à être un peu long. Je donnerai, à chaque fois des exemples de code pour
illustrer. Sommaire :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Comment gérer et débugger les problèmes d'encoding en Java&lt;/li&gt;
&lt;li&gt;La représentation interne des Strings en Java&lt;/li&gt;
&lt;li&gt;Encoding du fichier source&lt;/li&gt;
&lt;li&gt;Manipulation de Strings&lt;/li&gt;
&lt;li&gt;Ecriture dans un fichier&lt;/li&gt;
&lt;li&gt;Connaitre et modifier l'encoding de votre JVM&lt;/li&gt;
&lt;li&gt;Comprendre ce qu'est input method et inputContext&lt;/li&gt;
&lt;li&gt;Affichage dans un logiciel (navigateur,éditeur,...)&lt;/li&gt;
&lt;li&gt;Comment gérer l'encoding dans un environnement Web (Apache, Tomcat, ModJK,
framework MVC, base de données, navigateurs).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;tous les exemples de code sont réunis dans une classe que j'ai appelé
'EncodingProblemsRepairKit', disponible en annexe à la fin du post. Le code
n'est pas blindé (pas de tests, pas de vérification des paramètres
d'entrée,etc) et n'est donné qu'a titre d'illustration et fournit quelques
méthodes pour débugger plus facilement&lt;/p&gt;    &lt;h3&gt;Comment gérer et débugger les problèmes d'encoding en Java&lt;/h3&gt;
&lt;p&gt;Lorsque vous avez un problème d'encoding en Java, il vous faut trouver le ou
les maillons qui 'cassent' l'encoding. Est-ce :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La lecture de la source d'entrée : la soumission d'un formulaire web,
la saisie en ligne de commande, la lecture d'un fichier,...&lt;/li&gt;
&lt;li&gt;L'écriture des données sur la sortie : l'affichage sur une page web,
l'écriture dans un fichier, la sortie sur la console,...&lt;/li&gt;
&lt;li&gt;Le logiciel que l'on utilise qui affiche mal le résultat : votre
navigateur internet peut ne pas arriver à détecter l'encoding automatiquement,
ou cette option est peut être désactivée, vos préférences définissent un
encoding UTF-8 alors que le contenu est en ISO-8859-1,...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela ne veux pas dire que vous devez garder le même encoding, de bout en
bout, pour les différentes étapes, mais que la lecture ou l'écriture des
informations doivent être cohérentes : Vous ne pouvez lire des données en
UTF-8 si ces dernières sont en ISO-8859-1, et vice versa. Cela parait évident,
mais représente la majorité des problèmes d'encoding.&lt;/p&gt;
&lt;p&gt;Plusieurs règles doivent être respectées lors de votre debug :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les problèmes d'encoding sont dépendants de l'environnement (JVM, Système
d'exploitation, locale système, LC_TYPE, etc), il ne faut pas crier victoire
trop vite : un problème d'encoding résolu est un problème où la solution
ne laisse pas de place au hasard ! et où des tests unitaires couvrent les
différents environnements possibles !&lt;/li&gt;
&lt;li&gt;Travaillez en hexadécimal, vous permet de comprendre la représentation
'brute'. Des méthodes utilitaires sont disponibles dans la classe
EncodingProblemsRepairKit disponible en fichier joint à ce post&lt;/li&gt;
&lt;li&gt;N'utilisez pas les méthodes qui utilisent l'encoding par défaut, cela rend
votre code fragile à l'environnement d'exécution (nous allons y revenir en
détails)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour comprendre comment débugger ces problèmes, il faut comprendre la
différence entre travailler avec des octets (aka : byte / Byte) ou
travailler avec des caractères Unicode représentés par la classe Character ou
le type natif char. On peut par exemple connaitre le sens d'écriture d'un char
(de droite à gauche ou de gauche à droite) en appelant
&lt;code&gt;getDirectionality()&lt;/code&gt;. Ce qui n'est évidemment pas possible avec un
byte :)&lt;/p&gt;
&lt;p&gt;En java, les &lt;code&gt;Reader&lt;/code&gt; / &lt;code&gt;Writer&lt;/code&gt; travaillent avec des
caractères Unicode, alors que des &lt;code&gt;InputStream&lt;/code&gt; /
&lt;code&gt;OutputStream&lt;/code&gt; travaillent avec des octets. Il est par exemple
absurde de lire un fichier contenant une image avec un &lt;code&gt;Reader&lt;/code&gt;,
puisque son contenu est binaire. En revanche utiliser un
&lt;code&gt;InputStream&lt;/code&gt; a du sens. Un &lt;code&gt;Inputstream&lt;/code&gt; va lire octet
par octet sans ce soucier de l'encoding, tandis qu'un &lt;code&gt;Reader&lt;/code&gt; va
lire, caractère par caractère selon un algorithme défini par l'encoding. C'est
pourquoi il n'est pas possible de spécifier l'encoding lors de la lecture dans
un &lt;code&gt;InputStream&lt;/code&gt;, alors que pour un &lt;code&gt;Reader&lt;/code&gt;, on en a
besoin pour connaitre la façon de lire les caractères :&lt;/p&gt;
&lt;p&gt;Dans le code ci dessus, je crée un buffer où les octets vont être lus, un à
un, donc, peu importe l'encoding.&lt;/p&gt;
&lt;pre&gt;
InputStream stream = new FileInputStream(file);
&lt;/pre&gt;
&lt;p&gt;Voici maintenant un bout de code où Je lis, caractère par caractère, dans le
flux d'octets, en considérant que les caractères Unicode sont encodés en UTF-8
et je les place dans un tableau de char&lt;/p&gt;
&lt;pre&gt;
InputStream stream = new FileInputStream(file);
Reader reader = new InputStreamReader(stream, &amp;quot;UTF-8&amp;quot;);
char[] OneChar = new char[1];
reader.read(OneChar);
&lt;/pre&gt;
&lt;p&gt;L'erreur classique, qui engendre des problèmes est de ne pas spécifier
l'encoding, en effet :&lt;/p&gt;
&lt;pre&gt;
 reader = new InputStreamReader(stream);
&lt;/pre&gt;
&lt;p&gt;est tout à fait valide, et comme le stipule la javadoc, c'est l'encoding par
défaut qui sera utilisé, c'est à dire celui de la JVM (voir plus bas, comment
connaitre et modifier l'encoding de la JVM)&lt;/p&gt;
&lt;p&gt;Il existe la même différence entre un Writer et un Outputstream :&lt;/p&gt;
&lt;pre&gt;
OutputStream stream = new FileOutputStream(file);
&lt;/pre&gt;
&lt;p&gt;Pour un writer :&lt;/p&gt;
&lt;pre&gt;
OutputStream stream = new FileOutPutStream(file);
Writer writer = new OutputStreamWriter(stream, &amp;quot;UTF-8&amp;quot;);
writer.write(CharArray);
&lt;/pre&gt;
&lt;p&gt;Là aussi il faut TOUJOURS spécifier l'encoding même si le constructeur
&lt;code&gt;OutputStreamWriter(OutputStream out)&lt;/code&gt; existe, ne l'utilisez pas!
Cela vous évitera des problèmes du style : sur ma machine, l'encoding est
correct, mais en production, l'encoding comporte des caractères bizarres. C'est
pour cela que je disais que l'encoding dépendait de la plateforme.&lt;/p&gt;
&lt;h3&gt;La représentation interne des Strings en Java&lt;/h3&gt;
&lt;p&gt;Les strings déclarées dans les fichiers sources Java sont compilées et
encodées en UTF-8 (UTF-8 modifié, si on veux être rigoureux), et ce peu importe
l'encodage du fichier source !. C'est très facilement vérifiable : créons
une classe contenant la String suivante :&lt;/p&gt;
&lt;pre&gt;
String maString =&amp;quot;ééééé&amp;quot;;
&lt;/pre&gt;
&lt;p&gt;Puis ouvrons le .class compilé, avec un éditeur hexadécimal : on verra
(entre bien d'autres informations) 5 fois C3 A9. ce qui correspond à 5 'é'
encodés en UTF-8. Recommençons l'opération en changeant l'encoding du fichier
source : cela ne change rien, et nous aurions pu la déclarer en Unicode,
cela aurait donné la même chose :&lt;/p&gt;
&lt;pre&gt;
public static String maStringEnUnicode=&amp;quot;\u00E9\u00E9\u00E9\u00E9\u00E9&amp;quot;;
&lt;/pre&gt;
&lt;p&gt;Mais attention, il ne faut pas confondre l'encoding des Strings compilées
avec la représentation interne des Strings dans la JVM qui est un Integer de 16
bits non signé, pouvant soit représenté, un code Unicode allant de U+0000 à
U+FFFF ou un caractére UTF-16.&lt;/p&gt;
&lt;h3&gt;Encoding du fichier source&lt;/h3&gt;
&lt;p&gt;Puisque les chaines de caractères sont encodées une fois compilées vous
n'avez pas à vous soucier de l'encoding du fichier source. Vous évitez aussi le
problème de lecture de flux (puisque la String n'est pas lue mais
compilée).&lt;/p&gt;
&lt;p&gt;Pour spécifier l'encoding du fichier source, vous devez spécifier l'option
&lt;code&gt;encoding&lt;/code&gt; au compilateur. Exemple pour un fichier source encodé en
Shift_JIS (japonais) :&lt;/p&gt;
&lt;pre&gt;
javac -encoding Shift_JIS MonFichier.java
&lt;/pre&gt;
&lt;p&gt;Tous les IDE permettent également de spécifier l'encoding. Sinon des outils
comme &lt;a href=&quot;http://java.sun.com/javase/6/docs/technotes/tools/solaris/native2ascii.html&quot;&gt;NativeToAscii&lt;/a&gt;
permettent de convertir des fichiers en unicode/Latin1 afin d'être indépendant
de l'encoding.&lt;/p&gt;
&lt;h3&gt;Manipulation de Strings&lt;/h3&gt;
&lt;p&gt;Maintenant que l'on connait la représentation interne des Strings, on peut
se demander ce qui se passe lorsque l'on fait un getBytes sur une String,
c'est-a-dire que l'on veuille convertir les chars de la string en octets. Quel
sera l'encoding utilisé ? Si une String est mal encodée en sortie, c'est
que vous n'avez peut être pas spécifié l'encoding lors de l'écriture de la
chaine dans un Writer ou que vous avez fait un &lt;code&gt;getBytes()&lt;/code&gt; sur la
string sans spécifier l'encoding.&lt;/p&gt;
&lt;p&gt;Si vous faites un &lt;code&gt;getBytes&lt;/code&gt; sur une chaine, vous aurez un
encoding égal à celui de la JVM, et ce n'est pas forcément ce que vous
vouliez.&lt;/p&gt;
&lt;p&gt;Voici un exemple de code pour le prouver. Créez un fichier avec ce contenu
et executer le sur une machine dont l'encoding est UTF-16 :&lt;/p&gt;
&lt;pre&gt;
public static void writeBytesToFile(String string,File file)throws IOException{
    FileOutputStream out = new FileOutputStream(file);
    System.out.println(&amp;quot;will write '&amp;quot;+string+&amp;quot;' byte to &amp;quot;+file.getAbsolutePath());
    out.write(string.getBytes());
    out.flush();
    out.close();
}

writeBytesToFile(&amp;quot;aé&amp;quot;,File.createTempFile(EncodingProblemsRepairKit.class.getSimpleName(), &amp;quot;.txt&amp;quot;))
&lt;/pre&gt;
&lt;p&gt;Vous remarquerez qu'on utilise un OutputStream et donc que nous sérialisons
des octets, mais En ouvrant le fichier écrit, vous verrez le contenu
suivant&lt;/p&gt;
&lt;pre&gt;
FF FE 00 61 00 E9
&lt;/pre&gt;
&lt;p&gt;Le BOM FE FF pour dire qu'il s'agit d'UTF-16 Big Endian. puis les lettre 'a'
et 'é' en UTF-16&lt;/p&gt;
&lt;p&gt;Si vous recommencez, mais que vous l'éxecutez sur une machine dont
l'encoding est UTF-8, le fichier de sortie sera :&lt;/p&gt;
&lt;pre&gt;
61 C3 A9
&lt;/pre&gt;
&lt;p&gt;Pour être indépendant de l'encoding de la machine, n'utilisez pas
&lt;code&gt;getBytes()&lt;/code&gt; mais plutôt &lt;code&gt;getBytes(String charset)&lt;/code&gt;, Cela
rendra votre code plus robuste.&lt;/p&gt;
&lt;h3&gt;Ecriture dans un fichier&lt;/h3&gt;
&lt;p&gt;Pour sérialiser dans un fichier il existe une classe FileWriter, mais Je
déconseille fortement de l'utiliser. Explications :&lt;/p&gt;
&lt;pre&gt;
public static void writeStringCharToFile(String string,File file)throws IOException{
    FileWriter out = new FileWriter(file);
    System.out.println(&amp;quot;will write '&amp;quot;+string+&amp;quot;' char to &amp;quot;+file.getAbsolutePath());
    out.write(getStringAsChar(string));
    out.flush();
    out.close();
}

public static char[] getStringAsChar(String string){
      char[] charArray = new char[string.length()];
      string.getChars(0, string.length(),charArray , 0);
      return charArray;
  }


File tempFileChar = File.createTempFile(EncodingProblemsRepairKit.class.getSimpleName(), &amp;quot;.txt&amp;quot;);
writeStringCharToFile(&amp;quot;aé&amp;quot;,tempFileChar);

&lt;/pre&gt;
&lt;p&gt;En ouvrant le fichier vous constaterez que l'encoding à été choisi par la
JVM selon l'environnement d'execution&lt;/p&gt;
&lt;p&gt;Avec FileWriter, vous n'avez pas la possibilité de spécifier l'encoding.
Alors on peut se poser la question &amp;quot;Puis-je sérialiser dans un fichier sans
tenir compte de l'encoding de la machine ? &amp;quot;. La réponse se trouve dans la
javadoc de FileWriter :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FILEWRITER : Convenience class for writing character files. The
constructors of this class assume that the default character encoding and the
default byte-buffer size are acceptable. To specify these values yourself,
construct an OutputStreamWriter on a FileOutputStream. C'est pourquoi je
déconseille de l'utiliser.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Il existe d'ailleurs une méthode qui renvoie l'encoding du fileWriter
&lt;code&gt;getEncoding()&lt;/code&gt;. Pour spécifier l'encoding, il faut écrire la chose
suivante :&lt;/p&gt;
&lt;pre&gt;
public static void writeStringCharToFile(String string,File file,String encoding)throws IOException{
    OutputStream stream = new FileOutputStream(file);
    OutputStreamWriter out = new OutputStreamWriter(stream,encoding);
    System.out.println(&amp;quot;will write '&amp;quot;+string+&amp;quot;' char to &amp;quot;+file.getAbsolutePath()+&amp;quot; with encoding=&amp;quot;+encoding);
    out.write(getStringAsChar(string));
    out.flush();
    out.close();
}

writeStringCharToFile(&amp;quot;aé&amp;quot;,&amp;quot;UTF-8&amp;quot;);
&lt;/pre&gt;
&lt;p&gt;et le fichier contiendrait :&lt;/p&gt;
&lt;pre&gt;
61 C3 A9.
&lt;/pre&gt;
&lt;p&gt;CQFD! et ceci, peu importe l'encoding de la machine.&lt;/p&gt;
&lt;p&gt;A propos de la méthode getEncoding(), voici une chose importante à
savoir ;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If this instance was created with the OutputStreamWriter(OutputStream,
String) constructor then the returned name, being unique for the encoding, may
differ from the name passed to the constructor. This method may return null if
the stream has been closed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;En d'autres termes, la méthode &lt;code&gt;getEncoding()&lt;/code&gt; peut ne pas
retourner la même valeur que celle spécifiée par le constructeur. Toujours bon
à savoir !&lt;/p&gt;
&lt;h3&gt;Connaitre et modifier l'encoding de votre JVM&lt;/h3&gt;
&lt;p&gt;Au risque de me répéter, l'encoding par défaut dépend de l'environnement
d'exécution, et par conséquent de la JVM. plusieurs propriétés affectent
l'encoding et certaines sont dépendantes du vendeur de la JVM : pour Sun vous
avez les options 'sun.jnu.encoding' et &amp;quot;file.encoding&amp;quot;. Vous pouvez les
modifier en les spécifiant en ligne de commande avec -Doption=valeur.&lt;/p&gt;
&lt;p&gt;Mon but est de vous démontrer que même si &lt;code&gt;file.encoding&lt;/code&gt; permet
de spécifier l'encoding par défaut, ce n'est pas la bonne façon de le faire.
Comme nous allons le voir cela dépend de la version et du vendeur de JVM, n'est
pas modifiable au runtime, et n'est pas pris en compte par toutes les méthodes.
Pour spécifier l'encoding par défaut, Sun recommande de modifier la locale
système&lt;/p&gt;
&lt;p&gt;A mon sens, la meilleure pratique est de ne pas utiliser l'encoding par
défaut mais de le déclarer dans une variable, et de l'utiliser à chaque fois
qu'un encoding par défaut doit être utilisé. Cela n'empêche pas d'utiliser
quelque fois un encoding spécifique, quand ce dernier est déterminé au runtime
(e.g : dans le header 'content-encoding' d'une requête HTTP). Cette
technique à l'avantage d'avoir une gestion centralisée puisqu'il vous suffit de
changer cette valeur (au runtime, ou à la compilation), et de ne pas être
dépendant d'options de lancement ou du système d'exploitation sous-jacent, sur
lequel vous n'avez pas toujours la main.&lt;/p&gt;
&lt;p&gt;Commençons par voir comment modifier ces options au runtime :&lt;/p&gt;
&lt;pre&gt;
public static void setJVMEncoding(String charset) {
        setSystemProperty(&amp;quot;file.encoding&amp;quot;, charset);
        setSystemProperty(&amp;quot;sun.jnu.encoding&amp;quot;, charset);
}

public static void setSystemProperty(String name, String value) {
        if (System.getProperty(name) == null || !System.getProperty(name).equals(value)) {
            System.out.println(&amp;quot;change system property from &amp;quot; + System.getProperty(name) + &amp;quot; to &amp;quot; + value);
            System.setProperty(name, value);

            System.out.println(&amp;quot;System property&amp;quot; + name + &amp;quot; is now : &amp;quot; + System.getProperty(name));
        } else {
            System.out.println(name + &amp;quot;=&amp;quot; + System.getProperty(&amp;quot;file.encoding&amp;quot;));
        }
}

public static String UTF8_CHARSET=&amp;quot;UTF-8&amp;quot;;
setJVMEncoding(UTF8_CHARSET);
&lt;/pre&gt;
&lt;p&gt;La variable qui permet de spécifier le charset est &amp;quot;file.encoding&amp;quot;.
Malheureusement, le charset utilisé par les &lt;code&gt;Writer&lt;/code&gt; /
&lt;code&gt;Reader&lt;/code&gt; n'est pas celui renvoyé par Charset.getDefault(), changer
@file.encoding@@ n'aura donc aucun effet. (le comportement serait différent
avec la jvm 6.0, mais je n'ai pas vérifié). Démonstration :&lt;/p&gt;
&lt;pre&gt;
public static void printCharsetandFileEncoding(){
    System.out.println(&amp;quot;Default Charset for writer before change=&amp;quot; + getDefaultCharSet());
    System.out.println(&amp;quot;Default Charset=&amp;quot; + Charset.defaultCharset());
    System.setProperty(&amp;quot;file.encoding&amp;quot;, &amp;quot;Latin-1&amp;quot;);
    System.out.println(&amp;quot;file.encoding=&amp;quot; + System.getProperty(&amp;quot;file.encoding&amp;quot;));
    System.out.println(&amp;quot;Default Charset=&amp;quot; + Charset.defaultCharset());
    System.out.println(&amp;quot;Default Charset for writer after change=&amp;quot; + getDefaultCharSet());
}

private static String getDefaultCharSet() {
    OutputStreamWriter writer = new OutputStreamWriter(new ByteArrayOutputStream());
    String enc = writer.getEncoding();
    return enc;
}
&lt;/pre&gt;
&lt;p&gt;L'éxecution de ce code vous donnera avec une JVM 5.0 et
-Dfile.encoding=UTF-8 :&lt;/p&gt;
&lt;pre&gt;
Default Charset for writer before change=UTF8
Default Charset=UTF-8
file.encoding=Latin-1
Default Charset=ISO-8859-1
Default Charset for writer after change=UTF8
&lt;/pre&gt;
&lt;p&gt;En recommencant avec -Dfile.encoding=ISO-8859-1 :&lt;/p&gt;
&lt;pre&gt;
Default Charset for writer before change=ISO8859_1
Default Charset=ISO-8859-1
file.encoding=Latin-1
Default Charset=ISO-8859-1
Default Charset for writer after change=ISO8859_1

&lt;/pre&gt;
&lt;p&gt;L'encoding utilisé par les Writers utilise bien file.encoding, mais ne peux
être changer au runtime. (&lt;a href=&quot;http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4163515&quot;&gt;en savoir plus sur
les bugs de file.encoding&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;file.encoding&lt;/code&gt; ne change rien non plus à l'encoding utilisé par
défaut pour &lt;code&gt;String.getBytes()&lt;/code&gt; si on le change au runtime :
créons un fichier, encodé en UTF-8, avec ce code :&lt;/p&gt;
&lt;pre&gt;
public static String stringToHexa(String string) {
    System.out.println(&amp;quot;will get bytes for &amp;quot;+string+&amp;quot; without encoding&amp;quot;);
    byte[] bytes =string.getBytes();
    String hexaStringSubstring=&amp;quot;&amp;quot;;
        for (int i =0; i&amp;lt; bytes.length; i++){
        byte x = bytes[i];
        int intValue = Byte.valueOf(x).intValue();
        String hexaString = intValue==0?&amp;quot;00&amp;quot;:Integer.toHexString(intValue);
        hexaStringSubstring = hexaString.substring(hexaString.length()-2);
        System.out.println(&amp;quot;Hexa is:=&amp;quot; + hexaStringSubstring);
        }
        return hexaStringSubstring;
}

 System.setProperty(&amp;quot;file.encoding&amp;quot;, &amp;quot;ISO-8859-1&amp;quot;);
    stringToHexa(maString);
    System.setProperty(&amp;quot;file.encoding&amp;quot;, &amp;quot;UTF-16&amp;quot;);
    stringToHexa(maString);
&lt;/pre&gt;
&lt;p&gt;l'éxécution affichera :&lt;/p&gt;
&lt;pre&gt;
will get bytes for aé without encoding
Hexa is:=61
Hexa is:=c3
Hexa is:=a9
will get bytes for aé without encoding
Hexa is:=61
Hexa is:=c3
Hexa is:=a9
&lt;/pre&gt;
&lt;p&gt;La sortie prouve que l'encoding utilisé est UTF-8 : comme l'encoding du
fichier source. En revanche si vous spécifiez l'option
-Dfile.encoding=ISO-8859-1 au démarrage, sans changer l'encoding du fichier
source (UTF-8) : la sortie sera différente :&lt;/p&gt;
&lt;pre&gt;
will get bytes for aé without encoding
Hexa is:=61
Hexa is:=e9
will get bytes for aé without encoding
Hexa is:=61
Hexa is:=e9
&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;file.encoding&lt;/code&gt; ne change rien lorsqu'il est changé au runtime
mais est bien pris en compte au lancement de la JVM.&lt;/p&gt;
&lt;p&gt;Si vous ne la spécifier pas, sa valeur sera déterminée par la locale du
système d'exploitation, Changer la locale est la façon que Sun recommande pour
changer l'encoding par défaut. depuis la jvm 1.4 de sun, vous pouvez spécifiez
la locale avec les paramètres -Duser.language -Duser.country et -Duser.variant
mais cela rend votre code dépendant de la version et du vendeur de la JVM. Pas
top !&lt;/p&gt;
&lt;p&gt;Voici un résumé :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;file.encoding&lt;/code&gt; n'est pas la bonne façon de spécifier l'encoding
par défaut, même si cela fonctionnera dans certains cas lorsqu'il est spécifié
au lancement de la JVM.&lt;/li&gt;
&lt;li&gt;La bonne façon de modifier l'encoding par défaut est de changer la
locale.&lt;/li&gt;
&lt;li&gt;Ne pas Utiliser &lt;code&gt;filewriter&lt;/code&gt; dont l'exécution dépendra de la
machine.&lt;/li&gt;
&lt;li&gt;La meilleure pratique est de ne pas utilisé l'encoding par défaut, mais de
le spécifier à chaque fois (Reader, Writer, getBytes, ...)&lt;/li&gt;
&lt;li&gt;Changer &lt;code&gt;file.encoding&lt;/code&gt; au runtime change la valeur de
Charset.defaultCharset().&lt;/li&gt;
&lt;li&gt;Changer &lt;code&gt;file.encoding&lt;/code&gt; ne change rien à l'encoding par défaut
réellement utilisé par les &lt;code&gt;Writer&lt;/code&gt; / &lt;code&gt;Reader&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;file.encoding&lt;/code&gt; est utilisé pour determiner l'encoding à
utiliser par la méthode &lt;code&gt;getBytes&lt;/code&gt; de la classe String quand aucun
encoding est spécifié, mais ne sera pris en compte que s'il est spécifié au
lancement avec l'option -D.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tout ça peut vous paraitre un peu complexe, et je ne vous dirai pas le
contraire, mais le tout, c'est de le savoir. Au fil du temps, cela deviendra un
réflexe.&lt;/p&gt;
&lt;p&gt;Spécifier l'encoding n'est pas tout, et pour éviter les
UnsuportedEncodingException, vous pouvez vouloir connaitre les charsets
supportés par votre JVM :&lt;/p&gt;
&lt;pre&gt;
Charset.availableCharsets();
&lt;/pre&gt;
&lt;p&gt;Cela correspond au charsets déclarés dans le package Charset.jar dans le
dossier jre/lib/ de la JVM.&lt;/p&gt;
&lt;p&gt;Pour en savoir plus, consultez la &lt;a href=&quot;http://java.sun.com/javase/technologies/core/basic/intl/faq.jsp&quot; hreflang=&quot;en&quot;&gt;FAQ de Sun sur le sujet&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Comprendre ce qu'est input method et inputContext&lt;/h3&gt;
&lt;p&gt;Si vous utilisez eclipse, lorsque vous cliquez droit dans un éditeur de
texte, vous avez un menu qui se nomme 'Input Methods'. Plusieurs valeur sont
disponibles : System, Simpl, cédille,Thai-Lao, vietnamine, etc. Les input
Methods et inputcontext permettent à un utilisateur de pouvoir rentrer des
milliers de caractères en utilisant que quelque touche du clavier. Une séquence
de touches au clavier permet de saisir un caractère spécifique. Vous pouvez
modifier l'inputContext utilisé grace à la methode selectInputMethod de la
class &lt;a href=&quot;http://java.sun.com/javase/6/docs/api/java/awt/im/InputContext.html#selectInputMethod%28java.util.Locale%29&quot; hreflang=&quot;en&quot;&gt;InputContext&lt;/a&gt;. Ceci ne concerne que les caractères saisie par
des éditeurs écrits en Java, et vous ne devez que très rarement y toucher.&lt;/p&gt;
&lt;h3&gt;affichage dans un logiciel (navigateur,éditeur,...)&lt;/h3&gt;
&lt;p&gt;La dernière cause, peut ne pas venir de votre code mais d'un mauvais
affichage. Le plus simple pour diagnostiquer ce problème étant de visualiser le
flux en Héxadécimal, ou de visualiser les trames (avec etherReal par
exemple)&lt;/p&gt;
&lt;p&gt;Pour ce qui est des polices d'affichage en java , vous devez éditer un
fichier qui se nome font.properties qui permet d'associer les polices systèmes
aux polices java ( &lt;a href=&quot;http://java.sun.com/javase/6/docs/technotes/guides/intl/fontconfig.html&quot; hreflang=&quot;en&quot;&gt;en savoir plus&lt;/a&gt; ).&lt;/p&gt;
&lt;p&gt;Pour ce qui est des navigateurs, ils choisissent l’encoding à utiliser selon
des priorités :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Utilisateur — Si l’utilisateur choisit via son navigateur un encodage
spécifique à utiliser c’est ce dernier qui sera utilisé et aucun autre&lt;/li&gt;
&lt;li&gt;En-tête — En-tête envoyée par Apache. Si vous spécifiez une en-tête via
Java/Tomcat/Jetty c’est cet encodage qui aura la priorité sur celui
d’Apache&lt;/li&gt;
&lt;li&gt;Balise META — Si aucune en-tête n’a été envoyée par le serveur c’est
l’encoding spécifié dans la balise META qui sera utilisé (ou dans le prologue
XML si présent)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Voila! félicitation, vous êtes arrivés à la fin de l'article, j'espère que
cela vous servira et que vos efforts seront récompensés :)&lt;/p&gt;
&lt;p&gt;Le troisième et dernier post portera sur les différentes couches qui
rentrent en compte lors de la requête /réponse d'une requête HTTP, et comment
spécifier l'encoding Apache, Tomcat, ModJK, framework MVC, base de données,
navigateurs).&lt;/p&gt;</description>
    
          <enclosure url="http://davidmasclet.gisgraphy.com/public/EncodingProblemsRepairKit.java"
      length="6018" type="text/plain" />
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2009/12/11/Encoding-et-alphabets-internationaux-%3A-comprendre-et-debugger-%282e-partie%29#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2009/12/11/Encoding-et-alphabets-internationaux-%3A-comprendre-et-debugger-%282e-partie%29#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/467183</wfw:commentRss>
      </item>
    
  <item>
    <title>Encoding et alphabets internationaux : comprendre et debugger (1ere partie)</title>
    <link>http://davidmasclet.gisgraphy.com/post/2009/12/08/Travailler-avec-diff%C3%A9rents-alphabets</link>
    <guid isPermaLink="false">urn:md5:cd9f0ba95180d0e71520a85a543d2886</guid>
    <pubDate>Fri, 11 Dec 2009 12:22:00 +0100</pubDate>
    <dc:creator>Masclet</dc:creator>
        <category>Encoding</category>
        <category>big endian</category><category>BOM</category><category>encoding</category><category>little Endian</category>    
    <description>&lt;p&gt;Lors de l'affichage de pages avec des mots dans des alphabets différents il
peux arriver que des problèmes d'affichages apparaissent avec des � et des
Ã©&lt;/p&gt;
&lt;p&gt;Dans le cas de &lt;a href=&quot;http://www.gisgraphy.com/&quot;&gt;Gisgraphy&lt;/a&gt; (exemple
pour &lt;a href=&quot;http://services.gisgraphy.com/displayfeature.html?featureId=1816670&quot;&gt;Pékin&lt;/a&gt;),
j'ai voulu vérifier que tout s'affiche correctement et que ce qui est affiché
correspond bien à l'équivalent ASCII.&lt;/p&gt;
&lt;p&gt;Je me suis donc rendu sur cette &lt;a href=&quot;http://rcalvi.com/cu_archive/Transliteration/demo.html&quot; hreflang=&quot;en&quot;&gt;page&lt;/a&gt;
et j'ai testé 北京, ce qui m'a donne 'bei jing'.ouah ça marche! et mon navigateur
affiche le japonais! essayons en arabe avec بكين , cela me donne &amp;quot;bkyn&amp;quot; qui se
rapproche de Beijing et Pékin en phonétique, et là aussi ça s'affiche
correctement.&lt;/p&gt;
&lt;p&gt;Cela m'a donné l'idée d'un post pour démystifier l'encoding par une suite de
questions-réponses. (Finalement je le ferai en deux posts car c'est un peu
long, et je risque de perdre et décourager pas mal de lecteurs si je n'en fais
qu'un). Le premier expliquera ce qu'est l'encoding :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Qu'est ce qu'Unicode ?&lt;/li&gt;
&lt;li&gt;Qu'est-ce que l'encoding, le BOM, le Little et le Big Endian&lt;/li&gt;
&lt;li&gt;Quel est le meilleur encoding ?&lt;/li&gt;
&lt;li&gt;Comment fonctionne une police de caractère ?&lt;/li&gt;
&lt;li&gt;Comment gérer les sens d'écriture différents (par exemple, l'arabe qui
s'écrit de droite à gauche)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le second expliquera comment résoudre les problèmes fréquents avec de cas
concrets et des extraits de code :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Comment gérer l'encoding dans un environnement web en java (Java, Apache,
Tomcat, ModJK, base de données, navigateurs)&lt;/li&gt;
&lt;li&gt;Comment gérer et débugger des problèmes d'encoding en java&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Après ces deux posts vous ne devriez plus jamais voir ça : � sur vos
sites, ou si c'est le cas vous saurez comment résoudre le problème :)&lt;/p&gt;    &lt;h3&gt;Qu'est ce qu'Unicode&lt;/h3&gt;
&lt;p&gt;Unicode attribut à tout caractère, symbole, ponctuation de n’importe quel
système d’écriture, de langue, ou d'alphabet, un nom et un identifiant
numérique, quelle que soit la plate-forme informatique.&lt;/p&gt;
&lt;p&gt;On représente un caractère Unicode par la lettre U suivi d'un '+' et d'un
nombre en représentation hexadécimal. Exemple pour la lettre A : U+0041.
Le nom 'Unicode' associé à la lettre A est 'LATIN CAPITAL LETTER A'. On
remarquera qu'il s'agit de la lettre 'A' en majuscule et que son équivalent en
minuscule est différent (U+0061 et 'LATIN SMALL LETTER A'). Une liste de tous
les noms classés par ordre alphabétique est consultable &lt;a href=&quot;http://unicode.org/charts/charindex.html&quot;&gt;ici&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Qu'est-ce que l'encoding, le BOM, le Little et le Big endian&lt;/h3&gt;
&lt;p&gt;L'encoding est une façon binaire de représenter un caractère Unicode. Si on
prend l'exemple d'un fichier, l'encoding permet de stocker de façon binaire les
caractères Unicode.&lt;/p&gt;
&lt;p&gt;Les codages sont nombreux : (UTF-8, UTF-16, UTF-32, ISO-8859,
Windows-1252 ...) et souvent déclinés en plusieurs versions. Pour L’iso-latin,
il en existe plusieurs versions :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ISO-8859-1 où certains caractères comme 'œ' ont tout simplement été
oubliés, au même titre que le 'ÿ'. Si vous vendez des œufs à Haÿ-les-roses,
votre site risque de souffrir de problèmes d'encoding s'il est en ISO-8859-1
(tout savoir sur cette &lt;a href=&quot;http://www.gutenberg.eu.org/pub/GUTenberg/publicationsPDF/25-andre.pdf&quot;&gt;sombre
histoire&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;ISO-8859-15 où le symbole euro € à été ajouté ainsi que œ et Ÿ.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour l'UTF-16, c'est la même chose, il existe :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UTF-16&lt;/li&gt;
&lt;li&gt;UTF-16BE pour 'Big Endian'&lt;/li&gt;
&lt;li&gt;UTF-16LE pour 'Little Endian'&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Little Endian et big Endian correspondent au sens de lecture des octets. On
peut prendre la même analogie qu'entre l'arabe qui se lit de droite à gauche,
et l'anglais qui se lit de gauche à droite. Pour les ordinateurs c'est pareil,
il faut leur indiquer quel est le sens de lecture. il n'y a pas de règle, le
but étant de permettre au programme qui affichera les caractères, de connaitre
le sens de lecture.&lt;/p&gt;
&lt;p&gt;Pour Windows, à chaque région du monde correspond un codage:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows-1252 (connu sous le nom de ANSI ou CP1252) pour l'alphabet
latin&lt;/li&gt;
&lt;li&gt;Windows-1256 pour les pays arabes&lt;/li&gt;
&lt;li&gt;Windows-1257 pour les pays baltes&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le cyrillique et le japonais ont aussi plusieurs versions, il existe deux
versions du chinois,...L'encoding, c'est un peu la tour de babel de
l'informatique&lt;/p&gt;
&lt;p&gt;Cela vous parait abstrait? Prenons un exemple pour mieux comprendre :
Ouvrons un éditeur de texte, créons un nouveau fichier et écrivons un simple
'é', puis sauvegardons. Avant de sauvegarder, il nous est demandé de spécifier
l'encoding, choisissons UTF-8. et UTF-8.txt en nom de fichier. Recommençons
l'opération avec ISO-8859-1,UTF-16, UTF-16BE et UTF-16LE.&lt;/p&gt;
&lt;p&gt;Maintenant ouvrons les fichiers avec un éditeur hexadécimal, afin de voir
comment ils ont été sauvegardés. Voici le contenu Hexadécimal de chaque
fichier:&lt;/p&gt;
&lt;pre&gt;
ISO-8859-1 : E9 0A
UTF-8.txt : C3 A9 0A
UTF-16.txt : FF FE E9 00 FF FE 0A 00
UTF-16BE.txt: 00 E9 00 0A
UTF-16LE.txt : E9 00 0A 00
&lt;/pre&gt;
&lt;p&gt;On peux faire les observations suivantes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pour une même lettre, le fichier est enregistré différemment. C'est ça
l'encoding !&lt;/li&gt;
&lt;li&gt;Le caractère 0A est ajouté à la fin de chaque fichier pour en marquer la
fin.&lt;/li&gt;
&lt;li&gt;Tous les UTF-16 prennent plus de place que l'UTF-8 puisqu'ils sont stockés
sur 16 bits&lt;/li&gt;
&lt;li&gt;L'UTF-16 commence par FFFE, il s'agit du BOM ou byte order mark. Le BOM
sert à spécifier le type d'endian utilisé. Lorsque vous sauvegardez un fichier
en UTF-16 vous ne spécifiez pas s'il s'agit de Big Endian ou de Little Endian,
à la différence des UTF-16BE et UTF-16-LE. Un BOM égal à FFFE signifie qu'il
s'agit d'UTF-16 en Little Endian, et un BOM égal à FEFF signifie qu'il s'agit
d'UTF-16 en Big Endian. J'en veux pour preuve que le caractère 'é' est
représenté de la même façon (E9 00) en UTF-16LE qu'en UTF-16 avec le BOM FFFE,
tandis que l'ordre des octets est inversé en Big Endian (00 E9)&lt;/li&gt;
&lt;li&gt;L'UTF-16BE et l'UTF-16LE prennent moins de place car il ne faut pas
spécifier l'endian via le BOM&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Quel est le meilleur encodage&lt;/h3&gt;
&lt;p&gt;Par expérience je pense que l'UTF-8 est souvent le plus adapté (il permet de
coder tout les caractères unicode, et son support est répandu), mais bien sur
il n'y a pas de bonne ou mauvaise réponse, sinon on n'utiliserai que le
meilleur et on jetterai les autres. Les avantages et les inconvénients résident
dans la place qu'ils occupent et le traitement qu'il faut faire pour les
afficher.&lt;/p&gt;
&lt;p&gt;L’Unicode n’assigne pas un nombre d'octets prédéfini par caractère, on
l’appelle &amp;quot;multi-octet&amp;quot; : Ainsi, chaque caractère peut occuper entre 1 et
6 octets en UTF-8, ce qui optimise la taille de stockage mais augmente la
difficulté de traitement. l'UTF-16 code tout les caractères sur 2 * 16 bits (un
pour spécifier l'endian, l'autre pour le caractère), ce qui prend plus de place
mais minimise les traitements.&lt;/p&gt;
&lt;p&gt;Enfin L'UTF-16BE et UTF-16LE occupe moins de place, mais comme on ne connait
pas L'endian utilisé il faut le spécifier par un autre moyen : par exemple
dans le charset mime type du protocole HTTP, l'attribut encoding d'un fichier
XML, etc. S'il est impossible de spécifier le type d'endian, il faudra se
rabattre sur de l'UTF-16 avec BOM ou espérer que le logiciel qui affichera
l'unicode a un algorithme de détection de l'encoding performant...plutôt
hasardeux.&lt;/p&gt;
&lt;h3&gt;Qu'est-ce qu'une police de caractère&lt;/h3&gt;
&lt;p&gt;Maintenant que l'on a compris comment L'Unicode est représenté en binaire,
voyons comment il est affiché, cette représentation est appelée 'glyphe'. C'est
le rôle des polices de caractères Unicode. Souvent on pense à tord, qu'a un
caractères glyphe correspond un Unicode. C'est malheureusement faux et plus
complexe : un glyphe peut être associé à un ou plusieurs caractères
Unicode et il n'y a aucune bijection.&lt;/p&gt;
&lt;p&gt;Exemple avec le glyphe ê qui il peux être représenté de plusieurs façons en
Unicode :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Par le caractère unicode U+00EA (C3 AA en encoding UTF-8)&lt;/li&gt;
&lt;li&gt;Soit en précédent la lettre e par un autre caractère Unicode stipulant que
le caractère suivant aura un accent circonflexe.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;le même glyphe sera affiché.&lt;/p&gt;
&lt;p&gt;On dira du premier caractère qu’il est précomposé, du second que c’est une
composition (deux caractères forment un seul glyphe composé de deux caractères
Unicode)&lt;/p&gt;
&lt;p&gt;Démonstration : Sachant que l'accent circonflexe en Unicode est égal à
U+0302 et CC 82 en UTF-8.&lt;/p&gt;
&lt;p&gt;J'ouvre un éditeur hexadécimal et met CC 82 65 C3 AA A0 CC 82 :
COMBINING CIRCUMFLEX ACCENT 65 : LATIN SMALL LETTER E C3 AA : LATIN
SMALL LETTER E WITH CIRCUMFLEX A0 : ferme le fichier je sauvegarde, puis
j'ouvre le fichier avec GEdit : deux ê apparaissent. L'encoding a été
détecté en UTF-8 par GEdit et c'est bien la preuve qu'un glyphe n'est pas
associé qu'a un seul caractère Unicode&lt;/p&gt;
&lt;h3&gt;Comment gérer les sens d'écritures différents&lt;/h3&gt;
&lt;p&gt;En général les logiciels modernes le détectent automatiquement : si
vous copier coller 'الرباط' dans différents logiciels, le sens d'écriture
changera automatiquement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sous firefox ou internet explorer, si vous le copier-coller dans un champs
de saisie puis que vous appuyez plusieurs fois sur la barre d'espace, vous
verrez les espaces s'afficher à la gauche du texte : le sens de l'écriture
a changé.&lt;/li&gt;
&lt;li&gt;Sous Windows : Avec wordpad (Démarrer-&amp;gt;executer-&amp;gt;tapez wordpad,
ne pas confondre avec Word). une liste déroulant affichera 'arabe' et en
appuyant sur la flèche gauche du clavier, elle se déplacera à droite :) un peu
troublant mais logique.&lt;/li&gt;
&lt;li&gt;Sous Ubuntu / Linux : Avec GEdit c'est encore plus fort, le texte
s'affichera directement à droite.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En aucun cas c'est à vous de gérer cette complexité, au pire, il faut le
spécifier au logiciel qui affiche les glyphes, Unicode ne définit rien à ce
sujet, il s'agit de l'affichage, pas de la représentation informatique.vous
pouvez aller sur cette &lt;a href=&quot;http://www.lexilogos.com/clavier/zhongwen_pinyin.htm&quot;&gt;page&lt;/a&gt; pour écrire en
différentes langues.&lt;/p&gt;
&lt;p&gt;pour convertir d'Unicode en différents formats, j'ai trouvé cette page très
intéressante : http://rishida.net/tools/conversion/&lt;/p&gt;
&lt;p&gt;Le second expliquera comment résoudre les problèmes fréquents avec de cas
concrets et des extraits de code :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Comment gérer l'encoding dans un environnement web en java (Java, Apache,
Tomcat, ModJK, base de données, navigateurs)&lt;/li&gt;
&lt;li&gt;Comment gérer et débugger des problèmes d'encoding en java&lt;/li&gt;
&lt;/ul&gt;</description>
    
    
    
          <comments>http://davidmasclet.gisgraphy.com/post/2009/12/08/Travailler-avec-diff%C3%A9rents-alphabets#comment-form</comments>
      <wfw:comment>http://davidmasclet.gisgraphy.com/post/2009/12/08/Travailler-avec-diff%C3%A9rents-alphabets#comment-form</wfw:comment>
      <wfw:commentRss>http://davidmasclet.gisgraphy.com/feed/atom/comments/465773</wfw:commentRss>
      </item>
    
</channel>
</rss>