<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Kevin Decherf's blog</title>
	
	<link>http://blog.kdecherf.com</link>
	<description>Technologies de l'information ... et plus si affinité</description>
	<lastBuildDate>Sat, 14 Jan 2012 18:54:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/kdecherf_blog" /><feedburner:info uri="kdecherf_blog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>FFmpeg: converting M4A files to MP3 with the same bitrate</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/uRrnV-ClD4Q/</link>
		<comments>http://blog.kdecherf.com/2012/01/14/ffmpeg-converting-m4a-files-to-mp3-with-the-same-bitrate/#comments</comments>
		<pubDate>Sat, 14 Jan 2012 18:54:23 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Tips]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=2082</guid>
		<description><![CDATA[Hello World, Today I show you a (really) tiny tip to convert M4A files to MP3 keeping bitrate with FFmpeg. By using the command ffmpeg -i thefile we obtain data about all streams of the file (codec, bitrate, &#8230;), like this: Well, the line we need to use for the bitrate is: Stream #0.2(eng): Audio: [...]]]></description>
			<content:encoded><![CDATA[<p>Hello World,</p>
<p>Today I show you a (really) tiny tip to convert M4A files to MP3 keeping bitrate with FFmpeg.</p>
<p>By using the command <span style="font-family: mono">ffmpeg -i thefile</span> we obtain data about all streams of the file (codec, bitrate, &#8230;), like this:</p>
<pre class="brush: plain; title: ; notranslate">$ ffmpeg -i test.m4a
[...]
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.m4a':
  Metadata:
    major_brand     : M4A 
    minor_version   : 1
    compatible_brands: M4A mp42isom
    creation_time   : 2012-01-06 13:08:47
    composer        : Tiësto
    title           : clublife_episode249
    artist          : Tiësto
    album           : Tiësto
    encoder         : GarageBand 6.0.4
  Duration: 00:59:04.87, start: 0.000000, bitrate: 324 kb/s
    Chapter #0.0: start 0.000000, end 31.000000
    Metadata:
      title           : Begin
    [...]
    Stream #0.0(eng): Subtitle: tx3g / 0x67337874, 0 kb/s
    Metadata:
      creation_time   : 2012-01-06 13:08:47
    Stream #0.1(eng): Subtitle: tx3g / 0x67337874
    Metadata:
      creation_time   : 2012-01-06 13:08:47
    Stream #0.2(eng): Audio: aac, 44100 Hz, stereo, s16, 319 kb/s
    Metadata:
      creation_time   : 2012-01-06 13:08:47
    Stream #0.3(eng): Video: mjpeg, yuvj444p, 300x300 [PAR 72:72 DAR 1:1], 2 kb/s, 0k fps, 600 tbr, 600 tbn, 600 tbc
    Metadata:
      creation_time   : 2012-01-06 13:08:47
</pre>

<p>Well, the line we need to use for the bitrate is: <span style="font-family:mono">Stream #0.2(eng): Audio: aac, 44100 Hz, stereo, s16, 319 kb/s</span>. Now we can play with grep and awk to extract <em>319</em> (<em>according to the example line</em>):</p>
<pre class="brush: plain; title: ; notranslate">$ ffmpeg -i test.m4a 2>&#038;1 | grep Audio | awk -F', ' '{print $5}' | cut -d' ' -f1
319</pre>

<p>This output will be used for the -ab argument:</p>

<pre class="brush: plain; title: ; notranslate">ffmpeg -i test.m4a -ab `ffmpeg -i test.m4a 2>&#038;1 | grep Audio | awk -F', ' '{print $5}' | cut -d' ' -f1`k test.mp3</pre>

Finally, we verify the new file:

<pre class="brush: plain; title: ; notranslate">$ ffmpeg -i test.mp3
[...]
Input #0, mp3, from 'test.mp3':
  Metadata:
    major_brand     : M4A 
    minor_version   : 1
    compatible_brands: M4A mp42isom
    creation_time   : 2012-01-06 13:08:47
    composer        : Tiësto
    title           : clublife_episode249
    artist          : Tiësto
    album           : Tiësto
    encoder         : Lavf53.2.0
  Duration: 00:59:04.93, start: 0.000000, bitrate: 320 kb/s
    Stream #0.0: Audio: mp3, 44100 Hz, stereo, s16, 320 kb/s</pre>

<p>Enjoy it !</p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=uRrnV-ClD4Q:N4-4YlL6kRg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=uRrnV-ClD4Q:N4-4YlL6kRg:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=uRrnV-ClD4Q:N4-4YlL6kRg:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=uRrnV-ClD4Q:N4-4YlL6kRg:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/uRrnV-ClD4Q" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2012/01/14/ffmpeg-converting-m4a-files-to-mp3-with-the-same-bitrate/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2012/01/14/ffmpeg-converting-m4a-files-to-mp3-with-the-same-bitrate/</feedburner:origLink></item>
		<item>
		<title>Iftop : utiliser l’interface par défaut</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/xfjMuIfpVQw/</link>
		<comments>http://blog.kdecherf.com/2011/09/10/iftop-utiliser-interface-par-defaut/#comments</comments>
		<pubDate>Sat, 10 Sep 2011 12:29:47 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Tips]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=2050</guid>
		<description><![CDATA[Je ne poste pas souvent ces temps-ci, mais en ce début de week-end je vous offre une petite astuce pour iftop, l&#8217;utilitaire qui permet d&#8217;afficher en détail le traffic entrant et sortant d&#8217;une interface réseau. Venant de migrer sur tmux, je me suis réservé une fenêtre pour iftop. Le soucis c&#8217;est qu&#8217;il faut préciser à [...]]]></description>
			<content:encoded><![CDATA[<p>Je ne poste pas souvent ces temps-ci, mais en ce début de week-end je vous offre une petite astuce pour <em>iftop</em>, l&#8217;utilitaire qui permet d&#8217;afficher en détail le traffic entrant et sortant d&#8217;une interface réseau.</p>
<p>Venant de migrer sur tmux, je me suis réservé une fenêtre pour iftop. Le soucis c&#8217;est qu&#8217;il faut préciser à chaque fois quelle interface doit utiliser ce dernier au lancement et je ne suis pas tout le temps sur un réseau filaire. Voilà une petite astuce pour contourner ce problème :</p>
<pre class="brush: bash; light: true; title: ; notranslate">
iftop -i `ip route | grep -E "^default" | awk -F' ' '{print $5}' | sed -n 1p`
</pre>
<p>Cette commande va récupérer l&#8217;interface par défaut via la table de routage. S&#8217;il y a plusieurs routes par défaut, on prend la première. A noter que je pars du principe que la route par défaut précise toujours une interface et que vous avez installé <em>ip</em>, exemple :</p>
<pre class="brush: plain; light: true; title: ; notranslate">% ip route
default via 192.168.0.1 dev eth0  proto static</pre>
<p><em>Enjoy it !</em></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=xfjMuIfpVQw:OtNTQxtmlak:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=xfjMuIfpVQw:OtNTQxtmlak:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=xfjMuIfpVQw:OtNTQxtmlak:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=xfjMuIfpVQw:OtNTQxtmlak:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/xfjMuIfpVQw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2011/09/10/iftop-utiliser-interface-par-defaut/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2011/09/10/iftop-utiliser-interface-par-defaut/</feedburner:origLink></item>
		<item>
		<title>Java : faire des sommes SHA-512 comme un malpropre</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/HjVGKEhWnLA/</link>
		<comments>http://blog.kdecherf.com/2011/07/23/java-faire-des-sommes-sha-512-comme-un-malpropre/#comments</comments>
		<pubDate>Sat, 23 Jul 2011 21:57:16 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Tips]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=2016</guid>
		<description><![CDATA[Ou comment perdre plusieurs heures sur un problème bien planqué et très con. Dans le cadre d&#8217;un projet je devais stocker la somme SHA-512 de chaînes de caractères. Je suis parti à la recherche d&#8217;un bout de code pour faire ce que je voulais et j&#8217;ai trouvé le code suivant : On calcule la somme [...]]]></description>
			<content:encoded><![CDATA[<p>Ou comment perdre plusieurs heures sur un problème bien planqué et très con. Dans le cadre d&#8217;un projet je devais stocker la somme SHA-512 de chaînes de caractères. Je suis parti à la recherche d&#8217;un bout de code pour faire ce que je voulais et j&#8217;ai trouvé le code suivant :</p>

<pre class="brush: java; title: ; notranslate">
      MessageDigest md = MessageDigest.getInstance("SHA-512");

      md.update(myString.getBytes());
      byte[] mb = md.digest();
      StringBuilder hexString = new StringBuilder();
      for (int i = 0; i &lt; mb.length; i++) {
         hexString.append(Integer.toHexString(0xFF &#038; mb[i]));
      }
</pre>

<p>On calcule la somme d&#8217;un mot et on obtient ceci :</p>
<pre class="brush: plain; light: true; title: ; notranslate">f4baf3aec5ea176f1e641bdfaa1fa8fc25b7d6275b2690df1da571d6dc8bc8293923f2245bdb57be5a20a274612b9ccb232d91e9d840db4a6c62709d80f92e</pre>

<p>Et un sha512sum (dans un terminal) pour le même mot nous donne ceci :</p>
<pre class="brush: plain; light: true; title: ; notranslate">f4baf3aec5ea176f01e641bdfaa1fa8f0c25b7d6275b2690df1da571d6dc8bc8293923f2245bdb57be5a20a274612b9ccb232d91e9d840db4a6c62709d80f92e</pre>

<p>Avez-vous remarqué que bien qu&#8217;elles se ressemblent, ces chaînes ne correspondent pas ?<br />
La première fait 126 caractères alors qu&#8217;une somme SHA-512 doit en faire 128 &#8230; Je vous laisse trouver les deux caractères manquants &#8230; Vous m&#8217;en voulez ? Ok, les deux caractères manquants sont &#8230; des 0.</p>
<p>Mais pourtant il y en a déjà dans les deux chaînes, non ? En effet. Après quelques heures de recherche je me lance la suggestion suivante sachant qu&#8217;on calcule la somme par pas de deux caractères hexadécimaux : et si le 0 de gauche n&#8217;était jamais ajouté ?<br />Je me penche sur la méthode <em>Integer.toHexString()</em> en regardant la Javadoc :</p>
<blockquote>This value is converted to a string of ASCII digits in hexadecimal (base&nbsp;16) with no extra leading 0s</blockquote>
<p>TOUT S&#8217;EXPLIQUE. Du coup, on doit rajouter un zéro à la main quand il le faut :</p>
<pre class="brush: java; highlight: [8,9,10]; title: ; notranslate">
      MessageDigest md = MessageDigest.getInstance("SHA-512");

      md.update(myString.getBytes());
      byte[] mb = md.digest();
      StringBuilder hexString = new StringBuilder();
      for (int i = 0; i &lt; mb.length; i++) {
         Integer n = 0xFF &#038; mb[i];
         if (n &lt; 16) {
            hexString.append("0");
         }
         
         hexString.append(Integer.toHexString(n));
      }
</pre>

<p>La morale de cette histoire : coder la nuit pour trouver ce genre d&#8217;erreur, c&#8217;est cool.</p>

<p><em>Enjoy it !</em></p>

<p><em>PS : j&#8217;offre une bière à la première personne qui trouve le mot correspondant à cette somme SHA-512 <img src='http://blog.kdecherf.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </em></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=HjVGKEhWnLA:fKaTf1xnzE8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=HjVGKEhWnLA:fKaTf1xnzE8:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=HjVGKEhWnLA:fKaTf1xnzE8:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=HjVGKEhWnLA:fKaTf1xnzE8:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/HjVGKEhWnLA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2011/07/23/java-faire-des-sommes-sha-512-comme-un-malpropre/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2011/07/23/java-faire-des-sommes-sha-512-comme-un-malpropre/</feedburner:origLink></item>
		<item>
		<title>Java : un équivalent à Iconv//TRANSLIT</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/PJcMlgKWkVk/</link>
		<comments>http://blog.kdecherf.com/2011/06/19/java-equivalent-iconv-translit/#comments</comments>
		<pubDate>Sun, 19 Jun 2011 18:47:34 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Tips]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=1994</guid>
		<description><![CDATA[Il y a deux ans j&#8217;avais publié un petit billet sur le nettoyage d&#8217;accents en PHP à l&#8217;aide d&#8217;Iconv. J&#8217;ai eu besoin de faire la même chose en Java récemment, seulement le mode //TRANSLIT n&#8217;existe pas. Fort heureusement, une petite recherche m&#8217;a permis de trouver mon bonheur : En résumé, ce bout de code décompose [...]]]></description>
			<content:encoded><![CDATA[<p>Il y a deux ans j&#8217;avais publié un petit billet sur le <a href="/2009/04/14/php-nettoyer-des-accents-simplement-avec-iconv/">nettoyage d&#8217;accents en PHP à l&#8217;aide d&#8217;Iconv</a>. J&#8217;ai eu besoin de faire la même chose en Java récemment, seulement le mode //TRANSLIT n&#8217;existe pas.</p>

<p>Fort heureusement, une petite recherche m&#8217;a permis de trouver <a href="http://stackoverflow.com/questions/5806690/is-there-an-iconv-with-translit-equivalent-in-java">mon bonheur</a> :</p>
<pre class="brush: java; title: ; notranslate">
String decomposed = Normalizer.normalize(accented, Normalizer.Form.NFKD);
StringBuilder buf = new StringBuilder();
for (int idx = 0; idx &lt; decomposed.length(); ++idx) {
  char ch = decomposed.charAt(idx);
  if (ch &lt; 128)
    buf.append(ch);
}
String filtered = buf.toString();
</pre>
<p>En résumé, ce bout de code décompose les caractères accentués en suite de caractères simples (exemple : è donne e`) puis ne conserve que les caractères ASCII (code ASCII < 128).</p>
<p><em>Enjoy it !</em></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=PJcMlgKWkVk:ojNByU7qEI8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=PJcMlgKWkVk:ojNByU7qEI8:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=PJcMlgKWkVk:ojNByU7qEI8:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=PJcMlgKWkVk:ojNByU7qEI8:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/PJcMlgKWkVk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2011/06/19/java-equivalent-iconv-translit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2011/06/19/java-equivalent-iconv-translit/</feedburner:origLink></item>
		<item>
		<title>Java/Jersey: A CORS-Compliant REST API (die JSONP die)</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/f7meXYWok90/</link>
		<comments>http://blog.kdecherf.com/2011/06/19/java-jersey-a-cors-compliant-rest-api/#comments</comments>
		<pubDate>Sun, 19 Jun 2011 18:35:11 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[rest]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=1965</guid>
		<description><![CDATA[Une traduction française est disponible ici. Cross-Domain AJAX request is the developer&#8217;s nightmare with the awful JSONP workaround. But we can use a simple standard to kick off this bad practice. Reminder When a developer needs to make cross-domain requests (AJAX requests on another (sub-)domain or non-standard port, limited by browsers), he often uses the [...]]]></description>
			<content:encoded><![CDATA[<div class="mybox info">Une traduction française est disponible <a href="/2011/06/02/java-jersey-une-api-rest-cross-domain-sans-jsonp/">ici</a>.</div>
<p>Cross-Domain AJAX request is the developer&#8217;s nightmare with the awful JSONP workaround. But we can use a simple standard to kick off this bad practice.</p>
<h3>Reminder</h3>
<p>When a developer needs to make cross-domain requests (<em>AJAX requests on another (sub-)domain or non-standard port, limited by browsers</em>), he often uses the JSONP workaround : we add a Javascript callback in the API response and we &#8216;eval&#8217; it.</p>
<p>For a recent project I refused to use JSONP to make my REST API cross-domain compatible, so I looked for an alternative solution. This solution is Cross-Origin Resource Sharing (CORS), a W3C standard.</p>
<h3>Synopsis</h3>
<p>In the last revision of the document, new headers are added to the HTTP protocol (<em>Not used by RFC 2616</em>) and a special request (<em>preflight request</em>) was created for cross-domain rights access control during an AJAX request.</p>
<p><strong>Browser side</strong><br />
<ul>
<li><strong>Origin</strong>: shows the request domain</li>
<li><strong>Access-Control-Request-Method</strong>: shows the request HTTP verb</li>
<li><strong>Access-Control-Request-Headers</strong>: shows additional headers used by browser and must be authorised by server to continue AJAX requests</li>
</ul>
</p>
<p><strong>Server side</strong><br />
<ul>
<li><strong>Access-Control-Allow-Origin</strong>: indicates authorised domains to make cross-domain requests (<em>should contain at least value of &#8216;Origin&#8217; header or &#8216;*&#8217;</em>)</li>
<li><strong>Access-Control-Allow-Credentials</strong>: indicates if server allow credentials during CORS requests</li>
<li><strong>Access-Control-Expose-Headers</strong>: indicates allowed headers to be sent to the browser</li>
<li><strong>Access-Control-Max-Age</strong>: indicates how long a response to a preflight request can be cached</li>
<li><strong>Access-Control-Allow-Methods</strong>: indicates all allowed HTTP verbs for cross-domain requests (should contain at least the &#8216;Access-Control-Request-Method&#8217; header value)</li>
<li><strong>Access-Control-Allow-Headers</strong>: indicates allowed custom headers to be used by browser during cross-domain requests (should contain at least the &#8216;Access-Control-Request-Headers&#8217; header value)</li>
</ul></p>
<p>In this post, I don&#8217;t use <em>Access-Control-Allow-Credentials</em>, <em>Access-Control-Expose-Headers</em> and <em>Access-Control-Max-Age</em> headers.</p>
<h3>How does it work ?</h3>
<p>For standard requests, the browser will add <em>Origin</em> and <em>Access-Control-Request-Method</em> headers. A preflight request will be executed before the actual request if it contains custom headers, if it uses another HTTP verb than GET or POST or also if the body isn&#8217;t in text/plain format (ie. application/json).</p>
<p>There is a preflight request made by Firefox :
<pre>OPTIONS /url HTTP/1.1
Host: 127.0.0.1:5555
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://127.0.0.1
Access-Control-Request-Method: POST
Access-Control-Request-Headers: x-requested-with</pre></p>
<p>We can see <em>Origin</em>, <em>Access-Control-Request-Method</em> and <em>Access-Control-Request-Headers</em> headers. After this request, Firefox waits a similar response:
<pre>X-Powered-By: Servlet/3.0
Server: GlassFish Server Open Source Edition 3.0.1
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: x-requested-with</pre></p>
<p>After this, Firefox can continue with its requests and adds a custom header:
<pre>X-Requested-With: XMLHttpRequest</pre></p>
<h3>And our API ?</h3>
<p>Well, now we modify our API to be CORS-compliant using Java and Jersey. You can add a simple method like this:
<pre class="brush: java; title: ; notranslate">private String _corsHeaders;

private Response makeCORS(ResponseBuilder req, String returnMethod) {
   ResponseBuilder rb = req.ok()
      .header("Access-Control-Allow-Origin", "*")
      .header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");

   if (!"".equals(returnMethod)) {
      rb.header("Access-Control-Allow-Headers", returnMethod);
   }

   return rb.build();
}

private Response makeCORS(ResponseBuilder req) {
   return makeCORS(req, _corsHeaders);
}</pre>
Because of I didn&#8217;t find the catch-all @Path, we need to add methods as many as paths the API manage:
<pre class="brush: java; title: ; notranslate">   // This OPTIONS request/response is necessary
   // if you consumes other format than text/plain or
   // if you use other HTTP verbs than GET and POST
   @OPTIONS
   @Path("/myresource")
   public Response corsMyResource(@HeaderParam("Access-Control-Request-Headers") String requestH) {
      _corsHeaders = requestH;
      return makeCORS(Response.ok(), requestH);
   }

   @GET
   @Path("/myresource")
   public Response myResource() {
      // myResponse is a ResponseBuilder object
      return makeCORS(myResponse);
   }</pre>
These code snippets are given only as an example, you can change it to build <em>Access-Control-Allow-Methods</em> according to the API&#8217;s WADL scheme or add a restrictive <em>Access-Control-Allow-Origin</em> rule.</p>
<h3>What about browser compatibility ?</h3>
<p>With this standard you can miss Internet Explorer 6 and 7. Internet Explorer 8 is saved by a new <em>XDomainRequest</em> object replacing <em>XMLHttpRequest</em> but seems to be not compatible with preflight requests. Other browsers are globally compatible with their last versions.</p>
<p><strong>More information:</strong>
<ul>
<li><a href="http://www.w3.org/TR/cors/" target="_blank">W3C Worksheet about CORS</a></li>
<li><a href="https://developer.mozilla.org/En/HTTP_Access_Control" target="_blank">Using CORS with Firefox 3.5</a></li>
<li><a href="http://msdn.microsoft.com/en-us/library/cc288060(v=vs.85).aspx" target="_blank">XDomainRequest object on MSDN</a></li>
<li><a href="http://tools.ietf.org/html/rfc2616" target="_blank">RFC 2616: HTTP Protocol</a></li>
</ul></p>
<p><em>Enjoy it !</em></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=f7meXYWok90:9zNEfaKQnBg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=f7meXYWok90:9zNEfaKQnBg:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=f7meXYWok90:9zNEfaKQnBg:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=f7meXYWok90:9zNEfaKQnBg:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/f7meXYWok90" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2011/06/19/java-jersey-a-cors-compliant-rest-api/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2011/06/19/java-jersey-a-cors-compliant-rest-api/</feedburner:origLink></item>
		<item>
		<title>Java/Jersey : une API REST Cross-Domain sans JSONP</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/ZXJ_sH5q29k/</link>
		<comments>http://blog.kdecherf.com/2011/06/02/java-jersey-une-api-rest-cross-domain-sans-jsonp/#comments</comments>
		<pubDate>Thu, 02 Jun 2011 16:35:07 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[cors]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=1869</guid>
		<description><![CDATA[An english version of this post is available here. Ah les joies d&#8217;AJAX et du Cross-Domain &#8230; Ou plutôt le cauchemar des développeurs. Aujourd&#8217;hui, je vais vous présenter un concept pour rendre rapidement et simplement une API REST Java/Jersey compatible avec la norme W3C CORS pour faire du Cross-Domain sans utiliser JSONP. Contexte La plupart [...]]]></description>
			<content:encoded><![CDATA[<div class="mybox info">An english version of this post is available <a href="/2011/06/19/java-jersey-a-cors-compliant-rest-api/">here</a>.</div>
<p>Ah les joies d&#8217;AJAX et du Cross-Domain &#8230; Ou plutôt le cauchemar des développeurs. Aujourd&#8217;hui, je vais vous présenter un concept pour rendre rapidement et simplement une API REST Java/Jersey compatible avec la norme W3C CORS pour faire du Cross-Domain sans utiliser JSONP.</p>
<h3>Contexte</h3>
<p>La plupart du temps, quand un développeur doit faire des requêtes Cross-Domain, il n&#8217;a pas d&#8217;autres choix que d&#8217;utiliser du JSONP : un callback est injecté dans une réponse en JSON puis le résultat est directement exécuté en Javascript. Cette méthode est lourde et relativement sale (<em>et sur le coup j&#8217;avoue ne pas être assez sale pour l&#8217;utiliser</em>).</p>
<p>Dans le cadre d&#8217;une mission où nous devions réaliser une API REST, j&#8217;ai décidé de trouver une autre solution que de faire du JSONP. La réponse se trouve dans la dernière révision de la norme Cross-Origin Resource Sharing (CORS) du W3C.</p>
<h3>Principe</h3>
<p>Cette dernière révision nous présente l&#8217;ajout d&#8217;en-têtes spéciaux (<em>qui ne font pas partie de la RFC 2616</em>) et de la <em>preflight request</em> exécutée par le navigateur avant d&#8217;envoyer sa requête AJAX pour vérifier les permissions d&#8217;accès.</p>
<p><strong>Côté navigateur</strong><br />
<ul>
<li><strong>Origin</strong>: indique le domaine de la requête</li>
<li><strong>Access-Control-Request-Method</strong>: indique la méthode utilisée dans la requête</li>
<li><strong>Access-Control-Request-Headers</strong>: indique l&#8217;en-tête qui sera utilisé par le navigateur et devra être autorisé par le serveur pour terminer la requête (utilisé lors d&#8217;une requête <em>preflight</em>)</li>
</ul>
</p>
<p><strong>Côté serveur</strong><br />
<ul>
<li><strong>Access-Control-Allow-Origin</strong>: indique le(s) domaine(s) autorisé(s) à faire des requêtes Cross-Domain (doit contenir au minimum le résultat de <em>Origin</em> ou *)</li>
<li><strong>Access-Control-Allow-Credentials</strong>: indique si l&#8217;utilisation de credentials est autorisée lors d&#8217;une requête Cross-Domain (Cookie, HTTP Authentication, &#8230;)</li>
<li><strong>Access-Control-Expose-Headers</strong>: indique les en-têtes pouvant être exposés sans risque à un navigateur</li>
<li><strong>Access-Control-Max-Age</strong>: indique le temps dont une réponse à une <em>preflight request</em> peut être mise en cache par le navigateur</li>
<li><strong>Access-Control-Allow-Methods</strong>: indique la liste des verbes HTTP pouvant être utilisés pour une requête Cross-Domain (doit contenir au minimum le résultat de <em>Access-Control-Request-Method</em>)</li>
<li><strong>Access-Control-Allow-Headers</strong>: indique la liste des en-têtes personnalisés autorisés pour une requête Cross-Domain (doit contenir au minimum le résultat de <em>Access-Control-Request-Headers</em>)</li>
</ul></p>
<p>Dans ce billet, je mets de côté <em>Access-Control-Allow-Credentials</em>, <em>Access-Control-Expose-Headers</em> et <em>Access-Control-Max-Age</em>.</p>
<h3>Dans les faits</h3>
<p>En condition normale, le navigateur va ajouter les en-têtes <em>Origin</em> et <em>Access-Control-Request-Method</em> lors de la requête. Une requête préliminaire sera faite (<em>preflight request</em>) si des en-têtes personnalisés sont présents, si la requête utilise une méthode autre que <em>GET</em> et <em>POST</em> ou encore que le client envoie des données qui ne sont pas au format text/plain (du JSON par exemple).</p>
<p>Voici un exemple de <em>preflight request</em> envoyée par Firefox :
<pre>OPTIONS /monurl HTTP/1.1
Host: 127.0.0.1:5555
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://127.0.0.1
Access-Control-Request-Method: POST
Access-Control-Request-Headers: x-requested-with</pre></p>
<p>On remarque bien les en-têtes <em>Origin</em>, <em>Access-Control-Request-Method</em> et <em>Access-Control-Request-Headers</em>. Maintenant, Firefox s&#8217;attend à recevoir une réponse de ce style de la part du serveur (exemple) :
<pre>X-Powered-By: Servlet/3.0
Server: GlassFish Server Open Source Edition 3.0.1
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: x-requested-with</pre></p>
<p>A ce niveau, Firefox sait qu&#8217;il peut faire des requêtes AJAX vers ce serveur, il continue donc avec ses requêtes normales en ajoutant son en-tête personnalisé :
<pre>X-Requested-With: XMLHttpRequest</pre></p>
<h3>Et notre API ?</h3>
<p>J&#8217;y viens, côté code maintenant nous considérons avoir une API REST déjà faite (en utilisant Jersey). Il suffit d&#8217;ajouter une fonction similaire à celle-là :
<pre class="brush: java; title: ; notranslate">private String _corsHeaders;

private Response makeCORS(ResponseBuilder req, String returnMethod) {
   ResponseBuilder rb = req.ok()
      .header("Access-Control-Allow-Origin", "*")
      .header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");

   if (!"".equals(returnMethod)) {
      rb.header("Access-Control-Allow-Headers", returnMethod);
   }

   return rb.build();
}

private Response makeCORS(ResponseBuilder req) {
   return makeCORS(req, _corsHeaders);
}</pre>
Puis d&#8217;ajouter autant de fonctions comme celle-ci que de @Path à gérer (<em>je n&#8217;ai pas réussi à trouver le @Path &#8220;catch-all&#8221;</em>) :
<pre class="brush: java; title: ; notranslate">   // La méthode OPTIONS doit être gérée si vous faites des requêtes
   // avec autre chose que du GET, POST ou que le client transmet
   // des données dans un format différent de text/plain
   @OPTIONS
   @Path("/maressource")
   public Response corsMaRessource(@HeaderParam("Access-Control-Request-Headers") String requestH) {
      _corsHeaders = requestH;
      return makeCORS(Response.ok(), requestH);
   }

   @GET
   @Path("/maressource")
   public Response maRessource() {
      // Traitement de la requête, ResponseBuilder maReponse
      return makeCORS(maReponse);
   }</pre>
Bien entendu, ceci n&#8217;est donné qu&#8217;à titre d&#8217;exemple et libre à vous d&#8217;adapter. Entre autre renseigner dynamiquement <em>Access-Control-Allow-Methods</em> en fonction de l&#8217;API et de son WADL ou encore restreindre <em>Access-Control-Allow-Origin</em>.</p>
<h3>Et la compatibilité dans tout ça ?</h3>
<p>Point de vue compatibilité avec cette petite norme laissez tomber Internet Explorer 6 et 7, quant à Internet Explorer 8 on est sauvé par l&#8217;ajout d&#8217;un objet spécial <em>XDomainRequest</em> remplaçant <em>XMLHttpRequest</em>. A noter cependant que l&#8217;objet <em>XDomainRequest</em> ne semble pas être compatible avec les <em>preflight requests</em>. Pour les autres navigateurs ça tourne globalement partout avec les dernières versions.</p>
<p><strong>Plus d&#8217;informations :</strong>
<ul>
<li><a href="http://www.w3.org/TR/cors/" target="_blank">Document de travail du W3C pour la norme CORS</a></li>
<li><a href="https://developer.mozilla.org/En/HTTP_Access_Control" target="_blank">Présentation détaillée de l&#8217;utilisation de CORS avec Firefox 3.5</a></li>
<li><a href="http://msdn.microsoft.com/en-us/library/cc288060(v=vs.85).aspx" target="_blank">Présentation de l&#8217;objet XDomainRequest sur MSDN</a></li>
<li><a href="http://tools.ietf.org/html/rfc2616" target="_blank">RFC 2616 : Protocole HTTP</a></li>
</ul></p>
<p><em>Enjoy it !</em></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=ZXJ_sH5q29k:Hikq_b6XAOk:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=ZXJ_sH5q29k:Hikq_b6XAOk:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=ZXJ_sH5q29k:Hikq_b6XAOk:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=ZXJ_sH5q29k:Hikq_b6XAOk:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/ZXJ_sH5q29k" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2011/06/02/java-jersey-une-api-rest-cross-domain-sans-jsonp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2011/06/02/java-jersey-une-api-rest-cross-domain-sans-jsonp/</feedburner:origLink></item>
		<item>
		<title>Magento : bloc page/template_links et classes CSS</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/KCcRTKG2tQ0/</link>
		<comments>http://blog.kdecherf.com/2011/01/22/magento-bloc-pagetemplate_links-et-classes-css/#comments</comments>
		<pubDate>Sat, 22 Jan 2011 22:25:40 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Tips]]></category>
		<category><![CDATA[Magento]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=1835</guid>
		<description><![CDATA[C&#8217;est au cours d&#8217;une mission sur Magento que j&#8217;ai remarqué un fonctionnement plutôt limitant du bloc page/template_links. En effet on ne peut pas assigner de classes personnalisées à la balise li. Voici la solution (pour les plus fainéants ). Problème Vous avez surement déjà utilisé un bloc de ce type, il vous permet d&#8217;ajouter des [...]]]></description>
			<content:encoded><![CDATA[<p>C&#8217;est au cours d&#8217;une mission sur Magento que j&#8217;ai remarqué un fonctionnement plutôt limitant du bloc <strong>page/template_links</strong>. En effet on ne peut pas assigner de classes personnalisées à la balise <em>li</em>. Voici la solution (pour les plus fainéants <img src='http://blog.kdecherf.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ).</p>
<p><img class="aligncenter size-full wp-image-1237" title="magento_logo" src="http://blog.kdecherf.com/wp-content/uploads/2010/01/magento_logo.gif" alt="" width="140" height="140" /></p>
<p><strong>Problème</strong><br /> Vous avez surement déjà utilisé un bloc de ce type, il vous permet d&#8217;ajouter des liens depuis les fichiers xml de configuration du thème :</p>
<pre class="brush: xml; title: ; notranslate">&lt;block type="page/template_links" name="top.links" as="topLinks">
 &lt;action method="addLink" translate="label title" module="customer">
   &lt;label>My Account&lt;/label>
   &lt;url helper="customer/getAccountUrl"/>
   &lt;title>My Account&lt;/title>
   &lt;prepare/>
   &lt;urlParams/>
   &lt;position>10&lt;/position>
   &lt;liParams>class="myaccount" id="topAccount"&lt;/liParams>
 &lt;/action>
&lt;/block>
        </pre>
<p>Ce bloc, si pratique, a une faiblesse. Du moins avec le template par défaut :<br />
<em>app/design/frontend/base/default/template/page/template/links.phtml</em></p>
<pre class="brush: php; title: ; notranslate">&lt;?php $_links = $this->getLinks(); ?>
&lt;?php if(count($_links)>0): ?>
&lt;ul class="links"&lt;?php if($this->getName()): ?> id="&lt;?php echo $this->getName() ?>"&lt;?php endif;?>>
    &lt;?php foreach($_links as $_link): ?>
        &lt;li&lt;?php if($_link->getIsFirst()||$_link->getIsLast()): ?> class="&lt;?php if($_link->getIsFirst()): ?>first&lt;?php endif; ?>&lt;?php if($_link->getIsLast()): ?> last&lt;?php endif; ?>"&lt;?php endif; ?> &lt;?php echo $_link->getLiParams() ?>>&lt;?php echo $_link->getBeforeText() ?>&lt;a href="&lt;?php echo $_link->getUrl() ?>" title="&lt;?php echo $_link->getTitle() ?>" &lt;?php echo $_link->getAParams() ?>>&lt;?php echo $_link->getLabel() ?>&lt;/a>&lt;?php echo $_link->getAfterText() ?>&lt;/li>
    &lt;?php endforeach; ?>
&lt;/ul>
&lt;?php endif; ?></pre>
<p>On remarquera qu&#8217;il n&#8217;est pas possible, du moins pour les premiers et derniers éléments de listes, d&#8217;avoir un attribut <em>class</em> assigné via le paramètre <em>liParams</em> du layout (<em>cf. exemple du début</em>) car cela fait doublon.</p>
<p><strong>La solution</strong><br />
La solution consiste à traiter le contenu de la méthode <em>getLiParams()</em> afin d&#8217;y inclure les classes <em>first</em> et/ou <em>last</em> en fonction de la position du lien, évitant ainsi de générer un attribut html en double. En mettant le code PHP directement dans le fichier phtml, cela donne :</p>
<pre class="brush: php; title: ; notranslate">&lt;?php $_links = $this->getLinks(); ?>
&lt;?php if (count($_links) > 0): ?>
    &lt;ul class="links"&lt;?php if ($this->getName()): ?> id="&lt;?php echo $this->getName() ?>"&lt;?php endif; ?>>
    &lt;?php foreach ($_links as $_link): ?>
    &lt;?php
	    $liparams = $_liparams = $_link->getLiParams();

	    // Is class exists in liparams ?
	    if (preg_match('`class="([^"]*)"`i', $_liparams, $rtn)) {
		$orig = $rtn[0];
		$classes = explode(' ', $rtn[1]);

		if ($_link->getIsFirst())
		    $classes[] = 'first';

		if ($_link->getIsLast())
		    $classes[] = 'last';

		$rplc = implode(' ', $classes);
		$liparams = str_replace($orig, 'class="' . $rplc . '"', $_liparams);
	    } else {
		if ($_link->getIsFirst())
		    $liparams .= ' class="first ';

		if ($_link->getIsLast()) {
		    if (!$_link->getIsFirst())
			$liparams .= ' class="';

		    $liparams .= 'last';
		}

		if ($liparams != $_liparams)
		    $liparams = rtrim($liparams) . '"';
	    }
    ?>
	    &lt;li&lt;?php if($liparams): ?> &lt;?php echo $liparams ?>&lt;?php endif; ?>>&lt;?php echo $_link->getBeforeText() ?>&lt;a href="&lt;?php echo $_link->getUrl() ?>" title="&lt;?php echo $_link->getTitle() ?>" &lt;?php echo $_link->getAParams() ?>>&lt;?php echo $_link->getLabel() ?>&lt;/a>&lt;?php echo $_link->getAfterText() ?>&lt;/li>
    &lt;?php endforeach; ?>
	&lt;/ul>	
&lt;?php endif; ?>
</pre>
<p>De manière plus propre, on pourra mettre ce bout de code en tant qu&#8217;Helper dans un module perso. L&#8217;appel devient donc :</p>
<pre class="brush: php; title: ; notranslate">&lt;?php $_links = $this->getLinks(); ?>
&lt;?php if(count($_links)>0): ?>
&lt;ul class="links"&lt;?php if($this->getName()): ?> id="&lt;?php echo $this->getName() ?>"&lt;?php endif;?>>
    &lt;?php foreach($_links as $_link): ?>
	&lt;?php
	// Fix for li classes issue.
	$fixhlp = Mage::helper('mymodule/myhelper');
	$liparams = $fixhlp->liParamsHelper($_link);
	?>

        &lt;li&lt;?php if($liparams): ?> &lt;?php echo $liparams ?>&lt;?php endif; ?>>&lt;?php echo $_link->getBeforeText() ?>&lt;?php if($_link->getLabel()): ?>&lt;a href="&lt;?php echo $_link->getUrl() ?>" title="&lt;?php echo $_link->getTitle() ?>" &lt;?php echo $_link->getAParams() ?>>&lt;?php echo $_link->getLabel() ?>&lt;/a>&lt;?php endif; ?>&lt;?php echo $_link->getAfterText() ?>&lt;/li>
    &lt;?php endforeach; ?>
&lt;/ul>
&lt;?php endif; ?>
</pre>
<p>Enjoy it !</p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=KCcRTKG2tQ0:DJrIFz7EpSw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=KCcRTKG2tQ0:DJrIFz7EpSw:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=KCcRTKG2tQ0:DJrIFz7EpSw:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=KCcRTKG2tQ0:DJrIFz7EpSw:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/KCcRTKG2tQ0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2011/01/22/magento-bloc-pagetemplate_links-et-classes-css/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2011/01/22/magento-bloc-pagetemplate_links-et-classes-css/</feedburner:origLink></item>
		<item>
		<title>Jdep-grapher: make a dependency tree of your Java projects</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/ECvFzgsZrV8/</link>
		<comments>http://blog.kdecherf.com/2011/01/14/jdep-grapher-make-a-dependency-tree-of-your-java-projects/#comments</comments>
		<pubDate>Fri, 14 Jan 2011 10:00:31 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=1805</guid>
		<description><![CDATA[Oh, my first english post &#8230; so amazing (isn&#8217;t it ?). Well, I present a little (and awful) bash dependency graph generator for Java projects. One goal of my job is to maintain the Quercus project. It is currently highly dependent of Resin, another Caucho&#8217;s project, and a removal is needed. To know what I [...]]]></description>
			<content:encoded><![CDATA[<p>Oh, my first english post &#8230; so amazing (<em>isn&#8217;t it ?</em>). Well, I present a little (<em>and awful</em>) bash dependency graph generator for Java projects.</p>
<p>One goal of my job is to maintain the <a href="https://github.com/CleverCloud/Quercus">Quercus</a> project. It is currently highly dependent of Resin, another Caucho&#8217;s project, and a removal is needed. To know what I need to remove first, I wanted a graphical representation of all links between projects. We can consider that dependencies could be computed with &#8216;import&#8217; Java keyword. Yes I know, it&#8217;s not a perfect dependencies representation but it&#8217;s currently the best way I found (<em>and faster</em>). Finally, we need a graph generation tool &#8230; I found <a href="http://www.graphviz.org/">GraphViz</a> which can be directly used in CLI and we use <a href="http://www.graphviz.org/doc/info/lang.html">DOT format file</a> to make the graph specifications. One day after &#8230; <a href="https://github.com/Kdecherf/jdep-grapher">Jdep-grapher</a> is released.</p>
<p><strong>How does it work ?</strong><br />
It will first get the list of files to parse (with find) and extract all lines beginning with &#8216;import&#8217; and &#8216;package&#8217; (<em>we use temp files with mktemp</em>) :
<pre class="brush: bash; title: ; notranslate">find $DIR -type f -name "*.java" > $FILES
grep -E "^package|^import" $(&lt; $FILES) | awk -F':' '{print $2}' > $GREP</pre>
<br />
Next it creates links between packages and uses DOT format :
<pre class="brush: bash; title: ; notranslate">while read type name
do
	name=`echo $name | tr -d ";"`
	pkg=`echo $name | tr -d "."`
	if [[ "$type" == "package" ]]; then
		echo "$pkg [label=\"$name\", style = filled, shape = box];"
		CPKG=$pkg
	else
		ALL=`echo $pkg | grep "\*" | wc -l`
		SUP=""
		LNK=""
		if [[ $ALL -eq 1 ]]; then
			pkg=`echo $pkg | sed s/"*"/"allpkg"/`
			SUP=", color=red, style = filled"
                        LNK=" [color=red]"
		fi
		echo "$pkg [label=\"$name\"$SUP];"
		echo "$CPKG -> $pkg $LNK;"
	fi
done &lt; $GREP | sort -u > $COMPUTE</pre>
<em>sort -u</em> at the end of loop will automatically remove duplicates and send the result to a new temp file. All-inclusion packages (with *) are filled in red.<br /><br />
We can exclude useless links (eg. internal dependencies) with :
<pre class="brush: bash; title: ; notranslate">grep -vE "$EXCLUDE" $COMPUTE > $TMPDOT</pre><br />
To reduce the weight of graph, it removes single nodes :
<pre class="brush: bash; title: ; notranslate">CT=`grep -E "$rpkg( |;)" $TMPDOT | wc -l`
	if [[ $CT -gt 1 ]]; then
		echo $rpkg $rop $rchild >> $TMPDOT2
	fi</pre><br />
Finally, the script closes the DOT file and launches graphviz &#8230;
<pre class="brush: bash; title: ; notranslate">echo "digraph G {" > $DOT
cat $TMPDOT2 >> $DOT
echo "}" >> $DOT

fdp -Tpng &lt; $DOT > $GRAPH</pre><br />Enjoy</p>
<p>Example : Resin Dependency Graph of Quercus<br />
<a href="http://i.imgur.com/91AJN.jpg"><img src="http://blog.kdecherf.com/wp-content/uploads/2011/01/36cd6dc7df67944c199c3464d395a4848a130b8e.jpg" alt="" title="36cd6dc7df67944c199c3464d395a4848a130b8e" width="483" height="640" class="aligncenter size-full wp-image-1812" /></a></p>
<p><em>Note : the script currently uses &#8216;fdp&#8217; from graphviz which is not fully optimized for this kind of graph. Tell me if you have any other solution <img src='http://blog.kdecherf.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </em></p>
<p>More information on <a href="https://github.com/Kdecherf/jdep-grapher">GitHub</a>.</p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=ECvFzgsZrV8:udOkau5TUO4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=ECvFzgsZrV8:udOkau5TUO4:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=ECvFzgsZrV8:udOkau5TUO4:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=ECvFzgsZrV8:udOkau5TUO4:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/ECvFzgsZrV8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2011/01/14/jdep-grapher-make-a-dependency-tree-of-your-java-projects/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2011/01/14/jdep-grapher-make-a-dependency-tree-of-your-java-projects/</feedburner:origLink></item>
		<item>
		<title>Clôner un répertoire et déplacer les éléments obsolètes avec rsync</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/PHV6lpFdE18/</link>
		<comments>http://blog.kdecherf.com/2011/01/09/cloner-un-repertoire-et-deplacer-les-elements-obsoletes-avec-rsync/#comments</comments>
		<pubDate>Sun, 09 Jan 2011 14:40:30 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Tips]]></category>
		<category><![CDATA[rsync]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=1785</guid>
		<description><![CDATA[Depuis le temps que je devais le faire, hier j&#8217;ai mis en place ma petite stratégie de sauvegarde avec un script perso et rsync. Je voulais avoir un clône parfait de mes dossiers : ne pas garder les éléments qui ne sont plus dans la source. Et si jamais on supprime un élément par erreur [...]]]></description>
			<content:encoded><![CDATA[<p>Depuis le temps que je devais le faire, hier j&#8217;ai mis en place ma petite stratégie de sauvegarde avec un script perso et rsync. Je voulais avoir un clône parfait de mes dossiers : ne pas garder les éléments qui ne sont plus dans la source. Et si jamais on supprime un élément par erreur et qu&#8217;il est définitivement perdu lors de la sauvegarde ?</p>
<p>La solution la plus simple est de simuler une première sauvegarde avec l&#8217;option &#8211;dry-run (-n) afin d&#8217;obtenir la liste des fichiers qui vont être supprimés du dossier de destination puis de les déplacer avant de lancer la sauvegarde. Ainsi, on arrive à un script ressemblant à ça :</p>
<pre class="brush: bash; title: ; notranslate">#!/bin/bash

echo "Generating list of files to be rejected..."
# Préfixe de dossier à retirer lors du nettoyage (utile pour les chemins absolus)
# La chaîne doit être échappée pour passer avec 'sed'
RMPREFIX="\/prefixe\/"
# Le slash de fin est important pour la source, ainsi rsync va copier le contenu du dossier (au lieu du dossier lui-même)
SOURCE="/dir1/"
DEST="/dir2"
REJECTFOLDER="/dirr"
# On va chercher la liste des fichiers et dossiers allant être supprimés
rsync -avn --delete-after $SOURCE $DEST | grep ^deleting | sed s/"^deleting "/""/ | while read line; do
 if [[ -d "$DEST/$line" ]]; then
  echo "Deleting $line..."
  rmdir "$DEST/$line"
 else
  echo "Rejecting $line..."
  FDIR=`dirname "$DEST/$line"`
  if [[ ! -z $RMPREFIX ]]; then
   FDIR=`echo $FDIR | sed s/"$RMPREFIX"/""/`
  fi
  mkdir -p "$REJECTFOLDER/$FDIR"
  mv "$DEST/$line" "$REJECTFOLDER/$FDIR/"
 fi
done
# Ici on peut lancer le vrai rsync
</pre>
<p>Exemple de résultat :</p>
<p><img src="http://blog.kdecherf.com/wp-content/uploads/2011/01/Screenshot-113-1.png" alt="" title="Screenshot-113-1" width="406" height="91" class="aligncenter size-full wp-image-1796" /></p>
<p>Enjoy it !</p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=PHV6lpFdE18:vnomF6iIXVw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=PHV6lpFdE18:vnomF6iIXVw:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=PHV6lpFdE18:vnomF6iIXVw:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=PHV6lpFdE18:vnomF6iIXVw:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/PHV6lpFdE18" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2011/01/09/cloner-un-repertoire-et-deplacer-les-elements-obsoletes-avec-rsync/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2011/01/09/cloner-un-repertoire-et-deplacer-les-elements-obsoletes-avec-rsync/</feedburner:origLink></item>
		<item>
		<title>Iptables : Utiliser un VPN avec une IP failover</title>
		<link>http://feedproxy.google.com/~r/kdecherf_blog/~3/4EEpI1_ObFQ/</link>
		<comments>http://blog.kdecherf.com/2011/01/08/iptables-utiliser-un-vpn-avec-une-ip-failover/#comments</comments>
		<pubDate>Sat, 08 Jan 2011 15:37:31 +0000</pubDate>
		<dc:creator>Kevin Decherf</dc:creator>
				<category><![CDATA[Tips]]></category>

		<guid isPermaLink="false">http://blog.kdecherf.com/?p=1755</guid>
		<description><![CDATA[Allez, une petite astuce (très facile mais je la donne quand même) pour bien commencer le week-end. Considérons un serveur avec une interface réseau et plusieurs IP failover (un serveur chez OVH par exemple), comment pouvons-nous utiliser l&#8217;une de ces IP failover pour la sortie d&#8217;un VPN ? Avec Iptables, la table nat, la chaîne [...]]]></description>
			<content:encoded><![CDATA[<p>Allez, une petite astuce (<em>très facile mais je la donne quand même</em>) pour bien commencer le week-end. Considérons un serveur avec une interface réseau et plusieurs IP failover (<em>un serveur chez OVH par exemple</em>), comment pouvons-nous utiliser l&#8217;une de ces IP failover pour la sortie d&#8217;un VPN ? Avec Iptables, la table nat, la chaîne POSTROUTING et la cible SNAT :</p>

<blockquote><pre>iptables -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to-source ipfailover</pre></blockquote>

<p>Pensez à remplacer <em>ipfailover</em> par l&#8217;adresse IP publique à utiliser et <em>10.8.0.0/24</em> par le réseau de votre VPN. Et bon week-end !</p>


<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=4EEpI1_ObFQ:bIo3waXaE18:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=4EEpI1_ObFQ:bIo3waXaE18:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/kdecherf_blog?a=4EEpI1_ObFQ:bIo3waXaE18:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/kdecherf_blog?i=4EEpI1_ObFQ:bIo3waXaE18:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/kdecherf_blog/~4/4EEpI1_ObFQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.kdecherf.com/2011/01/08/iptables-utiliser-un-vpn-avec-une-ip-failover/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://blog.kdecherf.com/2011/01/08/iptables-utiliser-un-vpn-avec-une-ip-failover/</feedburner:origLink></item>
	</channel>
</rss>

