<?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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>IP-Tech's Blog</title>
	
	<link>http://www.iptech-offshore.net/blog</link>
	<description>Le blog officiel de IP-Tech</description>
	<pubDate>Mon, 16 Aug 2010 09:19:34 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ip-tech-blog" /><feedburner:info uri="ip-tech-blog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Internet Explorer bloque les cookies des pages dans les IFrames</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/G_7pmuA_0q8/</link>
		<comments>http://www.iptech-offshore.net/blog/2010/08/16/internet-explorer-bloque-les-cookies-des-pages-dans-les-iframes/#comments</comments>
		<pubDate>Mon, 16 Aug 2010 09:12:30 +0000</pubDate>
		<dc:creator>Hamdi DOUSS</dc:creator>
		
		<category><![CDATA[Technique]]></category>

		<category><![CDATA[authentification]]></category>

		<category><![CDATA[blocage]]></category>

		<category><![CDATA[confidentialité]]></category>

		<category><![CDATA[cookie]]></category>

		<category><![CDATA[IFrame]]></category>

		<category><![CDATA[Internet Explorer]]></category>

		<category><![CDATA[P3P]]></category>

		<category><![CDATA[session]]></category>

		<category><![CDATA[vie privée]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=80</guid>
		<description><![CDATA[L&#8217;une des applications Web développées par IP-Tech pour le compte de l’un de ses partenaires a eu un tel succès auprès des utilisateurs que certains éditeurs de solutions d&#8217;entreprises se sont intéressés à l&#8217;intégrer à leurs produits sous différentes formes. L&#8217;un d&#8217;entre eux a opté pour l&#8217;intégration du produit à l&#8217;intérieur d&#8217;une IFrame dans leur [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" style="float: left;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/08/internet-explorer-cookies.png" alt="Cookie - IE" />L&#8217;une des applications Web développées par IP-Tech pour le compte de l’un de ses partenaires a eu un tel succès auprès des utilisateurs que certains éditeurs de solutions d&#8217;entreprises se sont intéressés à l&#8217;intégrer à leurs produits sous différentes formes. L&#8217;un d&#8217;entre eux a opté pour l&#8217;intégration du produit à l&#8217;intérieur d&#8217;une IFrame dans leur solution.</p>
<p><span id="more-80"></span></p>
<p>Tout un travail de spécification a commencé pour définir :</p>
<ul>
<li> les nouveaux besoins pour pouvoir adapter le produit à leurs clients;</li>
<li> les interfaces et les flux d&#8217;échange entre les deux solutions.</li>
</ul>
<p>Dès que les premières phases de développement sont finies, nous avons commencé les premiers tests d&#8217;intégration. Premier constat : Aucune variable de session n&#8217;est sauvegardée. La raison était que nous testions avec Internet Explorer, qui lui et depuis sa version 6, bloque les cookies contenus dans les en-têtes HTTP se chargeant à l&#8217;intérieur d&#8217;IFrame.</p>
<p>En effet, IE implémente ce qu&#8217;on appelle la &#8220;<a href="http://www.w3.org/P3P/1.1/Overview.html" target="_blank">Platform for Privacy Preferences</a>&#8221; ou P3P. P3P est un standard <a href="http://www.w3.org" target="_blank">W3C</a> qui permet aux sites Web de définir leurs politiques de protection de données personnelles de façon standardisée et structurée compréhensible par les machines (donc les navigateurs implémentant le standard). L&#8217;internaute, en définissant les paramètres de confidentialité au niveau de son navigateur, permettra ou pas à ce site web de collecter des informations, d&#8217;accepter des cookies&#8230;Ce système a beaucoup de limites qui ne font pas l&#8217;objet de ce post. On s&#8217;intéresse plutôt à résoudre la problématique : comment faire si votre application web est contrainte de tourner dans une IFrame ?</p>
<p>Vous l&#8217;avez donc compris, la configuration par défaut au niveau d’IE bloquera votre cookie si vous vous ne déclarez pas en tant que site de confiance. Selon les spécifications, votre politique P3P peut être communiquée sous deux formes : la forme compacte et la forme complète. La forme complète implique la description de la politique sous format XML, décrite dans un fichier que les navigateurs implémentant le standard iront chercher sous <em>w3c/p3p.xml</em> (emplacement par défaut pouvant être redéfini avec une balise link). La forme compacte implique la description de la politique en envoyant un entête HTTP (associé à la réponse avec laquelle vous essayez d&#8217;envoyer le cookie). La clé de cet entête est : &#8220;P3P&#8221;. Sa valeur est une succession de codes abrégés. Sans rentrer dans les détails de ces codes bien expliqués dans les <a href="http://www.w3.org/TR/P3P/" target="_blank">spécifications</a>, pour permettre à IE d&#8217;accepter vos cookies même si votre site web se charge à l&#8217;intérieur d&#8217;une IFrame, la valeur de cet entête devrait être : CP=&#8221;IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT&#8221;.</p>
<p>Exemple : Si vous développez une application web Java, pour ajouter cet entête dans une JSP, il faudrait ajouter la ligne suivante :</p>
<pre name="code" class="java">&lt;% response.addHeader("P3P","CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""); %&gt;
</pre>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/G_7pmuA_0q8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2010/08/16/internet-explorer-bloque-les-cookies-des-pages-dans-les-iframes/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2010/08/16/internet-explorer-bloque-les-cookies-des-pages-dans-les-iframes/</feedburner:origLink></item>
		<item>
		<title>My Clojure explained solutions to the s99 problems 8 to 13</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/46W7B_J-VUc/</link>
		<comments>http://www.iptech-offshore.net/blog/2010/08/06/my-clojure-explained-solutions-to-the-s99-problems-8-to-13/#comments</comments>
		<pubDate>Fri, 06 Aug 2010 10:57:59 +0000</pubDate>
		<dc:creator>Jawher MOUSSA</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[Technique]]></category>

		<category><![CDATA[clojure]]></category>

		<category><![CDATA[s99]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=79</guid>
		<description><![CDATA[P08
Eliminate consecutive duplicates of list elements.
If a list contains repeated elements they should be replaced with a single copy of the element. The order of the elements should not be changed. 
Example:
user&#62; (compress &#8216;(\a, \a, \a, \a, \b, \c, \c, \a, \a, \d, \e, \e, \e, \e))
(\a, \b, \c, \a, \d, \e)
(defn compress "P08" [xs]
 [...]]]></description>
			<content:encoded><![CDATA[<h2>P08</h2>
<blockquote><p><strong>Eliminate consecutive duplicates of list elements.</strong><br />
If a list contains repeated elements they should be replaced with a single copy of the element. The order of the elements should not be changed. <span id="more-79"></span><br />
Example:<br />
user&gt; (compress &#8216;(\a, \a, \a, \a, \b, \c, \c, \a, \a, \d, \e, \e, \e, \e))<br />
(\a, \b, \c, \a, \d, \e)</p></blockquote>
<pre name="code" class="java">(defn compress "P08" [xs]
  (reduce #(if (= (last %1) %2) %1 (concat %1 (list %2)))
  &#8216;() xs))
</pre>
<p>The reduction function would check if the last element of its first argument equals its second argument, which would be the element of xs being processed. It the they are equal, it would just return the first argument as is, otherwise it would add the xs element to the end of the first argument list and return it.</p>
<p>Here&#8217;s how it would work on the characters list shown above:</p>
<blockquote><p>(&#8217;() \a)=&gt;<strong>(\a)</strong> because \a and (last &#8216;()) which is nil are not equal<br />
(<strong>(\a)</strong> \a)=&gt;<strong>(\a)</strong> because (last (\a)) which is \a is equal to the second argument \a<br />
:<br />
:<br />
(<strong>(\a)</strong> \b)=&gt;<strong>(\a \b)</strong> because \a and \b are not equal<br />
:<br />
:<br />
(<strong>(\a \b \c \a \d)</strong> \e)=&gt;<strong>(\a \b \c \a \d \e)</strong></p></blockquote>
<h2>P09</h2>
<blockquote><p><strong>Pack consecutive duplicates of list elements into sublists.</strong><br />
If a list contains repeated elements they should be placed in separate sublists.<br />
Example:<br />
user&gt; pack(&#8217;(\a, \a, \a, \a, \b, \c, \c, \a, \a, \d, \e, \e, \e, \e))<br />
(\a, \a, \a, \a), (\b), (\c, \c), (\a, \a), (\d), (\e, \e, \e, \e))</p></blockquote>
<pre name="code" class="java">(defn pack "P09" [xs]
  (reverse (reduce
   #(if (= (ffirst %1) %2)
      (cons (cons %2 (first %1)) (rest %1))
      (cons (list %2) %1 ))
   &#8216;()
   xs)))
</pre>
<h2>P10</h2>
<blockquote><p><strong>Run-length encoding of a list.</strong><br />
Use the result of problem P09 to implement the so-called run-length encoding data compression method. Consecutive duplicates of elements are encoded as tuples (N, E) where N is the number of duplicates of the element E.<br />
Example:</p>
<p>user&gt; (encode &#8216;(\a, \a, \a, \a, \b, \c, \c, \a, \a, \d, \e, \e, \e, \e))<br />
((4,\a), (1,\b), (2,\c), (2,\a), (1,\d), (4,\e))</p></blockquote>
<pre name="code" class="java">(defn encode "P10" [xs]
  (map #(list (first %) (count %)) (pack xs)))
</pre>
<p>This solution reutilizes the pack function defined in the previous problem. Once the list of items is packed, we map every element (which is a list) to a pair defined by any item of the list, the first for instance and the list&#8217;s length.</p>
<h2>P11</h2>
<blockquote><p><strong>Modified run-length encoding.</strong><br />
Modify the result of problem P10 in such a way that if an element has no duplicates it is simply copied into the result list. Only elements with duplicates are transferred as (N, E) terms.<br />
Example:</p>
<p>user&gt; (encode-modified &#8216;(\a, \a, \a, \a, \b, \c, \c, \a, \a, \d, \e, \e, \e, \e))<br />
((4,\a), \b, (2,\c), (2,\a), \d, (4,\e))</p></blockquote>
<pre name="code" class="java">(defn encode-modified "P11" [xs]
  (map #(if (= 1 (last %)) (first %) %) (encode xs)))
</pre>
<p>We rely on the encode function we defined in the tenth problem to do the actual work, and then we apply a mapping function on the result that for the pairs (count, item) with a count of one returns only the item, leaving the others as is.</p>
<h2>P12</h2>
<blockquote><p><strong>Decode a run-length encoded list.</strong><br />
Given a run-length code list generated as specified in problem P10, construct its uncompressed version.<br />
Example:</p>
<p>user&gt; (decode &#8216;((4, \a), (1, \b), (2, \c), (2, \a), (1, \d), (4, \e)))<br />
(\a, \a, \a, \a, \b, \c, \c, \a, \a, \d, \e, \e, \e, \e)</p></blockquote>
<pre name="code" class="java">(defn decode "P12" [xs]
  (reduce concat (map #(repeat (first %) (last %)) xs)))
</pre>
<p>We start by unpacking the encoded list elements by using the repeat function which given a count and an element, it yields a list of count elements.<br />
Afterwards, it&#8217;s simply a matter of concatenating the lists elements using the concat function (which concatenates two lists).</p>
<h2>P13</h2>
<blockquote><p><strong>Run-length encoding of a list (direct solution).</strong><br />
Implement the so-called run-length encoding data compression method directly. I.e. don&#8217;t use other methods you&#8217;ve written (like P09&#8217;s pack); do all the work directly.<br />
Example:</p>
<p>user&gt; (encode-direct &#8216;(\a, \a, \a, \a, \b, \c, \c, \a, \a, \d, \e, \e, \e, \e))<br />
((4,\a), (1,\b), (2,\c), (2,\a), (1,\d), (4,\e))</p></blockquote>
<pre name="code" class="java">(defn encode-direct "P13" [xs]
  (reverse
   (reduce
    (fn [res x]
      (let [pair (first res)
	        count (first pair)
	        e (last pair)]
	    (if (= x e)
	      (cons (list (inc count) x) (rest res))
	      (cons (list 1 x) res))))
    &#8216;() xs)))
</pre>
<p>Basically what I&#8217;ve done here is combine the pack and encode functions. This solution starts by applying the following reduction function:<br />
Given the current result res (which starts as an empty list) and an item x from the list to encode:</p>
<ul>
<li>Initialize the following locals:
<ul>
<li>pair: represents the current pair (count, item) of the encoded result list. To simplify matters, I&#8217;ll store the pairs in the result list in reverse order</li>
<li>count: the first element of pair</li>
<li>e: the encoded item which is stored as the second element of the pair</li>
</ul>
</li>
<li>If the current list element is equal the the current pair element, increment the latter&#8217;s count. We do this by creating a new result list with the pair (e or x, count+1) as it&#8217;s head and the rest of the old result list as it&#8217;s tail.</li>
<li>Else, add a new pair to the result with a count of 1 and x as it&#8217;s element</li>
<li>In both cases, I use cons to construct the result list, which explains why I&#8217;m constructing the result in a reverse order</li>
</ul>
<p>It remains to reverse the result to get the expected result.</p>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/46W7B_J-VUc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2010/08/06/my-clojure-explained-solutions-to-the-s99-problems-8-to-13/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2010/08/06/my-clojure-explained-solutions-to-the-s99-problems-8-to-13/</feedburner:origLink></item>
		<item>
		<title>My Clojure explained solutions to the s99 problems 4 to 7</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/qqqOHIxxn2M/</link>
		<comments>http://www.iptech-offshore.net/blog/2010/07/26/my-clojure-explained-solutions-to-the-s99-problems-4-to-7/#comments</comments>
		<pubDate>Mon, 26 Jul 2010 16:24:13 +0000</pubDate>
		<dc:creator>Jawher MOUSSA</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[Technique]]></category>

		<category><![CDATA[clojure]]></category>

		<category><![CDATA[s99]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=78</guid>
		<description><![CDATA[In this post, I&#8217;ll continue presenting my solutions for the s99 problems in Clojure. As a reminder, I&#8217;ve already covered the first 3 problems in a previous post.

P04
Find the number of elements of a list.
Example:
user&#62; (length &#8216;(1, 1, 2, 3, 5, 8))
6
My first solution relies on recursion to find out the length of a list: [...]]]></description>
			<content:encoded><![CDATA[<p>In this post, I&#8217;ll continue presenting my solutions for the <a href="http://aperiodic.net/phil/scala/s-99/" target="_blank">s99 problems</a> in Clojure. As a reminder, I&#8217;ve already covered the <a href="http://www.iptech-offshore.net/blog/2010/07/10/my-clojure-explained-solutions-to-the-s99-problems-1-to-3/">first 3 problems in a previous post</a>.<br />
<span id="more-78"></span></p>
<h2>P04</h2>
<blockquote><p><strong>Find the number of elements of a list.</strong><br />
Example:<br />
user&gt; (length &#8216;(1, 1, 2, 3, 5, 8))<br />
6</p></blockquote>
<p>My first solution relies on recursion to find out the length of a list: </p>
<ul>
<li>The length of an empty list is zero</li>
<li>The length of a non-empty list is 1 plus the length of that list without its first element</li>
</ul>
<p>The code shown below is a direct transcription of this:</p>
<pre name="code" class="java">
(defn length "P04" [l]
  (if (empty? l) 0 (inc (length (rest l)))))
</pre>
<p>Another solution relying on reduce:</p>
<pre name="code" class="java">
(defn length "P04" [xs]
  (reduce (fn [l x] (inc l)) 0 xs))
</pre>
<p>I&#8217;ve already described how reduce operates on a list, but in a nutshell, it&#8217;ll pick the first element of the list and feed it with the initial value (0 in this case) the the reduction function, which increments the 0 (representing the list&#8217;s length). The result of the reduction (1), along with the next element of the list will be fed again to the reduction function, and the whole process will be repeated until all the list elements are consumed, yielding the list&#8217;s length in the end.</p>
<p>You&#8217;ll notice that I used the fn form to define the reduction function instead of the shorthand notation (written with the # symbol). When I try to define a reduction function that doesn&#8217;t reference %2 (the second parameter) using the shorthand notation, reduce would throw a &#8220;Wrong number of parameters&#8221; exception:</p>
<pre name="code" class="java">
(defn length "P04" [xs]
  (reduce (inc %1) 0 xs))
</pre>
<blockquote><p>Wrong number of args passed to: user$eval&#8211;2528$fn<br />
  [Thrown class java.lang.IllegalArgumentException]</p></blockquote>
<p>I&#8217;ll quote here <a href="http://twitter.com/fogus/status/18160434346">a tweet</a> from <a href="http://twitter.com/fogus">Michael Fogus</a>, the Co-Author of &#8220;<a href="http://fogus.me/buy">The joy of clojure</a>&#8221; explaining why:</p>
<blockquote><p>#() will not gen a 2-arg fn without a reference to %2. Use the (fn [x y] ..) form, or use a %2. Or (reduce #(apply + %&amp;) [1 2 3])</p></blockquote>
<h2>P05</h2>
<blockquote><p><strong>Reverse a list.</strong><br />
Example:<br />
user&gt; (reverse &#8216;(1 1 2 3 5 8))<br />
(8 5 3 2 1 1)</p></blockquote>
<p>My first solution relies on recursion to find out the length of a list: </p>
<ul>
<li>The reverse of an empty list is an empty list</li>
<li>The reverse of a non-empty list <strong>xs</strong> is the list whose first element is xs&#8217;s last element and whose rest is the reverse of xs&#8217;s rest</li>
</ul>
<p>The code shown below is a direct transcription of this:</p>
<pre name="code" class="java">
(defn reverse-s99 "P05" [l]
  (if (empty? l)
    nil
    (cons
     (last l)
     (reverse-s99 (butlast l)))))
</pre>
<p>Again, another way to do it would be to use reduce:</p>
<pre name="code" class="java">
(defn reverse-s99 "P05" [xs]
  (reduce (fn [r x] (cons x r)) &#8216;() xs))
</pre>
<p>Basically, this starts with an empty list r, and iterates over xs&#8217;s items adding each one of them to r&#8217;s beginning.</p>
<h2>P06</h2>
<blockquote><p><strong>Find out whether a list is a palindrome.</strong><br />
Example:<br />
user&gt; (palindrome? &#8216;(1 2 3 2 1))<br />
true</p></blockquote>
<pre name="code" class="java">
(defn palyndrome? "P06" [l]
  (= l (reverse-99 l)))
</pre>
<p>No comment !</p>
<h2>P07</h2>
<blockquote><p>Flatten a nested list structure.<br />
Example:<br />
user&gt; (flatten &#8216;((1 1) 2 (3 (5 7))))<br />
(1 1 2 3 5 7)
</p></blockquote>
<pre name="code" class="java">
(defn flatten "P07" [l]
  (reduce
   #(concat %1 (if (list? %2) (flatten %2) (list %2)))
   &#8216;()
   l))
</pre>
<p>It&#8217;s getting a bit cryptic. What this does is:</p>
<ul>
<li>Start with an empty result list</li>
<li>For every item of the list to flatten, if it&#8217;s (%2) a list, flatten it, otherwise create a list out of it. In both cases, add all of the resulting list items to the result list</li>
</ul>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/qqqOHIxxn2M" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2010/07/26/my-clojure-explained-solutions-to-the-s99-problems-4-to-7/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2010/07/26/my-clojure-explained-solutions-to-the-s99-problems-4-to-7/</feedburner:origLink></item>
		<item>
		<title>My Clojure explained solutions to the s99 problems 1 to 3</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/dPTvogAG2xs/</link>
		<comments>http://www.iptech-offshore.net/blog/2010/07/10/my-clojure-explained-solutions-to-the-s99-problems-1-to-3/#comments</comments>
		<pubDate>Sat, 10 Jul 2010 07:33:32 +0000</pubDate>
		<dc:creator>Jawher MOUSSA</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[Technique]]></category>

		<category><![CDATA[clojure]]></category>

		<category><![CDATA[s99]]></category>

		<category><![CDATA[scala]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=77</guid>
		<description><![CDATA[A year (or maybe more) ago, I started toying with emacs and soon had to tweak its configuration file, which is actually a program written in elisp, a variant of the Lisps family.
I had no former experience with anything like lisp before, and anytime I touched the .emacs file, a pile of weird error messages [...]]]></description>
			<content:encoded><![CDATA[<p>A year (or maybe more) ago, I started toying with <a href="http://en.wikipedia.org/wiki/Emacs">emacs</a> and soon had to tweak its configuration file, which is actually a program written in elisp, a variant of the <a href="http://en.wikipedia.org/wiki/Lisp_(programming_language)">Lisps family</a>.</p>
<p>I had no former experience with anything like lisp before, and anytime I touched the .emacs file, a pile of weird error messages would show up when starting emacs. So I thought it might be a good idea to learn the lisp&#8217;s syntax so I&#8217;d at least know what was that unbalanced quote I kept seeing in .emacs snippets in the internet.</p>
<p><span id="more-77"></span></p>
<p>With time, I started appreciating more and more lisp&#8217;s minimalistic syntax, and here I am now, spending more and more time toying with <a href="http://en.wikipedia.org/wiki/Clojure">Clojure</a>, a modern Lisp variant that runs on the JVM (my platform of choice, being a Java developer for a couple of years already).</p>
<p>Before that, I learned me some <a href="http://en.wikipedia.org/wiki/Scala_(programming_language)">Scala</a>, and to help myself get rid of my imperative programmer reflexes (for loops, variables, etc.), I started coding the solutions for the excellent <a href="http://aperiodic.net/phil/scala/s-99/">s99 problems</a> using Scala. It was a joyful although painful experience: Painful because my mind would find the imperative solution first, but I had to refrain from using it while knowing that Scala&#8217;s constructs would let me do it, and rather keep reminding myself that the functional solution would have no variables. Joyful because I had to challenge my thought process to not pick the familiar path and  tackle problems differently.</p>
<p>Now that I&#8217;m trying to learn Clojure, I figured I&#8217;d do the same and solve the s99 problems using Clojure. The problems are generic enough and not tied to any specific language.</p>
<p>In the following you&#8217;ll find the first 3 problems and their <em>(hopefully)</em> explained solutions.</p>
<p>Disclaimer: I&#8217;m still in my baby steps with Clojure and did the best I could with the little I know so far. I would be glad to hear the other clojurists opinions on my approach and any constructive criticism.</p>
<h2>P01</h2>
<blockquote><p><strong>Find the last element of a list.</strong><br />
Example:<br />
user&gt; (last &#8216;(1 1 2 3 5 8))<br />
8</p></blockquote>
<p>My one-liner solution:</p>
<pre name="code" class="java">
(defn last-99 "P01" [l]
(reduce #(do %2) l))
</pre>
<p>I&#8217;m reducing the list and only keeping the second element. The reduce function would apply its first argument (a function) on the first 2 elements of the list, then on the result of this application and the third list element, etc.</p>
<p>Here&#8217;s how the reduction function would walk the list ( 1 1 2 3 5 8 ) :</p>
<blockquote><p>(1 1)=&gt;<strong>1</strong><br />
(<strong>1</strong> 2)=&gt;<strong>2</strong><br />
(<strong>2</strong> 3)=&gt;<strong>3</strong><br />
(<strong>3</strong> 5)=&gt;<strong>5</strong><br />
(<strong>5</strong> 8)=&gt;8</p></blockquote>
<p><em>I highlighted the result and the first argument of every line to visually show that the result of a reduction is fed as an argument to the next reduction.</em></p>
<p>Another solution relying only on basic list functions (first and rest, who with cons are the bare minimum operations you need with lists to implement almost all the other list operations) would be:</p>
<pre name="code" class="java">
(defn last2 "P01" [l]
(if (empty? (rest l))
(first l)
(last2 (rest l))))
</pre>
<p>The version will test if the list without its first element (which is what rest does) is empty, if so it&#8217;ll return the first element and if not, it&#8217;ll return the last element of the list remainder. Another way to view this is that the last2 function would consume the first element of the list until there remains only one, and return it.</p>
<h2>P02</h2>
<blockquote><p><strong>Find the last but one element of a list</strong><br />
Example:<br />
user&gt; (penultimate &#8216;(1, 1, 2, 3, 5, 8))<br />
5</p></blockquote>
<p>Another one-liner solution:</p>
<pre name="code" class="java">
(defn penultimate "P02" [l]
(first (rest (reverse l))))
</pre>
<p>Basically I return the first item of the list after reversing it and removing its first element. For the list &#8216;(1 1 2 3 5 8), this would go as :</p>
<blockquote><p>reverse: (8 5 3 2 1 1)<br />
remove the head: (5 3 2 1 1)<br />
return the first item: 5</p></blockquote>
<p>I&#8217;m not very happy with this solution as it relies on reverse, a rather complex operation. It&#8217;s like implementing multiplication using the power or sine function. Besides, the 5th problem is about implementing reverse. Another simpler (in that it only uses first and rest) solution would be :</p>
<pre name="code" class="java">
(defn penultimate2 "P02" [l]
(if (empty? (rest (rest l)))
(first l)
(penultimate2 (rest l))))
</pre>
<p>This solution, while being longer, only relies on basic functions (empty?, rest and first).</p>
<h2>P03</h2>
<blockquote><p><strong>Find the Kth element of a list.</strong><br />
By convention, the first element in the list is element 0.<br />
Example:<br />
user&gt; nth(2 &#8216;(1, 1, 2, 3, 5, 8))<br />
2</p></blockquote>
<p>Yet another one-liner solution:</p>
<pre name="code" class="java">
(defn nth-s99 "P03" [n l]
(if (= 0 n) (first l) (nth-s99 (- n 1) (rest l))))
</pre>
<p>This solution relies on the following principle: the 3rd element of a list is the 2nd element of that list without its first element. Applying this principle on the second statement would give that the 2nd element of a list is the 1st element of that same list without its first element.</p>
<p>It would the go that finding the nth element of any list can be brought back to finding the first element of a list. The trick is to remove the list&#8217;s first item n times, which is exactly what the solution shown above does.</p>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/dPTvogAG2xs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2010/07/10/my-clojure-explained-solutions-to-the-s99-problems-1-to-3/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2010/07/10/my-clojure-explained-solutions-to-the-s99-problems-1-to-3/</feedburner:origLink></item>
		<item>
		<title>Apache Pivot, l’autre RIA</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/rWyqFKbpIoU/</link>
		<comments>http://www.iptech-offshore.net/blog/2010/07/06/apache-pivot-l%e2%80%99autre-ria/#comments</comments>
		<pubDate>Tue, 06 Jul 2010 15:46:22 +0000</pubDate>
		<dc:creator>nbelaid</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[Publications]]></category>

		<category><![CDATA[Technique]]></category>

		<category><![CDATA[Apache Pivot]]></category>

		<category><![CDATA[Programmez]]></category>

		<category><![CDATA[RIA]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=76</guid>
		<description><![CDATA[Apache Pivot est un framework de développement d’applications Internet riches (RIA) qui a su se faire parler de lui de plus en plus dès sa promotion au rang de « Top Level Project » au sein de la fondation Apache. Ce que Apache Pivot apporte par rapport à JavaFX, c&#8217;est surtout :




Le code est en [...]]]></description>
			<content:encoded><![CDATA[<p>Apache Pivot est un framework de développement d’applications Internet riches (RIA) qui a su se faire parler de lui de plus en plus dès sa promotion au rang de « Top Level Project » au sein de la fondation Apache. Ce <a href="http://www.programmez.com/magazine_articles.php?titre=Apache%20Pivot,%20l%E2%80%99autre%20RIA&amp;id_article=1407&amp;magazine=132" target="_blank"><img class="alignleft" style="float:  left; margin: 10px;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/07/Prog_132_RIA_2_1.png" alt="" width="123" height="167" /></a>que Apache Pivot apporte par rapport à JavaFX, c&#8217;est surtout :</p>
<blockquote>
<blockquote>
<blockquote>
<ul>
<li>Le code est en Java (et non un langage de script à apprendre);</li>
<li>Le développeur a l’option de concevoir son interface graphique de façon déclarative (en XML);</li>
<li>L’intégration avec les composants serveur est plus souple.</li>
</ul>
</blockquote>
</blockquote>
</blockquote>
<p>Dans cet article, nous allons surtout se focaliser sur ce dernier point qui n&#8217;a pas été illustré dans les démos et les tutoriaux du site officiel du framework. Pour cela, nous allons essayer de développer une simple application client/serveur de gestion de contacts. Pour faire au plus simple, les fonctionnalités seront réduites à ajouter un contact ou modifier un contact existant.</p>
<p><span style="background-color: #ffff99;">Une version abrégée de cet article a été publiée dans le numéro 132 (Juillet 2010) du magazine <a href="http://www.programmez.com/magazine_articles.php?titre=Apache%20Pivot,%20l%E2%80%99autre%20RIA&amp;id_article=1407&amp;magazine=132" target="_blank">«PROgrammez»</a>. La version PDF de  l’article est mise à votre disposition en <a href="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/07/Apache%20Pivot,%20l%27autre%20RIA_Prog_132.pdf" target="_blank">téléchargement gratuit</a><strong>.</strong> Le code source de l&#8217;exemple donné est fourni (voir ci-dessous).</span><br />
<span id="more-76"></span></p>
<h3><span style="color: #800000;"><strong>PARTIE CLIENTE </strong></span></h3>
<p>L&#8217;écran principal se composera d&#8217;une liste de contacts et un bouton pour ajouter un contact à la liste. Un clic sur une entrée de la liste permettra à l&#8217;utilisateur de modifier les détails du contact.</p>
<p><img src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/07/Fig1%20Tableau1.jpg" alt="" /></p>
<p><img src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/07/Fig2%20Tableau2.jpg" alt="" /></p>
<p>Les deux écrans sont conçus déclarativement dans les deux fichiers table.xml et addContact.xml.</p>
<pre name="code" class="java">&lt;TableView wtkx:id="tableView"
	styles="{includeTrailingVerticalGridLine:true}"&gt;
	&lt;columns&gt;
		&lt;TableView.Column name="firstname" width="100"
			headerData="Prenom" /&gt;
		&lt;TableView.Column name="lastname" width="100"
			headerData="Nom" /&gt;
		&lt;TableView.Column name="email" width="60"
			headerData="E-Mail" /&gt;
		&lt;TableView.Column name="phone" width="60"
			headerData="Téléphone" /&gt;

	&lt;/columns&gt;
&lt;/TableView&gt;
</pre>
<p>Extrait (correspondant au tableau de contacts) de table.xml</p>
<pre name="code" class="java">&lt;Form styles="{rightAlignLabels:true}" wtkx:id="submitform"&gt;
	&lt;sections&gt;
		&lt;Form.Section&gt;
			&lt;Label Form.label="ID" textKey="id" /&gt;
			&lt;BoxPane Form.label="Nom"&gt;
				&lt;TextInput textKey="lastname" /&gt;
			&lt;/BoxPane&gt;
			&lt;BoxPane Form.label="Prénom"&gt;
				&lt;TextInput textKey="firstname" /&gt;
			&lt;/BoxPane&gt;
			&lt;BoxPane Form.label="Téléphone"&gt;
				&lt;TextInput textKey="phone" /&gt;
			&lt;/BoxPane&gt;
			&lt;BoxPane Form.label="Adresse E-mail"&gt;
				&lt;TextInput textKey="email" /&gt;
			&lt;/BoxPane&gt;
		&lt;/Form.Section&gt;
	&lt;/sections&gt;
&lt;/Form&gt;
</pre>
<p>Extrait (correspondant au formulaire) de addContact.xml</p>
<p>Notre classe Contact a les propriétés ID (id), Prénom (firstName), Nom (lastName), E-mail (email) et téléphone (phone). Noter que dans le composant TableView de table.xml, la propriété ‘name’ de chaque colonne correspond à la propriété de la classe Contact correspondante; et que dans chaque TextInput du formulaire d&#8217;ajout/modification de addContact.xml, la propriété textKey correspond aussi à la propriété de la classe Contact correspondante.</p>
<p>A part la classe Contact (modèle de donnée), la partie cliente de notre application se compose de 4 classes :</p>
<ul>
<li>Une classe Contacts qui joue le rôle du contrôleur (qui implémente trois interfaces d&#8217;écoutes d&#8217;événements)</li>
</ul>
<ul>
<li> Une classe TableContactsWindow (vue) qui représente la fenêtre principale contenant la liste des contacts</li>
</ul>
<ul>
<li> Une classe EditContactWindow (vue) qui représente la fenêtre d&#8217;ajout/modification d&#8217;un contact</li>
</ul>
<ul>
<li> Une classe RemoteContactModel (modèle) qui sera responsable de la communication avec le serveur</li>
</ul>
<p>Nos trois interfaces d&#8217;écoute d&#8217;événements (implémentées par le contrôleur) sont:</p>
<ul>
<li> ContactUpdateListener pour répondre à deux événements : demande (par l&#8217;utilisateur) d&#8217;ajout d&#8217;un contact et demande (par l&#8217;utilisateur) d&#8217;édition d&#8217;un contact.</li>
</ul>
<ul>
<li>ContactSubmitRequestListener pour répondre à l&#8217;événement de demande (par l&#8217;utilisateur) de sauvegarde d&#8217;un  contact.</li>
</ul>
<ul>
<li> ContactAccessListener pour répondre à deux événements : la fin du chargement de la liste des contacts et la réussite de la sauvegarde d&#8217;un objet contact.</li>
</ul>
<h3><span style="color: #800000;"><strong>LES VUES</strong></span></h3>
<p>La classe TableContactsWindow garde une référence sur un écouteur d&#8217;événements ContactUpdateListener (c&#8217;est la table de contacts qui déclenche les événements associés) et a deux propriétés : le tableau de contacts et la fenêtre contenant le tableau. Le constructeur prend en paramètre l&#8217;écouteur et demande à Pivot de parser table.xml pour construire la fenêtre et la table :</p>
<pre name="code" class="java">		WTKXSerializer wtkxSerializer = new WTKXSerializer();
		try {
			window = (Window)wtkxSerializer.readObject(this, "table.xml");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SerializationException e) {
			e.printStackTrace();
		}
		table = (TableView)wtkxSerializer.get("tableView");
</pre>
<p>Ensuite, on ajoute un gestionnaire pour l&#8217;événement de clic sur une ligne de la table (modification d&#8217;un contact) et pour l&#8217;événement de clic sur le bouton &#8216;Ajouter&#8217; :</p>
<pre name="code" class="java">//Ajoute un gestionnaire d'événement pour le clic sur une ligne du tableau
table.getComponentMouseButtonListeners().add(new ComponentMouseButtonListener.Adapter() {
	@SuppressWarnings("unchecked")
	@Override
	public boolean mouseClick(Component component,
		org.apache.pivot.wtk.Mouse.Button button, int x, int y, int count) {
		List&lt;Contact&gt; contacts = (List&lt;Contact&gt;)table.getTableData();
		//Récupère l'indice de la ligne cliqué
		int index = table.getRowAt(y);
		//Notifie le contrôleur de la demande d'édition d'un contact
		controller.editContactRequest(contacts.get(index), TableContactsWindow.this);
				return false;
			}
		});

		PushButton addButton  = (PushButton)wtkxSerializer.get("addButton");

		//Ajoute un gestionnaire d'événement pour le clic sur le bouton 'Ajouter'
		addButton.getButtonPressListeners().add(new ButtonPressListener() {
			@Override
			public void buttonPressed(Button button) {
				//Notifie le contrôleur du clic sur le bouton 'Ajouter'
				controller.addContactRequest(TableContactsWindow.this);
			}
		});
</pre>
<p>Elle admet aussi une méthode setContacts pour remplir la table avec une liste de contacts :</p>
<pre name="code" class="java">public void setContacts(List&lt;Contact&gt; result) {
		//Met à jour la table des contacts
		table.setTableData(result);
	}
</pre>
<p>Notons ici, qu&#8217;en appelant les méthodes setTableData et getTableData (qu&#8217;on a appelé dans le gestionnaire d&#8217;événement lors d&#8217;un clic sur une ligne du tableau), Apache Pivot se charge du mapping entre les propriétés de la classe Contact et les colonnes du tableau.</p>
<p>La classe EditContactWindow garde une référence sur un écouteur d&#8217;événements ContactSubmitRequestListener (c&#8217;est la fenêtre d&#8217;édition qui déclenche l&#8217;événement de demande de soumission) et a les propriétés suivantes :</p>
<ul>
<li>la fenêtre de tableau de contacts associée (à laquelle ajouter ou depuis laquelle modifier le contact)</li>
<li>la fenêtre d&#8217;édition</li>
<li>le formulaire d&#8217;édition</li>
<li>l&#8217;objet Contact en cours d&#8217;ajout/d&#8217;édition</li>
</ul>
<p>Le constructeur prend en paramètre le contrôleur et la fenêtre de tableau de contacts associée, demande à Pivot de parser addContact.xml pour construire la fenêtre d&#8217;édition et le formulaire:</p>
<pre name="code" class="java">		WTKXSerializer wtkxSerializer = new WTKXSerializer();
		try {
			editWindow = (Window)wtkxSerializer.readObject(this, 					"addContact.xml");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SerializationException e) {
			e.printStackTrace();
		}

		form = (Form)wtkxSerializer.get("submitform");
</pre>
<p>Ensuite, on ajoute un gestionnaire pour l&#8217;événement de clic sur le bouton de soumission du formulaire :</p>
<pre name="code" class="java">		PushButton submit = (PushButton)wtkxSerializer.get("submitButton");
	submit.setAction(new Action() {
		@Override
		public void perform() {
		//Met à jour l'objet contact avec les valeurs des champs du formulaire
		//Apache Pivot se charge de faire l'association
		//entre les propriétés de la classe Contact et les champs du formulaire
		form.store(contact);
		//Notifie le contrôleur de la soumission du formulaire
		listener.contactSubmit(contact,EditContactWindow.this);
		//Ferme la fenêtre d'edition
		editWindow.close();
		}
	});
</pre>
<p>La classe EditContactWindow admet aussi une méthode setContact pour mettre à jour l&#8217;objet Contact en cours d&#8217;édition :</p>
<pre name="code" class="java">public void setContact(Contact contact) {
	this.contact = contact;
	//Les champs du formulaire se remplissent avec les infos du contact
	//Apache Pivot se charge de faire l'association
	//entre les propriétés de la classe Contact et les champs du formulaire
	form.load(contact);
}
</pre>
<p>Notons ici qu&#8217;en appelant les méthodes load et store de l&#8217;objet Form, Pivot se charge de mapper les propriétés de la classe Contact avec les champs du formulaire.</p>
<h3><span style="color: #800000;"><strong>LE MODELE</strong></span></h3>
<p>A ce niveau de l&#8217;article, nous n&#8217;allons pas détailler la classe RemoteContactModel (nous allons le faire dans la partie qui traite la communication client/serveur) qui incarne le modèle dans notre application. Sachons seulement que cette classe implémente l&#8217;interface ContactModel, et est chargée de deux opérations :</p>
<ul>
<li> Récupérer la liste des contacts (opération à la fin de laquelle elle déclenche l&#8217;événement contactsRetrieved)</li>
</ul>
<ul>
<li> Poster un objet Contact pour ajout ou pour mise à jour (opération à la fin de laquelle elle déclenche l&#8217;événement contactSaved)</li>
</ul>
<h3><span style="color: #800000;"><strong>LE CONTROLEUR</strong></span></h3>
<p>La classe Contacts joue le rôle de contrôleur en implémentant les 3 interfaces d&#8217;écoutes et en gardant une référence vers la vue TableContactsWindow et le modèle ContactModel. Pour être notre point d&#8217;entrée de l&#8217;application, elle implémente l&#8217;interface org.apache.pivot.wtk.Application. Pivot appellera alors la méthode startup :</p>
<pre name="code" class="java">//Crée la fenêtre de table de contacts
		tcw = new TableContactsWindow(this);
		//Instancie le model
		model = new RemoteContactModel(this);
		//Demande au modèle de récupérer la liste des contacts
		model.retrieveAllContacts();
		//Affiche la fenêtre sur l'ecran principal
		tcw.getWindow().open(display);
</pre>
<p>Dans cette méthode, on instancie une fenêtre de tableau de contacts, on la remplit avec la méthode updateTable (qui ne fait que demander au modèle de récupérer la liste), et enfin on l&#8217;affiche sur l&#8217;écran principal (l&#8217;objet display qu&#8217;on reçoit en paramètre).</p>
<p>La classe Contacts, jouant le rôle de contrôleur, est responsable de gérer les événements :<br />
1. Demande d&#8217;édition d&#8217;un contact : instanciation d&#8217;une fenêtre d&#8217;édition et lui passant le contact à éditer</p>
<pre name="code" class="java">public void editContactRequest(Contact contact, TableContactsWindow source) {
		//Crée une fenêtre d'édition de contact
		EditContactWindow ecw = new EditContactWindow(this,source);
		//Met l'objet Contact cliqué pour édition
		ecw.setContact(contact);
		//Affiche la fenêtre
		ecw.show();
	}
</pre>
<p>2. Demande d&#8217;ajout d&#8217;un contact : instanciation d&#8217;une fenêtre d&#8217;édition et lui passant un nouvel objet contact « vide »</p>
<pre name="code" class="java">public void addContactRequest(TableContactsWindow source) {
		//Crée un nouvel objet Contact "vide"
		Contact newContact = new Contact(null, "", "", "", "");
		//Crée une fenêtre d'édition
		EditContactWindow ecw = new EditContactWindow(this,source);
		//Met le nouveau contact pour édition
		ecw.setContact(newContact);
		//Affiche la fenêtre d'édition
		ecw.show();
	}
</pre>
<p>3. Demande de soumission d&#8217;un contact : demande au modèle de sauvegarder (poster) l&#8217;objet</p>
<pre name="code" class="java">public void contactSubmit(Contact contact, EditContactWindow source) {
		model.saveContact(contact);
	}
</pre>
<p>4. Chargement de la liste des contacts : met à jour la table des contacts avec la nouvelle liste.</p>
<pre name="code" class="java">public void contactsRetrieved(List&lt;Contact&gt; contacts) {
		tcw.setContacts(contacts);
	}
</pre>
<p>5. Sauvegarde d&#8217;un contact : demande au modèle un rechargement de la liste des contacts</p>
<pre name="code" class="java">public void contactSaved() {
		model.retrieveAllContacts();
	}</pre>
<h3><span style="color: #800000;"><strong>LA PARTIE SERVEUR</strong></span></h3>
<p>Du côté serveur, on a deux composants essentiels :</p>
<ul>
<li> Une Servlet qui gère les requêtes de la partie cliente</li>
</ul>
<ul>
<li> Un DAO pour récupérer et sauvegarder les contacts</li>
</ul>
<p>Nous verrons les détails de la servlet plus en détail dans la troisième partie. Pour le moment, sachez qu&#8217;elle communique avec le DAO pour sauvegarder un objet posté par le client, ou pour récupérer une liste de contacts à renvoyer au client.</p>
<p>Pour simplifier les choses, nous avons opté pour une implémentation en mémoire du DAO, en tant que map dont les clés sont les ID et les valeurs sont les contacts associés.</p>
<pre name="code" class="java">public class MemoryContactDAO implements ContactDAO {

	private static Map&lt;Long, Contact&gt; data = new HashMap&lt;Long, Contact&gt;();
	static Long lastId;

	public List&lt;Contact&gt; getAll() {
		List&lt;Contact&gt; contacts = new ArrayList&lt;Contact&gt;();
		for(Contact c:data.values()){
			contacts.add(c);
		}
		return contacts;
	}

	@Override
	public Contact getById(Long id) {
		return data.get(id);
	}

	@Override
	public void save(Contact contact) {
		if(contact.getId() != null){
			data.put(contact.getId(), contact);
		}else{
			lastId ++;
			contact.setId(lastId);
			data.put(lastId, contact);
		}

	}
</pre>
<h3><span style="color: #800000;"><strong>L&#8217;INTERACTION CLIENT/SERVEUR</strong></span></h3>
<p>Apache Pivot dispose d&#8217;une API pour lancer des requêtes HTTP depuis la partie cliente. Deux classes de cet API sont utilisées dans notre classe modèle de la partie cliente.</p>
<p>Pour récupérer la liste des contacts, on lance une requête HTTP de type GET, et une fois exécutée avec succès, on notifie le contrôleur de la réponse reçue.</p>
<pre name="code" class="java">public void retrieveAllContacts() {
		//Crée une requête GET
		GetQuery getQuery = new GetQuery(HOST,PORT, "/"+APP_NAME+"/Contact",false);
		//Affecte le serialiseur de contacts
		getQuery.setSerializer(SerializerFactory.getContactSerializer());
		//Lance la requête en affectant un écouteur pour gérer le retour de la réponse
		getQuery.execute(new TaskListener&lt;Object&gt;() {
			@Override
			public void taskExecuted(Task&lt;Object&gt; task) {
				//task.getResult() = Réponse Objet désérialisé assumé en tant que liste de contacts
				//On notifie le controller de la liste récupérée
				controller.contactsRetrieved(((List&lt;Contact&gt;)task.getResult()));
			}
			@Override
			public void executeFailed(Task&lt;Object&gt; task) {
				//Ne fait rien du tout si erreur
			}

		});
	}
</pre>
<p>Pour sauvegarder (poster) un objet contact, on lance une requête HTTP de type POST, et une fois exécutée avec succès, on notifie le contrôleur.</p>
<pre name="code" class="java">public void saveContact(Contact contact) {
		//Crée une requête POST
		PostQuery post = new PostQuery(HOST,PORT, "/"+APP_NAME+"/Contact",false);
		//Affecte le serialiseur de contacts
		post.setSerializer(SerializerFactory.getContactSerializer());
		//Affecte l'objet Contact soumis (qui sera sérialisé)
		post.setValue(contact);
		//Lance la requête
		post.execute(new TaskListener&lt;URL&gt;() {

			@Override
			public void executeFailed(Task&lt;URL&gt; arg0) {
				//Ne fait rien du tout si erreur
			}

			@Override
			public void taskExecuted(Task&lt;URL&gt; arg0) {
				//On notifie le controller que la requête s'est bien déroulée
				controller.contactSaved();
			}
		});

	}
</pre>
<p>Vous avez peut être remarqué dans le code qu&#8217;on affecte à notre objet requête un sérialiseur. Les sérialiseurs de Apache Pivot peuvent faire l&#8217;objet d&#8217;un article qui leur est dédié. Sachez que Apache Pivot ne fait pas de différence entre sérialiseurs et désérialiseurs, et que dans ce contexte (requête GET), il s&#8217;agit plutôt d&#8217;un désérialiseur pour permettre à Pivot de « comprendre » la réponse renvoyé par le serveur (task.getResult()). Dans cette application, on a utilisé le BinarySerializer, qui ne fait qu&#8217;une sérialisation (pour les objets postés) et une désérialisation (pour les objets reçus) en binaire. Evidemment, ce type de sérialiseur est rarement pratique, nous l&#8217;avons choisi juste par simplicité et parce que c&#8217;est nous qui développons la partie cliente ainsi que la partie serveur. Par contre, si nous voudrons utiliser notre partie serveur pour exposer des services consommés par d’autres types de clients (Web, Application .NET…), le BinarySerializer est un choix à proscrire. Ce que Pivot apporte dans cette partie, c&#8217;est la sérialisation et la désérialisation transparente pour le développeur qui ne manipule que des objets. Remarquez que task.getResult() nous a retourné une liste de contacts, et que pour poster un objet contact, on n&#8217;a eu à faire que post.setValue(contact).</p>
<p>Apache Pivot dispose aussi d&#8217;une API pour traiter les requêtes HTTP du coté serveur. En effet, Pivot nous offre la classe QueryServlet. Dans notre application, la servlet de la partie serveur hérite de QueryServlet. Lorsqu&#8217;on hérite de QueryServlet, on doit implémenter (redéfinir) :</p>
<ul>
<li>La méthode newSerializer qui retourne le sérialiseur/désérialiseur que Pivot utilise pour sérialiser les objets retournés au client, et désérialiser les objets reçus du client</li>
</ul>
<ul>
<li> Une méthode par type de requête HTTP à gérer</li>
</ul>
<p>Dans notre cas, nous utiliserons le BinarySerializer comme retour de la méthode newSerializer : c&#8217;est ainsi que le client a sérialisé ses données, c&#8217;est ainsi donc que le serveur devrait les désérialisé. De plus, nous devrons redéfinir la méthode doGet et doPost pour gérer les deux types de requêtes que le client envoie.</p>
<pre name="code" class="java">protected Object doGet() throws ServletException, ClientException {

	//Récupère le DAO
	ContactDAO dao = DAOFactory.getContactDao();
	//Cas où la requête porte sur tous les contacts
	List&lt;Contact&gt; all = dao.getAll();
	org.apache.pivot.collections.List&lt;Contact&gt; result = new org.apache.pivot.collections.ArrayList&lt;Contact&gt;();
	//Transforme une java.util.List en l'implémentation List de Pivot
	for(Contact c:all){
		result.add(c);
	}
	//Retourne la liste
	//Apache Pivot se chargera par la suite de sérialiser la liste
	//et de l'inclure dans une réponse HTTP
	return result;
}

protected URL doPost(Object value) throws ServletException, ClientException {
	//Apache Pivot s'est déjà chargé de désérialiser l'objet envoyé
	//dans la requête POST et nous l'a passé en paramètre
	Contact contact = (Contact)value;
	//Récupère le DAO
	ContactDAO dao = DAOFactory.getContactDao();
	//Enregistre(ajoute ou met à jour) le contact envoyé
	dao.save(contact);
	try {
		String app = this.getContextPath();
		return new URL("http://localhost:8080"+app+"/Contact");
	} catch (MalformedURLException e) {
		e.printStackTrace();
	}
	return null;
}
</pre>
<p>Remarquez dans les deux méthodes, qu&#8217;on ne manipule pas les objets HTTPServletRequest, ni HTTPServletResponse. Les signatures des méthodes doGet et doPost sont différentes de celles des servlets « normales » (javax.servlet.http.HttpServlet). Le développeur, encore une fois, ne manipule que ses objets du modèle de donnée. Apache Pivot se charge de nous passer l&#8217;objet posté par le client (après désérialisation) comme paramètre de la méthode doPost et se charge aussi de formuler la réponse au client (après sérialisation) à partir de l&#8217;objet que nous retournons dans la méthode doGet.</p>
<p>Par cet article, nous avons montré quelques aspects de Pivot, comme la conception déclarative des interfaces utilisateurs ou le binding entre les objets (Contact) et les composants (TableView et Form), mais nous avons voulu surtout insisté sur le développement coté serveur à l&#8217;aide de Pivot, un aspect qui a été jusqu&#8217;à maintenant négligé dans les tutoriaux et les démos de référence du framework.</p>
<p><a href="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/07/Contacts%20-%20Codes%20sources.zip" target="_blank">Télécharger</a> le code source de l&#8217;exemple.</p>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/rWyqFKbpIoU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2010/07/06/apache-pivot-l%e2%80%99autre-ria/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2010/07/06/apache-pivot-l%e2%80%99autre-ria/</feedburner:origLink></item>
		<item>
		<title>Alfresco Share tip: content type selection when uploading a document</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/T22VtwNzf0A/</link>
		<comments>http://www.iptech-offshore.net/blog/2010/06/15/alfresco-share-tip-content-type-selection-when-uploading-a-document/#comments</comments>
		<pubDate>Tue, 15 Jun 2010 15:01:37 +0000</pubDate>
		<dc:creator>ibentamansourt</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[Alfresco]]></category>

		<category><![CDATA[Custom metadata]]></category>

		<category><![CDATA[ECM]]></category>

		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[Share]]></category>

		<category><![CDATA[tip]]></category>

		<category><![CDATA[Webscript]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=75</guid>
		<description><![CDATA[Alfresco Share types and form customization is now well understood and documented, as shown by this google search. But that is not the case when customizing the upload form in order to be able to select the type of the document to be uploaded.
At first, we thought that adding this functionality would require performing complex [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" style="float: left;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/06/alfresco.png" alt="" width="195" height="57" />Alfresco Share types and form customization is now well understood and documented, as shown by this <a href="http://www.google.com/search?hl=fr&#038;source=hp&#038;q=alfresco+forms+customization&#038;aq=f&#038;aqi=&#038;aql=&#038;oq=&#038;gs_rfai" target="_blank">google search</a>. But that is not the case when customizing the upload form in order to be able to select the type of the document to be uploaded.</p>
<p>At first, we thought that adding this functionality would require performing complex developments and altering the basic document upload action.<br />
We later discovered that this feature was already implemented in Alfresco Share though it is not documented anywhere (neither in official Alfresco documentation nor in the community sites and blogs).<br />
<span id="more-75"></span><br />
If you take a look at  the file “tomcat\webapps\share\WEB-INF\classes\alfresco\site-webscripts\org\alfresco\components\upload\flash-upload.get.html.ftl”, you’ll notice that while the upload form already contains the required code to generate a select input for the document type, its visibility is conditioned by the test “contentTypes?size == 1”.</p>
<pre name="code" class="html">         &lt;div id="${args.htmlid}-left-div" class="fileupload-left-div"&gt;
            &lt;span class="fileupload-percentage-span hidden"&gt; &lt;/span&gt;
            &lt;select class="fileupload-contentType-select &lt;#if (contentTypes?size == 1)&gt;hidden&lt;/#if&gt;"&gt;
               &lt;#if (contentTypes?size &gt; 0)&gt;
                  &lt;#list contentTypes as contentType&gt;
                     &lt;option value="${contentType.id}"&gt;${msg(contentType.value)}
                     &lt;/option&gt;
                  &lt;/#list&gt;
               &lt;/#if&gt;
            &lt;/select&gt;
         &lt;/div&gt;</pre>
<p>And so, it only takes filling in the contentTypes variable in the webscript Javascript controller with the custom document types for the select input to be shown to the final user.</p>
<p>The “contentTypes” variable’s declaration and initialization can be found in this file:  “tomcat\webapps\share\WEB-INF\classes\alfresco\site-webscripts\org\alfresco\components\upload\flash-upload.get.js”. This variable consists of an array of objects with 2 fields:</p>
<ul>
<li> id: the Alfresco content type identifier.</li>
<li> value: the content type label’s key in the webscript properties file.</li>
</ul>
<p>Find below an example of how to initialize contentTypes variable with multiple content types:</p>
<pre name="code" class="javascript">   var contentTypes = [
   {
      id: "cm:content",
      value: "cm_content"
   },
    {
      id: "custom:docJuri",
      value: "custom _docJuri"
   },
    {
      id: "custom:autres",
      value: "custom _autres"
   },
   {
      id:  "custom:presse",
      value: "custom _presse"
   },
   {
      id: "custom:biblio",
      value: "custom _biblio"
   }
    ];</pre>
<p>As a result, here’s how the upload form will look like:</p>
<p style="text-align: center;"><a href="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/06/UploadForm.png" target="_blank"><img style="vertical-align: middle;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/06/UploadForm.png" alt="" width="408" height="247" /></a></p>
<p style="text-align: center;"><strong>Figure 1. Upload form</strong></p>
<p style="text-align: left;">
<p style="text-align: left;">Now, you should be able to easily define types for your documents when adding them to the repository, as with Alfresco explorer, and then fill in their custom metadata as showing in Figure 2.</p>
<p style="text-align: center;"><a href="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/06/EditMetadata.png" target="_blank"><img style="vertical-align: middle;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/06/EditMetadata.png" alt="" width="407" height="342" /></a><br />
<strong></strong></p>
<p style="text-align: center;"><strong>Figure 2. Edit metadata form</strong></p>
<p style="text-align: left;">
<p style="text-align: left;">Hope it was useful, your comments are welcome.</p>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/T22VtwNzf0A" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2010/06/15/alfresco-share-tip-content-type-selection-when-uploading-a-document/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2010/06/15/alfresco-share-tip-content-type-selection-when-uploading-a-document/</feedburner:origLink></item>
		<item>
		<title>Migration d’applications pour les systèmes multi-cœurs et 64 bit</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/YWKrMc0gjq4/</link>
		<comments>http://www.iptech-offshore.net/blog/2010/05/25/migration-dapplications-pour-les-systemes-multi-coeurs-et-64-bit/#comments</comments>
		<pubDate>Tue, 25 May 2010 09:57:22 +0000</pubDate>
		<dc:creator>nbelaid</dc:creator>
		
		<category><![CDATA[C++]]></category>

		<category><![CDATA[Publications]]></category>

		<category><![CDATA[Technique]]></category>

		<category><![CDATA[64 bit]]></category>

		<category><![CDATA[Gestion de mémoire]]></category>

		<category><![CDATA[Migration]]></category>

		<category><![CDATA[multi-cœurs]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=74</guid>
		<description><![CDATA[&#8220;The Free Lunch Is Over&#8221; Herb Sutter.
Les processeurs à cœurs multiples et hybrides arrivent à la tête du classement établi par Gartner pour les technologies de rupture pour 2008 à 2012. Ces technologies sont depuis entrées dans le mainstream. Ainsi, le défi de supporter les processeurs de nouvelles générations est devenu une nécessité. Ceci nous [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: right;">&#8220;The Free Lunch Is Over&#8221; Herb Sutter.</p>
<p>Les processeurs à cœurs multiples et hybrides arrivent à la tête du classement établi par Gartner pour les technologies de rupture pour 2008 à 2012. Ces technologies sont depuis entrées dans le mainstream. Ainsi, <a href="http://www.programmez.com/magazine_articles.php?titre=Migrations-d%E2%80%99applications-pour-les-systemes-multi-coeurs-et-64-bit&amp;id_article=1373&amp;magazine=130" target="_blank"><img class="alignleft" style="float: left; margin: 5px 10px;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/05/ProgrammezMai2010.jpg" alt="" width="120" height="163" /></a>le défi de supporter les processeurs de nouvelles générations est devenu une nécessité. Ceci nous amène à nous interroger sur les possibilités de migration pour les applications existantes. La migration  pour les processeurs modernes peut poser plusieurs problèmes aux développeurs. Nous avons donc choisi de mettre l’accent sur les erreurs communes en développement 64 bit et les techniques les moins intrusives pour paralléliser son application.</p>
<p><span style="background-color: #ffffcc;">Cet article a été publié dans le  numéro 130 (Mai 2010) du magazine<a href="http://www.programmez.com/magazine_articles.php?titre=Migrations-d%E2%80%99applications-pour-les-systemes-multi-coeurs-et-64-bit&amp;id_article=1373&amp;magazine=130" target="_blank"> «PROgrammez»</a>.</span> <span style="background-color: #ffffcc;">La version PDF de  l’article est mise à votre disposition en <a href="http://www.iptech-offshore.net/pdf/PROgrammez-numero-130-mai-2010-Extrait-IP-Tech.pdf" target="_blank"><strong>téléchargement gratuit</strong></a></span>.<br />
<span id="more-74"></span></p>
<p><strong><span style="font-size: medium;">Processus 64 bit</span></strong></p>
<p>Est-il suffisant de recompiler le code avec un compilateur 64 bits ?<br />
La recompilation est suffisante pour plusieurs types de programmes. On vérifiera que l’application utilise seulement des librairies compilées en 64 bit, car un processus 64 bit peut exécuter uniquement du code 64 bit. Dans des applications triviales, cette étape suffit à elle seule. Cependant en réalité rien ne garantit que les libraires utilisées aient une version 64 bit, la conversion et d’autant plus compliquée si leurs codes source sont indisponibles. Dans ce cas, on pourra envisager des scénarios de communication interprocessus entre l’application et la bibliothèque en question. Outre cela, la compilation ne garantit pas le bon fonctionnement de l’application. De nouveaux bugs et anomalies peuvent avoir lieu. Allant des erreurs d’affichage jusqu’aux failles de sécurité les plus sévères dues à un dépassement de tampon « buffer overflow » par exemple. La mauvaise manipulation des pointeurs, la lecture des fichiers binaires ainsi que l’usage du code assembleur sont des sources potentielles de bugs. Prenons un exemple simple de programme qui a un comportement différent selon l’architecture matérielle.</p>
<pre name="code" class="c++">int main(int argc, char* argv[])
{
	///Le type size_t est 64bit pour le
	///compilateur 64bit.
	size_t size = sizeof(myObject);
	///L&#8217;affectation peut engendrer une
	///perte de données.
	long a = size;

	///%x assume que le type est 32 bit
	printf(&#8221;%x\n&#8221;,a);
	printf(&#8221;%x\n&#8221;,size);

	return 0;
}</pre>
<p>La procédure main ne compte que 5 lignes, pourtant on peut identifier déjà deux bugs. Le type size_t a une valeur 64 bit pour un compilateur 64 bit. Le type long est un type 32 bit, la conversion : long a = size;<br />
Peut engendrer donc la perte de données, car la taille du type size_t est supérieure à celle du type long. De plus %x qui permet de formater l’affichage en hexadécimal ne prend en compte que les valeurs 32 bit. On pourrait corriger la fonction d’affichage pour le programme précédent on en remplaçant %x par %I64x pour l’architecture x64. Essayons à présent de corriger l’opération d’affectation, on pourrait songer à remplacer le long par le type long long. Le programme fonctionne correctement pour l’architecture x64, mais il ne retourne plus la bonne valeur pour l’architecture x86 ! Ceci était prévisible puisque long long est un entier 64 bit. À l&#8217;évidence, la solution la plus simple serait de remplacer le long par size_t. Ce petit programme de quelques lignes montre que la correction de ce type d’erreurs est loin d’être aisé.</p>
<pre name="code" class="c++">#ifdef _WIN64
  #define xformat "I64"
#else
  #define xformat ""
#endif

int main(int argc, char* argv[])
{
	///Le type size_t est 64bit pour le
	///compilateur 64bit.
	size_t size = sizeof(myObject);
	///L&#8217;affectation peut engendrer une
	///perte de données.
	size_t a = size;

	///%x assume que le type est 32 bit
	printf(&#8221;%x&#8221; xformat &#8220;\n&#8221;,a);
	printf(&#8221;%x&#8221; xformat &#8220;\n&#8221;,size);

	return 0;
}</pre>
<p>Les deux erreurs dans le programme précédent sont causées par le changement de taille pour le type size_t. Ci-dessous une liste de quelque type prédéfinis en c++ et leurs tailles respectives pour les architectures 32 et 64 bit.</p>
<p style="text-align: center;"><img class="aligncenter" style="vertical-align: middle;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/05/Tabx86.png" alt="" width="206" height="86" /></p>
<p>Le tableau ci-dessus montre qu’une classe contenant des attributs de type pointeur par exemple, n’occupe pas la même taille en mémoire pour un programme 64 bit et 32 bit.</p>
<p><span style="font-size: medium;"><span style="font-size: small;"><strong>Structures de données</strong></span><br />
</span></p>
<p>Malgré la puissance brute des processeurs modernes, certains programmes compilés en 64 bit peuvent avoir une baisse de performance accompagnée généralement d&#8217;une augmentation importante en consommation de mémoire. L&#8217;augmentation en consommation mémoire n&#8217;est pas seulement due au changement de taille pour les types de base. En effet, il y a de fortes chances qu&#8217;un mauvais alignement des structures est la cause de cette augmentation. Considérons un cas ou ces effets secondaires se manifestent.<br />
Soit la structure suivante :</p>
<pre name="code" class="c++">struct heavyStruct
{
	char b;
	int *a;
	int  u;
};</pre>
<p>En 32 bit cette structure occupe une taille de  12 octets en mémoire. Par contre en 64 bit elle aura une taille de 24 octets. Observons le cas d&#8217;une structure qui a exactement les mêmes attributs que « heavyStruct » mais qui ne sont pas dans le même ordre. On pourrait s&#8217;attendre que cette dernière aura naturellement une taille identique que « heavyStruct » mais il n’en est rien ! En effet en 64 bit elle aura une taille 16 octets et en 32 bit elle reste égale à 12 octets. Retenons donc ceci, la taille d&#8217;une structure n&#8217;est pas égale à la somme des tailles de ces attributs. Dans la plupart des cas, ce fait n&#8217;a pas un impact perceptible sur la consommation en mémoire. Toutefois, l&#8217;ordre des attributs à l&#8217;intérieur d&#8217;une structure peut limiter la consommation en mémoire de l&#8217;application, notamment lorsqu&#8217;il s’agit de crée un tableau de grande taille.</p>
<pre name="code" class="c++">struct lightStruct
{
	int *a;
	int  u;
	char b;
};</pre>
<p>La taille d’un struct n’est pas égale à la somme des tailles de ces attributs comme le montre la figure suivante, les cases en rouge sont ceux occupés par les données de la structure tandis que ceux en gris représente l’espace gaspillé.</p>
<p style="text-align: center;">heavyStruct</p>
<p style="text-align: center;"><img style="vertical-align: middle;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/05/TabOctets2.png" alt="" width="221" height="80" /></p>
<p style="text-align: left;">Un agencement meilleur des attributs permet d’optimiser l’usage de  mémoire.</p>
<p style="text-align: left;">
<p style="text-align: center;">LightStruct</p>
<p style="text-align: center;"><img style="vertical-align: middle;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/05/TabOctets.png" alt="" width="251" height="68" /></p>
<p style="text-align: center;">
<p style="text-align: center;">
<p><span style="font-size: small;"><strong><span>Conclusion</span></strong></span></p>
<p>Les erreurs du au changement de taille des types prédéfinis sont très fréquentes. Et comme nous l’avons vu il y a de fortes probabilités que ces erreurs auront des conséquences dramatiques sur le comportement de l’application.</p>
<p><strong><span style="font-size: medium;">Systèmes multi-cœurs</span></strong></p>
<p>Nous avons vu quelques pièges à éviter lors de la migration d’applications pour une architecture 64 bit. Malheureusement, cela ne suffit plus à tirer avantage des processeurs à cœurs multiples. En effet si notre application est monothread son processus sera exécuté sur un seul cœur. Mais que ferait-on pour comment paralléliser son application existante, sans être amené à la réécriture du code source ?<br />
Bien entendu, on peut utiliser les threads Windows ou ceux de POSIX, mais pour la migration d’application on préfère utiliser la technologie la moins intrusive. La solution qui répond le plus à ce besoin est la technologie OpenMP. Car elle ne nécessite pas de modifier la structure de l’application. Est c’est une technologie standard multi plateformes supportées supportée par plusieurs compilateurs C/C++.<br />
Supposons que nous avons à notre disposition une technologie qui nous permet de paralléliser assez rapidement des portions de notre code existant. Si par exemple on parvient à paralléliser 60 % de notre code. Quel est le gain de performance maximal que l’on peut obtenir ?<br />
La loi d’Amdahl très utilisée en informatique permet d’avoir une idée sur la performance maximale que l’on peut atteindre en parallélisant une portion P du code sur N cœurs :</p>
<p style="text-align: center;"><img style="vertical-align: middle;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/05/Formule.png" alt="Formule" width="225" height="63" /></p>
<p>Si l&#8217;on parvient par exemple à paralléliser 60 % (P = 0,6) du code sur un système à quatre processeurs (N = 4) alors on peut s’attendre à une augmentation des performances allant jusqu’à deux fois la vitesse pour un seul processeur. Pour un ordinateur ayant 16 processeurs en parallèle le gain peut atteindre 2.35 fois la performance pour un seul processeur. En revanche si 100 % du code est parallèle alors sur un système à 16 processeurs le gain est multiplié par 16. En règle générale plus il y a de proportion du code source tournant en parallèle plus le programme est extensible (scalable).<br />
<strong></strong></p>
<p><strong><span style="font-size: small;">Granularité des threads</span></strong></p>
<p>Lorsqu’il s’agit de paralléliser son application, la solution la plus simple serait de décomposer le programme en tâches indépendantes. La décomposition est souvent fonctionnelle. Par exemple dans un jeu vidéo on peut exécuter le système sonore, l’intelligence artificielle et le rendu sur des threads séparés. Cette solution est assez répondue néanmoins lorsque le nombre de cœurs dépasse le nombre de blocs parallèles, les performances ne seront plus améliorées avec l’ajout d’autres cœurs au système. Une granularité grossière des threads ne permet pas toujours d’augmenter l’extensibilité du programme.<br />
Une granularité fine des threads permet une plus grande extensibilité. On doit créer les threads, assigner à chaque thread la tâche qu’il doit exécuter et finalement terminer leurs exécutions. L’implémentation manuelle de cette méthode peut apporter des gains considérables en termes de performances, mais elle est généralement assez difficile à réaliser. Encore une fois, la technologie OpenMP peut simplifier les choses en proposant d’automatiser la gestion des threads.</p>
<p><strong><span style="font-size: small;">Le couteau suisse du développeur</span></strong></p>
<p>La migration peut être facilitée à l’aide des outils d’analyse et d’inspection automatique. Ils permettent de détecter plusieurs erreurs classiques en programmation parallèle, telle que les deadlocks et race conditions. En outre à l’aide de la visualisation graphique on peut détecter facilement les goulots d’étranglement dans une application.<br />
Une gamme d’outils incontournables est Intel® Parallel Studio.</p>
<p><strong><span style="font-size: medium;">Exemple d’application</span></strong></p>
<p>Afin de mieux présenter les spécificités des processeurs de nouvelles générations, nous avons choisi de réaliser un simple système de particule. Car il présente un cas typique ou une région du code ralenti l’ensemble du programme.<br />
J&#8217;aimerais rappeler qu&#8217;un système de particule est une simulation d’un grand nombre de points dans l’espace soumis à un ensemble de forces comme la gravité. Il est souvent utilisé en infographie pour simuler l’apparence de nombreux phénomènes naturels et pour créer des effets spéciaux. Nous n&#8217;avons pas la prétention de créer un système complexe de A jusqu&#8217;à Z, mais seulement de retenir les détails les plus importants de la migration.<br />
L&#8217;idée de base était de trouver un cas d&#8217;application ou il y a un goulot d&#8217;étranglement au niveau des calculs effectués par le CPU. Les données relatives aux particules sont conservées dans la mémoire à haute performance du GPU. Les données sont encapsulées dans des &#8220;buffer objects&#8221; pour minimiser le transfert de données entre processeurs et carte graphique.</p>
<p style="text-align: center;"><img class="aligncenter" style="vertical-align: middle;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/05/Etoilefilante.png" alt="étoile filante" width="340" height="257" /></p>
<p>À chaque frame, on calcule la nouvelle position, vitesse et accélération de chaque particule. Ce temps de calcul est plus important que le temps pris par le rendu des particules par la carte graphique. Pour peu qu&#8217;on arrive à optimiser cette partie du code source, la vitesse d&#8217;exécution sera nettement améliorée. Mais avant d&#8217;entamer l&#8217;étape d&#8217;optimisation, examinons de plus près notre code pour y déceler d&#8217;éventuelles erreurs.<br />
La boucle permettant de mettre à jour les particules est sous la forme :</p>
<pre name="code" class="c++">	for (ptrdiff_t i = 0;i&lt;particleSystem-&gt;getParticleNumber();i++)
		particleSystem-&gt;Update(i);
</pre>
<p>Le type « ptrdiff _t»  a une taille de 32 bit pour les systèmes 32 bit et 64 bit pour les systèmes 64 bit. Ce qui le rend idéal pour les indexes des tableaux. Par ailleurs, il permet d&#8217;éviter de sacrifier inutilement de la mémoire pour les systèmes 32 bit. Et peut être utilisé invariablement quelque soit le système.<br />
La boucle peut être réécrite de la façon suivante, dans l&#8217;intention d&#8217;expliciter les instructions de la fonction de mise à jour.</p>
<pre name="code" class="c++">void particleSystem::Update()
{
	ptrdiff_t i;
	for (i = 0;i&lt;particleNb;i++)
	{
		ForceZ += sinf(ForceZaxis[i]);
		//L’acceleration est la somme des forces divisée par
		//la masse de la particule.
		particle[i].acceleration = ForceZ / particle[i].mass * ez;
		//On met à jour la vitesse de la particule.
		particle[i].velocity += particle[i].acceleration;
		//On met à jour la position de la particule.
		particle[i].position += particle[i].velocity;
}
}</pre>
<p>La fonction de mise à jour pour une particule donnée ne nécessite pas la connaissance de l&#8217;état des autres particules. Elles agissent indépendamment et par conséquent on peut imaginer que cette tâche peut être répartie sur plusieurs threads. Chaque thread mettra à jour un certain nombre de particules. Le problème ne nécessite aucune solution de synchronisation avancée. Sans doute, la solution doit être très simple, dans le cas d&#8217;OpenMP elle l&#8217;est, mais dans bien d&#8217;autre cas elle nécessite la modification du code. Pour indiquer au compilateur que la boucle peut être parallélisé, il suffit d&#8217;ajouter la directive :</p>
<pre name="code" class="c++">pragma omp parallel for shared(particle)</pre>
<p>Notons au passage que dans le cas où le support d&#8217;OpenMP est désactivé le compilateur ignore tout simplement cette ligne et on obtiendra la version sérielle de notre application.</p>
<pre name="code" class="c++">	#pragma omp parallel for shared(particle)
for (i = 0;i&lt;particleNb;i++)
	{
		ForceZ += sinf(ForceZaxis[i]);
		//L’acceleration est la somme des forces divisée par
		//la masse de la particule.
		particle[i].acceleration = ForceZ / particle[i].mass * ez;
		//On met à jour la vitesse de la particule.
		particle[i].velocity += particle[i].acceleration;
		//On met à jour la position de la particule.
		particle[i].position += particle[i].velocity;
	}</pre>
<p>La granularité des threads est un facteur important lors de la parallélisation de l&#8217;application. L&#8217;ajustement de la granularité en OpenMP se fait par le biais de la clause : schedule(type, nombre)</p>
<pre name="code" class="c++">schedule(static, 100)</pre>
<p>On indique que la capacité du lot est égale à 100. Par conséquent à chaque thread on affecte 100 itérations.</p>
<pre name="code" class="c++">#pragma omp parallel for schedule(static, 100) private(i)
\ shared(particle, ForceZ)
for (i = 0;i&lt;particleNb;i++)
	{
		ForceZ += sinf(ForceZaxis[i]);
		//L’acceleration est la somme des forces divisée par
		//la masse de la particule.
		particle[i].acceleration = ForceZ / particle[i].mass * ez;
		//On met à jour la vitesse de la particule.
		particle[i].velocity += particle[i].acceleration;
		//On met à jour la position de la particule.
		particle[i].position += particle[i].velocity;
	}</pre>
<p>Considérons à présent l’instruction suivante permettant d&#8217;effectuer la somme des éléments d&#8217;un tableau :</p>
<pre name="code" class="c++">ForceZ += sinf(ForceZaxis[i]);</pre>
<p>On peut indiquer à OpenMP d’optimiser l’opération de sommation. En spécifiant le nom de la variable et l&#8217;instruction. L&#8217;opération de réduction permet de se débarrasser des sémaphores de synchronisation. Dans le code suivant la variable ForceZ ne nécessite pas de primitive de synchronisation grâce à l’opération de réduction.</p>
<pre name="code" class="c++">#pragma omp parallel for schedule(static, 100) private(i)
\ shared(particle) reduction(+:ForceZ)
for (i = 0;i&lt;particleNb;i++)
	{
		ForceZ += sinf(ForceZaxis[i]);
		//L’acceleration est la somme des forces divisée par
		//la masse de la particule.
		particle[i].acceleration = ForceZ / particle[i].mass * ez;
		//On met à jour la vitesse de la particule.
		particle[i].velocity += particle[i].acceleration;
		//On met à jour la position de la particule.
		particle[i].position += particle[i].velocity;
	}</pre>
<p><span style="font-size: small;"><strong><span>Résultats</span></strong></span></p>
<p>Nous avons testé le temps d’exécution de l’application sur différents processeurs. Nous remarquons une baisse de performance pour la version sérielle de l’application pour le processeur Intel Xeon malgré qu’il est le plus puissant des processeurs testés. La version parallèle de l’application est nettement plus performante, en effet elle est 2.26 fois plus rapide que la version sérielle pour le processeur Intel Xeon.</p>
<p style="text-align: center;"><img src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/05/CB.png" alt="" /></p>
<p>La courbe suivante montre l’extensibilité de notre application. La version parallèle permet en effet d’exploiter la puissance des processeurs testés.</p>
<p style="text-align: center;"><img src="http://www.iptech-offshore.net/blog/wp-content/uploads/2010/05/Courbe.png" alt="" /></p>
<p><span style="font-size: medium;"><br />
</span></p>
<p><span style="font-size: small;"><strong><span>Conclusion</span></strong></span></p>
<p>Nous avons vu dans cet article quelques pièges à éviter lors de la migration d’applications pour les processeurs modernes. Une des technologies clefs pour une migration progressive est la technologie OpenMP. Ceci étant dit il reste encore de nombreuses voies à explorer afin d’exploiter au mieux les capacités des ordinateurs modernes et notamment l’exploitation de la puissance de calcul du GPU. Le standard OpenCL comme le fut OpenMP ouvre de nouvelles possibilités pour les développeurs pour paralléliser leurs applications.</p>
<p><span style="font-size: medium;"><span style="font-size: small;"><strong>Références et ressources</strong></span><br />
</span></p>
<ul>
<li><a href="http://developer.amd.com/documentation/articles/pages/6220067.aspx" target="_blank">http://developer.amd.com/documentation/articles/pages/6220067.aspx</a></li>
<li><a href="http://software.intel.com/en-us/blogs/2010/02/16/search-of-64-bit-errors-in-array-implementation/" target="_blank">http://software.intel.com/en-us/blogs/2010/02/16/search-of-64-bit-errors-in-array-implementation/</a></li>
<li><a href="http://software.intel.com/en-us/articles/granularity-and-parallel-performance/" target="_blank">http://software.intel.com/en-us/articles/granularity-and-parallel-performance/</a></li>
<li><a href="http://msdn.microsoft.com/en-us/magazine/cc300794.aspx" target="_blank">http://msdn.microsoft.com/en-us/magazine/cc300794.aspx</a></li>
<li><a href="http://msdn.microsoft.com/en-us/library/3b2e7499%28VS.80%29.aspx" target="_blank">http://msdn.microsoft.com/en-us/library/3b2e7499%28VS.80%29.aspx</a></li>
<li><a href=" http://www.viva64.com/content/articles/64-bit-development/?f=64-bit-migration-7-steps.html " target="_blank">http://www.viva64.com/content/articles/64-bit-development/?f=64-bit-migration-7-steps.html </a></li>
</ul>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/YWKrMc0gjq4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2010/05/25/migration-dapplications-pour-les-systemes-multi-coeurs-et-64-bit/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2010/05/25/migration-dapplications-pour-les-systemes-multi-coeurs-et-64-bit/</feedburner:origLink></item>
		<item>
		<title>Mettre en place un arbre paresseux avec GWT</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/GWWg9xN2dLg/</link>
		<comments>http://www.iptech-offshore.net/blog/2010/04/12/mettre-en-place-un-arbre-paresseux-avec-gwt/#comments</comments>
		<pubDate>Mon, 12 Apr 2010 08:41:11 +0000</pubDate>
		<dc:creator>Jawher MOUSSA</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[AJAX]]></category>

		<category><![CDATA[GWT]]></category>

		<category><![CDATA[Lazy tree]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=73</guid>
		<description><![CDATA[Le but de ce billet est de présenter une technique simple pour mettre en place un arbre paresseux (lazy tree) avec GWT.

GWT offre des primitives basiques pour la création d&#8217;un arbre, qui sont :

Le composant TreeItem : représente un nœud dans l&#8217;arbre et peut contenir de sous nœuds du même type.
Le composant Tree : représente [...]]]></description>
			<content:encoded><![CDATA[<p>Le but de ce billet est de présenter une technique simple pour mettre en place un arbre paresseux <em>(lazy tree)</em> avec <em>GWT</em>.</p>
<p><span id="more-73"></span></p>
<p><em>GWT</em> offre des primitives basiques pour la création d&#8217;un arbre, qui sont :</p>
<ul>
<li>Le composant <strong><em>TreeItem</em></strong> : représente un nœud dans l&#8217;arbre et peut contenir de sous nœuds du même type.</li>
<li>Le composant <em><strong>Tree </strong></em>: représente la racine de l&#8217;arbre et peut contenir des TreeItems</li>
</ul>
<p>Construire un arbre revient donc à créer autant d&#8217;instances de <em>TreeItem</em> que nécessaire et des les assembler pour obtenir l&#8217;hiérarchie souhaitée.<br />
Cependant, cette méthode n&#8217;est pas applicable dans le cas où :</p>
<ul>
<li>On dispose d&#8217;un grand nombre des nœuds (des centaines voire des milliers)</li>
<li>La récupération des nœuds est coûteuse : accès à la base de données et/ou au réseau</li>
</ul>
<p>Dans des cas pareils, mieux vaut être paresseux et ne charger des nœuds que quand nécessaire (ne charger les fils d&#8217;un nœud que quand l&#8217;utilisateur l&#8217;ouvre).<br />
Dans le cadre de ce billet, et comme source de données pour l&#8217;arbre, on suppose disposer d&#8217;un service exposé en <em>RPC</em> dont voici l&#8217;interface :</p>
<pre name="code" class="java">package com.iptech.client;

import java.util.ArrayList;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("treeService")
public interface TreeService extends RemoteService {
	ArrayList&lt;String&gt; getChildren(String parent)
			throws IllegalArgumentException;
}
</pre>
<p>Dans l&#8217;implémentation de ce service, j&#8217;ai introduit (via un <em>sleep</em>) un délais de 2 secondes, pour simuler l&#8217;obtention d&#8217;une ressource coûteuse et pour que le comportement paresseux de l&#8217;arbre soit plus visible.</p>
<pre name="code" class="java">public class Application implements EntryPoint {
	private static final String CHARGEMENT_EN_COURS = "Chargement en cours ...";
	private final TreeServiceAsync treeService = GWT.create(TreeService.class);

	public void onModuleLoad() {
		TreeItem root = new TreeItem(CHARGEMENT_EN_COURS);

		Tree browseTree = new Tree();
		browseTree.addItem(root);

		treeService.getChildren(null, new TreeRootCallback(browseTree));

		browseTree.addOpenHandler(new OpenHandler&lt;TreeItem&gt;() {

			public void onOpen(OpenEvent&lt;TreeItem&gt; event) {
				if (needsLoading(event.getTarget())) {

					treeService.getChildren(event.getTarget().getText(),
							new TreeItemCallback(event.getTarget()));
				}
			}
		});
		RootPanel.get().add(browseTree);
	}
</pre>
<p>A noter que l&#8217;interface TreeServiceAsync (qui est la version asynchrone de TreeService) n&#8217;est pas présente dans le code source de cet exemple car elle sera généré automatiquement par <a href="http://mojo.codehaus.org/gwt-maven-plugin/">GWT Maven plugin</a> lors de la construction du projet.</p>
<p>Je commence donc par créer l&#8217;instance du composant du composant <em>Tree</em> et d&#8217;y ajouter un élément avec le message &#8220;<em>Chargement en cours &#8230;</em>&#8221;</p>
<p>J&#8217;invoque ensuite le service présenté plus haut pour récupérer la liste des nœuds racines de l&#8217;arbre. Cet appel se fait de façon asynchrone, d&#8217;où l&#8217;utilisation d&#8217;un <em>callback </em>dont voici le code :</p>
<pre name="code" class="java">
public static final class TreeRootCallback implements
		AsyncCallback&lt;ArrayList&lt;String&gt;&gt; {

	private Tree browseTree;

	public TreeRootCallback(Tree browseTree) {
		super();
		this.browseTree = browseTree;
	}

	public void onFailure(Throwable caught) {
		caught.printStackTrace();
	}

	public void onSuccess(ArrayList&lt;String&gt; names) {
		browseTree.removeItems();
		for (String name : names) {
			TreeItem ti = new TreeItem(name);
			ti.addItem(CHARGEMENT_EN_COURS);
			browseTree.addItem(ti);
		}
	}

}</pre>
<p>A la réception de la réponse, le <em>callback</em> supprime les éléments de l&#8217;arbre (le message &#8220;<em>Chargement en cours &#8230;</em>&#8221; plus précisément) et pour chaque chaîne retournée :</p>
<ul>
<li>Crée un <em>item</em> avec la chaîne comme texte et l&#8217;ajoute à l&#8217;arbre</li>
<li>A chaque <em>item</em> crée ajoute un fils avec le message &#8220;<em>Chargement en cours &#8230;</em>&#8220;</li>
</ul>
<p>Retournons à la méthode <em>onModuleLoad</em>.</p>
<p>L&#8217;étape suivante consiste à attacher à l&#8217;arbre un <em>listener</em> qui intercepte les évènements d&#8217;ouverture d&#8217;un nœud, et si ce dernier nécessite le chargement de ses fils (dans le cas où il a un seul fils et que ce dernier a le texte &#8220;<em>Chargement en cours &#8230;</em>&#8220;), fait appel à <em>treeService</em> en lui passant un autre <em>callback</em> dont voici le code :</p>
<pre name="code" class="java">
public static final class TreeItemCallback implements
		AsyncCallback&lt;ArrayList&lt;String&gt;&gt; {

	private TreeItem treeItem;

	public TreeItemCallback(TreeItem treeItem) {
		super();
		this.treeItem = treeItem;
	}

	public void onFailure(Throwable caught) {
		caught.printStackTrace();
	}

	public void onSuccess(ArrayList&lt;String&gt; names) {
		treeItem.removeItems();
		for (String name : names) {
			TreeItem ti = new TreeItem(name);
			ti.addItem(CHARGEMENT_EN_COURS);
			treeItem.addItem(ti);
		}
	}
}
</pre>
<p>
Ce second <em>callback</em> ressemble beaucoup au premier, excepté qu&#8217;il opère sur un <em>TreeItem</em> au lieu d&#8217;un <em>Tree</em>.
</p>
<p>
<em> A ce propos, j&#8217;étais étonné du fait que Tree et TreeItem n&#8217;ont pas un ancêtre commun, bien qu&#8217;ils sont très similaires conceptuellement (tous les deux sont des conteneurs d&#8217;autres TreeItems, ce qui se fait trahir par le fait qu&#8217;ils partagent plusieurs méthodes avec le même nom et signatures).</em>
</p>
<h2>Code source</h2>
<p>Un projet maven avec le code source complet  de ce qui a été présenté dans ce billet est disponible sous la licence <a href="http://www.opensource.org/licenses/mit-license.php">MIT</a> sur GitHub à l&#8217;adresse suivante : <a href="http://github.com/jawher/gwt-lazy-tree/">http://github.com/jawher/gwt-lazy-tree/</a></p>
<p>Une fois ce projet récupéré, il suffit d&#8217;exécuter la commande &#8220;mvn gwt:run&#8221; dans son dossier pour exécuter et tester cette application.</p>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/GWWg9xN2dLg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2010/04/12/mettre-en-place-un-arbre-paresseux-avec-gwt/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2010/04/12/mettre-en-place-un-arbre-paresseux-avec-gwt/</feedburner:origLink></item>
		<item>
		<title>Apache Pivot : l’alternative à JavaFX ?</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/DqfSEbpYPWA/</link>
		<comments>http://www.iptech-offshore.net/blog/2010/02/07/apache-pivot-lalternative-a-javafx/#comments</comments>
		<pubDate>Sun, 07 Feb 2010 07:42:00 +0000</pubDate>
		<dc:creator>Hamdi DOUSS</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[Technique]]></category>

		<category><![CDATA[Apache Pivot]]></category>

		<category><![CDATA[RIA]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=72</guid>
		<description><![CDATA[
Apache Pivot est un framework de développement d&#8217;applications Internet riches (RIA). Du buzz autour de ce framework est en train de se produire sur la toile. Bien qu&#8217;entamé en 2007, Apache Pivot est mentionné de plus en plus ces derniers jours grâce à sa promotion au rang de &#8220;Top Level Project&#8221; par la fondation Apache [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" style="float: left; margin-right: 15px;margin-bottom: 10px;" src="http://pivot.apache.org/images/logo.png" alt="Apache Pivot Logo" width="132" height="140" /></p>
<p><a href="http://pivot.apache.org" target="_blank">Apache Pivot</a> est un framework de développement d&#8217;applications Internet riches (RIA). Du buzz autour de ce framework est en train de se produire sur la toile. Bien qu&#8217;entamé en 2007, Apache Pivot est mentionné de plus en plus ces derniers jours grâce à sa promotion au rang de &#8220;Top Level Project&#8221; par la fondation Apache en Décembre 2009.</p>
<p><span id="more-72"></span></p>
<p>En théorie, cela veut dire que les développeurs du framework, qui a rejoint <a href="http://incubator.apache.org" target="_blank">Apache Incubator</a> en Janvier 2009, respecte les principes et les process de la fondation, se basant sur <a href="http://www.apache.org/foundation/how-it-works.html#meritocracy" target="_blank">la méritocratie</a>. En pratique, cela signifie que le projet devient plus visible, et une certaine communauté de développeurs se formera autour du projet. On verra certainement plus de billets de blog, des exemples, de la documentation et des critiques autour du framework. La quantité et la qualité dépendra certainement du succès de celui-ci.</p>
<p>Tout développeur Java peut se demander comment se positionne Apache Pivot par rapport à JavaFX. Sur la page <a href="http://cwiki.apache.org/PIVOT/frequently-asked-questions-faq.html" target="_blank">FAQ du projet</a>, les développeurs présentent Pivot et JavaFX comme répondant à des besoins &#8220;légèrement&#8221; différents avant d&#8217;admettre que les deux technologies ne sont pas pour autant exclusives et que -je cite- &#8220;Pivot represents what we think Sun should have done instead of JavaFX.&#8221; (traduction: Pivot représente, à notre avis, ce qui aurait du être fait par Sun au lieu de JavaFX.)</p>
<p>A mon avis, Pivot se positionne comme une alternative sérieuse à JavaFX pour tout développeur Java travaillant sur un projet RIA. Les raisons sont assez claires:</p>
<ul>
<li> Le code est en Java (et non un langage de script à apprendre);</li>
<li> Le développeur a l&#8217;option de programmer son interface graphique déclarativement (en XML);</li>
<li> L&#8217;intégration avec les composants serveur est plus souple.</li>
</ul>
<p>Dans la page de la proposition de projet à <a href="http://wiki.apache.org/incubator/PivotProposal" target="_blank">Apache Incubator</a>, quelques comparaisons existent (formulées par les développeurs de Pivot) avec Swing, GWT et JavaFX.</p>
<p>Mon intention n&#8217;est pas de dire que Pivot est le framework de développement RIA par excellence, loin de là. A mon sens, ce n&#8217;est qu&#8217;un projet qui débute, mais qui reste prometteur.</p>
<p>Je vous invite à aller découvrir <a href="http://pivot.apache.org/demos" target="_blank">les démos</a> et <a href="http://pivot.apache.org/tutorials" target="_blank">les tutoriaux</a>, qui peuvent, je pense, être complétés par un exemple intégrant un traitement coté serveur avec Pivot. Un exemple que nous allons essayer de vous le proposer dans un prochain billet.</p>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/DqfSEbpYPWA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2010/02/07/apache-pivot-lalternative-a-javafx/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2010/02/07/apache-pivot-lalternative-a-javafx/</feedburner:origLink></item>
		<item>
		<title>L’interopérabilité de Microsoft SharePoint propulsée par CMIS</title>
		<link>http://feedproxy.google.com/~r/ip-tech-blog/~3/InHX652t6vQ/</link>
		<comments>http://www.iptech-offshore.net/blog/2009/11/20/cmis4sharepoint/#comments</comments>
		<pubDate>Fri, 20 Nov 2009 14:29:04 +0000</pubDate>
		<dc:creator>achaabouni</dc:creator>
		
		<category><![CDATA[Microsoft]]></category>

		<category><![CDATA[Technique]]></category>

		<category><![CDATA[CMIS]]></category>

		<category><![CDATA[ECM]]></category>

		<category><![CDATA[OASIS]]></category>

		<category><![CDATA[SharePoint]]></category>

		<category><![CDATA[SOA]]></category>

		<category><![CDATA[WCF]]></category>

		<guid isPermaLink="false">http://www.iptech-offshore.net/blog/?p=69</guid>
		<description><![CDATA[
L&#8217;usage des documents électroniques dans les processus de collaboration, de communication et de prise de décision ne cesse de s&#8217;intensifier: Les décisions stratégiques se basent, entre autres, sur des analyses documentées. La communication intra et inter-entreprise, qui se base sur les messages électroniques et les sites web, se doit de cibler une large audience et [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" style="border: 0pt none; float: left;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2009/11/CMIS.PNG" alt="" /></p>
<p><img class="alignleft" style="border: 0pt none; float: left;" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2009/11/moss2007.PNG" alt="" width="224" height="68" />L&#8217;usage des documents électroniques dans les processus de collaboration, de communication et de prise de décision ne cesse de s&#8217;intensifier: Les décisions stratégiques se basent, entre autres, sur des analyses documentées. La communication intra et inter-entreprise, qui se base sur les messages électroniques et les sites web, se doit de cibler une large audience et représente un media de choix pour communiquer avec les partenaires et les clients.</p>
<p><span id="more-69"></span></p>
<p>Les éditeurs logiciels se sont très tôt intéressés au domaine de la gestion des contenus de l&#8217;entreprise ou Entreprise Content Management (ECM). Au fil des années la pile logicielle (l&#8217;ensemble des logiciels inter opérant au sein d&#8217;une entreprise pour compléter un processus métier) s&#8217;est complexifiée et a vu une multitude d&#8217;intervenants opérants entre eux. Cette interopérabilité est essentielle et même stratégique dans certains cas. Elle est aussi couteuse car les éditeurs sont peu enclins à dévoiler leurs protocoles et leurs interfaces, ceci venant s&#8217;ajouter à une hétérogénéité des langages informatiques.</p>
<p>A défaut de pile ECM intégrée, les entreprises se sont donc tournées vers le développement de solution ad-hoc. Tâche qui peut s&#8217;avérer ardue et qui aboutit, généralement, à un produit non réutilisable et difficile à maintenir.</p>
<p>Ceci n&#8217;est pas sans rappeler les problèmes auxquels furent confrontés les développeurs dans les années 80 avec les systèmes de gestion des bases de données (SGBD). Ce problème a été résolu par l&#8217;adoption de standards (ODBC, SQL &#8230;) par l&#8217;ensemble des fournisseurs de SGBD. Cette solution ayant fait ses preuves, les principaux protagonistes du monde ECM, c&#8217;est-à-dire Microsoft, EMC et IBM ont élaborés un nouveau standard nommé CMIS (Content Management Interoperability Services) pour mettre fin à cette véritable tour de Babel des ECM.</p>
<p>La première version de a été soumise fin 2008, et très vite elle a suscité l&#8217;intérêt de plusieurs acteurs du marché logiciel tel Alfresco, SAP et Drupal &#8230;</p>
<p>Suite à cet engouement, l&#8217;organisme de standardisation OASIS adopta le projet pour en faire une norme de l&#8217;industrie des ECM. Dès lors, les implémentations de CMIS ont commencés à apparaitre et le premier à en proposer une fût Alfresco suivi par ECM et IBM. Mais Microsoft ne proposa aucune solution pour son produit SharePoint et ce n&#8217;est que vers février 2009, que l&#8217;on voit apparaitre un article sur MSDN traitant du sujet.</p>
<h3>Microsoft SharePoint : une success story</h3>
<p>SharePoint est devenu au fil des années un des produits phares de Microsoft (plus de 1 milliards de dollars (US$) en licences vendues durant l&#8217;année 2008). Cette large implémentation en entreprise a favorisé l&#8217;éclosion d&#8217;une communauté très active autour de SharePoint notamment à travers le programme MVP de Microsoft, Codeplex et les clubs SharePoint un peu partout dans le monde.</p>
<p>La part du marché des ECM qu&#8217;occupe Microsoft est tellement importante que plusieurs entreprises se sont spécialisées dans l&#8217;édition de connecteurs spécifiques pour des logiciels tiers interagissant avec SharePoint. Microsoft devant cet engouement pour le développement sous SharePoint a fait preuve de bonne volonté; en intégrant les protocoles de communication SharePoint dans ses initiatives Open Protocol et Open specification. Ceci a conduit à l&#8217;apparition de plusieurs produits compatibles comme par exemple l&#8217;intégration de SharePoint dans OpenOffice.org.</p>
<p>La norme CMIS est dans la droite lignée de cette ouverture de Microsoft et sa participation à l&#8217;élaboration des standards. Mais malgré ses bonnes intentions, Microsoft reste en retard par rapport à ses concurrents en ce qui concerne l&#8217;implémentation des services CMIS, menaçant ainsi son adoption en tel que standard de l&#8217;industrie.</p>
<h3>CMIS4SharePoint :</h3>
<p>S&#8217;inscrivant dans un objectif de veille technologique, CMIS4SharePoint est avant tout une étude de faisabilité. L&#8217;objectif initial était de démontrer qu&#8217;une implémentation de CMIS en utilisant uniquement le modèle objet SharePoint était possible.</p>
<p>Au fil des développements, et après quelques concessions dues à la structure même de SharePoint. Il s&#8217;est avéré que cette implémentation était opérationnelle. La décision fût alors prise de faire profiter la communauté de ce développement, c&#8217;est ainsi que IP-Tech propose la première implémentation du standard CMIS pour la plateforme SharePoint.</p>
<p style="150px" align="center"><img style="middle" src="http://www.iptech-offshore.net/blog/wp-content/uploads/2009/11/ArchiCMIS4MSSP.png" alt="Architecture deCMIS4SharePoint" width="406" height="523" /></p>
<p>L&#8217;architecture nécessite plusieurs aménagement du serveur SharePoint car il se base sur le .NET Framework 2.0 or nous utilisons WCF pour exposer les web services SOAP et REST. Ceci ne peut se faire qu&#8217;avec le .NET Framework 3.5 et un « tweak » de SharePoint pour rajouter le support de WCF.</p>
<p>Un scenario simple illustrant l&#8217;utilisation de CMIS, peut être la mise en place d&#8217;un outil pour l&#8217;utilisation offline de SharePoint. Cet outil peut s&#8217;avérer très utile pour fournir une meilleure expérience utilisateur dans l&#8217;utilisation de SharePoint, car il bénéficie de la puissance de calcul des clients lourds. Aussi on peut envisager sa réutilisation pour d&#8217;autres ECM, tel Alfresco ou FileNet. Ainsi, si l&#8217;entreprise procède à une migration elle sera presque transparente pour l&#8217;utilisateur final. Il conservera la même interface graphique et les mêmes habitudes de travail évitant le réapprentissage imposé par chaque changement d&#8217;ECM.</p>
<p align="center"><img src="http://www.iptech-offshore.net/blog/wp-content/uploads/2009/11/clientCMIS.jpg" alt="Image d'un client utilisant CMIS4SharePoint" width="435" height="276" /></p>
<p>Pour illustrer la facilité qu&#8217;apporte l&#8217;abstraction CMIS à la création des outils, nous avons développé un client qui liste le nom et l&#8217;URL de l&#8217;ensemble des bibliothèques de documents disponible dans un site SharePoint.<br />
Pour tester CMIS4SharePoint il faut commencer par créer sur une machine cliente (autre que le serveur SharePoint) un nouveau projet Console sous Visual Studio 2008. On appellera ce projet &#8220;ClientTestCMIS&#8221;.<br />
Après la création de ce projet, faites un clique droit sur le projet dans explorateur de solution, et sélectionnez &#8220;Ajouter référence de services&#8221; (Add Service Refrence).</p>
<p align="center"><img src="http://www.iptech-offshore.net/blog/wp-content/uploads/2009/11/addService.png" alt="Ajout d'une réference de services sous visual studio 2008" width="204" height="448" /></p>
<p>Dans la boite de dialogue qui s&#8217;est ouverte, saisissez l&#8217;adresse de votre service, appuyez sur &#8220;Go&#8221; puis sélectionnez le service retrouvé, comme l&#8217;illustre cette capture :</p>
<p align="center"><img src="http://www.iptech-offshore.net/blog/wp-content/uploads/2009/11/addService2.png" alt="" width="389" height="301" /></p>
<p>Renommez le Namespace du service puis validez.<br />
Maintenant Visual Studio a généré un proxy qui reprend tout les services exposé, il ne reste plus qu&#8217;a implémenter le programme dont voici le code source:</p>
<pre name="code" class="c#">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ClientTestCMIS.CMISReference;

namespace ClientTestCMIS
{
    class Program
    {
        static void Main(string[] args)
        {
            //création du proxy qui va nous permettre de consommer les services
            RepositoryServiceClient repositoryClient = new RepositoryServiceClient();

            //ouverture de la liaison
            repositoryClient.Open();

            //envoi de la requête pour l&#8217;obtention de
            //la liste des repositories (des bibliothèque de documents)disponible dans le site SharePoint
            cmisRepositoryEntryType[] repositories = repositoryClient.GetRepositories();

            //fermeture de la liaison
            repositoryClient.Close();

            //affichage du nom et de l&#8217;url de chaque bibliothèque de document
            foreach (cmisRepositoryEntryType item in repositories)
            {

                Console.WriteLine(&#8221;Name={0}\nURL={1}\n&#8221;, item.repositoryName, item.repositoryURI);

            }
        }
    }
}</pre>
<h3>Perspectives</h3>
<p>L&#8217;avenir de CMIS4SharePoint est totalement lié à l&#8217;intérêt qu&#8217;il suscitera auprès de la communauté. Du chemin reste à faire avant de pouvoir proposer une implémentation totalement opérationnelle mais ceci ne saurait tarder.</p>
<p>CMIS4SharePoint peut être utilisé comme une base pour de applications plus élaborée afin de promouvoir l&#8217;utilisation de CMIS. Car un standard qu&#8217;on n&#8217;exploite pas est un standard qui ne sert à rien. CMIS ne subira pas le même sort que les autres tentatives de standardisation des ECM et tout comme JCR il ne sera pas classé comme « une norme de plus ! » (Just another standard !).</p>
<img src="http://feeds.feedburner.com/~r/ip-tech-blog/~4/InHX652t6vQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.iptech-offshore.net/blog/2009/11/20/cmis4sharepoint/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.iptech-offshore.net/blog/2009/11/20/cmis4sharepoint/</feedburner:origLink></item>
	</channel>
</rss>
