<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
> <channel><title>Des Geeks et des lettres &#187; Programmation</title> <atom:link href="http://desgeeksetdeslettres.com/blog/category/programmation-java/feed" rel="self" type="application/rss+xml" /><link>http://desgeeksetdeslettres.com/blog</link> <description>La lettre &#34;J&#34; de JAVA EE à JEUX VIDÉOS</description> <lastBuildDate>Sat, 04 Feb 2012 01:38:29 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3</generator> <item><title>Mettre en pause un carrousel au survol de la souris</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/mettre-en-pause-un-carrousel-au-survol-de-la-souris</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/mettre-en-pause-un-carrousel-au-survol-de-la-souris#comments</comments> <pubDate>Fri, 27 Jan 2012 08:30:22 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[carrousel]]></category> <category><![CDATA[javascript]]></category> <category><![CDATA[jquery]]></category> <category><![CDATA[pause]]></category> <category><![CDATA[survol souris]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2680</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/mettre-en-pause-un-carrousel-au-survol-de-la-souris"><img
align="left" hspace="5" width="155" height="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2012/01/Carrousel_large01-150x150.jpg" class="alignleft wp-post-image tfe" alt="" title="Carrousel" /></a>Il n&#8217;existe quasiment plus de site d&#8217;informations sans carrousel qui présente les actualités à la une, les billets sponsorisés ou des publicités. Vous l&#8217;aurez compris je ne vous parle pas du manège &#171;&#160;chevaux en bois&#160;&#187; ou du tapis roulant dans les aéroports, mais bien de l&#8217;élément IHM qui consiste à afficher une liste d&#8217;items défilants, [...]]]></description> <content:encoded><![CDATA[<p></p><p>Il n&#8217;existe quasiment plus de site d&#8217;informations sans carrousel qui présente les actualités à la une, les billets sponsorisés ou des publicités.</p><p>Vous l&#8217;aurez compris je ne vous parle pas du manège &laquo;&nbsp;chevaux en bois&nbsp;&raquo; ou du tapis roulant dans les aéroports, mais bien de l&#8217;élément IHM qui consiste à afficher une liste d&#8217;items défilants, souvent de manière circulaire à l&#8217;image du Flip 3D dans Windows Vista et Windows 7 (touche Win+Tab), ou du célèbre affichage des albums dans iTunes.</p><p
style="text-align: center;"><img
class="aligncenter  wp-image-2681" title="Carrousel" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2012/01/Carrousel_large01.jpg" alt="" width="210" height="213" /></p><p>Ce ne sont pas les scripts ou plugins javascript qui manquent pour en mettre un en place sur votre site perso ou votre blog, l&#8217;un de ceux que j&#8217;utilise est le plugin <a
href="http://caroufredsel.frebsite.nl/" target="_blank">jQuery.carouFredSel</a> qui a le mérite d&#8217;être complet et simple d&#8217;utilisation, le seul reproche que je lui ferai est son poids car il pèse tout de même 30 ko en version minifiée.</p><h2>Structure du carrousel</h2><p>L&#8217;avantage de ce carrousel réside dans le fait qu&#8217;il est tout à fait possible de faire défiler du contenu HTML quel qu&#8217;il soit, au lieu d&#8217;être obligé de faire défiler des images. Voici un exemple de structure HTML simple que j&#8217;utilise :</p><pre class="brush: html">&lt;div id="carouselWrapper"&gt;
	&lt;div id="carousel"&gt;
		&lt;div class="work"&gt;
			&lt;img src="/img/portfolio/magicsupremacy.jpg" alt="Magic Supremacy" width="450" height="350" /&gt;
			&lt;h3&gt;Magic Supremacy&lt;/h3&gt;
			&lt;p&gt;
				Entièrement réalisé, ce site communautaire pour les joueurs de Magic l'Assemblée propose une multitude d'outils de qualité
				devenus rapidement indispensables pour les joueurs quel que soit leur niveau. Voici quelques fonctionnalités intéressantes :
				un simulateur de drafts qui permet de jouer entre amis, un générateur de paquets scellés, la création et le partage de decks
				et un design fluide et unique. Un exemple à suivre !
			&lt;/p&gt;
			&lt;a href="http://magicsupremacy.fr" target="_blank" title="Magic Supremacy"&gt;Visiter le site »&lt;/a&gt;
		&lt;/div&gt;
		&lt;div class="work"&gt;
			&lt;img src="/img/portfolio/desgeeksetdeslettres.jpg" alt="Des Geeks et des lettres" width="450" height="350" /&gt;
			&lt;h3&gt;Des Geeks et des lettres&lt;/h3&gt;
			&lt;p&gt;
				Ce blog WordPress a été customisé par nos soins. Simple mais efficace, il est la preuve que le minimalisme dans le design
				peut entraîner l'addiction du lecteur : avec plus de 1000 visites uniques journalières, ce blog peut vous donner envie
				d'ouvrir votre boutique en ligne et d'attirer les internautes dans votre &lt;em&gt;agence web&lt;/em&gt;.
			&lt;/p&gt;
			&lt;a href="http://desgeeksetdeslettres.com/blog" target="_blank" title="Des Geeks et des lettres"&gt;Visiter le site »&lt;/a&gt;
		&lt;/div&gt;
		&lt;div class="work"&gt;
			&lt;img src="/img/portfolio/agencewebdesigner.jpg" alt="Agence Web-Designer" width="450" height="350" /&gt;
			&lt;h3&gt;Agence Web-Designer&lt;/h3&gt;
			&lt;p&gt;
				Il ne s'agit ni plus ni moins que du site que vous êtes en train de consulter. Il vous présente rapidement ce en quoi vous
				êtes en mesure de vous attendre en nous confiant la création de votre espace web. Pour toute demande élaborée, n'hésitez pas
				à nous contacter directement.
			&lt;/p&gt;
			&lt;a href="http://agence-web-designer.fr" target="_blank" title="Agence Web-Designer"&gt;Visiter le site »&lt;/a&gt;
		&lt;/div&gt;
	&lt;/div&gt;
	&lt;a id="prev" href="#" title="Précédent"&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;
	&lt;a id="next" href="#" title="Suivant"&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;
&lt;/div&gt;</pre><h2>Déclenchement du défilement</h2><p>Le plugin carouFredSel possède un attribut qui permet de lancer le défilement automatiquement ainsi qu&#8217;un paramètre permettant de régler le temps d&#8217;affichage entre chaque diapo, à titre d&#8217;exemple voici ce que cela donnerait :</p><pre class="brush: javascript">$('#carousel').carouFredSel({
	scroll	: {
		duration	: 0,
		pauseDuration	: 5000
	},
	auto	: true
});</pre><p>Cependant pour faire ce que nous souhaitons, à savoir pouvoir mettre en pause et reprendre le défilement au survol de la souris sur une diapo, je n&#8217;utilise pas l&#8217;option de défilement automatique offert par le plugin, je déclenche moi-même le changement de diapo toutes les X secondes en appelant l&#8217;évènement <em>next</em> qui permet de passer à la diapo suivante.</p><p>Voici le script JavaScript :</p><pre class="brush: javascript">var timer = setInterval(function() {
		$("#carousel").trigger("next");
	}, 5000);</pre><h2>Arrêt/Reprise du défilement</h2><p>La variable <em>timer</em> représente désormais le moyen de simuler l&#8217;arrêt et la reprise de l&#8217;opération paramétrée à la fonction setInterval(), à savoir le déclenchement du passage à la diapo suivante. L&#8217;arrêt du processus se fait en appelant la méthode clearInterval() sur la variable <em>timer</em>.</p><p>Il vous suffit alors de programmer les endroits où vous souhaitez que votre souris agisse sur le défilement, dans mon cas je choisis la diapo tout entière mais cela pourrait être uniquement le titre ou l&#8217;image, et d&#8217;écrire ces quelques lignes JavaScript :</p><pre class="brush: javascript">$('#carousel .work').mouseenter(function() {
		clearInterval(timer);
	}).mouseleave(function() {
		clearInterval(timer);
		timer = setInterval(function () {
				$("#carousel").trigger("next");
			}, 5000);
	});</pre><p>Pour voir le mécanisme en action, je vous invite à visiter notre nouveau site à Greg et moi, et de survoler avec votre souris le carrousel présenté en page d&#8217;accueil :</p><p
style="text-align: center;"><a
title="Greg et Mimie se lancent !" href="http://agence-web-designer.fr"><img
class="aligncenter" src="http://agence-web-designer.fr/img/icon/logo.jpg" alt="Agence Web-Designer" width="225" height="60" /></a></p> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/mettre-en-pause-un-carrousel-au-survol-de-la-souris/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Soumission d&#8217;un formulaire au format JSON avec jQuery</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/soumission-dun-formulaire-au-format-json-avec-jquery</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/soumission-dun-formulaire-au-format-json-avec-jquery#comments</comments> <pubDate>Thu, 12 Jan 2012 08:00:49 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[formulaire]]></category> <category><![CDATA[jquery]]></category> <category><![CDATA[json]]></category> <category><![CDATA[json-lib]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2654</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/soumission-dun-formulaire-au-format-json-avec-jquery"><img
align="left" hspace="5" width="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/12/json-file.png" class="alignleft wp-post-image tfe" alt="" title="JSON" /></a>J&#8217;ai jusque là utilisé principalement le format de données JSON pour récupérer des informations d&#8217;un quelconque service métier ou d&#8217;une API, comme récupérer les données d&#8217;un joueur ou les caractéristiques d&#8217;une carte Magic par exemple. Cependant le format JSON se prête aussi bien à la récupération d&#8217;informations qu&#8217;à l&#8217;envoi de données, c&#8217;est sur cette deuxième [...]]]></description> <content:encoded><![CDATA[<p></p><p>J&#8217;ai jusque là utilisé principalement le format de données JSON pour récupérer des informations d&#8217;un quelconque service métier ou d&#8217;une API, comme récupérer les données d&#8217;un joueur ou les caractéristiques d&#8217;une carte Magic par exemple.</p><p><img
class="aligncenter size-full wp-image-2655" title="JSON" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/12/json-file.png" alt="" width="128" height="128" /></p><p>Cependant le format JSON se prête aussi bien à la récupération d&#8217;informations qu&#8217;à l&#8217;envoi de données, c&#8217;est sur cette deuxième option que porte la suite de ce billet.</p><h2>Formulaire de contact</h2><p>(Je vous renvoi à <a
title="Struts : comment renvoyer des données JSON en AJAX" href="http://desgeeksetdeslettres.com/blog/programmation-java/struts-comment-renvoyer-des-donnees-json-en-ajax">cet ancien article</a> pour ceux qui souhaiteraient connaître une façon de retourner des données au format JSON depuis un contrôleur Java.)</p><p>Pour mettre en application l&#8217;envoi de données JSON vers un contrôleur Java, partons du principe que nous avons un formulaire de contact et que les données saisies par l&#8217;utilisateur doivent faire l&#8217;objet d&#8217;une action de sauvegarde en base de données par exemple.<br
/> La structure du formulaire de contact est la même pour les deux cas présentés ci-dessous :</p><ul><li>HTML</li></ul><pre class="brush: html">&lt;form action="" id="formContact"&gt;
	&lt;label for="nom"&gt;Nom&lt;/label&gt;
	&lt;input type="text" id="nom"/&gt;&lt;br/&gt;
	&lt;label for="prenom"&gt;Prénom&lt;/label&gt;
	&lt;input type="text" id="prenom"/&gt;&lt;br/&gt;
	&lt;label for="age"&gt;Age&lt;/label&gt;
	&lt;input type="text" id="age"/&gt;&lt;br/&gt;
	&lt;input type="submit" value="Envoyer"/&gt;
&lt;/form&gt;</pre><h3 style="padding-left: 30px;">Envoi éparpillé</h3><p>L&#8217;envoi classique correspond à envoyer les données saisies par l&#8217;utilisateur de façon unitaire, chaque champ du formulaire correspondant à un paramètre de la requête HTTP. Pour une quantité très réduite de données à envoyer cela peut suffire, mais lorsque nous parlons de dizaine voire de centaine de données, vous vous rendrez vite compte que cela nécessite trop de travail.</p><ul><li>JS</li></ul><pre class="brush: javascript">&lt;script src="http://code.jquery.com/jquery-1.7.1.min.js"&gt;&lt;/script&gt;
    &lt;script type="text/javascript"&gt;
		$(document).ready(function() {
			$('#formContact').submit(function() {
				var contact = {
					nom : $('#nom').val(),
					prenom : $('#prenom').val(),
					age : $('#age').val()
				};
				$.ajax({
					type: 'POST',
					url: 'contact.do',
					data: contact,
					success: function() {
						alert("fin du traitement java");
					}
				});
				return false;
			});
		});
	&lt;/script&gt;</pre><ul><li>JAVA</li></ul><pre class="brush: java">// Récupère des données saisies par l'utilisateur
String nom = request.getParameter("nom");
String prenom = request.getParameter("prenom");
String age = request.getParameter("age");
// création de l'objet à persister en base
Contact contact = new Contact();
contact.setNom(nom);
contact.setPrenom(prenom);
contact.setAge(age);
// TODO : contrôles éventuels &amp;  insertion en base de données</pre><p>L&#8217;inconvénient de cette méthode est que les données soumises à l&#8217;aide de l&#8217;objet JavaScript &#8216;contact&#8217; ne sont pas automatiquement transformées vers un objet de classe &#8216;Contact&#8217; en Java, les paramètres sont à récupérer un à un, charge à nous de construire l&#8217;objet Java à la main &#8230; imaginez le travail avec des dizaines de paramètres dans tous les sens.</p><p>La deuxième méthode présentée ci-dessous permet de faciliter et d&#8217;optimiser tout ce travail.</p><h3 style="padding-left: 30px;">Envoi groupé</h3><p>Vous l&#8217;aurez compris, ce principe permet de convertir simplement l&#8217;objet JavaScript &#8216;contact&#8217; en objet Java de classe &#8216;Contact&#8217;, voici les outils que l&#8217;on devra utiliser pour réussir ceci :</p><p
style="padding-left: 30px;">- l&#8217;envoi des données, côté client, se fait à présent au format JSON à l&#8217;aide d&#8217;un script JavaScript nommé <a
title="json2.js" href="https://github.com/douglascrockford/JSON-js">json2.js</a><br
/> - les données reçues au format JSON, côté serveur, peuvent à présent être converties en objet Java en une ligne grâce à la librairie <a
title="Json-lib" href="http://json-lib.sourceforge.net">Json-lib</a></p><ul><li>JS</li></ul><pre class="brush: javascript">&lt;script src="http://code.jquery.com/jquery-1.7.1.min.js"&gt;&lt;/script&gt;
&lt;script src="js/json2.js"&gt;&lt;/script&gt;
    &lt;script type="text/javascript"&gt;
		$(document).ready(function() {
			$('#formContact').submit(function() {
				var contact = {
					nom : $('#nom').val(),
					prenom : $('#prenom').val(),
					age : $('#age').val()
				};
				$.ajax({
					type: 'POST',
					url: 'contact.do',
					data: 'contact=' + JSON.stringify(contact),
					success: function() {
						alert("fin du traitement java");
					}
				});
				return false;
			});
		});
	&lt;/script&gt;</pre><ul><li>JAVA</li></ul><pre class="brush: java">// Récupère des données saisies par l'utilisateur
String contactAsString = request.getParameter("contact");
// création de l'objet à persister en base
JSONObject jsonObject = JSONObject.fromObject(contactAsString);
Contact contact = (Contact) JSONObject.toBean(jsonObject, Contact.class);
// TODO : contrôles éventuels &amp; insertion en base de données</pre><p>L&#8217;exemple simpliste de ce billet ne permet pas de se rendre compte tout à fait des possibilités offertes par l&#8217;utilisation du format JSON, étant donné que nous avons pris pour exemple un formulaire avec 3 champs très simples, mais croyez-moi l&#8217;utilisation de ce format d&#8217;échange entre le client et le serveur (dans les deux sens) est une véritable bénédiction et se fait très facilement <img
src='http://desgeeksetdeslettres.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/soumission-dun-formulaire-au-format-json-avec-jquery/feed</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>JavaScript : Les bonnes pratiques</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/javascript-les-bonnes-pratiques</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/javascript-les-bonnes-pratiques#comments</comments> <pubDate>Mon, 12 Dec 2011 13:27:32 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[bonne pratique]]></category> <category><![CDATA[guide]]></category> <category><![CDATA[javascript]]></category> <category><![CDATA[language rules]]></category> <category><![CDATA[style rules]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2645</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/javascript-les-bonnes-pratiques"><img
align="left" hspace="5" width="155" height="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/12/WikiBookTitel_JavaScript-150x150.jpg" class="alignleft wp-post-image tfe" alt="" title="JavaScript Guide" /></a>Avant d&#8217;appréhender un nouveau langage de programmation, il est utile voire primordiale de se documenter en amont pour comprendre un peu les subtilités et les rouages techniques du langage. Du temps bien dépensé qui vous permettra de ne pas pondre du code douteux ou mal écrit, surtout lorsque ce langage est JavaScript. Ce n&#8217;est pas [...]]]></description> <content:encoded><![CDATA[<p></p><p>Avant d&#8217;appréhender un nouveau langage de programmation, il est utile voire primordiale de se documenter en amont pour comprendre un peu les subtilités et les rouages techniques du langage. Du temps bien dépensé qui vous permettra de ne pas pondre du code douteux ou mal écrit, surtout lorsque ce langage est JavaScript.</p><p
style="text-align: center;"><a
href="http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml"><img
class="aligncenter size-full wp-image-2646" title="JavaScript Guide" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/12/WikiBookTitel_JavaScript.jpg" alt="" width="240" height="180" /></a></p><p>Ce n&#8217;est pas moi qui vais vous donner les bonnes pratiques du langage JavaScript, mais Google. Je viens de tomber sur cette page très bien faite qui va vous permettre de démarrer de la meilleure des façons : <a
href="http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml">Google JavaScript Style Guide</a>.</p><p>Chaque thème abordé par le document propose un exemple et un lien vers une page qui approfondie la bonne pratique, voici les sujets traités par le document :</p><ul><li><strong>JavaScript Language Rules</strong></li></ul><ol><li>var</li><li>Constants</li><li>Semicolons</li><li>Nested functions</li><li>Function Declarations Within Blocks</li><li>Exceptions</li><li>Custom exceptions</li><li>Standards features</li><li>Wrapper objects for primitive types</li><li>Multi-level prototype hierarchies</li><li>Method definitions</li><li>Closures</li><li>eval()</li><li>with() {}</li><li>this</li><li>for-in loop</li><li>Associative Arrays</li><li>Multiline string literals</li><li>Array and Object literals</li><li>Modifying prototypes of builtin objects</li><li>Internet Explorer&#8217;s Conditional Comments</li></ol><ul><li><strong>JavaScript Style Rules</strong></li></ul><ol><li>Naming</li><li>Custom toString() methods</li><li>Deferred initialization</li><li>Explicit scope</li><li>Code formatting</li><li>Parentheses</li><li>Strings Visibility (private and protected fields)</li><li>JavaScript Types</li><li>Comments</li><li>Inner Classes and Enums</li><li>Compiling</li><li>Tips and Tricks</li></ol><div>Avez-vous vous même des bonnes pratiques ou des astuces à nous faire partager ?</div> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/javascript-les-bonnes-pratiques/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Gravatar : Mise en place d&#8217;une Tag Library</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/gravatar-mise-en-place-dune-tag-library</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/gravatar-mise-en-place-dune-tag-library#comments</comments> <pubDate>Fri, 04 Nov 2011 13:37:02 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[avatar]]></category> <category><![CDATA[email]]></category> <category><![CDATA[gravatar]]></category> <category><![CDATA[image]]></category> <category><![CDATA[tag library]]></category> <category><![CDATA[taglib]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2629</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/gravatar-mise-en-place-dune-tag-library"><img
align="left" hspace="5" width="155" src="http://s.gravatar.com/images/logo.png" class="alignleft wp-post-image tfe" alt="" title="Gravatar logo" /></a>Dans &#171;&#160;Gravatar&#160;&#187; il y a le mot &#171;&#160;Avatar&#160;&#187;, en disant ça tout est dit, ce service propose à n&#8217;importe qui de lier une image à une adresse email, généralement une image de profil représentant la personne physique liée à une adresse email virtuelle (avatar). Lorsque cette adresse email sera utilisée dans un blog ou forum [...]]]></description> <content:encoded><![CDATA[<p></p><p>Dans &laquo;&nbsp;Gravatar&nbsp;&raquo; il y a le mot &laquo;&nbsp;Avatar&nbsp;&raquo;, en disant ça tout est dit, <a
title="Gravatar" href="http://fr.wikipedia.org/wiki/Gravatar">ce service</a> propose à n&#8217;importe qui de lier une image à une adresse email, généralement une image de profil représentant la personne physique liée à une adresse email virtuelle (avatar).</p><p>Lorsque cette adresse email sera utilisée dans un blog ou forum au moment de la saisie d&#8217;un commentaire par exemple (un plug-in Gravatar est disponible sur la plupart des moteurs de blog connus), l&#8217;image &laquo;&nbsp;gravatar&nbsp;&raquo; associée à cette adresse email sera affichée automatiquement à côté de ce commentaire.</p><p>Pratique non ? plus besoin d&#8217;avoir à gérer sur chaque blog ou forum son avatar, l&#8217;adresse email suffit à présent.</p><p
style="text-align: center;"><a
href="http://fr.gravatar.com/"><img
class="aligncenter" title="Gravatar logo" src="http://s.gravatar.com/images/logo.png" alt="" width="274" height="55" /></a></p><p>J&#8217;ai voulu utiliser ce mécanisme sur mon site perso sur Magic l&#8217;Assemblée, au niveau des commentaires de cartes et de decks, c&#8217;est à présent chose faite (<a
href="http://magicsupremacy.fr/index.jsp#!menu=deck&amp;id=415">voici un exemple</a> en dépliant le bloc &laquo;&nbsp;Commentaires&nbsp;&raquo; en bas), et voici ce que j&#8217;ai du faire pour en arriver à ce résultat.</p><h2>Comment ça marche ?</h2><p>Le mécanisme mis en place par gravatar pour afficher une image à partir d&#8217;une adresse email est simple, il suffit d&#8217;utiliser la balise HTML &lt;img/&gt; et d&#8217;y mettre en attribut &laquo;&nbsp;src&nbsp;&raquo; une url gravatar avec l&#8217;adresse email concernée, cela pourrait se traduire de la façon suivante :</p><pre class="brush: html">&lt;img src="http://www.gravatar.com/avatar/monadresseemail@gmail.com"&gt;</pre><p>Toutefois, pour éviter le spam, les adresses e-mail sont hachées avec la fonction de <a
href="http://fr.wikipedia.org/wiki/Md5">cryptage MD5</a>. Cela empêche les robots de récupérer les adresses emails en clair dans le code source de la page.</p><p>Voici donc véritablement à quoi ressemble le code HTML qui permet d&#8217;afficher l&#8217;image gravatar liée à une adresse email.</p><pre class="brush: html">&lt;img src="http://www.gravatar.com/avatar/f628cd552adb34c7601d18bb0b94611f"&gt;</pre><p>Un avatar peut avoir des dimensions allant jusqu&#8217;à 512 pixels, il est toujours carré, et fait 80 pixels de côté par défaut. Si l&#8217;email n&#8217;est associé à aucun compte gravatar, une image par défaut apparaîtra.</p><p><img
class="aligncenter size-full wp-image-2630" title="Exemples image gravatar" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/11/gravatar_image.png" alt="" width="700" height="242" /></p><h2>Tag Library</h2><p>Les pages de mon site étant des JSP, c&#8217;est tout naturellement que je me suis penché du côté d&#8217;une taglib pour automatiser la génération du code présenté ci-dessus, cependant aucune taglib officielle n&#8217;existe.</p><p>Etant simple à écrire, voici le code qui m&#8217;a permis de réaliser cette &laquo;&nbsp;taglib gravatar maison&nbsp;&raquo; en s&#8217;inspirant de bouts de code par ci par là.</p><ul><li><span
style="text-decoration: underline;">Fichier de descripteur gravatar.tld</span></li></ul><pre class="brush: xml">&lt;?xml version="1.0" encoding="iso-8859-1" ?&gt;
&lt;!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
" http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"&gt;
&lt;taglib&gt;
	&lt;tlibversion&gt;1.0&lt;/tlibversion&gt;
	&lt;jspversion&gt;1.2&lt;/jspversion&gt;
	&lt;shortname&gt;Gravatar&lt;/shortname&gt;
	&lt;tag&gt;
		&lt;name&gt;image&lt;/name&gt;
		&lt;tagclass&gt;fr.mtg.supremacy.taglib.GravatarImageTag&lt;/tagclass&gt;
		&lt;bodycontent&gt;empty&lt;/bodycontent&gt;
		&lt;info&gt;Get avatar image from gravatar.com for the given email&lt;/info&gt;
		&lt;attribute&gt;
			&lt;name&gt;email&lt;/name&gt;
			&lt;required&gt;true&lt;/required&gt;
			&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
		&lt;/attribute&gt;
		&lt;attribute&gt;
			&lt;name&gt;alt&lt;/name&gt;
			&lt;required&gt;false&lt;/required&gt;
			&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
		&lt;/attribute&gt;
		&lt;attribute&gt;
			&lt;name&gt;size&lt;/name&gt;
			&lt;required&gt;false&lt;/required&gt;
			&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
		&lt;/attribute&gt;
		&lt;attribute&gt;
			&lt;name&gt;className&lt;/name&gt;
			&lt;required&gt;false&lt;/required&gt;
			&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
		&lt;/attribute&gt;
		&lt;attribute&gt;
			&lt;name&gt;defaultImage&lt;/name&gt;
			&lt;required&gt;false&lt;/required&gt;
			&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
		&lt;/attribute&gt;
		&lt;attribute&gt;
			&lt;name&gt;rating&lt;/name&gt;
			&lt;required&gt;false&lt;/required&gt;
			&lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
		&lt;/attribute&gt;
	&lt;/tag&gt;
&lt;/taglib&gt;</pre><ul><li><span
style="text-decoration: underline;">Fichier web.xml</span></li></ul><pre class="brush: xml">...
&lt;jsp-config&gt;
	&lt;taglib&gt;
		&lt;taglib-uri&gt;gravatar.tld&lt;/taglib-uri&gt;
		&lt;taglib-location&gt;/WEB-INF/tld/gravatar.tld&lt;/taglib-location&gt;
	&lt;/taglib&gt;
&lt;/jsp-config&gt;
...</pre><ul><li><span
style="text-decoration: underline;">Classe fr.mtg.supremacy.taglib.GravatarImageTag</span></li></ul><pre class="brush: java">public class GravatarImageTag extends TagSupport {
	private static final long serialVersionUID = 2766493961169800616L;
	private static final String GRAVATAR_URL = "http://www.gravatar.com/avatar/";
	private String email;
	private String size = "80";
	private String rating = "g";
	private String defaultImage = "";
	private String alt = "";
	private String className = "";
	public void setEmail(Object email) {
		this.email = (String) email;
	}
	public void setSize(Object size) {
		this.size = (String) size;
	}
	public void setRating(Object rating) {
		this.rating = (String) rating;
	}
	public void setDefaultImage(String defaultImage) {
		this.defaultImage = defaultImage;
	}
	public void setAlt(Object alt) {
		this.alt = (String) alt;
	}
	public void setClassName(Object className) {
		this.className = (String) className;
	}
	/**
	 * create the img html tag
	 */
	@Override
	public int doStartTag() throws JspException {
		try {
			String hash = DigestUtils.md5Hex(email.toLowerCase());
			String avatar_url = GRAVATAR_URL + "" + hash;
			StringBuilder img = new StringBuilder("&lt;img src=\"");
			img.append(avatar_url);
			img.append("?size=" + size);
			img.append("&amp;d=" + defaultImage);
			img.append("&amp;r=" + rating + "\"");
			img.append(" alt=\"" + alt + "\"");
			img.append(" class=\"" + className + "\"");
			img.append("/&gt;");
			pageContext.getOut().print(img.toString());
		} catch (IOException e) {
			throw new JspException(e.getMessage());
		}
		return SKIP_BODY;
	}
}</pre><p>On notera au passage l&#8217;utilisation de la bibliothèque d&#8217;<a
href="http://commons.apache.org/codec/">Apache Commons Codec</a> (classe DigestUtils) qui nous permet en une ligne d&#8217;obtenir le hash MD5 de l&#8217;email.</p><ul><li><span
style="text-decoration: underline;">Utilisation dans une JSP</span></li></ul><pre class="brush: java">&lt;%@ taglib uri="gravatar.tld" prefix="gravatar" %&gt;
&lt;gravatar:profil email="mon.adresse.email@gmail.com"/&gt;</pre>]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/gravatar-mise-en-place-dune-tag-library/feed</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>SEO : Bien référencer son site Web full Ajax</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/seo-bien-referencer-son-site-web-full-ajax</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/seo-bien-referencer-son-site-web-full-ajax#comments</comments> <pubDate>Thu, 27 Oct 2011 07:52:19 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[SEO]]></category> <category><![CDATA[ajax]]></category> <category><![CDATA[google]]></category> <category><![CDATA[htmlunit]]></category> <category><![CDATA[java]]></category> <category><![CDATA[javascript]]></category> <category><![CDATA[référencement]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2558</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/seo-bien-referencer-son-site-web-full-ajax"><img
align="left" hspace="5" width="155" height="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/ajax-seo-150x150.jpg" class="alignleft wp-post-image tfe" alt="" title="AJAX &amp; SEO" /></a>AJAX Le gain d&#8217;un site Web full Ajax est clair d&#8217;après moi, tout le monde y gagne : le serveur : il est sollicité plus souvent mais ne retourne que des fragments de pages au lieu de l&#8217;intégralité du contenu des pages, souvent inutiles. Un gain de bande passante notable au passage le client : l&#8217;interface [...]]]></description> <content:encoded><![CDATA[<p></p><h2>AJAX</h2><p>Le gain d&#8217;un site Web full Ajax est clair d&#8217;après moi, tout le monde y gagne :</p><ul><li><strong>le serveur</strong> : il est sollicité plus souvent mais ne retourne que des fragments de pages au lieu de l&#8217;intégralité du contenu des pages, souvent inutiles. Un gain de bande passante notable au passage</li><li><strong>le client</strong> : l&#8217;interface est souvent plus fluide, les pages ne se rechargent pas entièrement à chaque clic et les actions nécessitant du temps ne bloquent pas l&#8217;utilisateur</li></ul><p>Ces points se vérifient si vous vous rendez sur mon site <a
href="http://magicsupremacy.fr">Magic l&#8217;Assemblée</a> qui est basé sur l&#8217;idée d&#8217;une application à page unique (<a
href="http://en.wikipedia.org/wiki/Single-page_application">Single-page application</a>).</p><p
style="text-align: center;"><a
href="http://fr.wikipedia.org/wiki/Seo"><img
class="size-full wp-image-2622 aligncenter" title="AJAX &amp; SEO" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/ajax-seo.jpg" alt="" width="236" height="236" /></a></p><h2>Méfiance</h2><p>L&#8217;AJAX (Asynchronous JavaScript And Xml) est pour beaucoup de personnes synonyme de mauvais référencement dans les moteurs de recherche, et elles n&#8217;ont pas tout à fait tort au premier abord même si le sujet de ce billet est justement de présenter des solutions de contournement (SEO).</p><p>Voici donc les deux principaux arguments contre une utilisation massive d&#8217;AJAX pour les sites web <span
style="text-decoration: underline;">Internet</span> &nbsp;&raquo;grand public&nbsp;&raquo; :</p><ol><li>les requêtes AJAX ne permettent pas de changer l&#8217;url de l&#8217;application, impossible donc d&#8217;utiliser l&#8217;historique de navigation des navigateurs et impossible de partager un lien vers le site pour qu&#8217;il affiche l&#8217;état sur lequel nous nous trouvons actuellement.</li><li>les robots d&#8217;indexation ne pouvant exécuter le JavaScript (langage du navigateur côté client) et les pages du site étant justement chargées par le biais de JavaScript (AJAX) signifie en effet que les robots (Googlebot, Bingbot, etc.) ne pourront pas indexer correctement les urls de vos pages &laquo;&nbsp;ajaxifiées&nbsp;&raquo;.</li></ol><p
style="text-align: center;"><a
href="http://fr.wikipedia.org/wiki/Robot_d'indexation"><img
class="aligncenter size-full wp-image-2623" title="Robots d'indexation ou Web crawler ou Web spider" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/web_spider.gif" alt="" width="300" height="191" /></a></p><h2>Contournement</h2><p>Le premier point soulevé dans les arguments contre est aujourd&#8217;hui assez facilement contournable mais reste néanmoins obligatoire à mettre en place pour résoudre le deuxième point sur le référencement des pages.</p><p>J&#8217;utilise personnellement une bibliothèque JavaScript, nommée <a
href="http://code.google.com/p/unfocus-history-keeper/">History Keeper</a>, qui me permet d&#8217;ajouter une ancre au niveau de l&#8217;url de l&#8217;application (#nom_ancre) et de mapper cette ancre vers une fonction JavaScript qui se chargera de lancer une requête AJAX vers le serveur. Cette bibliothèque me permet donc de changer l&#8217;état d&#8217;une page (ou de mon unique page) sans la recharger entièrement.</p><p
style="text-align: center;"><img
class="aligncenter size-full wp-image-2624" title="History navigation : back and next" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/back_next_history.gif" alt="" width="100" height="100" /></p><p>Chaque page possède désormais une url dédiée (<code>.../#page1</code>, <code>.../#page2</code>, etc.) qui rend l&#8217;historisation fonctionnelle au niveau du navigateur et permet le partage de liens vers un contenu spécifique.</p><p>A noter qu&#8217;il existe beaucoup d&#8217;autres solutions pour contourner ce premier point qui font qu&#8217;il n&#8217;en est plus vraiment un : jQuery propose <a
title="jQuery history" href="http://tkyk.github.com/jquery-history-plugin/">un plugin</a>, GWT propose <a
title="The GWT History Mechanism" href="http://code.google.com/intl/fr/webtoolkit/doc/latest/DevGuideCodingBasicsHistory.html">un mécanisme</a> en interne, Flex <a
title="Using the HistoryManager" href="http://livedocs.adobe.com/flex/3/html/help.html?content=deep_linking_8.html">en fait autant</a>, Vaadin <a
title="URI fragment &amp; history management" href="https://vaadin.com/book/-/page/advanced.urifu.html">aussi</a>, etc.</p><h2>Référencement</h2><p>Le second point évoqué dans les arguments contre est beaucoup plus complexe à appréhender et la solution proposée ci-dessous est plus délicate à mettre en place. En effet le nerf de la guerre reste le positionnement de son site dans les <a
title="SERP : search engine results page" href="http://fr.wikipedia.org/wiki/Page_de_r%C3%A9sultats_d%27un_moteur_de_recherche">SERPs</a> de Google principalement, un site mal référencé dans les moteurs de recherche est un site invisible ce qui entraîne donc peu de trafic, peu de backlinks, peu de ventes, peu de revenus publicitaires, etc.</p><p>Google a donc mis au point <a
title="Exploration AJAX : guide destiné aux webmasters et aux développeurs" href="http://www.google.com/support/webmasters/bin/answer.py?answer=174992">un mécanisme</a> destiné aux webmasters et aux développeurs qui va permettre à son robot de correctement indexer les urls AJAX d&#8217;un site, il n&#8217;y a plus qu&#8217;à comprendre ce qui est demandé et à le mettre en place.</p><p
style="text-align: center;"><img
class="aligncenter size-full wp-image-2626" title="Solution to make ajax applications crawlable" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/crawlerserver.png" alt="" width="708" height="356" /></p><ul><li><strong>Fragment de hachage</strong> (#! et _escaped_fragment_)</li></ul><p>Après résolution du premier point, nos URL AJAX présentent toutes à présent une ancre (ou fragment de hachage), tel que <code>www.example.com/index.html#mon_état</code>, où <code>#mon_état</code> correspond à l&#8217;ancre. Cependant le fragment de hachage est une particularité du navigateur et ne fait pas partie du protocole HTTP ce qui fait que ce paramètre n&#8217;est pas envoyé au serveur Web (Apache, Nginx, Lighttpd, etc.).</p><p>Ce qui implique que lorsque le robot va requêter des urls avec un #, l&#8217;état souhaité de la page ne pourra lui être retourné correctement, il faut donc que le robot interroge notre application sans fragment de hachage. C&#8217;est ce que propose Google en suffixant nos &laquo;&nbsp;ancres dynamiques&nbsp;&raquo; à l&#8217;aide du point d&#8217;exclamation (pour différencier les vraies &laquo;&nbsp;ancres statiques&nbsp;&raquo; des nouvelles) et en incluant dans son algorithme un mécanisme de transformation des urls contenant un tel paramètre (#!), sur le schéma &nbsp;&raquo;Pretty URL to Ugly URL&nbsp;&raquo; :</p><p><span
style="text-decoration: underline;">impact sur notre application</span></p><p><code>www.example.com/index.html#mont_état</code> devient alors <code>www.example.com/index.html#!mon_état</code></p><p><span
style="text-decoration: underline;">impact chez Googlebot</span></p><p><code>www.example.com/index.html#!mon_état</code> sera transformé en <code>www.example.com/index.html?_escaped_fragment_=mon_état</code></p><p>Cette fois-ci l&#8217;état de la page est envoyé au serveur Web, car le paramètre _escaped_fragment_ est un paramètre comme un autre autorisé dans le protocole HTTP. Il ne nous reste plus qu&#8217;à intercepter cette url dans notre application et de lui renvoyer le contenu de la page souhaitée (ça a l&#8217;air simple dit comme ça ? ^^).</p><ul><li><strong>Navigateur sans tête</strong> (servlet et htmlunit)</li></ul><p>Puisque les robots d&#8217;indexation ne peuvent exécuter de code JavaScript, il faut le faire à leur place, sous-entendu qu&#8217;il faut que notre serveur délivre un &laquo;&nbsp;snapshot&nbsp;&raquo; (un <em>instantané HTML)</em> de la page demandée lorsque le robot va requêter notre site. Ce snapshot doit correspondre à l&#8217;ensemble du contenu qui apparaît sur la page <span
style="text-decoration: underline;">après l&#8217;exécution du JavaScript</span>.</p><p
style="text-align: center;"><a
href="http://htmlunit.sourceforge.net/"><img
class="aligncenter size-full wp-image-2627" title="HtmlUnit - Headless Browser" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/htmlunit.gif" alt="" width="480" height="156" /></a></p><p>Pour cela nous avons donc besoin d&#8217;une servlet pour intercepter les url contenant _escaped_fragment_ et d&#8217;un headless browser qui va simuler pour nous le comportement du navigateur pour récupérer le contenu des pages demandées, l&#8217;outil HtmlUnit a été choisi pour réaliser cette tâche.</p><p>Google a mis à disposition un exemple de Servlet utilisée dans une démo GWT, le code est <a
href="http://code.google.com/p/google-web-toolkit/source/browse/branches/crawlability/samples/showcase/src/com/google/gwt/sample/showcase/server/CrawlServlet.java?spec=svn6231&amp;r=6231">accessible ici</a>. Voici cependant ma version qui inclut quelques nouveautés avec entre autre le moyen d&#8217;éviter que ces requêtes, appelées par les robots, ne soient prises en compte dans les statistiques de Google Analytics par exemple.</p><pre class="brush: java">/**
 * Servlet that makes this application crawlable
 */
public final class CrawlServlet implements Filter {
	private transient FilterConfig filterConfig = null;
	/**
	 * Initializes the filter configuration
	 */
	public void init(final FilterConfig filterConfig) {
		this.filterConfig = filterConfig;
	}
	/**
	 * Destroys the filter configuration
	 */
	public void destroy() {
		this.filterConfig = null;
	}
	/**
	 * Filters all requests and invokes headless browser if necessary
	 */
	@SuppressWarnings("unchecked")
	public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException {
		if (filterConfig == null) {
			return;
		}
		final HttpServletRequest req = (HttpServletRequest) request;
		final HttpServletResponse res = (HttpServletResponse) response;
		String queryString = req.getQueryString();
		if ((queryString != null) &amp;&amp; (queryString.contains("_escaped_fragment_"))) {
			final StringBuilder pageNameSb = new StringBuilder("http://");
			pageNameSb.append(req.getServerName());
			if (req.getServerPort() != 0) {
				pageNameSb.append(":");
				pageNameSb.append(req.getServerPort());
			}
			pageNameSb.append(req.getRequestURI());
			queryString = rewriteQueryString(queryString);
			pageNameSb.append(queryString);
			final WebClient webClient = new WebClient(BrowserVersion.FIREFOX_3_6);
			new PinConnectionWrapper(webClient);
			webClient.setJavaScriptEnabled(true);
			webClient.setThrowExceptionOnScriptError(false);
			webClient.setAjaxController(new NicelyResynchronizingAjaxController());
			final String pageName = pageNameSb.toString();
			final HtmlPage page = webClient.getPage(pageName);
			res.setContentType("text/html;charset=UTF-8");
			final PrintWriter out = res.getWriter();
			out.println("&lt;hr&gt;");
			out.println("&lt;center&gt;&lt;h3&gt;You are viewing a non-interactive page that is intended for the crawler.  You probably want to see this page: &lt;a href=\""
					+ pageName + "\"&gt;" + pageName + "&lt;/a&gt;&lt;/h3&gt;&lt;/center&gt;");
			out.println("&lt;hr&gt;");
			out.println(page.asXml());
			webClient.closeAllWindows();
			out.close();
		} else {
			try {
				chain.doFilter(request, response);
			} catch (ServletException e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 * rewrite the URL back to the original #! version.
	 * remember to unescape any %XX characters.
	 * @param queryString
	 * @return
	 * @throws IOException
	 */
	private String rewriteQueryString(final String queryString) throws IOException {
		StringBuilder queryStringSb = new StringBuilder(queryString);
		int i = queryStringSb.indexOf("&amp;_escaped_fragment_");
		if (i != -1) {
			final StringBuilder tmpSb = new StringBuilder(queryStringSb.substring(0, i));
			tmpSb.append("#!");
			tmpSb.append(URLDecoder.decode(queryStringSb.substring(i + 20, queryStringSb.length()), "UTF-8"));
			queryStringSb = tmpSb;
		}
		i = queryStringSb.indexOf("_escaped_fragment_");
		if (i != -1) {
			final StringBuilder tmpSb = new StringBuilder(queryStringSb.substring(0, i));
			tmpSb.append("#!");
			tmpSb.append(URLDecoder.decode(queryStringSb.substring(i + 19, queryStringSb.length()), "UTF-8"));
			queryStringSb = tmpSb;
		}
		if (queryStringSb.indexOf("#!") != 0) {
			queryStringSb.insert(0, '?');
		}
		return queryStringSb.toString();
	}
	/**
	 * @author Mimie
	 *
	 */
	public class PinConnectionWrapper extends FalsifyingWebConnection {
	    public PinConnectionWrapper(final WebClient webClient) throws IllegalArgumentException {
	        super(webClient);
	    }
	    @Override
	    public WebResponse getResponse(final WebRequest request) throws IOException {
	        final WebResponse res = super.getResponse(request);
	        if (res.getWebRequest().getUrl().toExternalForm().endsWith(".css")
	        		|| res.getWebRequest().getUrl().toString().indexOf("plusone") != -1
	        		|| res.getWebRequest().getUrl().toString().indexOf("twitter") != -1
	        		|| res.getWebRequest().getUrl().toString().indexOf("facebook") != -1
	        		|| res.getWebRequest().getUrl().toString().indexOf("moderator") != -1
	        		|| res.getWebRequest().getUrl().toString().indexOf("google-analytics") != -1) {
	            return createWebResponse(res.getWebRequest(), "", "application/javascript", 200, "Ok");
	        }
	        return res;
	    }
	}
}</pre><p>Pour tester que tout fonctionne correctement, il vous suffit de tester une &laquo;&nbsp;Ugly URL&nbsp;&raquo; pointant sur une de vos pages pour voir le résultat, par exemple : <a
href="http://magicsupremacy.fr/index.jsp?_escaped_fragment_=menu=card&amp;submenu=advanced_search">http://magicsupremacy.fr/index.jsp?_escaped_fragment_=menu=card&amp;submenu=advanced_search</a>, ou sinon vous pouvez aussi le faire en vous connectant au site <a
href="https://www.google.com/webmasters/tools/home?hl=fr">Google Webmaster Tools</a>.</p><h2>A noter</h2><p>- Le robot n&#8217;indexe que le contenu des pages (le source HTML) et non la présentation, c&#8217;est pourquoi j&#8217;ai volontairement fait en sorte que les fichiers css ne soient pas téléchargés avec HtmlUnit.</p><p>- Pour vérifier que Googlebot a correctement indexé vos urls AJAX, il suffit de taper site:votre_nom_de_domaine dans le moteur de recherche pour voir le nombre de lien qu&#8217;il en ressort, par exemple : <a
href="http://www.google.fr/search?gcx=w&amp;sourceid=chrome&amp;ie=UTF-8&amp;q=site%3Amagicsupremacy.fr">site:magicsupremacy.fr</a> affiche 19400 résultats.</p><p>- Toutes les urls dans le code de votre application et celles de votre sitemap si vous en avez un, ne doivent contenir que les &laquo;&nbsp;Pretty URL&nbsp;&raquo; avec les #!, le paramètre _escaped_fragment_ ne doit jamais être présent.</p><p>- La bonne nouvelle est qu&#8217;il semblerait que ce mécanisme ait été repris par Bing dans une <a
href="http://www.bing.com/community/site_blogs/b/webmaster/archive/2011/06/08/updates-to-bing-webmaster-tools-data-and-content.aspx">mise à jour récente</a> de son Webmaster tools.</p><p
style="text-align: center;"><a
href="http://www.bing.com/toolbox/webmaster"><img
class="aligncenter size-full wp-image-2628" title="bing_#!" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/bing_.png" alt="" width="601" height="56" /></a></p><h2>Liens utiles</h2><p><a
href="http://code.google.com/intl/fr-FR/web/ajaxcrawling/">Making AJAX Applications Crawlable</a><br
/> <a
href="http://www.seomix.fr/referencement/naturel/ajax-headless-browser/">Le référencement de l&#8217;Ajax avec un Headless Browser</a><br
/> <a
href="http://www.webseoanalytics.com/blog/googles-ajax-crawling-scheme-and-its-effects-on-seo/">Google’s AJAX crawling scheme and its effects on SEO</a></p> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/seo-bien-referencer-son-site-web-full-ajax/feed</wfw:commentRss> <slash:comments>11</slash:comments> </item> <item><title>Menu fixe mais flottant selon la scrollbar (JS + CSS)</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/menu-fixe-mais-flottant-selon-la-scrollbar-js-css</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/menu-fixe-mais-flottant-selon-la-scrollbar-js-css#comments</comments> <pubDate>Wed, 19 Oct 2011 14:15:16 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[css]]></category> <category><![CDATA[gmail]]></category> <category><![CDATA[javascript]]></category> <category><![CDATA[menu fixe]]></category> <category><![CDATA[menu flottant]]></category> <category><![CDATA[scrollbar]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2617</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/menu-fixe-mais-flottant-selon-la-scrollbar-js-css"><img
align="left" hspace="5" width="155" height="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/css_bloc2-150x150.jpg" class="alignleft wp-post-image tfe" alt="" title="Position relative/fixed" /></a>Certains ne comprennent peut-être pas ce dont je veux parler par &#171;&#160;menu fixe mais flottant selon la scrollbar&#160;&#187;, en fait je fais référence au nouveau menu dans Gmail qui est figé en haut mais suit la scrollbar lorsque nous descendons l&#8217;ascenseur. Cet effet permet d&#8217;avoir toujours à disposition le menu où que nous soyons dans [...]]]></description> <content:encoded><![CDATA[<p></p><p><img
class="aligncenter size-medium wp-image-2619" title="Position relative/fixed" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/css_bloc2-300x206.jpg" alt="" width="210" height="144" /><br
/> Certains ne comprennent peut-être pas ce dont je veux parler par &laquo;&nbsp;menu fixe mais flottant selon la scrollbar&nbsp;&raquo;, en fait je fais référence au nouveau menu dans Gmail qui est figé en haut mais suit la scrollbar lorsque nous descendons l&#8217;ascenseur.<br
/> Cet effet permet d&#8217;avoir toujours à disposition le menu où que nous soyons dans la lecture de nos mails.</p><p
style="text-align: center;"><img
class="aligncenter size-full wp-image-2618" title="Menu fixe-flottant Gmail" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/10/menu-fixe-gmail.png" alt="" width="697" height="31" /></p><p>Ça semble anodin au premier abord pour la plupart des gens, un peu de CSS, un petit &laquo;&nbsp;position: fixed&nbsp;&raquo; et le tour est joué ? hé bien non, c&#8217;est plus fort que ça, car au départ le menu est incrusté et c&#8217;est seulement lorque la scrollbar atteint un point précis (ici lorsque le haut de la fenêtre atteint le menu) que le menu devient flottant, c&#8217;est donc dynamique, à comprendre qu&#8217;il va falloir utiliser du JavaScript.</p><p>Ce menu m&#8217;a intrigué dès le départ car je ne savais pas moi-même réaliser un tel effet, jusqu&#8217;au jour où j&#8217;en ai eu besoin pour l&#8217;affichage d&#8217;un deck de type &laquo;&nbsp;Commander&nbsp;&raquo; sur <a
title="Magic Supremacy - Création et analyse de decks" href="http://magicsupremacy.fr">MS</a>.</p><h2>Démo</h2><p>En effet au niveau de la vue d&#8217;un deck, le survol de la souris sur le nom d&#8217;une carte fait afficher l&#8217;image de celle-ci sur le côté gauche. Le problème c&#8217;est que la quantité de cartes d&#8217;un deck &laquo;&nbsp;Commander&nbsp;&raquo; fait que l&#8217;image ne devient plus visible lorsque nous scrollons vers le bas pour voir l&#8217;intégralité des cartes. D&#8217;où l&#8217;idée de rendre flottante l&#8217;image de la carte lorsque la scrollbar tente de la masquer.</p><p>Pour vous rendre compte du résutlat obtenu, je vous invite à visiter cette page et à scroller vers le bas pour voir que l&#8217;image suit la scrollbar : <a
href="http://magicsupremacy.fr/#!/deck/id/598">Deck Magic l&#8217;Assemblée Momentary Rafiq EDH</a>.</p><h2>Code</h2><p>Finalement c&#8217;est assez simple à comprendre et donc à mettre en place.</p><ul><li>CSS</li></ul><p>Votre élément (menu, image, bandeau, div, etc.) a rendre flottant a par défaut une position &laquo;&nbsp;relative&nbsp;&raquo; par rapport à son conteneur, cette position devra passée à &laquo;&nbsp;fixed&nbsp;&raquo; au moment où nous le choisirons, dans notre cas lorsque la scrollbar tentera de masquer notre élément.</p><p>Le changement d&#8217;état de notre élement se fera par le biais d&#8217;une classe CSS, appliquée par du JavaScript, la voici :</p><pre class="brush: html">#votre_menu_ou_votre_image_ou_votre_element.floatable {
	position: fixed;
	top: 10px; // non-obligatoire
}</pre><ul><li>JS</li></ul><p>Pour détecter qu&#8217;il est temps de changer la position de notre élément, il faut connaître le positionnement de la scrollbar et la comparer au positionnement de notre élément. Pour cela j&#8217;utilise la fonction $(window).scroll() de jQuery.<br
/> Voici le mécanisme à mettre en place :</p><pre class="brush: javascript">// listen for scroll
var positionElementInPage = $('#votre_menu_ou_votre_image_ou_votre_element').offset().top;
$(window).scroll(
	function() {
		if ($(window).scrollTop() &gt;= positionElementInPage) {
			// fixed
			$('#votre_menu_ou_votre_image_ou_votre_element').addClass("floatable");
		} else {
			// relative
			$('#votre_menu_ou_votre_image_ou_votre_element').removeClass("floatable");
		}
	}
);</pre><p>Lorsque nous descendons l&#8217;image nous suit et reste visible, lorsque nous remontons, elle se recale à l&#8217;endroit du départ si nous dépassons son emplacement d&#8217;origine.</p><h2>Sources</h2><p>Si mes explications ne sont pas claires, voici un fichier d&#8217;exemple : <a
href="http://desgeeksetdeslettres.com/sources/menu_fixe_mais_flottant.html">menu_fixe_mais_flottant.html</a>.</p> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/menu-fixe-mais-flottant-selon-la-scrollbar-js-css/feed</wfw:commentRss> <slash:comments>24</slash:comments> </item> <item><title>jRSS : Création de flux RSS en Java</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/jrss-creation-de-flux-rss-en-java</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/jrss-creation-de-flux-rss-en-java#comments</comments> <pubDate>Tue, 27 Sep 2011 15:37:48 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[flux]]></category> <category><![CDATA[generator]]></category> <category><![CDATA[java]]></category> <category><![CDATA[jrss]]></category> <category><![CDATA[rss]]></category> <category><![CDATA[xml]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2566</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/jrss-creation-de-flux-rss-en-java"><img
align="left" hspace="5" width="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/09/jrss.jpg" class="alignleft wp-post-image tfe" alt="" title="jrss" /></a>J&#8217;entends déjà de loin ceux qui pensent que créer son flux RSS à la main est chose facile (ce n&#8217;est que de l&#8217;XML) et qu&#8217;il n&#8217;est pas nécessaire d&#8217;utiliser une bibliothèque tierce pour réaliser cette tâche, je ne suis pas de cette avis et surtout lorsque la bibliothèque en question ne fait que 22 ko. [...]]]></description> <content:encoded><![CDATA[<p></p><p>J&#8217;entends déjà de loin ceux qui pensent que créer son flux RSS à la main est chose facile (ce n&#8217;est que de l&#8217;XML) et qu&#8217;il n&#8217;est pas nécessaire d&#8217;utiliser une bibliothèque tierce pour réaliser cette tâche, je ne suis pas de cette avis et surtout lorsque la bibliothèque en question ne fait que 22 ko.</p><p><a
href="http://jrss.sourceforge.net/"><img
class="aligncenter size-full wp-image-2594" title="jrss" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/09/jrss.jpg" alt="" width="537" height="130" /></a></p><p><a
href="http://jrss.sourceforge.net/" rel="external">jRSS</a> m&#8217;a permis de me concentrer uniquement sur le contenu à mettre à disposition sans s&#8217;occuper des balises XML et autres fioritures englobant ce contenu, c&#8217;est exactement ce que je voulais.</p><h2>Ce que je souhaite</h2><p>Je souhaite présenter à mes abonnés les derniers tournois répertoriés sur mon site <a
href="http://magicsupremacy.fr">magicsupremacy.fr</a>, voici donc à quoi doit ressembler le flux RSS que je dois fournir (volontairement simplifié pour plus de clarté) :</p><pre class="brush: xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;rss version="2.0"&gt;
	&lt;channel&gt;
		&lt;title&gt;Les derniers Tournois ajoutés sur Magic Supremacy&lt;/title&gt;
		&lt;link&gt;http://magicsupremacy.fr/index.jsp#!menu=deck&amp;submenu=search_by_events&lt;/link&gt;
		&lt;description&gt;Une liste des derniers Tournois Magic l'Assemblée ajoutés, tout format confondu&lt;/description&gt;
		&lt;webMaster&gt;admin@magicsupremacy.fr&lt;/webMaster&gt;
		&lt;item&gt;
			&lt;title&gt;Magic Online Premier #2853353 - Pauper&lt;/title&gt;
			&lt;link&gt;http://magicsupremacy.fr/index.jsp#!menu=deck&amp;event=115&lt;/link&gt;
			&lt;description&gt;description simplifiée du tournoi #2853353&lt;/description&gt;
			&lt;author&gt;Admin&lt;/author&gt;
			&lt;pubDate&gt;sam., 24 sept. 2011 09:11:00 CEST&lt;/pubDate&gt;
		&lt;/item&gt;
		&lt;item&gt;
			&lt;title&gt;Magic Online Premier #2853358 - Standard 2010-2011 [ZEN M11 SOM]&lt;/title&gt;
			&lt;link&gt;http://magicsupremacy.fr/index.jsp#!menu=deck&amp;event=114&lt;/link&gt;
			&lt;description&gt;description simplifiée du tournoi #2853358&lt;/description&gt;
			&lt;author&gt;Admin&lt;/author&gt;
			&lt;pubDate&gt;dim., 25 sept. 2011 17:23:00 CEST&lt;/pubDate&gt;
		&lt;/item&gt;
	&lt;/channel&gt;
&lt;/rss&gt;</pre><p>Ce n&#8217;est pas forcément très complexe à réaliser seul, cependant l&#8217;utilisation de jRSS permet de donner un cadre de développement, une direction, en utilisant les objets adéquates, voici la façon dont j&#8217;ai procédé.</p><h2>Comment je m&#8217;y prends</h2><p>L&#8217;utilisation de jRSS permet soit de générer à la volée le flux RSS soit de générer le flux sous forme de fichier XML afin de le déposer dans un répertoire de l&#8217;application. J&#8217;ai opté pour la première solution, je ne stocke aucun fichier sur mon serveur.<br
/> Au lieu de manipuler des chaînes de caractères, jRSS permet de manipuler des objets Java, voici l&#8217;exemple qui permet de générer le flux présenté plus haut :</p><pre class="brush:java">final RSSFeedGenerator rssFeed = RSSFeedGeneratorFactory.getDefault();
final RSS rss = new RSS(RSS.VERSION_2_0);
final Channel channel = new Channel("Les derniers Tournois ajoutés sur Magic Supremacy", "http://magicsupremacy.fr/index.jsp#!menu=deck&amp;submenu=search_by_events", "Une liste des derniers Tournois Magic l'Assemblée ajoutés, tout format confondu");
channel.setWebMaster("admin@magicsupremacy.fr");
final Item item1 = new Item("Magic Online Premier #2853353 - Pauper", "http://magicsupremacy.fr/index.jsp#!menu=deck&amp;event=115", "description simplifiée du tournoi #2853353");
item1.setAuthor("Admin");
item1.setPubDate(new Date());
final Item item2 = new Item("Magic Online Premier #2853358 - Standard 2010-2011 [ZEN M11 SOM]", "http://magicsupremacy.fr/index.jsp#!menu=deck&amp;event=114", "description simplifiée du tournoi #2853358");
item2.setAuthor("Admin");
item2.setPubDate(new Date());
channel.addItem(item1);
channel.addItem(item2);
rss.addChannel(channel);
final String rssFeedGenerated = rssFeed.generateAsString(rss);</pre><p>La variable rssFeedGenerated représente le flux RSS souhaité. Il vous suffit ensuite de mettre à disposition de vos utilisateurs un lien ou un bouton pointant sur l&#8217;action qui appelle ce bout de code.</p><h2>Google Feedburner</h2><p>Pour masquer l&#8217;url de mon flux RSS un peu verbeuse à mes utilisateurs et surtout pour avoir des statistiques sur mes abonnés, j&#8217;utilise <a
href="http://feedburner.google.com" rel="external">Google Feedburner</a> qui me permet alors d&#8217;offrir une url &laquo;&nbsp;propre&nbsp;&raquo; à mes utilisateurs, celle-ci pointera ensuite sur la véritable url de l&#8217;application sans que cela se voit.</p><p>URL de l&#8217;action qui génère le flux RSS :</p><pre class="brush: html">http://magicsupremacy.fr/rss.do?action=getLatestEventsInFrench</pre><p>URL présentée aux utilisateurs grâce à Feedburner :</p><pre class="brush: html">http://feeds.feedburner.com/latest_events_magic_supremacy_fr</pre><p>Feedburner se charge au passage d&#8217;enregistrer le trafic au niveau de mes RSS et de me les présenter sous forme graphique, ça ne vaut pas le coup de s&#8217;en priver.</p> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/jrss-creation-de-flux-rss-en-java/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>jCookies : Gestion simple des cookies avec jQuery</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/jcookies-gestion-simple-des-cookies-avec-jquery</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/jcookies-gestion-simple-des-cookies-avec-jquery#comments</comments> <pubDate>Tue, 13 Sep 2011 12:30:21 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[cookies]]></category> <category><![CDATA[jcookies]]></category> <category><![CDATA[jquery]]></category> <category><![CDATA[navigateur]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2588</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/jcookies-gestion-simple-des-cookies-avec-jquery"><img
align="left" hspace="5" width="155" height="153" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/09/cookie-300x298.png" class="alignleft tfe wp-post-image" alt="Cookie (informatique)" title="Cookie (informatique)" /></a>Vous avez peur de ces petites bébêtes stockés sur votre ordinateur à votre insu par le biais de votre navigateur ? vous avez tord, les cookies ne sont que des fichiers texte (ce ne sont pas des exécutables), même s&#8217;ils stockent certaines informations vous concernant sur la navigation du site lié au cookie, ils ne [...]]]></description> <content:encoded><![CDATA[<p></p><p>Vous avez peur de ces petites bébêtes stockés sur votre ordinateur à votre insu par le biais de votre navigateur ? vous avez tord, les <a
href="http://fr.wikipedia.org/wiki/Cookie_(informatique)">cookies</a> ne sont que des fichiers texte (ce ne sont pas des exécutables), même s&#8217;ils stockent certaines informations vous concernant sur la navigation du site lié au cookie, ils ne peuvent pas vous infecter d&#8217;un quelconque virus.</p><p>Ces petits fichiers sont principalement utilisés sans mauvaises intentions :</p><ol><li>Simplification de l&#8217;authentification utilisateur</li><li>Maintient d&#8217;une session active</li><li>Stockage d&#8217;informations spécifique à l&#8217;utilisateur comme les préférences d&#8217;un site ou le contenu de son panier d&#8217;achat</li></ol><p><img
class="aligncenter size-full wp-image-2589" title="Cookie (informatique)" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/09/cookie.png" alt="" width="191" height="190" /></p><p>Généralement créé par un langage serveur (programe CGI, script PHP, etc.) qui ajoute une ligne dans l&#8217;entête HTTP (<em>Set-Cookie: name=value</em>), la création d&#8217;un cookie peut se faire aussi côté client, en JavaScript.</p><p>L&#8217;utilisation d&#8217;un plugin jQuery comme jCookies apporte une souplesse et une simplicité quant à la création de cookie côté client, voici comment l&#8217;utiliser.</p><h2>Création</h2><p>La création d&#8217;un cookie devient un jeu d&#8217;enfant, il suffit de renseigner le nom du cookie et sa valeur JSON :</p><pre class="brush: javascript">$.jCookies({
    name : 'Magic Supremacy',
    value : { card : 'Birds of Paradise', edition : 'M12', rating : '4.5', price : 5}
});</pre><p>Pour paramétrer d&#8217;avantage le cookie, vous avez la possibilité de passer d&#8217;autres arguments à la méthode, comme par exemple la durée d&#8217;expiration qui est par défaut de 27 jours :</p><pre class="brush: javascript">$.jCookies({ name : 'MS login', value : { username : 'Mimie' , level : 'Admin' }, minutes : 60 });</pre><h2>Récupération</h2><p>La récupération d&#8217;un cookie nécessite de connaître le nom de celui-ci, ensuite grâce à la propriété <span
style="text-decoration: underline;">get</span> du plugin, vous récupérez son contenu si celui-ci a été créer par le procédé décrit précédemment :</p><pre class="brush: javascript">var magic_supremacy = $.jCookies({ get : 'Magic Supremacy' });
    // réponse : { card : 'Birds of Paradise', edition : 'M12', rating : '4.5', price : 5}
var password = $.jCookies({ get : 'MS password' }); // (cookie créé par un autre procédé)
    // réponse : false</pre><h2>Suppression</h2><p>Pour invalider définitivement un cookie, la propriété <span
style="text-decoration: underline;">erase</span> du plugin est faite pour vous, elle vous retournera <em>true</em> si l&#8217;opération a été exécutée avec succès, <em>false</em> sinon :</p><pre class="brush: javascript">var erased_magic_supremacy = $.jCookies({ erase : 'Magic Supremacy' });
    // réponse : true
var erased_password = $.jCookies({ erase : 'MS password' });
    // réponse : false</pre><p>Difficile de faire plus simple non ?</p><p>Pour plus de détails sur le plugin, voici la <a
href="http://tympanus.net/codrops/2011/09/04/j-is-for-jcookies-http-cookie-handling-for-jquery/">présentation officielle</a> faite par son créateur.</p> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/jcookies-gestion-simple-des-cookies-avec-jquery/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Ignorer les accents lors de la comparaison de chaînes en Java</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/ignorer-les-accents-lors-de-la-comparaison-de-chaines-en-java</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/ignorer-les-accents-lors-de-la-comparaison-de-chaines-en-java#comments</comments> <pubDate>Fri, 02 Sep 2011 12:05:58 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[accent]]></category> <category><![CDATA[chaine de caractères]]></category> <category><![CDATA[collator]]></category> <category><![CDATA[comparaison]]></category> <category><![CDATA[java]]></category> <category><![CDATA[normalizer]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2584</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/ignorer-les-accents-lors-de-la-comparaison-de-chaines-en-java"><img
align="left" hspace="5" width="155" height="103" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/09/accents.jpg" class="alignleft tfe wp-post-image" alt="accents" title="accents" /></a>Le problème s&#8217;est manifesté dans la &#171;&#160;recherche rapide&#160;&#187; de cartes sur mon site Magic Supremacy, les joueurs étaient obligé de saisir les accents d&#8217;une carte, si celle-ci en comportait, pour que le système la retrouve correctement et la propose dans sa liste de résultats possibles. Exemple, si nous commencions à saisir &#171;&#160;Viande frai&#160;&#187;, la carte française [...]]]></description> <content:encoded><![CDATA[<p></p><p>Le problème s&#8217;est manifesté dans la &laquo;&nbsp;recherche rapide&nbsp;&raquo; de cartes sur mon site <a
href="http://magicsupremacy.fr/index.jsp#!menu=card">Magic Supremacy</a>, les joueurs étaient obligé de saisir les accents d&#8217;une carte, si celle-ci en comportait, pour que le système la retrouve correctement et la propose dans sa liste de résultats possibles.</p><p>Exemple, si nous commencions à saisir &laquo;&nbsp;Viande frai&nbsp;&raquo;, la carte française nommée &laquo;&nbsp;Viande fraîche&nbsp;&raquo; (avec un i circonflexe) n&#8217;était pas proposée. La raison est tout simplement due à l&#8217;utilisation de la méthode <em>startsWith()</em> de la classe <em>java.lang.String</em> pour comparer les noms de cartes et ça se comprend car après tout le <strong>i</strong> et le <strong>î</strong> sont différents, ainsi que le <strong>e</strong> et le <strong>é</strong>, ou le <strong>u</strong> et le <strong>ü</strong>, cependant ce n&#8217;est pas pratique de devoir taper les accents pour retrouver une carte en particulier, j&#8217;ai donc dû trouver un moyen pour satisfaire mes joueurs ^^</p><p
style="text-align: center;"><img
class="aligncenter size-full wp-image-2585" title="Comparaison d'accents" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/09/accents.jpg" alt="" width="158" height="106" /></p><p>Je suis tombé sur la classe <a
href="http://download.oracle.com/javase/1,5.0/docs/api/java/text/Collator.html">java.text.Collator</a> qui de prime à bord semblait faire exactement ce que je voulais puis finalement je me suis plutôt tourné vers une méthode plus manuelle pour des questions de performance.</p><p>Voici trois solutions testées pour l&#8217;occasion.</p><h2>Collator</h2><p>Cette classe permet de déterminer si les caractères accentués doivent être considéré comme des caractères non-accentués ou si les caractères majuscules doivent être considéré comme des caractères minuscules. Ce niveau  de comparaison est paramétré grâce aux constantes suivantes de la classe : PRIMARY, SECONDARY, TERTIARY ou IDENTICAL, la Locale rentrant aussi en considération. Cette classe était donc faite pour moi.</p><p>Cependant les méthodes de cette classe sont assez limitées, il n&#8217;y a quasiment que le <em>compare()</em> qui ne m&#8217;aide pas étant donné que je souhaite comparer les chaines de caractères qui commencent par une autre (le <em>startsWith</em>).</p><p>Déception et abandon de cette solution.</p><h2>Normalizer</h2><p>L&#8217;idée a été ensuite de trouver un moyen de transformer en amont les chaînes à comparer, pour ensuite utiliser le <em>startsWith()</em>. Les caractères accentués doivent être converti en leurs caractères non-accentués équivalent (é -&gt; e, ü -&gt; u, î -&gt; i, etc.) ainsi que les majuscules en minuscules (E -&gt; e, Ü -&gt; u, etc.).</p><p>La classe <a
href="http://download.oracle.com/javase/6/docs/api/java/text/Normalizer.html">java.text.Normalizer</a> s&#8217;occupe très bien de la conversion des accents et pour les majuscules un simple toLowerCase() permet de s&#8217;en arranger :</p><pre class="brush: java">public static String formatStringNormalizer(String s) {
	String temp = Normalizer.normalize(s, Normalizer.Form.NFD);
	return temp.replaceAll("[^\\p{ASCII}]", "").toLowerCase();
}</pre><p>Ainsi la recherche du nom &laquo;&nbsp;Viande fraîche&nbsp;&raquo; ou &laquo;&nbsp;viande fraiche&nbsp;&raquo; parmis tous les noms de cartes disponibles sera équivalente en appliquant cette méthode au nom de la carte à chercher ET au nom des cartes à comparer.</p><h2>Méthode maison</h2><p>Plus les chaînes de caractères sont longues, plus la transformation des accents avec Normalizer prend du temps, de même qu&#8217;un grand nombre d&#8217;exécution successive de la méthode <em>formatStringNormalizer()</em> met un certain temps.</p><p>J&#8217;ai donc décidé d&#8217;utiliser une méthode non disponible dans la JRE qui permet à la fois de convertir les accents en non-accent et les majuscules en minuscules, une méthode fort appréciable car environ 8x plus rapide, trouvée <a
href="http://www.eteks.com/tips/tip4.html">à cette adresse</a>.</p><pre class="brush: java">static public String formatStringManual(String string) {
	char[] charsData = new char[string.length()];
	string.getChars(0, charsData.length, charsData, 0);
	char c;
	for (int i = 0; i &lt; charsData.length; i++) {
		if ((c = charsData[i]) &gt;= 'A' &amp;&amp; c &lt;= 'Z') {
			charsData[i] = (char) (c - 'A' + 'a');
		} else {
			switch (c) {
			case '\u00e0':
			case '\u00e2':
			case '\u00e4':
				charsData[i] = 'a';
				break;
			case '\u00e7':
				charsData[i] = 'c';
				break;
			case '\u00e8':
			case '\u00e9':
			case '\u00ea':
			case '\u00eb':
				charsData[i] = 'e';
				break;
			case '\u00ee':
			case '\u00ef':
				charsData[i] = 'i';
				break;
			case '\u00f4':
			case '\u00f6':
				charsData[i] = 'o';
				break;
			case '\u00f9':
			case '\u00fb':
			case '\u00fc':
				charsData[i] = 'u';
				break;
			}
		}
	}
	return new String(charsData);
}</pre><h2>Temps d&#8217;exécution</h2><p>Voici la façon utilisée pour me rendre compte que la méthode <em>formatStringNormalizer()</em> est plus lente que la méthode <em>formatStringManual()</em> :</p><pre class="brush: java">public static void main(String[] args) {
	String search = "frai";
	String text = "Une ";
	for (int i = 0; i &lt; 100; i++) {
		text += "viande ";
	}
	text += "fraîchement découpée";
	long time = System.currentTimeMillis();
	// Recherche de la sous-chaîne search dans text plusieurs fois de suite
	for (int i = 0; i &lt; 100000; i++) {
		formatStringNormalizer(text).indexOf(search);
		//formatStringManual(text).indexOf(search);
	}
	// Affichage du temps écoulé
	System.out.println((System.currentTimeMillis() - time) + " ms");
}</pre>]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/ignorer-les-accents-lors-de-la-comparaison-de-chaines-en-java/feed</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Récupérer les paramètres d&#8217;une URL en JavaScript</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/recuperer-les-parametres-dune-url-en-javascript</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/recuperer-les-parametres-dune-url-en-javascript#comments</comments> <pubDate>Tue, 23 Aug 2011 13:20:59 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[document]]></category> <category><![CDATA[javascript]]></category> <category><![CDATA[paramètres]]></category> <category><![CDATA[script]]></category> <category><![CDATA[url]]></category> <category><![CDATA[variables]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2560</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/recuperer-les-parametres-dune-url-en-javascript"><img
align="left" hspace="5" width="155" height="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/08/url_parameters-150x150.jpg" class="alignleft wp-post-image tfe" alt="" title="Paramètres d" /></a>Vous ne vous êtes jamais demandé à quoi servait tous ces paramètres incompréhensibles dans les URL que vous saisissez pour vous rendre sur tel ou tel site ou page Web ? pour s&#8217;en rendre compte je vous invite à regarder attentivement l&#8217;URL générée par Google lors de vos recherches. Finalement c&#8217;est assez simple à comprendre, [...]]]></description> <content:encoded><![CDATA[<p></p><p>Vous ne vous êtes jamais demandé à quoi servait tous ces paramètres incompréhensibles dans les URL que vous saisissez pour vous rendre sur tel ou tel site ou page Web ? pour s&#8217;en rendre compte je vous invite à regarder attentivement l&#8217;URL générée par Google lors de vos recherches.</p><p><img
class="aligncenter size-full wp-image-2561" title="Paramètres d'URL" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/08/url_parameters.jpg" alt="" width="158" height="210" /></p><p>Finalement c&#8217;est assez simple à comprendre, les paramètres d&#8217;une URL servent simplement à transmettre des variables au script ou document cible, particulièrement utilisés dans les sites Web dynamiques.</p><h2>Rappel</h2><p>Les URL (<a
href="http://fr.wikipedia.org/wiki/Uniform_Resource_Locator">Uniform Resource Locator</a>) sont les chemins d&#8217;accès aux différentes ressources sur Internet. Ils sont utilisés pour identifier les pages et sites Web et permettent d&#8217;accéder à n&#8217;importe quel document. Ces chemins sont communément appelées &laquo;&nbsp;adresses Web&nbsp;&raquo;.</p><p><span
style="text-decoration: underline;">Syntaxe (protocole http)</span></p><pre class="brush: plain">http://&lt;machine&gt;:&lt;port&gt;/&lt;chemin&gt;?&lt;paramètres&gt;#&lt;fragment&gt;</pre><p>Le caractère <strong>?</strong> indique que la suite de l&#8217;URL sont des paramètres et ne font pas partie du nom de fichier cible. Le caractère <strong>=</strong> sépare un nom de paramètre de sa valeur. Le caractère <strong>&amp;</strong> sépare deux couples (nom, valeur).</p><p><span
style="text-decoration: underline;">Exemple</span></p><pre class="brush: html">&lt;a href="http://magicsupremacy.fr/index.jsp?menu=deck&amp;id=75"&gt;nom du lien&lt;/a&gt;</pre><p>Dans cet exemple on transmet deux variables au script index.jsp : $menu de valeur &laquo;&nbsp;deck&nbsp;&raquo; et $id de valeur &laquo;&nbsp;75&#8243;.</p><h2>Méthode classique</h2><p>De prime à bord ça ne semble pas très compliqué grâce à la fonction <em>split</em> qui nous permet d&#8217;isoler les valeurs de part et d&#8217;autre d&#8217;un séparateur, dans notre cas les séparateurs sont &amp; et =.</p><p>Voici donc une façon simple qui permet de récupérer les paramètres d&#8217;une URL en JavaScript sous la forme d&#8217;un tableau associatif (clé =&gt; valeur) :</p><pre class="brush: javascript">// read a page's GET URL variables and return them as an associative array.
var getUrlVars = function (historyHash) {
	var vars = [], hash;
	var hashes = historyHash.split('&amp;');
	for (var i = 0; i &lt; hashes.length; i++) {
        hash = hashes[i].split('=');
        vars.push(hash[0]);
        vars[hash[0]] = hash[1];
    }
    return vars;
};
var url = location.href;
var parameters = getUrlVars(url.substring(url.indexOf("?") + 1));</pre><h2>Méthode avancée</h2><p>Qu&#8217;en est-il lorsque les valeurs des variables à transmettre comportent des &amp; ? comme par exemple :</p><pre class="brush: html">http://magicsupremacy.fr/index.jsp?menu=card&amp;artist=Chippy+&amp;+Matthew+Wilson</pre><p>Le script précédent ne fonctionne plus, il faut un peu ruser pour contourner le problème, voici la façon que j&#8217;ai trouvé qui m&#8217;a permis d&#8217;avancer :</p><pre class="brush: javascript">// read a page's GET URL variables and return them as an associative array.
// tiens compte des &amp; dans la valeur d'une clé (&amp;artist=titi&amp;toto =&gt; "artist" : "titi&amp;toto")
var getUrlVars = function (historyHash) {
	var vars = [];
	var hashes = historyHash.split('&amp;');
	var previousHash;
	for (var i = 0; i &lt; hashes.length; i++) {
        var hash = hashes[i].split('=');
		if (hash.length == 1) {
			vars[previousHash] += "&amp;" + hash[0];
		} else {
			previousHash = hash[0];
        	vars.push(hash[0]);
        	vars[hash[0]] = hash[1];
		}
    }
    return vars;
};
var url = location.href;
var parameters = getUrlVars(url.substring(url.indexOf("?") + 1));</pre>]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/recuperer-les-parametres-dune-url-en-javascript/feed</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Générer son sitemap.xml en Java avec la bibliothèque SitemapGen4j</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/generer-son-sitemap-xml-en-java-avec-la-bibliotheque-sitemapgen4j</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/generer-son-sitemap-xml-en-java-avec-la-bibliotheque-sitemapgen4j#comments</comments> <pubDate>Thu, 11 Aug 2011 14:18:10 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[google]]></category> <category><![CDATA[java]]></category> <category><![CDATA[sitemap]]></category> <category><![CDATA[sitemap.xml]]></category> <category><![CDATA[sitemapgen4j]]></category> <category><![CDATA[sitemaps]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2507</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/generer-son-sitemap-xml-en-java-avec-la-bibliotheque-sitemapgen4j"><img
align="left" hspace="5" width="155" height="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/08/sitemap_page-150x150.jpg" class="alignleft wp-post-image tfe" alt="" title="Sitemap en image" /></a>Le terme Sitemap désigne finalement deux choses, dans un premier temps il désigne une page Web qui permet aux utilisateurs d&#8217;avoir un &#171;&#160;plan du site&#160;&#187; (sous forme d&#8217;arborescence) afin de mieux les repérer et faciliter l&#8217;accès à l&#8217;information, dans un second temps il désigne le fichier que l&#8217;on doit générer et fournir aux moteurs de [...]]]></description> <content:encoded><![CDATA[<p></p><p>Le terme Sitemap désigne finalement deux choses, dans un premier temps il désigne une page Web qui permet aux utilisateurs d&#8217;avoir un &laquo;&nbsp;plan du site&nbsp;&raquo; (sous forme d&#8217;arborescence) afin de mieux les repérer et faciliter l&#8217;accès à l&#8217;information, dans un second temps il désigne le fichier que l&#8217;on doit générer et fournir aux moteurs de recherche pour que les robots d&#8217;indexation (<a
href="http://fr.wikipedia.org/wiki/Robot_d'indexation">crawlers</a>) trouvent et indexent des pages qu&#8217;ils n&#8217;auraient pas pu trouver autrement, ce qui favorise un meilleur référencement.</p><p>Ce terme, dans le second cas, désigne en fait le protocole <a
href="http://www.sitemaps.org/fr/">Sitemaps</a> conçu par Google et adopté par les autres, qui consiste en une représentation du plan des sites en texte ou en XML, à destination exclusive des moteurs de recherche.</p><p><img
class="aligncenter size-full wp-image-2559" title="Sitemap en image" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/08/sitemap_page.jpg" alt="" width="416" height="320" /></p><p>J&#8217;avoue ne pas avoir fait de page Sitemap sur mon site sur <a
href="http://magicsupremacy.fr">Magic</a>, ça me semble ringard, par contre j&#8217;ai fait en sorte de générer au mieux le fichier XML Sitemap (voire même deux) par le biais d&#8217;une bibliothèque Java que je vais vous présenter.</p><h2>Fichier Sitemap</h2><p>- Bien entendu le fichier Sitemap.xml doit respecter une norme et un squelette prédéfini afin qu&#8217;il soit lisible par les robots : définitions des balises XML, encodage du fichier et caractères d&#8217;échappement d&#8217;entité entre autres. Je vous invite à lire cet article <a
href="http://www.sitemaps.org/fr/protocol.php">Format XML de plans Sitemap</a> et  celui-là <a
href="http://www.google.com/support/webmasters/bin/answer.py?answer=35653">Utilisation de caractères non alphanumériques dans les URL d&#8217;un sitemap</a> pour de plus amples informations car il y en a trop pour être résumées dans ce billet.</p><p>L&#8217;avantage de passer par une bibliothèque, telle <a
href="http://code.google.com/p/sitemapgen4j/">SitemapGen4j</a>, est que tout est prévu d&#8217;avance, ainsi toutes les bonnes pratiques sont implémentées et facilement exploitable, attention tout de même aux caractères spéciaux à encoder obligatoirement et autres caractères interdits à proscrire.</p><p>- Voici un extrait de ma classe permettant de générer mes deux fichiers Sitemap XML, un par langue du site (le fichier généré est automatiquement validé avec les schémas officiels du protocole) :</p><pre class="brush: java">public class GenerateSitemap {
	public static void main(String[] args) throws MalformedURLException, UnsupportedEncodingException {
		// un fichier sitemap par langue
		generateSitemapForTld("com");
		generateSitemapForTld("fr");
	}
	private static void generateSitemapForTld(final String tld) throws MalformedURLException, UnsupportedEncodingException {
		// Use DAY pattern (2009-02-07), Greenwich Mean Time timezone
		final W3CDateFormat dateFormat = new W3CDateFormat(Pattern.DAY);
		dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
		final WebSitemapGenerator wsg = WebSitemapGenerator.builder("http://magicsupremacy." + tld, myFile).dateFormat(dateFormat).fileNamePrefix("sitemap-" + tld).autoValidate(true).build();
		// home
		wsg.addUrl(new WebSitemapUrl.Options("http://magicsupremacy." + tld).lastMod(new Date()).priority(1.0).changeFreq(ChangeFreq.WEEKLY).build());
		// menus
		wsg.addUrl(new WebSitemapUrl.Options("http://magicsupremacy." + tld + "/index.jsp#!menu=home").lastMod(new Date()).priority(1.0).changeFreq(ChangeFreq.WEEKLY).build());
		wsg.addUrl(new WebSitemapUrl.Options("http://magicsupremacy." + tld + "/index.jsp#!menu=card").lastMod(new Date()).priority(1.0).changeFreq(ChangeFreq.WEEKLY).build());
		wsg.addUrl(new WebSitemapUrl.Options("http://magicsupremacy." + tld + "/index.jsp#!menu=deck").lastMod(new Date()).priority(1.0).changeFreq(ChangeFreq.WEEKLY).build());
		wsg.addUrl(new WebSitemapUrl.Options("http://magicsupremacy." + tld + "/index.jsp#!menu=account").lastMod(new Date()).priority(1.0).changeFreq(ChangeFreq.WEEKLY).build());
		// cards
		for (CardDto card : cardService.getAllCardsForCrawlers()) {
			wsg.addUrl(new WebSitemapUrl.Options("http://magicsupremacy." + tld + "/index.jsp#!menu=card&amp;name=" + URLEncoder.encode(card.getName(), "UTF-8")).lastMod(new Date()).priority(0.8).changeFreq(ChangeFreq.WEEKLY).build());
			wsg.addUrl(new WebSitemapUrl.Options("http://magicsupremacy." + tld + "/index.jsp#!menu=card&amp;id=" + card.getPkCard()).lastMod(new Date()).priority(0.8).changeFreq(ChangeFreq.WEEKLY).build());
		}
		wsg.write();
	}
}</pre><h2>Fichier d&#8217;index Sitemap</h2><p>Pour les sites Web dynamiques qui génèrent un nombre importants d&#8217;URLs et étant donné les limites imposées par fichier Sitemap (maximum 50 000 URLs et une taille de 10 Mo), vous avez la possibilité de découper vos fichiers par thème ou groupement puis de les référencer dans un unique fichier Sitemap index qui deviendra alors l&#8217;unique point d&#8217;entrée du robot pour accéder à vos liens repertoriés (article à lire <a
href="http://www.google.com/support/webmasters/bin/answer.py?hl=fr&amp;answer=71453&amp;from=35655&amp;rd=1">Fichier d&#8217;index Sitemap</a>).</p><p>Voici la façon de procéder avec SitemapGen4j :</p><pre class="brush: java">WebSitemapGenerator wsg;
// generate cards sitemap
wsg = WebSitemapGenerator.builder("http://magicsupremacy.fr", myDir).fileNamePrefix("cards").build();
for (int i = 0; i &lt; 50; i++) wsg.addUrl("http://magicsupremacy.fr/index.jsp#!menu=card&amp;id="+i);
wsg.write();
// generate decks sitemap
wsg = WebSitemapGenerator.builder("http://magicsupremacy.fr", myDir).fileNamePrefix("decks").build();
for (int i = 0; i &lt; 50; i++) wsg.addUrl("http://magicsupremacy.fr/index.jsp#!menu=deck&amp;id="+i);
wsg.write();
// generate sitemap index for cards + decks
SitemapIndexGenerator sig = new SitemapIndexGenerator("http://magicsupremacy.fr", myFile);
sig.addUrl("http://magicsupremacy.fr/cards.xml");
sig.addUrl("http://magicsupremacy.fr/decks.xml");
sig.write();</pre><p>Le fichier généré en sortie devrait ressembler à ceci, il ne vous manquera plus qu&#8217;à l&#8217;envoyer aux différents moteurs de recherche :</p><pre class="brush: xml">&lt;sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;http://magicsupremacy.fr/cards.xml&lt;/loc&gt;
    &lt;lastmod&gt;2011-08-11T14:32:18.825+02:00&lt;/lastmod&gt;
  &lt;/sitemap&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;http://magicsupremacy.fr/decks.xml&lt;/loc&gt;
    &lt;lastmod&gt;2011-08-11T14:32:18.825+02:00&lt;/lastmod&gt;
  &lt;/sitemap&gt;
&lt;/sitemapindex&gt;</pre><h2>Outils d&#8217;aide</h2><p>Pour vous aider à générer votre fichier sitemap.xml, il existe un tas de plugins pour les CMS, des outils à installer soit-même sur sa machine et des applications en ligne qui vous permettent de faire tout ça à la volée, voici une liste plutôt conséquente qui vous aidera dans votre recherche : <a
href="http://code.google.com/p/sitemap-generators/wiki/SitemapGenerators">Web Sitemap Generators</a>.</p> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/generer-son-sitemap-xml-en-java-avec-la-bibliotheque-sitemapgen4j/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Google +1 : chargement asynchrone + validation W3C</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/google-1-chargement-asynchrone-validation-w3c</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/google-1-chargement-asynchrone-validation-w3c#comments</comments> <pubDate>Tue, 26 Jul 2011 15:58:49 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[+1]]></category> <category><![CDATA[asynchrone]]></category> <category><![CDATA[google]]></category> <category><![CDATA[plusone]]></category> <category><![CDATA[social]]></category> <category><![CDATA[w3c]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2514</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/google-1-chargement-asynchrone-validation-w3c"><img
align="left" hspace="5" width="155" height="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/07/Google-Plus-One-150x150.jpg" class="alignleft wp-post-image tfe" alt="" title="Google-Plus-One" /></a>Pour les nombreux utilisateurs du moteur de recherche Google, l&#8217;arrivée du récent bouton +1 (plusone) dans les résultats de recherche n&#8217;est pas passé inaperçu. Ce même bouton est en train de fleurir un peu partout sur les sites Web car en plus de conseiller et partager du contenu à son entourage, ce bouton a la [...]]]></description> <content:encoded><![CDATA[<p></p><p>Pour les nombreux utilisateurs du moteur de recherche Google, l&#8217;arrivée du récent bouton +1 (plusone) dans les résultats de recherche n&#8217;est pas passé inaperçu.</p><p>Ce même bouton est en train de fleurir un peu partout sur les sites Web car en plus de conseiller et partager du contenu à son entourage, ce bouton a la vertue d&#8217;améliorer le classement du site Web en question dans les résultats de recherche Google.</p><p><a
href="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/07/Google-Plus-One.jpg"><img
class="aligncenter size-full wp-image-2515" title="Google-Plus-One" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/07/Google-Plus-One.jpg" alt="" width="325" height="185" /></a></p><p>Pour connaître les principaux boutons sociaux à mettre en place sur votre site, <a
href="http://desgeeksetdeslettres.com/blog/web/implanter-les-boutons-de-suivi-des-reseaux-sociaux">cet article</a> vous sera utile.</p><h2>Utilisation classique</h2><ul><li>Chargement du script de façon synchrone</li></ul><pre class="brush: javascript">
&lt;script type="text/javascript" src="https://apis.google.com/js/plusone.js"&gt;
    {lang: 'fr'}
&lt;/script&gt;</pre><ul><li>Placement du bouton en utilisant une balise spécifique à Google</li></ul><pre class="brush: html">
&lt;g:plusone size="medium" href="http://magicsupremacy.com"&gt;&lt;/g:plusone&gt;</pre><h2>Utilisation améliorée</h2><ul><li>Chargement du script de façon asynchrone pour ne pas ralentir l&#8217;affichage des pages de vote site Web</li></ul><pre class="brush: javascript">&lt;script&gt;
  	(function() {
		var gp = document.createElement('script');
		gp.type = 'text/javascript';
		gp.async = true;
		gp.src = 'https://apis.google.com/js/plusone.js';
		gp.textContent = "{lang: 'fr'}";
		var s = document.getElementsByTagName('script')[0];
		s.parentNode.insertBefore(gp, s);
	})();
&lt;/script&gt;</pre><ul><li>Placement du bouton en utilisant les balises standard HTML pour qu&#8217;il soit valide <a
href="http://validator.w3.org/" rel="external">W3C</a> (Doctype HTML5)</li></ul><pre class="brush: html">&lt;div class="g-plusone" id="gplusone"&gt;&lt;/div&gt;</pre><pre class="brush: javascript">&lt;script type="text/javascript"&gt;
	var gplusone = document.getElementById("gplusone");
	gplusone.setAttribute("data-size", "medium");
	gplusone.setAttribute("data-count", "true");
	gplusone.setAttribute("data-href", "http://magicsupremacy.com");
&lt;/script&gt;</pre><p>C&#8217;est sûr qu&#8217;au lieu de 2 lignes il nous en faut à présent 10 mais pour les puristes de la validation W3C et de la vitesse d&#8217;affichage des pages, ce n&#8217;est pas grand chose et très simple à mettre en place.</p> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/google-1-chargement-asynchrone-validation-w3c/feed</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>Dozer à la rescousse du lazy-loading</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/dozer-a-la-rescousse-du-lazy-loading</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/dozer-a-la-rescousse-du-lazy-loading#comments</comments> <pubDate>Sun, 17 Jul 2011 12:23:52 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[dozer]]></category> <category><![CDATA[dto]]></category> <category><![CDATA[hibernate]]></category> <category><![CDATA[lazy-loading]]></category> <category><![CDATA[mapping]]></category> <category><![CDATA[model]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2449</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/dozer-a-la-rescousse-du-lazy-loading"><img
align="left" hspace="5" width="155" src="https://desgeeksetdeslettres.s3.amazonaws.com/Images%2fdozer.jpg" class="alignleft wp-post-image tfe" alt="" title="dozer_lazy-loading" /></a>Pour ceux qui ne connaissent pas mon chouchou Dozer, voici deux articles pour vous donner envie d&#8217;y goûter, ici et là. Cette fois-ci Dozer vient en aide au problème récurrent du lazy-loading (chargement à la demande), dans mon cas les problèmes se sont posés avec Hibernate qui a adopté cette stratégie par défaut depuis la version [...]]]></description> <content:encoded><![CDATA[<p></p><p>Pour ceux qui ne connaissent pas mon chouchou <a
href="http://dozer.sourceforge.net/" rel="external">Dozer</a>, voici deux articles pour vous donner envie d&#8217;y goûter, <a
href="http://desgeeksetdeslettres.com/blog/programmation-java/dozer-la-copie-dobjets-java-simplifiee-et-automatique" rel="follow">ici</a> et <a
href="http://desgeeksetdeslettres.com/blog/programmation-java/dozer-utilisation-avancee-des-convertisseurs" rel="follow">là</a>.</p><p>Cette fois-ci Dozer vient en aide au problème récurrent du <a
href="http://www.jtips.info/index.php?title=Hibernate/Lazy_loading" rel="external">lazy-loading</a> (chargement à la demande), dans mon cas les problèmes se sont posés avec <a
href="http://fr.wikipedia.org/wiki/Hibernate" rel="external">Hibernate</a> qui a adopté cette stratégie par défaut depuis la version 3.</p><p
style="text-align: center;"><img
class="aligncenter size-full wp-image-2499" title="dozer_lazy-loading" src="https://desgeeksetdeslettres.s3.amazonaws.com/Images%2fdozer.jpg" alt="" width="290" height="220" /></p><p>Si vous non plus vous n&#8217;êtes pas convaincu par l&#8217;utilisation du design pattern nommé <a
href="http://community.jboss.org/wiki/OpenSessionInView" rel="external"><em>Open Session in View</em></a>, et que vos services métiers renvoient systématiquement des objets de type DTO par exemple, alors la suite de ce billet peut vous aider dans vos développements.</p><h2>Model vers DTO</h2><p>Si vos services métiers retournent d&#8217;autres objets que ceux provenant du modèle de données mappés avec Hibernate, alors la copie automatique de l&#8217;objet Model vers DTO peut se faire avec Dozer. Cependant la copie de chaque attribut de l&#8217;objet Model vers l&#8217;objet DTO utilise systématiquement les getters de celui-ci, annulant du même coup l&#8217;effet du lazy-loading car l&#8217;appel d&#8217;un getter d&#8217;un objet Model sur un attribut configuré en chargement à la demande, déclenche automatiquement une requête en base pour récupérer les informations qu&#8217;il ne possède pas encore.</p><p>Pour cela j&#8217;utilise deux mapping Dozer différents par objet Model qui utilise des associations lazy-loadées (il est bien entendu possible d&#8217;en définir plus), et dans mes services métiers j&#8217;indique quel mapping utiliser.</p><h2>Mapping Dozer</h2><p>Les deux mapping en question ressemblent à ceci, tout est paramétré dans le fichier de configuration XML de Dozer en utilisant l&#8217;attribut <em>map-id</em> de la balise <em>mapping</em>, ainsi que la balise <em>field-exlucde</em> pour jouer sur les attributs que nous ne souhaitons pas charger :</p><ul><li>le mapping qui ne charge pas les associations en lazy-loading, appelé <em>miniDeck</em></li></ul><pre class="brush: xml">&lt;mapping map-id="miniDeck"&gt;
    	&lt;class-a&gt;fr.mtg.supremacy.core.model.Deck&lt;/class-a&gt;
    	&lt;class-b&gt;fr.mtg.supremacy.core.dto.DeckDto&lt;/class-b&gt;
    	&lt;field custom-converter="fr.mtg.supremacy.core.converter.ColorEnumConverter"&gt;
    		&lt;a&gt;colors&lt;/a&gt;
    		&lt;b&gt;colors&lt;/b&gt;
  	&lt;/field&gt;
  	&lt;field custom-converter="fr.mtg.supremacy.core.converter.DeckStyleEnumConverter"&gt;
    		&lt;a&gt;styles&lt;/a&gt;
    		&lt;b&gt;styles&lt;/b&gt;
  	&lt;/field&gt;
  	&lt;field-exclude&gt;
    		&lt;a&gt;contents&lt;/a&gt;
    		&lt;b&gt;contents&lt;/b&gt;
    	&lt;/field-exclude&gt;
    	&lt;field-exclude&gt;
    		&lt;a&gt;comments&lt;/a&gt;
    		&lt;b&gt;comments&lt;/b&gt;
    	&lt;/field-exclude&gt;
&lt;/mapping&gt;</pre><ul><li>le mapping qui charge entièrement l&#8217;objet et donc les associations en lazy-loading, appelé <em>maxiDeck</em></li></ul><pre class="brush: xml">&lt;mapping map-id="fullDeck"&gt;
    	&lt;class-a&gt;fr.mtg.supremacy.core.model.Deck&lt;/class-a&gt;
    	&lt;class-b&gt;fr.mtg.supremacy.core.dto.DeckDto&lt;/class-b&gt;
    	&lt;field custom-converter="fr.mtg.supremacy.core.converter.ColorEnumConverter"&gt;
    		&lt;a&gt;colors&lt;/a&gt;
    		&lt;b&gt;colors&lt;/b&gt;
  	&lt;/field&gt;
  	&lt;field custom-converter="fr.mtg.supremacy.core.converter.DeckStyleEnumConverter"&gt;
    		&lt;a&gt;styles&lt;/a&gt;
    		&lt;b&gt;styles&lt;/b&gt;
  	&lt;/field&gt;
&lt;/mapping&gt;</pre><h2>Impact</h2><p>L&#8217;impact au niveau de vos services métiers est minime, il suffit juste de récupérer la &laquo;&nbsp;moulinette Dozer&nbsp;&raquo; que vous avez défini préalablement via Spring ou autre (je vous renvois pour cela au premier lien en haut du billet), et ensuite il faut appeler la méthode <em>public void map(Object source, Object destination, String mapId) throws MappingException</em> qui va vous permettre de transformer vos objets Model en DTO avec le mapping de votre choix.</p><pre class="brush: java">Deck deck = this.deckDao.read(deckId);
DeckDto miniDeck = (DeckDto) mapperDozerBean.map(deck, DeckDto.class, "miniDeck");
ou
DeckDto fullDeck = (DeckDto) mapperDozerBean.map(deck, DeckDto.class, "fullDeck");</pre><p>Avec ce mécanisme mis en place, vous savez toujours de quoi sont composés les objets DTO qui sont remontés à vos clients et vous ne vous faites plus avoir avec des données non chargées au moment où vous en avez besoin, ce qui évitera par ailleurs toute Lazy-Loading Exception du style : &laquo;&nbsp;org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: fr.mtg.supremacy.core.model.Deck.contents, no session or session was closed&nbsp;&raquo;.</p> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/dozer-a-la-rescousse-du-lazy-loading/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>JSTL : Utilisation des constantes Java sans scriptlet</title><link>http://desgeeksetdeslettres.com/blog/programmation-java/jstl-utilisation-des-constantes-java-sans-scriptlet</link> <comments>http://desgeeksetdeslettres.com/blog/programmation-java/jstl-utilisation-des-constantes-java-sans-scriptlet#comments</comments> <pubDate>Tue, 21 Jun 2011 16:27:30 +0000</pubDate> <dc:creator>Mimie</dc:creator> <category><![CDATA[Programmation]]></category> <category><![CDATA[constante]]></category> <category><![CDATA[java]]></category> <category><![CDATA[jsp]]></category> <category><![CDATA[jstl]]></category> <category><![CDATA[map]]></category> <category><![CDATA[reflection]]></category> <category><![CDATA[scriptlet]]></category> <category><![CDATA[unstandard tag library]]></category> <guid
isPermaLink="false">http://desgeeksetdeslettres.com/blog/?p=2244</guid> <description><![CDATA[<a
href="http://desgeeksetdeslettres.com/blog/programmation-java/jstl-utilisation-des-constantes-java-sans-scriptlet"><img
align="left" hspace="5" width="155" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/06/scriptletless.jpg" class="alignleft wp-post-image tfe" alt="scriptletless" title="scriptletless" /></a>Tout le monde a vraisemblablement déjà dû se confronter au problème des constantes Java inutilisables avec la librairie de tags JSTL, j&#8217;en mettrai ma main au feu, et c&#8217;est généralement le prétexte utilisé pour mettre en place du code Java en dur à la place (une scriptlet), chose à proscrire à mon sens. La suite [...]]]></description> <content:encoded><![CDATA[<p></p><p>Tout le monde a vraisemblablement déjà dû se confronter au problème des constantes Java inutilisables avec la librairie de tags <a
href="http://adiguba.developpez.com/tutoriels/j2ee/jsp/jstl/" target="_blank">JSTL</a>, j&#8217;en mettrai ma main au feu, et c&#8217;est généralement le prétexte utilisé pour mettre en place du code Java en dur à la place (<a
href="http://www.commentcamarche.net/contents/jsp/jspscriptlets.php3" target="_blank">une scriptlet</a>), chose à proscrire à mon sens.</p><p><img
class="aligncenter size-full wp-image-2480" title="scriptletless" src="http://desgeeksetdeslettres.com/blog/wp-content/uploads/2011/06/scriptletless.jpg" alt="scriptletless" width="219" height="44" /></p><p>La suite de ce billet va vous montrer qu&#8217;il est désormais possible de se passer définitivement des scriptlets pour ce genre d&#8217;utilisation, en proposant deux solutions raisonnables.</p><h2>Constat</h2><ul><li>Imaginons une classe Java de constantes LoginConstants :</li></ul><pre class="brush: java">package blog.geeks.and.letters;
public class LoginConstants {
   public static final String KEY_USERNAME = "username";
   public static final String KEY_EMAIL = "email";
   (...)
}</pre><ul><li>L&#8217;exploitation d&#8217;une de ces constantes dans nos pages est malheureusement impossible en utilisant l&#8217;expression <a
href="http://www.ibm.com/developerworks/java/library/j-jstl0211/index.html#N100AF" target="_blank">JSTL EL</a> suivante, la raison est que EL ne dispose d&#8217;aucun moyen de référencer les valeurs des constantes de classes (de même que l&#8217;utilisation de &lt;jsp:useBean&gt; est impossible étant donné que les constantes n&#8217;ont pas d&#8217;accesseurs) :</li></ul><pre class="brush:xml">&lt;input type="text" name="${blog.geeks.and.letters.LoginConstants.KEY_USERNAME}"/&gt;</pre><ul><li>A la place nous voyons ce genre de contournement qui encombre très souvent nos pages :</li></ul><pre class="brush: xml">&lt;input type="text" name="&lt;%= blog.geeks.and.letters.LoginConstants.KEY_USERNAME %&gt;"/&gt;</pre><p>A partir de ce constat, voyons à présent les alternatives possibles.</p><h2>Choix 1 : API Reflection + Map</h2><p>L&#8217;idée est d&#8217;utiliser l&#8217;objet java.util.Map pour stocker en entrée et en sortie les noms des différentes constantes de votre application et leurs valeurs.</p><ul><li>L&#8217;avantage de l&#8217;objet Map est qu&#8217;il est facilement accessible en JSTL EL :</li></ul><pre class="brush: xml">${mapObject["MY_KEY"]} ou ${mapObject.MY_KEY}</pre><p>Pour cela nous utilisons l&#8217;introspection rendue possible via l&#8217;API Reflection, qui nous permet de parcourir les classes données et d&#8217;extraire les informations des attributs &laquo;&nbsp;<em>public static final</em>&nbsp;&raquo; afin d&#8217;alimenter notre Map.</p><ul><li>Voici une des façons possible en rendant accessible cet objet depuis le scope application de l&#8217;application Web :</li></ul><p><span
style="text-decoration: underline;">Paramétrage XML : web.xml</span></p><pre class="brush:xml">&lt;listener&gt;
    	&lt;listener-class&gt;MyApplicationContextListener&lt;/listener-class&gt;
&lt;/listener&gt;</pre><p><span
style="text-decoration: underline;">Listener Java : MyApplicationContextListener.java</span></p><pre class="brush: java">public class MyApplicationContextListener implements ServletContextListener {
	private static final String CONSTANTS = "constants";
	public void contextInitialized(ServletContextEvent event) {
		Map constants = new HashMap();
		Class[] declaringClasses = { blog.geeks.and.letters.LoginConstants.class };
		for (Class declaringClass : declaringClasses) {
			Field[] fields = declaringClass.getFields();
			for (int n = 0; n &lt; fields.length; n++) {
				int modifiers = fields[n].getModifiers();
				if (Modifier.isPublic(modifiers) &amp;&amp; Modifier.isStatic(modifiers) &amp;&amp; Modifier.isFinal(modifiers)) {
					try {
						constants.put(fields[n].getName(), fields[n].get(null));
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		event.getServletContext().setAttribute(CONSTANTS, constants);
	}
	public void contextDestroyed(ServletContextEvent event) {
		Map constants = (Map) event.getServletContext().getAttribute(CONSTANTS);
		try {
			event.getServletContext().removeAttribute(CONSTANTS);
		} finally {
			constants = null;
		}
	}
}</pre><p><span
style="text-decoration: underline;">Utilisation dans une page JSP</span></p><pre class="brush: xml">     ${applicationScope.constants.KEY_USERNAME}</pre><h2>Choix 2 : Apache Unstandard Tag Library</h2><p>&laquo;&nbsp;Un bon développeur est un développeur fainéant&nbsp;&raquo;, je n&#8217;ai bizarrement jamais oublié cette phrase provenant d&#8217;un de mes profs d&#8217;université &#8230; le mieux est donc de ne pas réinventer la roue, c&#8217;est pourquoi je ne peux que vous conseiller d&#8217;utiliser la <a
href="http://jakarta.apache.org/taglibs/sandbox/doc/unstandard-doc/intro.html" target="_blank">librairie de tags Unstandard</a> distribué par Apache qui permet entre autres de gérer le problème des constantes facilement.</p><ul><li>Il suffit pour cela d&#8217;inclure ces quelques lignes en entête de vos pages</li></ul><pre class="brush: xml">&lt;%@ taglib prefix="un" uri="http://jakarta.apache.org/taglibs/unstandard-1.0"%&gt;
&lt;un:useConstants var="LoginConstants" className="blog.geeks.and.letters.LoginConstants" /&gt;</pre><ul><li>Son utilisation est alors celle que nous recherchions dès le départ, à savoir :</li></ul><pre class="brush:xml">&lt;input type="text" name="${LoginConstants.KEY_USERNAME}"/&gt;</pre><div
class="wp-caption aligncenter" style="width: 350px"> <a
href="http://jakarta.apache.org/taglibs/sandbox/doc/unstandard-doc/intro.html"><img
src="http://jakarta.apache.org/taglibs/images/jakarta-logo.gif" alt="" width="350" height="100" /></a><p
class="wp-caption-text">Merci Apache <img
src='http://desgeeksetdeslettres.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p></div> ]]></content:encoded> <wfw:commentRss>http://desgeeksetdeslettres.com/blog/programmation-java/jstl-utilisation-des-constantes-java-sans-scriptlet/feed</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>
